From 602c2ca5dbd72161c952617cd3598d09cf48f0ea Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Wed, 15 Jun 2022 08:32:17 -0400 Subject: [PATCH 01/54] Add ability to have multiple flashing strategies. --- src/api/index.ts | 22 ++++++++-- .../graphql/resolvers/Firmware.resolver.ts | 22 ++++++---- src/api/src/models/BuildLogUpdate.ts | 2 +- .../src/models/BuildProgressNotification.ts | 2 +- .../BuildFlashFirmwareParams.ts | 14 ++++++ .../BuildLogUpdatePayload.ts | 3 ++ .../BuildProgressNotificationPayload.ts | 8 ++++ .../FirmwareVersionData.ts | 11 +++++ .../FlashingStrategy.ts | 16 +++++++ .../services/FlashingStrategyLocator/index.ts | 33 ++++++++++++++ .../index.ts | 43 ++++++------------- 11 files changed, 132 insertions(+), 44 deletions(-) create mode 100644 src/api/src/services/FlashingStrategyLocator/BuildFlashFirmwareParams.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/BuildLogUpdatePayload.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/BuildProgressNotificationPayload.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/FirmwareVersionData.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/index.ts rename src/api/src/services/{Firmware => PlatformioFlashingStrategy}/index.ts (96%) diff --git a/src/api/index.ts b/src/api/index.ts index 1c6e16b91..c0bdf0119 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -9,7 +9,7 @@ import getPort from 'get-port'; import { buildSchema } from 'type-graphql'; import { Container } from 'typedi'; import { ConfigToken, FirmwareParamsLoaderType, IConfig } from './src/config'; -import FirmwareService from './src/services/Firmware'; +import PlatformioFlashingStrategyService from './src/services/PlatformioFlashingStrategy'; import Platformio from './src/library/Platformio'; import FirmwareBuilder from './src/library/FirmwareBuilder'; import PubSubToken from './src/pubsub/PubSubToken'; @@ -38,6 +38,7 @@ import HttpTargetsService from './src/services/TargetsLoader/HttpTargets'; import TargetsLoader from './src/services/TargetsLoader'; import HttpUserDefinesLoader from './src/services/UserDefinesLoader/HttpUserDefinesLoader'; import GitUserDefinesLoader from './src/services/UserDefinesLoader/GitUserDefinesLoader'; +import FlashingStrategyLocatorService from './src/services/FlashingStrategyLocator'; export default class ApiServer { app: Express | undefined; @@ -61,17 +62,30 @@ export default class ApiServer { config.env, logger ); - Container.set( - FirmwareService, - new FirmwareService( + + const platformioFlashingStrategyService = + new PlatformioFlashingStrategyService( config.PATH, config.firmwaresPath, platformio, new FirmwareBuilder(platformio), pubSub, logger + ); + + Container.set( + PlatformioFlashingStrategyService, + PlatformioFlashingStrategyService + ); + + Container.set( + FlashingStrategyLocatorService, + new FlashingStrategyLocatorService( + [platformioFlashingStrategyService], + logger ) ); + Container.set( UpdatesService, new UpdatesService( diff --git a/src/api/src/graphql/resolvers/Firmware.resolver.ts b/src/api/src/graphql/resolvers/Firmware.resolver.ts index 647b8d692..9a232a9ac 100644 --- a/src/api/src/graphql/resolvers/Firmware.resolver.ts +++ b/src/api/src/graphql/resolvers/Firmware.resolver.ts @@ -11,10 +11,6 @@ import { Service } from 'typedi'; import UserDefine from '../../models/UserDefine'; import BuildFlashFirmwareInput from '../inputs/BuildFlashFirmwareInput'; import BuildFlashFirmwareResult from '../../models/BuildFlashFirmwareResult'; -import FirmwareService, { - BuildLogUpdatePayload, - BuildProgressNotificationPayload, -} from '../../services/Firmware'; import BuildProgressNotification from '../../models/BuildProgressNotification'; import PubSubTopic from '../../pubsub/enum/PubSubTopic'; import BuildLogUpdate from '../../models/BuildLogUpdate'; @@ -26,12 +22,17 @@ import TargetsLoader from '../../services/TargetsLoader'; import TargetArgs from '../args/Target'; import Device from '../../models/Device'; import GitRepository from '../inputs/GitRepositoryInput'; +import FlashingStrategyLocatorService from '../../services/FlashingStrategyLocator'; +import { BuildProgressNotificationPayload } from '../../services/FlashingStrategyLocator/BuildProgressNotificationPayload'; +import { BuildLogUpdatePayload } from '../../services/FlashingStrategyLocator/BuildLogUpdatePayload'; +import PlatformioFlashingStrategy from '../../services/PlatformioFlashingStrategy'; @Service() @Resolver() export default class FirmwareResolver { constructor( - private firmwareService: FirmwareService, + private platformioFlashingStrategy: PlatformioFlashingStrategy, + private flashingStrategyLocatorService: FlashingStrategyLocatorService, private userDefinesBuilder: UserDefinesBuilder, private targetsLoaderService: TargetsLoader ) {} @@ -57,7 +58,12 @@ export default class FirmwareResolver { @Arg('input') input: BuildFlashFirmwareInput, @Arg('gitRepository') gitRepository: GitRepository ): Promise { - return this.firmwareService.buildFlashFirmware( + const strategy = await this.flashingStrategyLocatorService.locate( + input, + gitRepository.url, + gitRepository.srcFolder + ); + return strategy.buildFlashFirmware( input, gitRepository.url, gitRepository.srcFolder @@ -67,7 +73,7 @@ export default class FirmwareResolver { @Mutation(() => ClearPlatformioCoreDirResult) async clearPlatformioCoreDir(): Promise { try { - await this.firmwareService.clearPlatformioCoreDir(); + await this.platformioFlashingStrategy.clearPlatformioCoreDir(); return new ClearPlatformioCoreDirResult(true); } catch (e) { return new ClearPlatformioCoreDirResult( @@ -80,7 +86,7 @@ export default class FirmwareResolver { @Mutation(() => ClearFirmwareFilesResult) async clearFirmwareFiles(): Promise { try { - await this.firmwareService.clearFirmwareFiles(); + await this.platformioFlashingStrategy.clearFirmwareFiles(); return new ClearFirmwareFilesResult(true); } catch (e) { return new ClearFirmwareFilesResult( diff --git a/src/api/src/models/BuildLogUpdate.ts b/src/api/src/models/BuildLogUpdate.ts index 34899a5a7..66a6d011b 100644 --- a/src/api/src/models/BuildLogUpdate.ts +++ b/src/api/src/models/BuildLogUpdate.ts @@ -1,5 +1,5 @@ import { Field, ObjectType } from 'type-graphql'; -import { BuildLogUpdatePayload } from '../services/Firmware'; +import { BuildLogUpdatePayload } from '../services/FlashingStrategyLocator/BuildLogUpdatePayload'; @ObjectType('BuildLogUpdate') export default class BuildLogUpdate implements BuildLogUpdatePayload { diff --git a/src/api/src/models/BuildProgressNotification.ts b/src/api/src/models/BuildProgressNotification.ts index 127b17417..b3749aea6 100644 --- a/src/api/src/models/BuildProgressNotification.ts +++ b/src/api/src/models/BuildProgressNotification.ts @@ -1,7 +1,7 @@ import { Field, ObjectType } from 'type-graphql'; import BuildFirmwareStep from './enum/FirmwareBuildStep'; import BuildProgressNotificationType from './enum/BuildProgressNotificationType'; -import { BuildProgressNotificationPayload } from '../services/Firmware'; +import { BuildProgressNotificationPayload } from '../services/FlashingStrategyLocator/BuildProgressNotificationPayload'; @ObjectType('BuildProgressNotification') export default class BuildProgressNotification diff --git a/src/api/src/services/FlashingStrategyLocator/BuildFlashFirmwareParams.ts b/src/api/src/services/FlashingStrategyLocator/BuildFlashFirmwareParams.ts new file mode 100644 index 000000000..f377a7fae --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/BuildFlashFirmwareParams.ts @@ -0,0 +1,14 @@ +import BuildJobType from '../../models/enum/BuildJobType'; +import UserDefinesMode from '../../models/enum/UserDefinesMode'; +import UserDefine from '../../models/UserDefine'; +import { FirmwareVersionData } from './FirmwareVersionData'; + +export interface BuildFlashFirmwareParams { + type: BuildJobType; + serialDevice?: string | undefined; + firmware: FirmwareVersionData; + target: string; + userDefinesMode: UserDefinesMode; + userDefines: UserDefine[]; + userDefinesTxt: string; +} diff --git a/src/api/src/services/FlashingStrategyLocator/BuildLogUpdatePayload.ts b/src/api/src/services/FlashingStrategyLocator/BuildLogUpdatePayload.ts new file mode 100644 index 000000000..5e064bd49 --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/BuildLogUpdatePayload.ts @@ -0,0 +1,3 @@ +export interface BuildLogUpdatePayload { + data: string; +} diff --git a/src/api/src/services/FlashingStrategyLocator/BuildProgressNotificationPayload.ts b/src/api/src/services/FlashingStrategyLocator/BuildProgressNotificationPayload.ts new file mode 100644 index 000000000..e95e6158a --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/BuildProgressNotificationPayload.ts @@ -0,0 +1,8 @@ +import BuildProgressNotificationType from '../../models/enum/BuildProgressNotificationType'; +import BuildFirmwareStep from '../../models/enum/FirmwareBuildStep'; + +export interface BuildProgressNotificationPayload { + type: BuildProgressNotificationType; + step?: BuildFirmwareStep; + message?: string; +} diff --git a/src/api/src/services/FlashingStrategyLocator/FirmwareVersionData.ts b/src/api/src/services/FlashingStrategyLocator/FirmwareVersionData.ts new file mode 100644 index 000000000..9ce885419 --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/FirmwareVersionData.ts @@ -0,0 +1,11 @@ +import FirmwareSource from '../../models/enum/FirmwareSource'; +import PullRequest from '../../models/PullRequest'; + +export interface FirmwareVersionData { + source: FirmwareSource; + gitTag: string; + gitBranch: string; + gitCommit: string; + localPath: string; + gitPullRequest: PullRequest | null; +} diff --git a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts new file mode 100644 index 000000000..6ea57a25f --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts @@ -0,0 +1,16 @@ +import BuildFlashFirmwareResult from '../../models/BuildFlashFirmwareResult'; +import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; + +export interface FlashingStrategy { + isCompatible: ( + params: BuildFlashFirmwareParams, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ) => Promise; + + buildFlashFirmware: ( + params: BuildFlashFirmwareParams, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ) => Promise; +} diff --git a/src/api/src/services/FlashingStrategyLocator/index.ts b/src/api/src/services/FlashingStrategyLocator/index.ts new file mode 100644 index 000000000..9c695162f --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/index.ts @@ -0,0 +1,33 @@ +import { Service } from 'typedi'; +import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; +import { FlashingStrategy } from './FlashingStrategy'; +import { LoggerService } from '../../logger'; + +@Service() +export default class FlashingStrategyLocatorService { + constructor( + private flashingStrategies: FlashingStrategy[], + private logger: LoggerService + ) {} + + async locate( + params: BuildFlashFirmwareParams, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ): Promise { + for (let i = 0; i < this.flashingStrategies.length; i++) { + const strategy = this.flashingStrategies[i]; + if ( + // eslint-disable-next-line no-await-in-loop + await strategy.isCompatible( + params, + gitRepositoryUrl, + gitRepositorySrcFolder + ) + ) { + return strategy; + } + } + throw new Error('No compatible flashing strategy found!'); + } +} diff --git a/src/api/src/services/Firmware/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts similarity index 96% rename from src/api/src/services/Firmware/index.ts rename to src/api/src/services/PlatformioFlashingStrategy/index.ts index f3bc22e88..2ea91fd61 100644 --- a/src/api/src/services/Firmware/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -26,25 +26,8 @@ import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; import PullRequest from '../../models/PullRequest'; import UploadType from '../../library/Platformio/Enum/UploadType'; - -interface FirmwareVersionData { - source: FirmwareSource; - gitTag: string; - gitBranch: string; - gitCommit: string; - localPath: string; - gitPullRequest: PullRequest | null; -} - -interface BuildFlashFirmwareParams { - type: BuildJobType; - serialDevice?: string | undefined; - firmware: FirmwareVersionData; - target: string; - userDefinesMode: UserDefinesMode; - userDefines: UserDefine[]; - userDefinesTxt: string; -} +import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import { FlashingStrategy } from '../FlashingStrategyLocator/FlashingStrategy'; const maskSensitiveData = (haystack: string): string => { const needles = [ @@ -85,18 +68,10 @@ const maskBuildFlashFirmwareParams = ( return result; }; -export interface BuildProgressNotificationPayload { - type: BuildProgressNotificationType; - step?: BuildFirmwareStep; - message?: string; -} - -export interface BuildLogUpdatePayload { - data: string; -} - @Service() -export default class FirmwareService { +export default class PlatformioFlashingStrategyService + implements FlashingStrategy +{ mutex: Mutex; constructor( @@ -174,6 +149,14 @@ export default class FirmwareService { return false; } + async isCompatible( + params: BuildFlashFirmwareParams, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ) { + return true; + } + async buildFlashFirmware( params: BuildFlashFirmwareParams, gitRepositoryUrl: string, From 6d610fbe6dd90639c132760d57718ca209ad0302 Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:40:45 -0400 Subject: [PATCH 02/54] Add python class to handle executing python scripts --- src/api/src/library/Python/index.ts | 93 +++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 src/api/src/library/Python/index.ts diff --git a/src/api/src/library/Python/index.ts b/src/api/src/library/Python/index.ts new file mode 100644 index 000000000..b201a5a7a --- /dev/null +++ b/src/api/src/library/Python/index.ts @@ -0,0 +1,93 @@ +/* eslint-disable no-await-in-loop */ +import fs from 'fs'; +import path from 'path'; +import Commander, { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander'; +import { LoggerService } from '../../logger'; + +export default class Python { + constructor( + private getPlatformioPath: string, + private PATH: string, + private env: NodeJS.ProcessEnv, + private logger: LoggerService + ) {} + + async runPythonScript( + script: string, + args: string[], + onUpdate: OnOutputFunc = NoOpFunc + ): Promise { + const pyExec = await this.findPythonExecutable(this.PATH); + if (pyExec === null) { + throw new Error('python executable not found'); + } + return new Commander().runCommand( + pyExec, + [script, ...args], + { + env: this.env, + }, + onUpdate + ); + } + + async checkPython(): Promise { + const pyExec = await this.findPythonExecutable(this.PATH); + return new Commander().runCommand( + pyExec, + [this.getPlatformioPath, 'check', 'python'], + { + env: this.env, + } + ); + } + + async findPythonExecutable(envPath: string): Promise { + const IS_WINDOWS = process.platform.startsWith('win'); + const exenames = IS_WINDOWS + ? ['python3.exe', 'python.exe'] + : ['python3', 'python', 'python2']; + const pythonAssertCode = [ + 'import sys', + 'assert sys.version_info >= (3, 6)', + 'print(sys.executable)', + ]; + // eslint-disable-next-line no-restricted-syntax + for (const location of envPath.split(path.delimiter)) { + // eslint-disable-next-line no-restricted-syntax + for (const exename of exenames) { + const executable = path + .normalize(path.join(location, exename)) + .replace(/"/g, ''); + try { + let res: CommandResult | null = null; + if ( + fs.existsSync(executable) && + // eslint-disable-next-line no-cond-assign + (res = await new Commander().runCommand(executable, [ + '-c', + pythonAssertCode.join(';'), + ])) + ) { + this.logger.log('testing python exec', { + executable, + stdout: res.stdout, + stderr: res.stderr, + success: res.success, + }); + if (res.success) { + return executable; + } + } + } catch (err) { + this.logger.warn('got an exception in python search', { + executable, + err, + }); + } + } + } + + throw new Error('python not found'); + } +} From 26d92545b8baeb750c74f0230bafa1e2da701fb2 Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:42:09 -0400 Subject: [PATCH 03/54] Refactor services to use the new python class --- src/api/index.ts | 21 ++- .../graphql/resolvers/Firmware.resolver.ts | 6 +- src/api/src/library/Platformio/index.ts | 170 ++++++++---------- .../PlatformioFlashingStrategy/index.ts | 66 +------ 4 files changed, 100 insertions(+), 163 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index c0bdf0119..7cc82b038 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -39,6 +39,7 @@ import TargetsLoader from './src/services/TargetsLoader'; import HttpUserDefinesLoader from './src/services/UserDefinesLoader/HttpUserDefinesLoader'; import GitUserDefinesLoader from './src/services/UserDefinesLoader/GitUserDefinesLoader'; import FlashingStrategyLocatorService from './src/services/FlashingStrategyLocator'; +import Python from './src/library/Python'; export default class ApiServer { app: Express | undefined; @@ -55,14 +56,25 @@ export default class ApiServer { Container.set([{ id: PubSubToken, value: pubSub }]); Container.set([{ id: LoggerToken, value: logger }]); - const platformio = new Platformio( + const python = new Python( config.getPlatformioPath, - config.platformioStateTempStoragePath, config.PATH, config.env, logger ); + Container.set(Python, python); + + const platformio = new Platformio( + config.getPlatformioPath, + config.platformioStateTempStoragePath, + config.env, + logger, + python + ); + + Container.set(Platformio, platformio); + const platformioFlashingStrategyService = new PlatformioFlashingStrategyService( config.PATH, @@ -70,12 +82,13 @@ export default class ApiServer { platformio, new FirmwareBuilder(platformio), pubSub, - logger + logger, + python ); Container.set( PlatformioFlashingStrategyService, - PlatformioFlashingStrategyService + platformioFlashingStrategyService ); Container.set( diff --git a/src/api/src/graphql/resolvers/Firmware.resolver.ts b/src/api/src/graphql/resolvers/Firmware.resolver.ts index 9a232a9ac..aa1ff4cc5 100644 --- a/src/api/src/graphql/resolvers/Firmware.resolver.ts +++ b/src/api/src/graphql/resolvers/Firmware.resolver.ts @@ -26,6 +26,7 @@ import FlashingStrategyLocatorService from '../../services/FlashingStrategyLocat import { BuildProgressNotificationPayload } from '../../services/FlashingStrategyLocator/BuildProgressNotificationPayload'; import { BuildLogUpdatePayload } from '../../services/FlashingStrategyLocator/BuildLogUpdatePayload'; import PlatformioFlashingStrategy from '../../services/PlatformioFlashingStrategy'; +import Platformio from '../../library/Platformio'; @Service() @Resolver() @@ -34,7 +35,8 @@ export default class FirmwareResolver { private platformioFlashingStrategy: PlatformioFlashingStrategy, private flashingStrategyLocatorService: FlashingStrategyLocatorService, private userDefinesBuilder: UserDefinesBuilder, - private targetsLoaderService: TargetsLoader + private targetsLoaderService: TargetsLoader, + private platformio: Platformio ) {} @Query(() => [Device]) @@ -73,7 +75,7 @@ export default class FirmwareResolver { @Mutation(() => ClearPlatformioCoreDirResult) async clearPlatformioCoreDir(): Promise { try { - await this.platformioFlashingStrategy.clearPlatformioCoreDir(); + await this.platformio.clearPlatformioCoreDir(); return new ClearPlatformioCoreDirResult(true); } catch (e) { return new ClearPlatformioCoreDirResult( diff --git a/src/api/src/library/Platformio/index.ts b/src/api/src/library/Platformio/index.ts index 1f094e96f..303642fa1 100644 --- a/src/api/src/library/Platformio/index.ts +++ b/src/api/src/library/Platformio/index.ts @@ -1,11 +1,12 @@ -/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ -/* eslint-disable no-await-in-loop */ import fs from 'fs'; import path from 'path'; import child_process from 'child_process'; +import rimraf from 'rimraf'; +import * as os from 'os'; import Commander, { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander'; import { LoggerService } from '../../logger'; import UploadType from './Enum/UploadType'; +import Python from '../Python'; interface PlatformioCoreState { core_version: string; @@ -35,119 +36,39 @@ export default class Platformio { constructor( private getPlatformioPath: string, private stateTempStoragePath: string, - private PATH: string, private env: NodeJS.ProcessEnv, - private logger: LoggerService + private logger: LoggerService, + private python: Python ) {} async install(onUpdate: OnOutputFunc = NoOpFunc): Promise { - const pyExec = await this.findPythonExecutable(this.PATH); - if (pyExec === null) { - throw new Error('python executable not found'); - } - return new Commander().runCommand( - pyExec, - [this.getPlatformioPath], - { - env: this.env, - }, - onUpdate - ); + return this.python.runPythonScript(this.getPlatformioPath, [], onUpdate); } async checkCore(): Promise { - const pyExec = await this.findPythonExecutable(this.PATH); - return new Commander().runCommand( - pyExec, - [this.getPlatformioPath, 'check', 'core'], - { - env: this.env, - } - ); - } - - async checkPython(): Promise { - const pyExec = await this.findPythonExecutable(this.PATH); - return new Commander().runCommand( - pyExec, - [this.getPlatformioPath, 'check', 'python'], - { - env: this.env, - } - ); + const cmdArgs = ['check', 'core']; + return this.python.runPythonScript(this.getPlatformioPath, cmdArgs); } async verifyDependencies(): Promise { const pio = await this.checkCore(); - const py = await this.checkPython(); + const py = await this.python.checkPython(); return pio.success && py.success; } - async findPythonExecutable(envPath: string): Promise { - const IS_WINDOWS = process.platform.startsWith('win'); - const exenames = IS_WINDOWS - ? ['python3.exe', 'python.exe'] - : ['python3', 'python', 'python2']; - const pythonAssertCode = [ - 'import sys', - 'assert sys.version_info >= (3, 6)', - 'print(sys.executable)', - ]; - // eslint-disable-next-line no-restricted-syntax - for (const location of envPath.split(path.delimiter)) { - // eslint-disable-next-line no-restricted-syntax - for (const exename of exenames) { - const executable = path - .normalize(path.join(location, exename)) - .replace(/"/g, ''); - try { - let res: CommandResult | null = null; - if ( - fs.existsSync(executable) && - // eslint-disable-next-line no-cond-assign - (res = await new Commander().runCommand(executable, [ - '-c', - pythonAssertCode.join(';'), - ])) - ) { - this.logger.log('testing python exec', { - executable, - stdout: res.stdout, - stderr: res.stderr, - success: res.success, - }); - if (res.success) { - return executable; - } - } - } catch (err) { - this.logger.warn('got an exception in python search', { - executable, - err, - }); - } - } - } - - throw new Error('python not found'); - } - async getPlatformioState(): Promise { const statePath = path.join( this.stateTempStoragePath, `core-dump-${Math.round(Math.random() * 1000000)}.json` ); - const pyExec = await this.findPythonExecutable(this.PATH!); - const cmdArgs = [ + + const cmdArgs = ['check', 'core', '--dump-state', statePath]; + + const result = await this.python.runPythonScript( this.getPlatformioPath, - 'check', - 'core', - '--dump-state', - statePath, - ]; - const result = await new Commander().runCommand(pyExec, cmdArgs, { - env: this.env, - }); + cmdArgs + ); + if (!result.success) { throw new Error( `failed to get state json: ${result.stderr} ${result.stdout}` @@ -256,4 +177,63 @@ export default class Platformio { onUpdate ); } + + async removePlatformioDir(): Promise { + const dotPlatformio = path.join(os.homedir(), '.platformio'); + const statResult = await fs.promises.lstat(dotPlatformio); + if (!statResult.isDirectory()) { + return Promise.resolve(); + } + return new Promise((resolve, reject) => { + rimraf(dotPlatformio, (err) => { + if (err) { + reject(); + } else { + resolve(); + } + }); + }); + } + + async clearPlatformioUsingCoreState(): Promise { + const platformioStateJson = await this.getPlatformioState(); + if ( + platformioStateJson.core_dir === undefined || + platformioStateJson.core_dir.length === 0 || + platformioStateJson.core_dir.indexOf('.platformio') === -1 + ) { + throw new Error(`core_dir is invalid: ${platformioStateJson.core_dir}`); + } + + const statResult = await fs.promises.lstat(platformioStateJson.core_dir); + if (!statResult.isDirectory()) { + throw new Error(`core_dir is invalid: ${platformioStateJson.core_dir}`); + } + + return new Promise((resolve, reject) => { + rimraf(platformioStateJson.core_dir, (err) => { + if (err) { + reject(); + } else { + resolve(); + } + }); + }); + } + + async clearPlatformioCoreDir(): Promise { + try { + await this.clearPlatformioUsingCoreState(); + } catch (e) { + this.logger?.error( + 'failed to clear platformio files using platformio state', + undefined, + { + err: e, + } + ); + return this.removePlatformioDir(); + } + return Promise.resolve(); + } } diff --git a/src/api/src/services/PlatformioFlashingStrategy/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts index 2ea91fd61..14c2f74df 100644 --- a/src/api/src/services/PlatformioFlashingStrategy/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -24,10 +24,10 @@ import Platformio from '../../library/Platformio'; import FirmwareBuilder from '../../library/FirmwareBuilder'; import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; -import PullRequest from '../../models/PullRequest'; import UploadType from '../../library/Platformio/Enum/UploadType'; import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; import { FlashingStrategy } from '../FlashingStrategyLocator/FlashingStrategy'; +import Python from '../../library/Python'; const maskSensitiveData = (haystack: string): string => { const needles = [ @@ -80,7 +80,8 @@ export default class PlatformioFlashingStrategyService private platformio: Platformio, private builder: FirmwareBuilder, private pubSub: PubSubEngine, - private logger: LoggerService + private logger: LoggerService, + private python: Python ) { this.mutex = new Mutex(); } @@ -192,7 +193,7 @@ export default class PlatformioFlashingStrategyService ); } - const pythonCheck = await this.platformio.checkPython(); + const pythonCheck = await this.python.checkPython(); if (!pythonCheck.success) { this.logger?.error('python dependency check error', undefined, { stderr: pythonCheck.stderr, @@ -568,63 +569,4 @@ export default class PlatformioFlashingStrategyService } await Promise.all(files.map((item) => rmrf(item))); } - - async removePlatformioDir(): Promise { - const dotPlatformio = path.join(os.homedir(), '.platformio'); - const statResult = await fs.promises.lstat(dotPlatformio); - if (!statResult.isDirectory()) { - return Promise.resolve(); - } - return new Promise((resolve, reject) => { - rimraf(dotPlatformio, (err) => { - if (err) { - reject(); - } else { - resolve(); - } - }); - }); - } - - async clearPlatformioUsingCoreState(): Promise { - const platformioStateJson = await this.platformio.getPlatformioState(); - if ( - platformioStateJson.core_dir === undefined || - platformioStateJson.core_dir.length === 0 || - platformioStateJson.core_dir.indexOf('.platformio') === -1 - ) { - throw new Error(`core_dir is invalid: ${platformioStateJson.core_dir}`); - } - - const statResult = await fs.promises.lstat(platformioStateJson.core_dir); - if (!statResult.isDirectory()) { - throw new Error(`core_dir is invalid: ${platformioStateJson.core_dir}`); - } - - return new Promise((resolve, reject) => { - rimraf(platformioStateJson.core_dir, (err) => { - if (err) { - reject(); - } else { - resolve(); - } - }); - }); - } - - async clearPlatformioCoreDir(): Promise { - try { - await this.clearPlatformioUsingCoreState(); - } catch (e) { - this.logger?.error( - 'failed to clear platformio files using platformio state', - undefined, - { - err: e, - } - ); - return this.removePlatformioDir(); - } - return Promise.resolve(); - } } From 5f80d250e05dc57afc99353334549eda10bb08de Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Tue, 21 Jun 2022 13:59:03 -0400 Subject: [PATCH 04/54] Include clearFirmwareFiles as part of FlashingStrategy --- src/api/index.ts | 5 ----- src/api/src/graphql/resolvers/Firmware.resolver.ts | 4 +--- .../services/FlashingStrategyLocator/FlashingStrategy.ts | 2 ++ src/api/src/services/FlashingStrategyLocator/index.ts | 8 ++++++++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index 7cc82b038..fa8e455c8 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -86,11 +86,6 @@ export default class ApiServer { python ); - Container.set( - PlatformioFlashingStrategyService, - platformioFlashingStrategyService - ); - Container.set( FlashingStrategyLocatorService, new FlashingStrategyLocatorService( diff --git a/src/api/src/graphql/resolvers/Firmware.resolver.ts b/src/api/src/graphql/resolvers/Firmware.resolver.ts index aa1ff4cc5..4ae06af54 100644 --- a/src/api/src/graphql/resolvers/Firmware.resolver.ts +++ b/src/api/src/graphql/resolvers/Firmware.resolver.ts @@ -25,14 +25,12 @@ import GitRepository from '../inputs/GitRepositoryInput'; import FlashingStrategyLocatorService from '../../services/FlashingStrategyLocator'; import { BuildProgressNotificationPayload } from '../../services/FlashingStrategyLocator/BuildProgressNotificationPayload'; import { BuildLogUpdatePayload } from '../../services/FlashingStrategyLocator/BuildLogUpdatePayload'; -import PlatformioFlashingStrategy from '../../services/PlatformioFlashingStrategy'; import Platformio from '../../library/Platformio'; @Service() @Resolver() export default class FirmwareResolver { constructor( - private platformioFlashingStrategy: PlatformioFlashingStrategy, private flashingStrategyLocatorService: FlashingStrategyLocatorService, private userDefinesBuilder: UserDefinesBuilder, private targetsLoaderService: TargetsLoader, @@ -88,7 +86,7 @@ export default class FirmwareResolver { @Mutation(() => ClearFirmwareFilesResult) async clearFirmwareFiles(): Promise { try { - await this.platformioFlashingStrategy.clearFirmwareFiles(); + await this.flashingStrategyLocatorService.clearFirmwareFiles(); return new ClearFirmwareFilesResult(true); } catch (e) { return new ClearFirmwareFilesResult( diff --git a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts index 6ea57a25f..2c31043fe 100644 --- a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts +++ b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts @@ -13,4 +13,6 @@ export interface FlashingStrategy { gitRepositoryUrl: string, gitRepositorySrcFolder: string ) => Promise; + + clearFirmwareFiles(): Promise; } diff --git a/src/api/src/services/FlashingStrategyLocator/index.ts b/src/api/src/services/FlashingStrategyLocator/index.ts index 9c695162f..a36adff86 100644 --- a/src/api/src/services/FlashingStrategyLocator/index.ts +++ b/src/api/src/services/FlashingStrategyLocator/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-await-in-loop */ import { Service } from 'typedi'; import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; import { FlashingStrategy } from './FlashingStrategy'; @@ -30,4 +31,11 @@ export default class FlashingStrategyLocatorService { } throw new Error('No compatible flashing strategy found!'); } + + async clearFirmwareFiles(): Promise { + for (let i = 0; i < this.flashingStrategies.length; i++) { + const strategy = this.flashingStrategies[i]; + await strategy.clearFirmwareFiles(); + } + } } From 189339d4eed33138033cc8a1a5113943e8728be5 Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Mon, 1 Aug 2022 18:02:54 -0400 Subject: [PATCH 05/54] Binary Flashing Strategy --- package.json | 1 + src/api/index.ts | 98 +-- src/api/src/config/index.ts | 1 + .../graphql/resolvers/Firmware.resolver.ts | 16 +- .../src/library/ConfigureFirmware/Config.ts | 11 + .../src/library/ConfigureFirmware/Domain.ts | 10 + .../src/library/ConfigureFirmware/Firmware.ts | 4 + .../ConfigureFirmware/FirmwareFiles.ts | 8 + .../src/library/ConfigureFirmware/Options.ts | 19 + .../src/library/ConfigureFirmware/Targets.ts | 19 + .../src/library/ConfigureFirmware/index.ts | 282 +++++++ src/api/src/library/Melody/index.ts | 83 +++ .../services/BinaryFlashingStrategy/index.ts | 685 ++++++++++++++++++ .../FlashingStrategy.ts | 28 +- .../services/FlashingStrategyLocator/index.ts | 5 +- src/api/src/services/GitHubClient/index.ts | 9 + .../PlatformioFlashingStrategy/index.ts | 31 +- src/main.dev.ts | 1 + yarn.lock | 5 + 19 files changed, 1262 insertions(+), 54 deletions(-) create mode 100644 src/api/src/library/ConfigureFirmware/Config.ts create mode 100644 src/api/src/library/ConfigureFirmware/Domain.ts create mode 100644 src/api/src/library/ConfigureFirmware/Firmware.ts create mode 100644 src/api/src/library/ConfigureFirmware/FirmwareFiles.ts create mode 100644 src/api/src/library/ConfigureFirmware/Options.ts create mode 100644 src/api/src/library/ConfigureFirmware/Targets.ts create mode 100644 src/api/src/library/ConfigureFirmware/index.ts create mode 100644 src/api/src/library/Melody/index.ts create mode 100644 src/api/src/services/BinaryFlashingStrategy/index.ts diff --git a/package.json b/package.json index 39385f144..862d6ea06 100644 --- a/package.json +++ b/package.json @@ -307,6 +307,7 @@ "@octokit/rest": "^19.0.4", "apollo-server-express": "^3.9.0", "autosuggest-highlight": "^3.2.1", + "bluejay-rtttl-parse": "^2.0.2", "class-validator": "^0.13.2", "cross-spawn": "^7.0.3", "electron-debug": "^3.2.0", diff --git a/src/api/index.ts b/src/api/index.ts index fa8e455c8..b03474bec 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -8,6 +8,7 @@ import * as http from 'http'; import getPort from 'get-port'; import { buildSchema } from 'type-graphql'; import { Container } from 'typedi'; +import path from 'path'; import { ConfigToken, FirmwareParamsLoaderType, IConfig } from './src/config'; import PlatformioFlashingStrategyService from './src/services/PlatformioFlashingStrategy'; import Platformio from './src/library/Platformio'; @@ -40,6 +41,7 @@ import HttpUserDefinesLoader from './src/services/UserDefinesLoader/HttpUserDefi import GitUserDefinesLoader from './src/services/UserDefinesLoader/GitUserDefinesLoader'; import FlashingStrategyLocatorService from './src/services/FlashingStrategyLocator'; import Python from './src/library/Python'; +import BinaryFlashingStrategyService from './src/services/BinaryFlashingStrategy'; export default class ApiServer { app: Express | undefined; @@ -75,25 +77,6 @@ export default class ApiServer { Container.set(Platformio, platformio); - const platformioFlashingStrategyService = - new PlatformioFlashingStrategyService( - config.PATH, - config.firmwaresPath, - platformio, - new FirmwareBuilder(platformio), - pubSub, - logger, - python - ); - - Container.set( - FlashingStrategyLocatorService, - new FlashingStrategyLocatorService( - [platformioFlashingStrategyService], - logger - ) - ); - Container.set( UpdatesService, new UpdatesService( @@ -127,44 +110,71 @@ export default class ApiServer { Container.set(DeviceService, deviceService); + let userDefinesBuilder: UserDefinesBuilder; if (config.userDefinesLoader === FirmwareParamsLoaderType.Git) { - Container.set( - UserDefinesBuilder, - new UserDefinesBuilder( - new GitUserDefinesLoader( - logger, - config.PATH, - config.userDefinesStoragePath - ), - deviceService - ) + userDefinesBuilder = new UserDefinesBuilder( + new GitUserDefinesLoader( + logger, + config.PATH, + config.userDefinesStoragePath + ), + deviceService ); } else if (config.userDefinesLoader === FirmwareParamsLoaderType.Http) { - Container.set( - UserDefinesBuilder, - new UserDefinesBuilder(new HttpUserDefinesLoader(logger), deviceService) + userDefinesBuilder = new UserDefinesBuilder( + new HttpUserDefinesLoader(logger), + deviceService ); + } else { + throw new Error('FirmwareTargetsLoaderType is not set'); } + Container.set(UserDefinesBuilder, userDefinesBuilder); + + let targetsLoader: TargetsLoader; if (config.targetsLoader === FirmwareParamsLoaderType.Git) { - Container.set( - TargetsLoader, - new GitTargetsService( - logger, - deviceService, - config.PATH, - config.targetsStoragePath - ) + targetsLoader = new GitTargetsService( + logger, + deviceService, + config.PATH, + config.targetsStoragePath ); } else if (config.targetsLoader === FirmwareParamsLoaderType.Http) { - Container.set( - TargetsLoader, - new HttpTargetsService(logger, deviceService) - ); + targetsLoader = new HttpTargetsService(logger, deviceService); } else { throw new Error('FirmwareTargetsLoaderType is not set'); } + Container.set(TargetsLoader, targetsLoader); + + const platformioFlashingStrategyService = + new PlatformioFlashingStrategyService( + config.PATH, + config.firmwaresPath, + platformio, + new FirmwareBuilder(platformio), + pubSub, + logger, + python, + userDefinesBuilder, + targetsLoader + ); + + const binaryFlashingStrategyService = new BinaryFlashingStrategyService( + config.PATH, + path.join(config.userDataPath, 'firmwares', 'binary'), + pubSub, + logger + ); + + Container.set( + FlashingStrategyLocatorService, + new FlashingStrategyLocatorService( + [binaryFlashingStrategyService, platformioFlashingStrategyService], + logger + ) + ); + Container.set(LuaService, new LuaService(logger)); } diff --git a/src/api/src/config/index.ts b/src/api/src/config/index.ts index 55da95fad..7a0bd90fe 100644 --- a/src/api/src/config/index.ts +++ b/src/api/src/config/index.ts @@ -22,6 +22,7 @@ export interface IConfig { targetsStoragePath: string; userDefinesLoader: FirmwareParamsLoaderType; userDefinesStoragePath: string; + userDataPath: string; } export const ConfigToken = new Token('CONFIG_TOKEN'); diff --git a/src/api/src/graphql/resolvers/Firmware.resolver.ts b/src/api/src/graphql/resolvers/Firmware.resolver.ts index 4ae06af54..c91fa3a4d 100644 --- a/src/api/src/graphql/resolvers/Firmware.resolver.ts +++ b/src/api/src/graphql/resolvers/Firmware.resolver.ts @@ -42,7 +42,12 @@ export default class FirmwareResolver { @Args() args: TargetArgs, @Arg('gitRepository') gitRepository: GitRepository ): Promise { - return this.targetsLoaderService.loadTargetsList(args, gitRepository); + const strategy = await this.flashingStrategyLocatorService.locate( + args, + gitRepository.url, + gitRepository.srcFolder + ); + return strategy.availableFirmwareTargets(args, gitRepository); } @Query(() => [UserDefine]) @@ -50,7 +55,12 @@ export default class FirmwareResolver { @Args() args: TargetDeviceOptionsArgs, @Arg('gitRepository') gitRepository: GitRepository ): Promise { - return this.userDefinesBuilder.build(args, gitRepository); + const strategy = await this.flashingStrategyLocatorService.locate( + args, + gitRepository.url, + gitRepository.srcFolder + ); + return strategy.targetDeviceOptions(args, gitRepository); } @Mutation(() => BuildFlashFirmwareResult) @@ -59,7 +69,7 @@ export default class FirmwareResolver { @Arg('gitRepository') gitRepository: GitRepository ): Promise { const strategy = await this.flashingStrategyLocatorService.locate( - input, + input.firmware, gitRepository.url, gitRepository.srcFolder ); diff --git a/src/api/src/library/ConfigureFirmware/Config.ts b/src/api/src/library/ConfigureFirmware/Config.ts new file mode 100644 index 000000000..7a4e932fe --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/Config.ts @@ -0,0 +1,11 @@ +export interface Config { + product_name: string; + lua_name?: string; + layout_file?: string; + upload_methods: string[]; + platform: string; + firmware: string; + features?: string[]; + prior_target_name: string; + overlay?: { [key: string]: string }; +} diff --git a/src/api/src/library/ConfigureFirmware/Domain.ts b/src/api/src/library/ConfigureFirmware/Domain.ts new file mode 100644 index 000000000..b7f44383c --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/Domain.ts @@ -0,0 +1,10 @@ +enum Domain { + AU915 = 0, + FCC915 = 1, + EU868 = 2, + IN866 = 3, + AU433 = 4, + EU433 = 5, +} + +export default Domain; diff --git a/src/api/src/library/ConfigureFirmware/Firmware.ts b/src/api/src/library/ConfigureFirmware/Firmware.ts new file mode 100644 index 000000000..a19d3af19 --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/Firmware.ts @@ -0,0 +1,4 @@ +export interface Firmware { + data: Uint8Array; + address: number; +} diff --git a/src/api/src/library/ConfigureFirmware/FirmwareFiles.ts b/src/api/src/library/ConfigureFirmware/FirmwareFiles.ts new file mode 100644 index 000000000..3e6c1a651 --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/FirmwareFiles.ts @@ -0,0 +1,8 @@ +import { Firmware } from './Firmware'; + +export interface FirmwareFiles { + bootloader?: Firmware; + partitions?: Firmware; + boot_app0?: Firmware; + firmware?: Firmware; +} diff --git a/src/api/src/library/ConfigureFirmware/Options.ts b/src/api/src/library/ConfigureFirmware/Options.ts new file mode 100644 index 000000000..eb6faaeb3 --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/Options.ts @@ -0,0 +1,19 @@ +import Domain from './Domain'; + +export interface Options { + uid?: number[]; + 'wifi-on-interval'?: number | undefined; + 'wifi-ssid'?: string | undefined; + 'wifi-password'?: string | undefined; + 'rcvr-uart-baud'?: number | undefined; + 'rcvr-invert-tx'?: boolean | undefined; + 'lock-on-first-connection'?: boolean | undefined; + 'tlm-interval'?: number | undefined; + 'fan-runtime'?: number | undefined; + 'uart-inverted'?: boolean | undefined; + 'unlock-higher-power'?: boolean | undefined; + 'r9mm-mini-sbus'?: boolean | undefined; + beeptype?: number | undefined; + domain?: Domain | undefined; + melody?: number[][] | undefined; +} diff --git a/src/api/src/library/ConfigureFirmware/Targets.ts b/src/api/src/library/ConfigureFirmware/Targets.ts new file mode 100644 index 000000000..195072dc4 --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/Targets.ts @@ -0,0 +1,19 @@ +import { Config } from './Config'; + +export type Targets = { + [key: string]: { + name: string; + tx_2400: { + [key: string]: Config; + }; + rx_2400: { + [key: string]: Config; + }; + tx_900: { + [key: string]: Config; + }; + rx_900: { + [key: string]: Config; + }; + }; +}; diff --git a/src/api/src/library/ConfigureFirmware/index.ts b/src/api/src/library/ConfigureFirmware/index.ts new file mode 100644 index 000000000..7f2c28680 --- /dev/null +++ b/src/api/src/library/ConfigureFirmware/index.ts @@ -0,0 +1,282 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-bitwise */ +/* eslint-disable import/prefer-default-export */ +import fs from 'fs'; +import { Config } from './Config'; +import { Firmware } from './Firmware'; +import { FirmwareFiles } from './FirmwareFiles'; +import { Options } from './Options'; + +export default class Configure { + static #MAGIC = new Uint8Array([ + 0xbe, 0xef, 0xba, 0xbe, 0xca, 0xfe, 0xf0, 0x0d, + ]); + + static #find_patch_location(binary: Uint8Array) { + return binary.findIndex((_, i, a) => { + let j = 0; + while (j < Configure.#MAGIC.length && a[i + j] === Configure.#MAGIC[j]) { + j++; + } + return j === Configure.#MAGIC.length; + }); + } + + static #write32(binary: Uint8Array, pos: number, val: number) { + if (val !== undefined) { + binary[pos + 0] = (val >> 0) & 0xff; + binary[pos + 1] = (val >> 8) & 0xff; + binary[pos + 2] = (val >> 16) & 0xff; + binary[pos + 3] = (val >> 24) & 0xff; + } + return pos + 4; + } + + static #patch_buzzer(binary: Uint8Array, pos: number, options: Options) { + binary[pos] = options.beeptype ?? 2; + pos += 1; + for (let i = 0; i < 32 * 4; i++) { + binary[pos + i] = 0; + } + const { melody } = options; + if (melody) { + for (let i = 0; i < melody.length; i++) { + binary[pos + i * 4 + 0] = melody[i][0] & 0xff; + binary[pos + i * 4 + 1] = (melody[i][0] >> 8) & 0xff; + binary[pos + i * 4 + 2] = melody[i][1] & 0xff; + binary[pos + i * 4 + 3] = (melody[i][1] >> 8) & 0xff; + } + } + pos += 32 * 4; + return pos; + } + + static #patch_tx_params(binary: Uint8Array, pos: number, options: Options) { + pos = this.#write32(binary, pos, options['tlm-interval'] ?? 240); + pos = this.#write32(binary, pos, options['fan-runtime'] ?? 30); + let val = binary[pos]; + if (options['uart-inverted'] !== undefined) { + val &= ~1; + val |= options['uart-inverted'] ? 1 : 0; + } + if (options['unlock-higher-power'] !== undefined) { + val &= ~2; + val |= options['unlock-higher-power'] ? 2 : 0; + } + binary[pos] = val; + return pos + 1; + } + + static #patch_rx_params(binary: Uint8Array, pos: number, options: Options) { + pos = this.#write32(binary, pos, options['rcvr-uart-baud'] ?? 400000); + let val = binary[pos]; + if (options['rcvr-invert-tx'] !== undefined) { + val &= ~1; + val |= options['rcvr-invert-tx'] ? 1 : 0; + } + if (options['lock-on-first-connection'] !== undefined) { + val &= ~2; + val |= options['lock-on-first-connection'] ? 2 : 0; + } + if (options['r9mm-mini-sbus'] !== undefined) { + val &= ~4; + val |= options['r9mm-mini-sbus'] ? 4 : 0; + } + binary[pos] = val; + return pos + 1; + } + + static #configureSTM32( + binary: Uint8Array, + deviceType: string, + radioType: string, + options: Options + ) { + let pos = this.#find_patch_location(binary); + if (pos === -1) + throw new Error( + 'Configuration magic not found in firmware file. Is this a 3.x firmware?' + ); + + pos += 8; // Skip magic + const version = (binary[pos] + binary[pos + 1]) << 8; + pos += 2; // Skip version + if (version === 0) { + pos += 1; // Skip the (old) hardware flag + } + + // Poke in the domain + if (radioType === 'sx127x' && options.domain) { + binary[pos] = options.domain; + } + pos += 1; + + // Poke in the UID (if there is one) + if (options.uid) { + binary[pos] = 1; + for (let i = 0; i < 6; i++) { + binary[pos + 1 + i] = options.uid[i]; + } + } else { + binary[pos] = 0; + } + pos += 7; + + if (deviceType === 'TX') { + // TX target + pos = this.#patch_tx_params(binary, pos, options); + if (options.beeptype) { + // Has a Buzzer + pos = this.#patch_buzzer(binary, pos, options); + } + } else if (deviceType === 'RX') { + // RX target + pos = this.#patch_rx_params(binary, pos, options); + } + + return binary; + } + + static #fetch_file = async ( + file: string, + address: number, + transform = (e: Uint8Array) => e + ): Promise => { + const buffer = await fs.promises.readFile(file); + const dataArray = new Uint8Array(buffer); + const data = transform(dataArray); + return { data, address }; + }; + + static #findFirmwareEnd = (binary: Uint8Array, config: Config) => { + let pos = 0x0; + if (config.platform === 'esp8285') pos = 0x1000; + if (binary[pos] !== 0xe9) + throw new Error( + 'The file provided does not the right magic for a firmware file!' + ); + let segments = binary[pos + 1]; + if (config.platform === 'esp32') pos = 24; + else pos = 0x1008; + while (segments--) { + const size = + binary[pos + 4] + + (binary[pos + 5] << 8) + + (binary[pos + 6] << 16) + + (binary[pos + 7] << 24); + pos += 8 + size; + } + pos = (pos + 16) & ~15; + if (config.platform === 'esp32') pos += 32; + return pos; + }; + + static #appendArray = (arr1: Uint8Array, arr2: Uint8Array) => { + const c = new Uint8Array(arr1.length + arr2.length); + c.set(arr1, 0); + c.set(arr2, arr1.length); + return c; + }; + + static #ui8ToBstr = (u8Array: Uint8Array) => { + const len = u8Array.length; + let bStr = ''; + for (let i = 0; i < len; i++) { + bStr += String.fromCharCode(u8Array[i]); + } + return bStr; + }; + + static #bstrToUi8 = (bStr: string) => { + const len = bStr.length; + const u8array = new Uint8Array(len); + for (let i = 0; i < len; i++) { + u8array[i] = bStr.charCodeAt(i); + } + return u8array; + }; + + static #configureESP = ( + binary: Uint8Array, + config: Config, + options: Options + ) => { + const end = this.#findFirmwareEnd(binary, config); + binary = binary.slice(0, end); + binary = this.#appendArray( + binary, + this.#bstrToUi8(config.product_name.padEnd(128, '\x00')) + ); + binary = this.#appendArray( + binary, + this.#bstrToUi8(config.lua_name.padEnd(16, '\x00')) + ); + binary = this.#appendArray( + binary, + this.#bstrToUi8(JSON.stringify(options).padEnd(512, '\x00')) + ); + return binary; + }; + + static getFirmware = async ( + deviceType: string, + radioType: string, + config: Config, + options: Options, + folder: string, + fcclbt: string + ): Promise => { + const firmware = `${folder}/${fcclbt}/${config.firmware}/firmware.bin`; + if (config.platform === 'stm32') { + const entry = await this.#fetch_file(firmware, 0, (bin) => + this.#configureSTM32(bin, deviceType, radioType, options) + ); + return { firmware: { data: entry.data, address: 0 } }; + } + const hardwareLayoutFile = await this.#fetch_file( + `${folder}/hardware/${deviceType}/${config.layout_file}`, + 0 + ); + const firmwareFiles: FirmwareFiles = {}; + if (config.platform === 'esp32') { + firmwareFiles.bootloader = await Promise.any([ + this.#fetch_file(`${folder}/bootloader_dio_40m.bin`, 0x1000), + this.#fetch_file(`${folder}/bootloader_qio_40m.bin`, 0x1000), + ]); + firmwareFiles.partitions = await this.#fetch_file( + `${folder}/partitions.bin`, + 0x8000 + ); + firmwareFiles.boot_app0 = await this.#fetch_file( + `${folder}/boot_app0.bin`, + 0xe000 + ); + firmwareFiles.firmware = await this.#fetch_file( + firmware, + 0x10000, + (bin) => Configure.#configureESP(bin, config, options) + ); + } else if (config.platform === 'esp8285') { + firmwareFiles.firmware = await this.#fetch_file(firmware, 0x0, (bin) => + Configure.#configureESP(bin, config, options) + ); + } + + if (config.overlay) { + config.overlay = {}; + } + const hardwareLayoutData = this.#bstrToUi8( + JSON.stringify({ + ...JSON.parse(this.#ui8ToBstr(hardwareLayoutFile.data)), + ...config.overlay, + }) + ); + if (firmwareFiles.firmware) { + firmwareFiles.firmware.data = await this.#appendArray( + firmwareFiles.firmware.data, + this.#appendArray(hardwareLayoutData, new Uint8Array([0])) + ); + } + return firmwareFiles; + }; +} diff --git a/src/api/src/library/Melody/index.ts b/src/api/src/library/Melody/index.ts new file mode 100644 index 000000000..29ac04ec0 --- /dev/null +++ b/src/api/src/library/Melody/index.ts @@ -0,0 +1,83 @@ +/* eslint-disable import/prefer-default-export */ +import { Rtttl } from 'bluejay-rtttl-parse'; + +export class MelodyParser { + static #NOTES = [ + 'A', + 'A#', + 'B', + 'C', + 'C#', + 'D', + 'D#', + 'E', + 'F', + 'F#', + 'G', + 'G#', + ]; + + static #getFrequency(note: string, transposeBy = 0, A4 = 440) { + // example note: A#5, meaning: 5th octave A sharp + const octave = note.length === 3 ? Number(note[2]) : Number(note[1]); + let keyNumber = this.#NOTES.indexOf(note.slice(0, -1)); + if (keyNumber < 3) { + keyNumber = keyNumber + 12 + (octave - 1) * 12 + 1; + } else { + keyNumber = keyNumber + (octave - 1) * 12 + 1; + } + keyNumber += transposeBy; + return Math.floor(A4 * 2 ** ((keyNumber - 49) / 12.0)); + } + + static #getDurationInMs(bpm: number, duration: number) { + return Math.floor((1000 * ((60 * 4) / bpm)) / duration); + } + + static #parseMelody( + melodyString: string, + bpm = 120, + transposeBySemitones = 0 + ) { + // parse string to python list + const tokenizedNotes = melodyString.split(' '); + const operations = []; + for (let i = 0; i < tokenizedNotes.length; i++) { + const token = tokenizedNotes[i]; + const nextToken = tokenizedNotes[i + 1]; + if (token[0] === 'P') { + // Token is a pause operation, use frequency 0 + const duration = Number.parseInt(token.substring(1), 10); + operations.push([0, this.#getDurationInMs(bpm, duration)]); + } else if ('ABCDEFG'.indexOf(token[0]) !== -1) { + // Token is a note; next token will be duration of this note + const frequency = this.#getFrequency(token, transposeBySemitones); + const duration = Number.parseInt(nextToken, 10); + const durationMs = this.#getDurationInMs(bpm, duration); + operations.push([frequency, durationMs]); + } + } + return operations; + } + + static parseToArray(melodyOrRTTTL: string) { + if (melodyOrRTTTL.indexOf('|') !== -1) { + const defineValue = melodyOrRTTTL.split('|'); + const transposeBySemitones = + defineValue.length > 2 ? Number(defineValue[2]) : 0; + return this.#parseMelody( + defineValue[0].trim(), + Number(defineValue[1]), + transposeBySemitones + ); + } + const melody = Rtttl.parse(melodyOrRTTTL).melody.map( + (v: { frequency: number; duration: number }) => [ + Math.floor(v.frequency), + Math.floor(v.duration), + ] + ); + if (melody.length > 32) melody.length = 32; + return melody; + } +} diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts new file mode 100644 index 000000000..e8b10c9da --- /dev/null +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -0,0 +1,685 @@ +/* eslint-disable no-bitwise */ +import { Service } from 'typedi'; +import { PubSubEngine } from 'graphql-subscriptions'; +import * as os from 'os'; +import * as fs from 'fs'; +import * as path from 'path'; +import rimraf from 'rimraf'; +import cloneDeep from 'lodash/cloneDeep'; +import Crypto from 'crypto'; +import semver from 'semver'; +import UserDefine from '../../models/UserDefine'; +import FirmwareSource from '../../models/enum/FirmwareSource'; +import BuildFlashFirmwareResult from '../../models/BuildFlashFirmwareResult'; +import Mutex from '../../library/Mutex'; +import BuildFirmwareErrorType from '../../models/enum/BuildFirmwareErrorType'; +import PubSubTopic from '../../pubsub/enum/PubSubTopic'; +import BuildProgressNotificationType from '../../models/enum/BuildProgressNotificationType'; +import BuildFirmwareStep from '../../models/enum/FirmwareBuildStep'; +import { LoggerService } from '../../logger'; +import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; +import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import { + FlashingStrategy, + IsCompatibleArgs, +} from '../FlashingStrategyLocator/FlashingStrategy'; +import ConfigureFirmware from '../../library/ConfigureFirmware'; +import { Options } from '../../library/ConfigureFirmware/Options'; +import { Config } from '../../library/ConfigureFirmware/Config'; +import Domain from '../../library/ConfigureFirmware/Domain'; +import Target from '../../models/Target'; +import { Targets } from '../../library/ConfigureFirmware/Targets'; +import TargetArgs from '../../graphql/args/Target'; +import GitRepository from '../../graphql/inputs/GitRepositoryInput'; +import Device from '../../models/Device'; +import { UserDefineFilters } from '../UserDefinesLoader'; +import FlashingMethod from '../../models/enum/FlashingMethod'; +import DeviceType from '../../models/enum/DeviceType'; +import TargetUserDefinesFactory from '../../factories/TargetUserDefinesFactory'; + +const maskSensitiveData = (haystack: string): string => { + const needles = [ + 'HOME_WIFI_SSID', + 'HOME_WIFI_PASSWORD', + 'MY_BINDING_PHRASE', + 'MY_UID', + ]; + for (let i = 0; i < needles.length; i++) { + if (haystack.includes(needles[i])) { + return '***'; + } + } + return haystack; +}; + +const maskBuildFlashFirmwareParams = ( + params: BuildFlashFirmwareParams +): BuildFlashFirmwareParams => { + const result = cloneDeep(params); + if (result.userDefinesTxt?.length > 0) { + result.userDefinesTxt = '******'; + } + result.userDefines = result.userDefines.map((userDefine) => { + const sensitiveDataKeys = [ + UserDefineKey.BINDING_PHRASE, + UserDefineKey.HOME_WIFI_SSID, + UserDefineKey.HOME_WIFI_PASSWORD, + ]; + if (sensitiveDataKeys.includes(userDefine.key)) { + return { + ...userDefine, + value: '***', + }; + } + return userDefine; + }); + return result; +}; + +@Service() +export default class BinaryFlashingStrategyService implements FlashingStrategy { + mutex: Mutex; + + constructor( + private PATH: string, + private firmwaresPath: string, + private pubSub: PubSubEngine, + private logger: LoggerService + ) { + this.mutex = new Mutex(); + } + + private async updateProgress( + type: BuildProgressNotificationType, + step: BuildFirmwareStep + ): Promise { + this.logger?.log('build progress notification', { + type, + step, + }); + return this.pubSub!.publish(PubSubTopic.BuildProgressNotification, { + type, + step, + }); + } + + private async updateLogs(data: string): Promise { + const maskedData = maskSensitiveData(data); + this.logger?.log('logs stream output', { + data: maskedData, + }); + return this.pubSub!.publish(PubSubTopic.BuildLogsUpdate, { + data: maskedData, + }); + } + + async getTargets(workingDirectory: string) { + const targetsFile = path.join(workingDirectory, 'hardware', 'targets.json'); + this.logger.log(`Loading targets from ${targetsFile}`); + const targets = JSON.parse( + await fs.promises.readFile(targetsFile, 'utf-8') + ) as Targets; + return targets; + } + + async getConfig(workingDirectory: string, devicePath: string) { + const [manufacturer, type, product] = devicePath.split('.'); + const targets = await this.getTargets(workingDirectory); + let config: Config; + if (type === 'rx_2400') { + config = targets[manufacturer].rx_2400[product]; + } else if (type === 'rx_900') { + config = targets[manufacturer].rx_900[product]; + } else if (type === 'tx_2400') { + config = targets[manufacturer].tx_2400[product]; + } else if (type === 'tx_900') { + config = targets[manufacturer].tx_900[product]; + } else { + throw new Error(`Unknown type ${type} encountered`); + } + return config; + } + + uploadMethodToFlashingMethod(uploadMethod: string): FlashingMethod { + switch (uploadMethod.toLowerCase()) { + case 'betaflight': + return FlashingMethod.BetaflightPassthrough; + break; + case 'dfu': + return FlashingMethod.DFU; + break; + case 'etx': + return FlashingMethod.EdgeTxPassthrough; + break; + case 'stlink': + return FlashingMethod.STLink; + break; + case 'uart': + return FlashingMethod.UART; + break; + case 'wifi': + return FlashingMethod.WIFI; + break; + + default: + throw new Error(`Upload Method ${uploadMethod} Not Recognized!`); + break; + } + } + + configToDevice(id: string, category: string, config: Config): Device { + return new Device( + id, + config.product_name, + category, + config.upload_methods.map((uploadMethod) => { + const targetName = `${id}.${uploadMethod}`; + return new Target( + targetName, + targetName, + this.uploadMethodToFlashingMethod(uploadMethod) + ); + }), + [], + DeviceType.ExpressLRS, + true + ); + } + + verifyConfig(id: string, config: Config) { + let missingFields = ''; + const logMissingField = (field: string) => { + if (missingFields.length > 0) { + missingFields += ', '; + } + missingFields += field; + }; + let valid = true; + if (!config.firmware) { + logMissingField('firmware'); + valid = false; + } + if (!config.platform) { + logMissingField('platform'); + valid = false; + } + if (!config.product_name) { + logMissingField('product_name'); + valid = false; + } + if (!config.upload_methods) { + logMissingField('upload_methods'); + valid = false; + } + + if (!valid) { + this.logger.error( + `${id} in targets file is not a valid Config, missing fields ${missingFields}` + ); + } + return valid; + } + + async availableFirmwareTargets( + args: TargetArgs, + gitRepository: GitRepository + ): Promise { + const targets = await this.getTargets(this.firmwaresPath); + const devices: Device[] = []; + Object.keys(targets).forEach((manufacturerKey) => { + const manufacturerConfig = targets[manufacturerKey]; + if (manufacturerConfig.rx_2400) { + Object.keys(manufacturerConfig.rx_2400).forEach((deviceKey) => { + const deviceConfig = manufacturerConfig.rx_2400[deviceKey]; + const id = `${manufacturerKey}.rx_2400.${deviceKey}`; + + if (this.verifyConfig(id, deviceConfig)) { + const device = this.configToDevice( + id, + manufacturerConfig.name, + deviceConfig + ); + devices.push(device); + } + }); + } + if (manufacturerConfig.rx_900) { + Object.keys(manufacturerConfig.rx_900).forEach((deviceKey) => { + const deviceConfig = manufacturerConfig.rx_900[deviceKey]; + const id = `${manufacturerKey}.rx_900.${deviceKey}`; + + if (this.verifyConfig(id, deviceConfig)) { + const device = this.configToDevice( + id, + manufacturerConfig.name, + deviceConfig + ); + devices.push(device); + } + }); + } + if (manufacturerConfig.tx_2400) { + Object.keys(manufacturerConfig.tx_2400).forEach((deviceKey) => { + const deviceConfig = manufacturerConfig.tx_2400[deviceKey]; + const id = `${manufacturerKey}.tx_2400.${deviceKey}`; + + if (this.verifyConfig(id, deviceConfig)) { + const device = this.configToDevice( + id, + manufacturerConfig.name, + deviceConfig + ); + devices.push(device); + } + }); + } + if (manufacturerConfig.tx_900) { + Object.keys(manufacturerConfig.tx_900).forEach((deviceKey) => { + const deviceConfig = manufacturerConfig.tx_900[deviceKey]; + const id = `${manufacturerKey}.tx_900.${deviceKey}`; + + if (this.verifyConfig(id, deviceConfig)) { + const device = this.configToDevice( + id, + manufacturerConfig.name, + deviceConfig + ); + devices.push(device); + } + }); + } + }); + return devices; + } + + async targetDeviceOptions( + args: UserDefineFilters, + gitRepository: GitRepository + ): Promise { + const config = await this.getConfig(this.firmwaresPath, args.target); + const userDefines: UserDefine[] = []; + const targetUserDefinesFactory = new TargetUserDefinesFactory(); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.BINDING_PHRASE) + ); + if (args.target.includes('_2400.')) { + userDefines.push( + targetUserDefinesFactory.build( + UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 + ) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_ISM_2400) + ); + } + if (args.target.includes('_900.')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_AU_915) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_EU_868) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_FCC_915) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_IN_866) + ); + } + if (['esp32', 'esp8285'].includes(config.platform)) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.AUTO_WIFI_ON_INTERVAL) + ); + } + if (config.features && config.features.includes('buzzer')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.DISABLE_ALL_BEEPS) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.JUST_BEEP_ONCE) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.MY_STARTUP_MELODY) + ); + } + if (config.features && config.features.includes('unlock-higher-power')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.UNLOCK_HIGHER_POWER) + ); + } + if (config.features && config.features.includes('sbus-uart')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.USE_R9MM_R9MINI_SBUS) + ); + } + if (args.target.includes('.tx_')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.TLM_REPORT_INTERVAL_MS) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.UART_INVERTED) + ); + } + if (args.target.includes('.rx_')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.RCVR_UART_BAUD) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.RCVR_INVERT_TX) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.LOCK_ON_FIRST_CONNECTION) + ); + } + return userDefines; + } + + private osUsernameContainsAmpersand(): boolean { + if ( + os.platform() === 'win32' && + os.userInfo({ encoding: 'utf8' }).username.indexOf('&') > -1 + ) { + return true; + } + return false; + } + + async isCompatible( + params: IsCompatibleArgs, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ) { + if ( + gitRepositoryUrl.toLowerCase() === + 'https://github.com/ExpressLRS/ExpressLRS'.toLowerCase() && + params.source === FirmwareSource.GitTag && + semver.compare(params.gitTag, '3.0.0') >= 0 + ) { + return true; + } + + return false; + } + + async buildFlashFirmware( + params: BuildFlashFirmwareParams, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ): Promise { + this.logger?.log('received build firmware request', { + params: maskBuildFlashFirmwareParams(params), + gitRepositoryUrl, + }); + + if (this.mutex.isLocked()) { + this.logger?.error('there is another build/flash request in progress...'); + return new BuildFlashFirmwareResult( + false, + 'there is another build/flash request in progress...', + BuildFirmwareErrorType.GenericError + ); + } + this.mutex.tryLock(); + + try { + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.VERIFYING_BUILD_SYSTEM + ); + + const badUsername = this.osUsernameContainsAmpersand(); + if (badUsername) { + return new BuildFlashFirmwareResult( + false, + 'Windows username contains & ampersand character. At this time it is not supported and build process will fail. Please change the Windows username.', + BuildFirmwareErrorType.GenericError + ); + } + + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.DOWNLOADING_FIRMWARE + ); + + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.FLASHING_FIRMWARE + ); + + const config = await this.getConfig(this.firmwaresPath, params.target); + // need a better way to do this, it should really be included in the config + const deviceType = params.target.includes('.tx_') ? 'TX' : 'RX'; + const radioType = params.target.includes('_900.') ? 'sx127x' : 'sx128x'; + + // assign some default values + const options: Options = { + 'wifi-on-interval': 60, + }; + + if (deviceType === 'RX') { + options['rcvr-uart-baud'] = 420000; + options['rcvr-invert-tx'] = false; + options['lock-on-first-connection'] = false; + } else { + options['tlm-interval'] = 240; + } + + let fcclbt = 'FCC'; + params.userDefines.forEach((userDefine) => { + if ( + userDefine.key === UserDefineKey.AUTO_WIFI_ON_INTERVAL && + userDefine.value && + userDefine.enabled + ) { + const wifiOnInterval = Number.parseInt(userDefine.value, 10); + if (!Number.isNaN(wifiOnInterval)) { + options['wifi-on-interval'] = wifiOnInterval; + } + } else if ( + userDefine.key === UserDefineKey.BINDING_PHRASE && + userDefine.value && + userDefine.enabled + ) { + const bindingPhraseFull = `-DMY_BINDING_PHRASE="${userDefine.value}"`; + const bindingPhraseHashed = new Uint8Array( + Crypto.createHash('md5').update(bindingPhraseFull).digest() + ); + options.uid = Array.from(bindingPhraseHashed.subarray(0, 6)); + } else if ( + userDefine.key === UserDefineKey.DISABLE_ALL_BEEPS && + userDefine.enabled + ) { + options.beeptype = 0; + } else if ( + userDefine.key === UserDefineKey.JUST_BEEP_ONCE && + userDefine.enabled + ) { + options.beeptype = 1; + } else if ( + userDefine.key === UserDefineKey.MY_STARTUP_MELODY && + userDefine.enabled && + userDefine.value + ) { + options.beeptype = 4; + // TODO: Fix melody parsing, importing module bluejay-rtttl-parse gives the error Unexpected token 'export' + // options.melody = MelodyParser.parseToArray(userDefine.value); + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_AU_433 && + userDefine.enabled + ) { + options.domain = Domain.AU433; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_AU_915 && + userDefine.enabled + ) { + options.domain = Domain.AU915; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_EU_433 && + userDefine.enabled + ) { + options.domain = Domain.EU433; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_EU_868 && + userDefine.enabled + ) { + options.domain = Domain.EU868; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_FCC_915 && + userDefine.enabled + ) { + options.domain = Domain.FCC915; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_IN_866 && + userDefine.enabled + ) { + options.domain = Domain.IN866; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 && + userDefine.enabled + ) { + fcclbt = 'LBT'; + } else if ( + userDefine.key === UserDefineKey.REGULATORY_DOMAIN_ISM_2400 && + userDefine.enabled + ) { + fcclbt = 'FCC'; + } else if (userDefine.key === UserDefineKey.LOCK_ON_FIRST_CONNECTION) { + options['lock-on-first-connection'] = userDefine.enabled; + } else if ( + userDefine.key === UserDefineKey.RCVR_UART_BAUD && + userDefine.enabled && + userDefine.value + ) { + const RCVR_UART_BAUD = Number.parseInt(userDefine.value, 10); + options['rcvr-uart-baud'] = RCVR_UART_BAUD; + } else if (userDefine.key === UserDefineKey.RCVR_INVERT_TX) { + options['rcvr-invert-tx'] = userDefine.enabled; + } else if ( + userDefine.key === UserDefineKey.TLM_REPORT_INTERVAL_MS && + userDefine.enabled && + userDefine.value + ) { + const TLM_REPORT_INTERVAL_MS = Number.parseInt(userDefine.value, 10); + options['tlm-interval'] = TLM_REPORT_INTERVAL_MS; + } else if (userDefine.key === UserDefineKey.UART_INVERTED) { + options['uart-inverted'] = userDefine.enabled; + } else if (userDefine.key === UserDefineKey.UNLOCK_HIGHER_POWER) { + options['unlock-higher-power'] = userDefine.enabled; + } else if ( + userDefine.key === UserDefineKey.HOME_WIFI_PASSWORD && + userDefine.enabled + ) { + options['wifi-password'] = userDefine.value; + } else if ( + userDefine.key === UserDefineKey.HOME_WIFI_SSID && + userDefine.enabled + ) { + options['wifi-ssid'] = userDefine.value; + } else if (userDefine.key === UserDefineKey.USE_R9MM_R9MINI_SBUS) { + options['r9mm-mini-sbus'] = userDefine.enabled; + } + }); + const folder = this.firmwaresPath; + const firmware = await ConfigureFirmware.getFirmware( + deviceType, + radioType, + config, + options, + folder, + fcclbt + ); + if (firmware.firmware) { + const firmwareBinPath = path.join( + this.firmwaresPath, + `${this.generateFirmwareName(config.product_name, params)}.bin` + ); + fs.promises.writeFile( + firmwareBinPath, + Buffer.from(firmware.firmware.data.buffer) + ); + return new BuildFlashFirmwareResult( + true, + undefined, + undefined, + firmwareBinPath + ); + } + + return new BuildFlashFirmwareResult(true); + } catch (e) { + this.logger?.error('generic error', undefined, { + err: e, + }); + return new BuildFlashFirmwareResult( + false, + `Error: ${e}`, + BuildFirmwareErrorType.GenericError + ); + } finally { + this.mutex.unlock(); + } + } + + generateFirmwareName( + deviceName: string, + params: BuildFlashFirmwareParams + ): string { + const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = + params.firmware; + + const firmwareName = deviceName?.replaceAll(' ', '_'); + + switch (source) { + case FirmwareSource.GitTag: + return `${firmwareName}-${gitTag}`; + case FirmwareSource.GitBranch: + return `${firmwareName}-${gitBranch}`; + case FirmwareSource.GitCommit: + return `${firmwareName}-${gitCommit}`; + case FirmwareSource.GitPullRequest: + return `${firmwareName}-PR_${gitPullRequest?.number}`; + default: + return `${firmwareName}`; + } + } + + async clearFirmwareFiles(): Promise { + const rmrf = async (file: string): Promise => { + return new Promise((resolve, reject) => { + rimraf(file, (err) => { + if (err) { + reject(); + } else { + resolve(); + } + }); + }); + }; + const listFiles = async (directory: string): Promise => { + return new Promise((resolve, reject) => { + fs.readdir(directory, (err, files) => { + if (err) { + reject(err); + } else { + resolve(files.map((file) => path.join(directory, file))); + } + }); + }); + }; + const files = await listFiles(this.firmwaresPath); + this.logger?.log('removing firmware files', { + firmwaresPath: this.firmwaresPath, + files, + }); + if (files.length > 4) { + throw new Error(`unexpected number of files to remove: ${files}`); + } + await Promise.all(files.map((item) => rmrf(item))); + } +} diff --git a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts index 2c31043fe..fb04888d1 100644 --- a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts +++ b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts @@ -1,9 +1,25 @@ +import TargetArgs from '../../graphql/args/Target'; +import GitRepository from '../../graphql/inputs/GitRepositoryInput'; import BuildFlashFirmwareResult from '../../models/BuildFlashFirmwareResult'; import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; +import Device from '../../models/Device'; +import { UserDefineFilters } from '../UserDefinesLoader'; +import UserDefine from '../../models/UserDefine'; +import FirmwareSource from '../../models/enum/FirmwareSource'; +import PullRequest from '../../models/PullRequest'; + +export interface IsCompatibleArgs { + source: FirmwareSource; + gitTag: string; + gitBranch: string; + gitCommit: string; + localPath: string; + gitPullRequest: PullRequest | null; +} export interface FlashingStrategy { isCompatible: ( - params: BuildFlashFirmwareParams, + params: IsCompatibleArgs, gitRepositoryUrl: string, gitRepositorySrcFolder: string ) => Promise; @@ -14,5 +30,15 @@ export interface FlashingStrategy { gitRepositorySrcFolder: string ) => Promise; + availableFirmwareTargets: ( + args: TargetArgs, + gitRepository: GitRepository + ) => Promise; + + targetDeviceOptions: ( + input: UserDefineFilters, + gitRepository: GitRepository + ) => Promise; + clearFirmwareFiles(): Promise; } diff --git a/src/api/src/services/FlashingStrategyLocator/index.ts b/src/api/src/services/FlashingStrategyLocator/index.ts index a36adff86..692dd21e2 100644 --- a/src/api/src/services/FlashingStrategyLocator/index.ts +++ b/src/api/src/services/FlashingStrategyLocator/index.ts @@ -1,7 +1,6 @@ /* eslint-disable no-await-in-loop */ import { Service } from 'typedi'; -import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; -import { FlashingStrategy } from './FlashingStrategy'; +import { FlashingStrategy, IsCompatibleArgs } from './FlashingStrategy'; import { LoggerService } from '../../logger'; @Service() @@ -12,7 +11,7 @@ export default class FlashingStrategyLocatorService { ) {} async locate( - params: BuildFlashFirmwareParams, + params: IsCompatibleArgs, gitRepositoryUrl: string, gitRepositorySrcFolder: string ): Promise { diff --git a/src/api/src/services/GitHubClient/index.ts b/src/api/src/services/GitHubClient/index.ts index 64ddb7cf6..4bce12c7e 100644 --- a/src/api/src/services/GitHubClient/index.ts +++ b/src/api/src/services/GitHubClient/index.ts @@ -1,3 +1,4 @@ +import { RepeatOneSharp } from '@mui/icons-material'; import { Octokit } from '@octokit/rest'; import { Service } from 'typedi'; import PullRequest from '../../models/PullRequest'; @@ -76,4 +77,12 @@ export default class OctopusGitHubClient implements IGitHubClient { }; }); } + + async getRelease(owner: string, repository: string, tag: string) { + const response = await this.client.repos.listReleases({ + owner, + repo: repository, + }); + return response.data.find((value) => value.tag_name === tag); + } } diff --git a/src/api/src/services/PlatformioFlashingStrategy/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts index 14c2f74df..5d0023771 100644 --- a/src/api/src/services/PlatformioFlashingStrategy/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -26,8 +26,17 @@ import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; import UploadType from '../../library/Platformio/Enum/UploadType'; import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; -import { FlashingStrategy } from '../FlashingStrategyLocator/FlashingStrategy'; +import { + FlashingStrategy, + IsCompatibleArgs, +} from '../FlashingStrategyLocator/FlashingStrategy'; import Python from '../../library/Python'; +import UserDefinesBuilder from '../UserDefinesBuilder'; +import TargetsLoader from '../TargetsLoader'; +import TargetArgs from '../../graphql/args/Target'; +import Device from '../../models/Device'; +import { UserDefineFilters } from '../UserDefinesLoader'; +import GitRepository from '../../graphql/inputs/GitRepositoryInput'; const maskSensitiveData = (haystack: string): string => { const needles = [ @@ -81,7 +90,9 @@ export default class PlatformioFlashingStrategyService private builder: FirmwareBuilder, private pubSub: PubSubEngine, private logger: LoggerService, - private python: Python + private python: Python, + private userDefinesBuilder: UserDefinesBuilder, + private targetsLoader: TargetsLoader ) { this.mutex = new Mutex(); } @@ -110,6 +121,20 @@ export default class PlatformioFlashingStrategyService }); } + async availableFirmwareTargets( + args: TargetArgs, + gitRepository: GitRepository + ): Promise { + return this.targetsLoader.loadTargetsList(args, gitRepository); + } + + async targetDeviceOptions( + args: UserDefineFilters, + gitRepository: GitRepository + ): Promise { + return this.userDefinesBuilder.build(args, gitRepository); + } + private processUserDefines(userDefines: UserDefine[]): UserDefine[] { const overrideUserDefineTo = ( // eslint-disable-next-line @typescript-eslint/no-shadow @@ -151,7 +176,7 @@ export default class PlatformioFlashingStrategyService } async isCompatible( - params: BuildFlashFirmwareParams, + params: IsCompatibleArgs, gitRepositoryUrl: string, gitRepositorySrcFolder: string ) { diff --git a/src/main.dev.ts b/src/main.dev.ts index fdbe49303..ffd848674 100644 --- a/src/main.dev.ts +++ b/src/main.dev.ts @@ -296,6 +296,7 @@ const createWindow = async () => { targetsLoader: targetsLoaderType, userDefinesLoader: userDefinesLoaderType, userDefinesStoragePath, + userDataPath: app.getPath('userData'), }, logger, port diff --git a/yarn.lock b/yarn.lock index d95a128fc..ae7428325 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4257,6 +4257,11 @@ bluebird@^3.5.0, bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bluejay-rtttl-parse@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/bluejay-rtttl-parse/-/bluejay-rtttl-parse-2.0.2.tgz#44111849591a22aac315b5791400b72fb02b88e5" + integrity sha512-Rs6eGqbCjSM4n9V93+tC6b9xZMCkTF67aRkfFzaVFlrFPJo8Gxi0swLisdUL4n4bBNfgkGEVzkEPww9L60gMng== + body-parser@1.20.0, body-parser@^1.19.0: version "1.20.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" From ff8014e54f65f06e20325f251d1c3fd5e1970c32 Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Wed, 14 Sep 2022 12:31:51 -0400 Subject: [PATCH 06/54] Add WIFI Upload Support to Binary Flashing Strategy --- package.json | 1 + .../services/BinaryFlashingStrategy/index.ts | 222 ++++++++++++++++-- 2 files changed, 204 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 862d6ea06..9ef2d0d57 100644 --- a/package.json +++ b/package.json @@ -315,6 +315,7 @@ "electron-updater": "^5.0.5", "express": "^4.17.1", "extract-zip": "^2.0.1", + "form-data": "^4.0.0", "get-port": "^5.1.1", "graphql": "15.7.2", "graphql-subscriptions": "^2.0.0", diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index e8b10c9da..21fead511 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -8,6 +8,8 @@ import rimraf from 'rimraf'; import cloneDeep from 'lodash/cloneDeep'; import Crypto from 'crypto'; import semver from 'semver'; +import FormData from 'form-data'; +import { Blob } from 'buffer'; import UserDefine from '../../models/UserDefine'; import FirmwareSource from '../../models/enum/FirmwareSource'; import BuildFlashFirmwareResult from '../../models/BuildFlashFirmwareResult'; @@ -36,6 +38,8 @@ import { UserDefineFilters } from '../UserDefinesLoader'; import FlashingMethod from '../../models/enum/FlashingMethod'; import DeviceType from '../../models/enum/DeviceType'; import TargetUserDefinesFactory from '../../factories/TargetUserDefinesFactory'; +import BuildJobType from '../../models/enum/BuildJobType'; +import { FirmwareFiles } from '../../library/ConfigureFirmware/FirmwareFiles'; const maskSensitiveData = (haystack: string): string => { const needles = [ @@ -103,13 +107,13 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { }); } - private async updateLogs(data: string): Promise { + private async updateLogs(data: string, newline = true): Promise { const maskedData = maskSensitiveData(data); this.logger?.log('logs stream output', { data: maskedData, }); return this.pubSub!.publish(PubSubTopic.BuildLogsUpdate, { - data: maskedData, + data: maskedData + (newline ? '\n' : ''), }); } @@ -451,7 +455,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildProgressNotificationType.Info, BuildFirmwareStep.FLASHING_FIRMWARE ); - + const [manufacturer, type, device, uploadMethod] = + params.target.split('.'); + const flashingMethod = this.uploadMethodToFlashingMethod(uploadMethod); const config = await this.getConfig(this.firmwaresPath, params.target); // need a better way to do this, it should really be included in the config const deviceType = params.target.includes('.tx_') ? 'TX' : 'RX'; @@ -594,21 +600,51 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { folder, fcclbt ); - if (firmware.firmware) { - const firmwareBinPath = path.join( - this.firmwaresPath, - `${this.generateFirmwareName(config.product_name, params)}.bin` - ); - fs.promises.writeFile( - firmwareBinPath, - Buffer.from(firmware.firmware.data.buffer) - ); - return new BuildFlashFirmwareResult( - true, - undefined, - undefined, - firmwareBinPath - ); + if (params.type === BuildJobType.Build) { + if (firmware.firmware) { + const firmwareBinPath = path.join( + this.firmwaresPath, + `${this.generateFirmwareName(config.product_name, params)}.bin` + ); + fs.promises.writeFile( + firmwareBinPath, + Buffer.from(firmware.firmware.data.buffer) + ); + return new BuildFlashFirmwareResult( + true, + undefined, + undefined, + firmwareBinPath + ); + } + } else if ( + params.type === BuildJobType.BuildAndFlash || + params.type === BuildJobType.ForceFlash + ) { + const forceFlash = params.type === BuildJobType.ForceFlash; + + if (flashingMethod === FlashingMethod.WIFI) { + try { + const response = await this.WIFI( + params.serialDevice ?? '10.0.0.1', + firmware, + forceFlash + ); + + return response; + } catch (error: any) { + this.logger.error(error); + this.updateLogs(error.message); + return new BuildFlashFirmwareResult(false, error); + } + } + const message = `Upload Method ${flashingMethod} is not currently supported`; + this.updateLogs(message); + return new BuildFlashFirmwareResult(false, message); + } else { + const message = `Build Job Type ${params.type} is not currently supported`; + this.updateLogs(message); + return new BuildFlashFirmwareResult(false, message); } return new BuildFlashFirmwareResult(true); @@ -633,7 +669,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = params.firmware; - const firmwareName = deviceName?.replaceAll(' ', '_'); + const firmwareName = deviceName + ?.replace(/([^a-z0-9. ]+)/gi, '-') + .replaceAll(' ', '_'); switch (source) { case FirmwareSource.GitTag: @@ -649,6 +687,152 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } } + async WIFI( + host: string, + firmware: FirmwareFiles, + force: boolean + ): Promise { + /// https://wanago.io/2019/03/18/node-js-typescript-6-sending-http-requests-understanding-multipart-form-data/ + return new Promise((resolve) => { + const output: string[] = []; + + const form = new FormData(); + + if (force) { + console.log('forcing flash'); + form.append('force', '1'); + } + const buffer = Buffer.from(firmware.firmware!.data); + let uploadSize = 0; + let uploaded = 0; + + form.append('data', buffer); + + // get upload size + form.getLength((err, size) => { + console.log(`upload size: ${size}`); + uploadSize = size; + }); + + const options = { + host, + path: '/update', + headers: { + 'X-FileSize': buffer.byteLength, + }, + }; + + this.updateLogs(`starting upload to ${host}`); + const req = form.submit(options, (err, response) => { + if (err) { + this.logger.error(err.message); + resolve( + new BuildFlashFirmwareResult( + false, + err.message, + BuildFirmwareErrorType.FlashError + ) + ); + + return; + } + + const { statusCode, statusMessage, headers } = response; + console.log(req.getHeaders()); // 200 + console.log(req.path); // 200 + console.log(statusCode); // 200 + console.log(statusMessage); // 200 + const chunks: any = []; + response.on('data', (chunk) => { + chunks.push(chunk); + }); + response.on('end', () => { + console.log('response end'); + const data = Buffer.concat(chunks).toString(); + const result = { + data, + headers, + }; + + console.log(result); + this.updateLogs(`response from ${host}`); + this.updateLogs(result.data); + if (response.statusCode === 200) { + let json: any = {}; + try { + json = JSON.parse(data); + } catch (e) { + this.logger.error(e); + console.group(e); + } + + if (json.status === 'ok') { + resolve( + new BuildFlashFirmwareResult( + true, + data, + BuildFirmwareErrorType.FlashError + ) + ); + } else if (json.status === 'mismatch') { + resolve( + new BuildFlashFirmwareResult( + false, + data.replaceAll(//g, ''), + BuildFirmwareErrorType.TargetMismatch + ) + ); + } else { + resolve( + new BuildFlashFirmwareResult( + false, + data, + BuildFirmwareErrorType.FlashError + ) + ); + } + } else { + resolve( + new BuildFlashFirmwareResult( + false, + data, + BuildFirmwareErrorType.FlashError + ) + ); + } + }); + }); + + req.on('error', (error) => { + console.log('req error'); + this.logger.error(error.message); + this.updateLogs(error.message); + console.log(error); // 200 + resolve( + new BuildFlashFirmwareResult( + false, + error.message, + BuildFirmwareErrorType.FlashError + ) + ); + }); + + form.on('data', (data) => { + console.log('data'); + if (uploadSize > 0) { + uploaded += data.length; + const progress = (uploaded / uploadSize) * 100; + const progressMessage = `progress: ${progress.toLocaleString( + undefined, + { minimumFractionDigits: 0, maximumFractionDigits: 2 } + )}%`; + this.updateLogs(progressMessage); + // console.log(progressMessage); + } + }); + }); + } + async clearFirmwareFiles(): Promise { const rmrf = async (file: string): Promise => { return new Promise((resolve, reject) => { From 1812a0cd8c1c2043eec54f6fd4eca8595faa0a1d Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 12 Nov 2022 18:53:30 +0200 Subject: [PATCH 07/54] fix eslint --- .../services/BinaryFlashingStrategy/index.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index bed8c9329..36f415c7e 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -1,6 +1,6 @@ /* eslint-disable no-bitwise */ -import {Service} from 'typedi'; -import {PubSubEngine} from 'graphql-subscriptions'; +import { Service } from 'typedi'; +import { PubSubEngine } from 'graphql-subscriptions'; import * as os from 'os'; import * as fs from 'fs'; import * as path from 'path'; @@ -16,28 +16,28 @@ import BuildFirmwareErrorType from '../../models/enum/BuildFirmwareErrorType'; import PubSubTopic from '../../pubsub/enum/PubSubTopic'; import BuildProgressNotificationType from '../../models/enum/BuildProgressNotificationType'; import BuildFirmwareStep from '../../models/enum/FirmwareBuildStep'; -import {LoggerService} from '../../logger'; +import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; -import {BuildFlashFirmwareParams} from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; import { FlashingStrategy, IsCompatibleArgs, } from '../FlashingStrategyLocator/FlashingStrategy'; import ConfigureFirmware from '../../library/ConfigureFirmware'; -import {Options} from '../../library/ConfigureFirmware/Options'; -import {Config} from '../../library/ConfigureFirmware/Config'; +import { Options } from '../../library/ConfigureFirmware/Options'; +import { Config } from '../../library/ConfigureFirmware/Config'; import Domain from '../../library/ConfigureFirmware/Domain'; import Target from '../../models/Target'; -import {Targets} from '../../library/ConfigureFirmware/Targets'; +import { Targets } from '../../library/ConfigureFirmware/Targets'; import TargetArgs from '../../graphql/args/Target'; import GitRepository from '../../graphql/inputs/GitRepositoryInput'; import Device from '../../models/Device'; -import {UserDefineFilters} from '../UserDefinesLoader'; +import { UserDefineFilters } from '../UserDefinesLoader'; import FlashingMethod from '../../models/enum/FlashingMethod'; import DeviceType from '../../models/enum/DeviceType'; import TargetUserDefinesFactory from '../../factories/TargetUserDefinesFactory'; import BuildJobType from '../../models/enum/BuildJobType'; -import {FirmwareFiles} from '../../library/ConfigureFirmware/FirmwareFiles'; +import { FirmwareFiles } from '../../library/ConfigureFirmware/FirmwareFiles'; import BuildFlashFirmwareResult from '../../graphql/objects/BuildFlashFirmwareResult'; const maskSensitiveData = (haystack: string): string => { @@ -82,6 +82,7 @@ const maskBuildFlashFirmwareParams = ( @Service() export default class BinaryFlashingStrategyService implements FlashingStrategy { readonly name: string = 'BinaryFlashingStrategy'; + mutex: Mutex; constructor( @@ -387,7 +388,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { private osUsernameContainsAmpersand(): boolean { if ( os.platform() === 'win32' && - os.userInfo({encoding: 'utf8'}).username.indexOf('&') > -1 + os.userInfo({ encoding: 'utf8' }).username.indexOf('&') > -1 ) { return true; } @@ -401,7 +402,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { ) { if ( gitRepositoryUrl.toLowerCase() === - 'https://github.com/ExpressLRS/ExpressLRS'.toLowerCase() && + 'https://github.com/ExpressLRS/ExpressLRS'.toLowerCase() && params.source === FirmwareSource.GitTag && semver.compare(params.gitTag, '3.0.0') >= 0 ) { @@ -666,7 +667,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { deviceName: string, params: BuildFlashFirmwareParams ): string { - const {source, gitBranch, gitCommit, gitTag, gitPullRequest} = + const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = params.firmware; const firmwareName = deviceName @@ -737,7 +738,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return; } - const {statusCode, statusMessage, headers} = response; + const { statusCode, statusMessage, headers } = response; console.log(req.getHeaders()); // 200 console.log(req.path); // 200 console.log(statusCode); // 200 @@ -824,7 +825,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { const progress = (uploaded / uploadSize) * 100; const progressMessage = `progress: ${progress.toLocaleString( undefined, - {minimumFractionDigits: 0, maximumFractionDigits: 2} + { minimumFractionDigits: 0, maximumFractionDigits: 2 } )}%`; this.updateLogs(progressMessage); // console.log(progressMessage); From 00eef7374655d817d6700aff7124b7b076fbc4c7 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 13 Nov 2022 19:58:12 +0200 Subject: [PATCH 08/54] WIP --- .gitignore | 1 + devices/axis-2400.json | 14 +- devices/betafpv-2400.json | 31 +- devices/betafpv-900.json | 17 - devices/diy-2400.json | 48 +- devices/diy-900.json | 34 +- devices/diy-backpack.json | 3 +- devices/foxeer-2400.json | 4 - devices/frsky-900.json | 39 +- devices/ghost-2400.json | 25 - devices/happymodel-2400.json | 25 - devices/happymodel-900.json | 20 - devices/happymodel-backpack.json | 3 +- devices/hdzero-backpack.json | 46 +- devices/hglrc-2400.json | 11 - devices/hglrc-900.json | 3 - devices/iflight-2400.json | 6 +- devices/iflight-900.json | 6 +- devices/jumper-2400.json | 14 +- devices/jumper-900.json | 3 - devices/matek-2400.json | 14 - devices/namimnorc-2400.json | 26 - devices/namimnorc-900.json | 20 - devices/neutronrc-900.json | 3 - devices/quadcopters-2400.json | 14 +- devices/radiomaster-2400.json | 25 +- devices/siyi-2400.json | 22 +- devices/steadyview-backpack.json | 12 +- devices/vantac-2400.json | 6 +- src/api/index.ts | 68 +- src/api/src/config/index.ts | 7 - .../src/factories/TargetUserDefinesFactory.ts | 36 - src/api/src/graphql/inputs/UserDefineInput.ts | 2 +- .../graphql/resolvers/Firmware.resolver.ts | 20 +- .../src/library/ConfigureFirmware/Config.ts | 11 - .../src/library/ConfigureFirmware/Domain.ts | 10 - .../src/library/ConfigureFirmware/Firmware.ts | 4 - .../ConfigureFirmware/FirmwareFiles.ts | 8 - .../src/library/ConfigureFirmware/Options.ts | 19 - .../src/library/ConfigureFirmware/Targets.ts | 19 - .../src/library/ConfigureFirmware/index.ts | 282 ------ .../FirmwareBuilder/Enum/UserDefineKey.ts | 18 - src/api/src/library/FirmwareBuilder/index.ts | 5 +- src/api/src/library/Platformio/index.ts | 20 +- src/api/src/library/Python/index.ts | 21 +- src/api/src/models/enum/FlashingMethod.ts | 1 - .../DeviceDescriptionsLoader/index.ts | 296 +++++++ .../TargetsJSONLoader/index.ts | 109 +++ .../services/BinaryFlashingStrategy/index.ts | 831 +++++------------- src/api/src/services/GitHubClient/index.ts | 6 +- .../MulticastDns/MulticastDnsSimulator.ts | 2 - .../PlatformioFlashingStrategy/index.ts | 27 +- .../src/services/TargetsLoader/HttpTargets.ts | 138 --- .../src/services/UserDefinesBuilder/index.ts | 37 - .../HttpUserDefinesLoader.ts | 77 -- src/main.dev.ts | 24 - src/ui/components/DeviceOptionsForm/index.tsx | 20 +- .../FlashingMethodDescription/index.tsx | 22 - .../UserDefineDescription/index.tsx | 415 --------- .../components/UserDefinesAdvisor/index.tsx | 19 - src/ui/components/UserDefinesList/index.tsx | 2 - src/ui/gql/generated/types.ts | 48 +- .../ConfiguratorView/UserDefinesValidator.ts | 13 - src/ui/views/ConfiguratorView/index.tsx | 50 +- 64 files changed, 822 insertions(+), 2360 deletions(-) delete mode 100644 src/api/src/library/ConfigureFirmware/Config.ts delete mode 100644 src/api/src/library/ConfigureFirmware/Domain.ts delete mode 100644 src/api/src/library/ConfigureFirmware/Firmware.ts delete mode 100644 src/api/src/library/ConfigureFirmware/FirmwareFiles.ts delete mode 100644 src/api/src/library/ConfigureFirmware/Options.ts delete mode 100644 src/api/src/library/ConfigureFirmware/Targets.ts delete mode 100644 src/api/src/library/ConfigureFirmware/index.ts create mode 100644 src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts create mode 100644 src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts delete mode 100644 src/api/src/services/TargetsLoader/HttpTargets.ts delete mode 100644 src/api/src/services/UserDefinesLoader/HttpUserDefinesLoader.ts diff --git a/.gitignore b/.gitignore index 7975a5155..8a9871824 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,5 @@ npm-debug.log.* *.scss.d.ts /src/api/src/library/Platformio/.piocore-* +/src/api/src/services/BinaryFlashingStrategy/firmware __pycache__ diff --git a/devices/axis-2400.json b/devices/axis-2400.json index 787b7dcfd..8b77c95e3 100644 --- a/devices/axis-2400.json +++ b/devices/axis-2400.json @@ -16,19 +16,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/axis-thor/", "deviceType": "ExpressLRS", @@ -55,11 +47,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/betafpv-2400.json b/devices/betafpv-2400.json index b307c2a7e..9cddb3623 100644 --- a/devices/betafpv-2400.json +++ b/devices/betafpv-2400.json @@ -16,19 +16,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB", "UNLOCK_HIGHER_POWER" ], @@ -61,19 +53,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB", "UNLOCK_HIGHER_POWER" ], @@ -99,14 +83,10 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/betafpv2400/", "deviceType": "ExpressLRS", @@ -134,8 +114,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -177,11 +155,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -235,10 +209,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/betafpv-900.json b/devices/betafpv-900.json index b7e3a449c..19ce80751 100644 --- a/devices/betafpv-900.json +++ b/devices/betafpv-900.json @@ -18,15 +18,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -64,15 +57,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -106,9 +92,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", diff --git a/devices/diy-2400.json b/devices/diy-2400.json index 4497fc0fb..a326685ef 100644 --- a/devices/diy-2400.json +++ b/devices/diy-2400.json @@ -17,13 +17,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "", "deviceType": "ExpressLRS", @@ -72,19 +68,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", @@ -108,20 +96,12 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "USE_TX_BACKPACK", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", @@ -150,13 +130,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", "deviceType": "ExpressLRS", @@ -179,19 +155,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", @@ -219,11 +187,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -291,7 +255,7 @@ { "category": "BETAFPV 2.4 GHz", "name": "BETAFPV AIO 2400 RX", - "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/betafpv2400/", + "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/betafpv2400/", "abbreviatedName": "BFPV AIO 2G4RX" }, { @@ -331,10 +295,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -400,10 +361,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/diy2400/", diff --git a/devices/diy-900.json b/devices/diy-900.json index e7d843429..4ea1cc35c 100644 --- a/devices/diy-900.json +++ b/devices/diy-900.json @@ -18,14 +18,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", - "USE_DYNAMIC_POWER", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -53,13 +47,7 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_DYNAMIC_POWER", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -88,15 +76,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -126,15 +107,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -167,9 +141,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", @@ -193,7 +164,7 @@ "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/diy900/", "abbreviatedName": "EMAX 900RX" } - ] + ] }, { "category": "DIY 900 MHz", @@ -214,9 +185,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", diff --git a/devices/diy-backpack.json b/devices/diy-backpack.json index 66229f73e..b7baf21dc 100644 --- a/devices/diy-backpack.json +++ b/devices/diy-backpack.json @@ -24,7 +24,8 @@ { "flashingMethod": "Passthrough", "name": "DUPLETX_TX_Backpack_via_PASSTHRU" - },{ + }, + { "flashingMethod": "UART", "name": "DUPLETX_TX_Backpack_via_UART" }, diff --git a/devices/foxeer-2400.json b/devices/foxeer-2400.json index 01a810c01..6f0e0cf68 100644 --- a/devices/foxeer-2400.json +++ b/devices/foxeer-2400.json @@ -20,11 +20,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/frsky-900.json b/devices/frsky-900.json index e65ab330e..9c0a8804f 100644 --- a/devices/frsky-900.json +++ b/devices/frsky-900.json @@ -22,18 +22,12 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "USE_ESP8266_BACKPACK", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", "DISABLE_ALL_BEEPS", "MY_STARTUP_MELODY", - "USE_DYNAMIC_POWER", "R9M_UNLOCK_HIGHER_POWER", "UNLOCK_HIGHER_POWER", "USE_TX_BACKPACK" @@ -61,16 +55,10 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", - "DISABLE_ALL_BEEPS", - "USE_DYNAMIC_POWER" + "DISABLE_ALL_BEEPS" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/frsky-r9modules/", "deviceType": "ExpressLRS", @@ -91,17 +79,11 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UNLOCK_HIGHER_POWER", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", - "DISABLE_ALL_BEEPS", - "USE_DYNAMIC_POWER" + "DISABLE_ALL_BEEPS" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/frsky-r9modules/", "deviceType": "ExpressLRS", @@ -126,9 +108,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "USE_R9MM_R9MINI_SBUS", "RCVR_UART_BAUD" @@ -156,11 +135,7 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", - "USE_DIVERSITY", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/r9/", @@ -186,9 +161,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "RCVR_UART_BAUD" ], @@ -215,11 +187,7 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", - "USE_DIVERSITY", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/r9/", @@ -245,9 +213,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "RCVR_UART_BAUD" ], diff --git a/devices/ghost-2400.json b/devices/ghost-2400.json index 65406fc5d..9c4fc4b2f 100644 --- a/devices/ghost-2400.json +++ b/devices/ghost-2400.json @@ -6,24 +6,13 @@ { "flashingMethod": "STLink", "name": "GHOST_2400_TX_via_STLINK" - }, - { - "flashingMethod": "Radio", - "name": "GHOST_2400_TX_via_STLINK" } ], "userDefines": [ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", - "USE_DYNAMIC_POWER", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", "DISABLE_ALL_BEEPS", @@ -40,24 +29,13 @@ { "flashingMethod": "STLink", "name": "GHOST_2400_TX_LITE_via_STLINK" - }, - { - "flashingMethod": "Radio", - "name": "GHOST_2400_TX_LITE_via_STLINK" } ], "userDefines": [ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", - "USE_DYNAMIC_POWER", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", "DISABLE_ALL_BEEPS", @@ -84,10 +62,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/ghost2400/", diff --git a/devices/happymodel-2400.json b/devices/happymodel-2400.json index 1e42d165a..cc6e9f034 100644 --- a/devices/happymodel-2400.json +++ b/devices/happymodel-2400.json @@ -16,19 +16,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/es24tx/", @@ -52,19 +44,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/es24tx/", @@ -99,10 +83,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -142,10 +123,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -174,10 +152,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/hmpp2400/", diff --git a/devices/happymodel-900.json b/devices/happymodel-900.json index 2c5149db4..905940fa2 100644 --- a/devices/happymodel-900.json +++ b/devices/happymodel-900.json @@ -18,15 +18,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "BLE_HID_JOYSTICK", "UNLOCK_HIGHER_POWER", - "USE_DYNAMIC_POWER", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -59,9 +52,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", @@ -97,19 +87,12 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", "UNLOCK_HIGHER_POWER", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", "DISABLE_ALL_BEEPS", "MY_STARTUP_MELODY", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -137,9 +120,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "RCVR_UART_BAUD" ], diff --git a/devices/happymodel-backpack.json b/devices/happymodel-backpack.json index a8d97b728..dc6614786 100644 --- a/devices/happymodel-backpack.json +++ b/devices/happymodel-backpack.json @@ -6,7 +6,8 @@ { "flashingMethod": "Passthrough", "name": "HappyModel_TX_Backpack_via_PASSTHRU" - },{ + }, + { "flashingMethod": "UART", "name": "HappyModel_TX_Backpack_via_UART" }, diff --git a/devices/hdzero-backpack.json b/devices/hdzero-backpack.json index f079d477f..92f26683e 100644 --- a/devices/hdzero-backpack.json +++ b/devices/hdzero-backpack.json @@ -1,24 +1,24 @@ [ - { - "category": "VRX", - "name": "HDZero RX5.1 VRX Backpack", - "targets": [ - { - "flashingMethod": "UART", - "name": "HDZero_RX5.1_ESP8285_Backpack_via_UART" - }, - { - "flashingMethod": "WIFI", - "name": "HDZero_RX5.1_ESP8285_Backpack_via_WIFI" - } - ], - "userDefines": [ - "BINDING_PHRASE", - "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD" - ], - "wikiUrl": "", - "deviceType": "Backpack", - "aliases": [] - } - ] + { + "category": "VRX", + "name": "HDZero RX5.1 VRX Backpack", + "targets": [ + { + "flashingMethod": "UART", + "name": "HDZero_RX5.1_ESP8285_Backpack_via_UART" + }, + { + "flashingMethod": "WIFI", + "name": "HDZero_RX5.1_ESP8285_Backpack_via_WIFI" + } + ], + "userDefines": [ + "BINDING_PHRASE", + "HOME_WIFI_SSID", + "HOME_WIFI_PASSWORD" + ], + "wikiUrl": "", + "deviceType": "Backpack", + "aliases": [] + } +] diff --git a/devices/hglrc-2400.json b/devices/hglrc-2400.json index d3d5cf0f0..d41fa8256 100644 --- a/devices/hglrc-2400.json +++ b/devices/hglrc-2400.json @@ -16,19 +16,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "", @@ -56,10 +48,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/hglrc-900.json b/devices/hglrc-900.json index 1827d4600..a5627d98b 100644 --- a/devices/hglrc-900.json +++ b/devices/hglrc-900.json @@ -22,9 +22,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", diff --git a/devices/iflight-2400.json b/devices/iflight-2400.json index 706a356b2..2f0baed74 100644 --- a/devices/iflight-2400.json +++ b/devices/iflight-2400.json @@ -17,14 +17,10 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "", "deviceType": "ExpressLRS", diff --git a/devices/iflight-900.json b/devices/iflight-900.json index 223c12a34..b47f32ae8 100644 --- a/devices/iflight-900.json +++ b/devices/iflight-900.json @@ -19,14 +19,10 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "", "deviceType": "ExpressLRS", diff --git a/devices/jumper-2400.json b/devices/jumper-2400.json index 404e1e713..18a016cbf 100644 --- a/devices/jumper-2400.json +++ b/devices/jumper-2400.json @@ -17,14 +17,10 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/jumper-aion/", "deviceType": "ExpressLRS", @@ -52,8 +48,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -84,8 +78,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -115,11 +107,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/jumper-900.json b/devices/jumper-900.json index f31fa9893..83ddd2d61 100644 --- a/devices/jumper-900.json +++ b/devices/jumper-900.json @@ -18,9 +18,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "RCVR_UART_BAUD" ], diff --git a/devices/matek-2400.json b/devices/matek-2400.json index f8f123574..2d21d37d2 100644 --- a/devices/matek-2400.json +++ b/devices/matek-2400.json @@ -20,11 +20,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -53,10 +49,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -87,14 +80,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - { - "key": "USE_DIVERSITY", - "enabled": true - }, "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/namimnorc-2400.json b/devices/namimnorc-2400.json index 77a0f55c3..0dfb94432 100644 --- a/devices/namimnorc-2400.json +++ b/devices/namimnorc-2400.json @@ -16,16 +16,8 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -51,16 +43,8 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -86,10 +70,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/flash2400/", @@ -117,10 +98,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", @@ -153,11 +131,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - { "key": "USE_DIVERSITY", "enabled": true }, "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", diff --git a/devices/namimnorc-900.json b/devices/namimnorc-900.json index a07b85eb2..74313229b 100644 --- a/devices/namimnorc-900.json +++ b/devices/namimnorc-900.json @@ -17,14 +17,7 @@ "REGULATORY_DOMAIN_EU_868", "REGULATORY_DOMAIN_FCC_915", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -51,14 +44,7 @@ "REGULATORY_DOMAIN_EU_868", "REGULATORY_DOMAIN_FCC_915", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -86,9 +72,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "RCVR_UART_BAUD" ], @@ -119,9 +102,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", diff --git a/devices/neutronrc-900.json b/devices/neutronrc-900.json index 23e0061ec..ac7f67c39 100644 --- a/devices/neutronrc-900.json +++ b/devices/neutronrc-900.json @@ -22,9 +22,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", diff --git a/devices/quadcopters-2400.json b/devices/quadcopters-2400.json index 0b3334bda..2282b9b90 100644 --- a/devices/quadcopters-2400.json +++ b/devices/quadcopters-2400.json @@ -16,19 +16,11 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "WS2812_IS_GRB" ], "wikiUrl": "", @@ -63,17 +55,13 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", "RCVR_UART_BAUD", - "RCVR_INVERT_TX", - "USE_DIVERSITY" + "RCVR_INVERT_TX" ], "wikiUrl": "", "deviceType": "ExpressLRS", diff --git a/devices/radiomaster-2400.json b/devices/radiomaster-2400.json index 47dddd6a2..c6b5cb88f 100644 --- a/devices/radiomaster-2400.json +++ b/devices/radiomaster-2400.json @@ -21,7 +21,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -52,13 +51,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-internal/", "deviceType": "ExpressLRS", @@ -89,13 +84,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-external/", "deviceType": "ExpressLRS" @@ -118,13 +109,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-external/", "deviceType": "ExpressLRS", @@ -148,13 +135,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-external/", "deviceType": "ExpressLRS", diff --git a/devices/siyi-2400.json b/devices/siyi-2400.json index 2186cebd0..4ce84d80d 100644 --- a/devices/siyi-2400.json +++ b/devices/siyi-2400.json @@ -16,15 +16,8 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", - "UART_INVERTED", - "USE_DYNAMIC_POWER" + "UART_INVERTED" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/siyifm30/", "deviceType": "ExpressLRS", @@ -47,11 +40,7 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "LOCK_ON_FIRST_CONNECTION", - "USE_500HZ", - "USE_DIVERSITY", "RCVR_UART_BAUD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/siyiFRmini/", @@ -75,15 +64,8 @@ "REGULATORY_DOMAIN_ISM_2400", "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "USE_500HZ", - "UART_INVERTED", - "USE_DYNAMIC_POWER" + "UART_INVERTED" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/siyiFRmini/", "deviceType": "ExpressLRS", diff --git a/devices/steadyview-backpack.json b/devices/steadyview-backpack.json index f054a29e5..fd826b123 100644 --- a/devices/steadyview-backpack.json +++ b/devices/steadyview-backpack.json @@ -12,7 +12,11 @@ "name": "Skyzone_DIY_SteadyView_Backpack_via_WIFI" } ], - "userDefines": ["BINDING_PHRASE", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD"], + "userDefines": [ + "BINDING_PHRASE", + "HOME_WIFI_SSID", + "HOME_WIFI_PASSWORD" + ], "wikiUrl": "", "deviceType": "Backpack", "aliases": [] @@ -30,7 +34,11 @@ "name": "Skyzone_Onboard_ESP32_Backpack_via_WIFI" } ], - "userDefines": ["BINDING_PHRASE", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD"], + "userDefines": [ + "BINDING_PHRASE", + "HOME_WIFI_SSID", + "HOME_WIFI_PASSWORD" + ], "wikiUrl": "", "deviceType": "Backpack", "aliases": [] diff --git a/devices/vantac-2400.json b/devices/vantac-2400.json index 8c9de1c2a..db9776ef1 100644 --- a/devices/vantac-2400.json +++ b/devices/vantac-2400.json @@ -17,14 +17,10 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/vantac2400/", "deviceType": "ExpressLRS", diff --git a/src/api/index.ts b/src/api/index.ts index b03474bec..c08992326 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -9,7 +9,7 @@ import getPort from 'get-port'; import { buildSchema } from 'type-graphql'; import { Container } from 'typedi'; import path from 'path'; -import { ConfigToken, FirmwareParamsLoaderType, IConfig } from './src/config'; +import { ConfigToken, IConfig } from './src/config'; import PlatformioFlashingStrategyService from './src/services/PlatformioFlashingStrategy'; import Platformio from './src/library/Platformio'; import FirmwareBuilder from './src/library/FirmwareBuilder'; @@ -35,13 +35,12 @@ import LuaService from './src/services/Lua'; import LuaResolver from './src/graphql/resolvers/Lua.resolver'; import MulticastDnsSimulatorService from './src/services/MulticastDns/MulticastDnsSimulator'; import MulticastDnsNotificationsService from './src/services/MulticastDnsNotificationsService'; -import HttpTargetsService from './src/services/TargetsLoader/HttpTargets'; import TargetsLoader from './src/services/TargetsLoader'; -import HttpUserDefinesLoader from './src/services/UserDefinesLoader/HttpUserDefinesLoader'; import GitUserDefinesLoader from './src/services/UserDefinesLoader/GitUserDefinesLoader'; import FlashingStrategyLocatorService from './src/services/FlashingStrategyLocator'; import Python from './src/library/Python'; import BinaryFlashingStrategyService from './src/services/BinaryFlashingStrategy'; +import DeviceDescriptionsLoader from './src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader'; export default class ApiServer { app: Express | undefined; @@ -58,12 +57,7 @@ export default class ApiServer { Container.set([{ id: PubSubToken, value: pubSub }]); Container.set([{ id: LoggerToken, value: logger }]); - const python = new Python( - config.getPlatformioPath, - config.PATH, - config.env, - logger - ); + const python = new Python(config.PATH, config.env, logger); Container.set(Python, python); @@ -110,41 +104,23 @@ export default class ApiServer { Container.set(DeviceService, deviceService); - let userDefinesBuilder: UserDefinesBuilder; - if (config.userDefinesLoader === FirmwareParamsLoaderType.Git) { - userDefinesBuilder = new UserDefinesBuilder( - new GitUserDefinesLoader( - logger, - config.PATH, - config.userDefinesStoragePath - ), - deviceService - ); - } else if (config.userDefinesLoader === FirmwareParamsLoaderType.Http) { - userDefinesBuilder = new UserDefinesBuilder( - new HttpUserDefinesLoader(logger), - deviceService - ); - } else { - throw new Error('FirmwareTargetsLoaderType is not set'); - } - - Container.set(UserDefinesBuilder, userDefinesBuilder); - - let targetsLoader: TargetsLoader; - if (config.targetsLoader === FirmwareParamsLoaderType.Git) { - targetsLoader = new GitTargetsService( + const userDefinesBuilder = new UserDefinesBuilder( + new GitUserDefinesLoader( logger, - deviceService, config.PATH, - config.targetsStoragePath - ); - } else if (config.targetsLoader === FirmwareParamsLoaderType.Http) { - targetsLoader = new HttpTargetsService(logger, deviceService); - } else { - throw new Error('FirmwareTargetsLoaderType is not set'); - } + config.userDefinesStoragePath + ), + deviceService + ); + + Container.set(UserDefinesBuilder, userDefinesBuilder); + const targetsLoader = new GitTargetsService( + logger, + deviceService, + config.PATH, + config.targetsStoragePath + ); Container.set(TargetsLoader, targetsLoader); const platformioFlashingStrategyService = @@ -152,18 +128,24 @@ export default class ApiServer { config.PATH, config.firmwaresPath, platformio, - new FirmwareBuilder(platformio), + new FirmwareBuilder(platformio, logger), pubSub, logger, - python, userDefinesBuilder, targetsLoader ); + const deviceDescriptionsLoader = new DeviceDescriptionsLoader( + logger, + config.PATH, + path.join(config.userDataPath, 'firmwares', 'binary-targets') + ); const binaryFlashingStrategyService = new BinaryFlashingStrategyService( config.PATH, path.join(config.userDataPath, 'firmwares', 'binary'), pubSub, + python, + deviceDescriptionsLoader, logger ); diff --git a/src/api/src/config/index.ts b/src/api/src/config/index.ts index 7a0bd90fe..de9528803 100644 --- a/src/api/src/config/index.ts +++ b/src/api/src/config/index.ts @@ -1,10 +1,5 @@ import { Token } from 'typedi'; -export enum FirmwareParamsLoaderType { - Git = 'git', - Http = 'http', -} - export interface IConfig { configuratorGit: { url: string; @@ -18,9 +13,7 @@ export interface IConfig { env: NodeJS.ProcessEnv; getPlatformioPath: string; platformioStateTempStoragePath: string; - targetsLoader: FirmwareParamsLoaderType; targetsStoragePath: string; - userDefinesLoader: FirmwareParamsLoaderType; userDefinesStoragePath: string; userDataPath: string; } diff --git a/src/api/src/factories/TargetUserDefinesFactory.ts b/src/api/src/factories/TargetUserDefinesFactory.ts index 7ade7df1e..da21ecfbe 100644 --- a/src/api/src/factories/TargetUserDefinesFactory.ts +++ b/src/api/src/factories/TargetUserDefinesFactory.ts @@ -50,40 +50,14 @@ export default class TargetUserDefinesFactory { UserDefineOptionGroup.RegulatoryDomain2400 ); // Hybrid switches - case UserDefineKey.HYBRID_SWITCHES_8: - return UserDefine.Boolean(UserDefineKey.HYBRID_SWITCHES_8, true); - case UserDefineKey.ENABLE_TELEMETRY: - return UserDefine.Boolean(UserDefineKey.ENABLE_TELEMETRY); case UserDefineKey.TLM_REPORT_INTERVAL_MS: return UserDefine.Text(UserDefineKey.TLM_REPORT_INTERVAL_MS, '240LU'); // Performance options - case UserDefineKey.FAST_SYNC: - return UserDefine.Boolean(UserDefineKey.FAST_SYNC); - case UserDefineKey.R9M_UNLOCK_HIGHER_POWER: - return UserDefine.Boolean(UserDefineKey.R9M_UNLOCK_HIGHER_POWER); case UserDefineKey.UNLOCK_HIGHER_POWER: return UserDefine.Boolean(UserDefineKey.UNLOCK_HIGHER_POWER, true); - case UserDefineKey.USE_DIVERSITY: - return UserDefine.Boolean(UserDefineKey.USE_DIVERSITY); - case UserDefineKey.NO_SYNC_ON_ARM: - return UserDefine.Boolean(UserDefineKey.NO_SYNC_ON_ARM); - case UserDefineKey.ARM_CHANNEL: - return UserDefine.Enum( - UserDefineKey.ARM_CHANNEL, - ['AUX1', 'AUX2', 'AUX3', 'AUX4', 'AUX5', 'AUX6'], - 'AUX1' - ); - case UserDefineKey.FEATURE_OPENTX_SYNC: - return UserDefine.Boolean(UserDefineKey.FEATURE_OPENTX_SYNC, true); - case UserDefineKey.FEATURE_OPENTX_SYNC_AUTOTUNE: - return UserDefine.Boolean(UserDefineKey.FEATURE_OPENTX_SYNC_AUTOTUNE); case UserDefineKey.LOCK_ON_FIRST_CONNECTION: return UserDefine.Boolean(UserDefineKey.LOCK_ON_FIRST_CONNECTION, true); - case UserDefineKey.LOCK_ON_50HZ: - return UserDefine.Boolean(UserDefineKey.LOCK_ON_50HZ); // Compatibility options - case UserDefineKey.USE_UART2: - return UserDefine.Boolean(UserDefineKey.USE_UART2); case UserDefineKey.UART_INVERTED: return UserDefine.Boolean(UserDefineKey.UART_INVERTED, true); case UserDefineKey.USE_R9MM_R9MINI_SBUS: @@ -93,10 +67,6 @@ export default class TargetUserDefinesFactory { case UserDefineKey.RCVR_INVERT_TX: return UserDefine.Boolean(UserDefineKey.RCVR_INVERT_TX); // Other options - case UserDefineKey.BLE_HID_JOYSTICK: - return UserDefine.Boolean(UserDefineKey.BLE_HID_JOYSTICK); - case UserDefineKey.USE_ESP8266_BACKPACK: - return UserDefine.Boolean(UserDefineKey.USE_ESP8266_BACKPACK, true); case UserDefineKey.USE_TX_BACKPACK: return UserDefine.Boolean(UserDefineKey.USE_TX_BACKPACK); case UserDefineKey.JUST_BEEP_ONCE: @@ -107,10 +77,6 @@ export default class TargetUserDefinesFactory { return UserDefine.Boolean(UserDefineKey.DISABLE_STARTUP_BEEP); case UserDefineKey.MY_STARTUP_MELODY: return UserDefine.Text(UserDefineKey.MY_STARTUP_MELODY); - case UserDefineKey.USE_500HZ: - return UserDefine.Boolean(UserDefineKey.USE_500HZ); - case UserDefineKey.USE_DYNAMIC_POWER: - return UserDefine.Boolean(UserDefineKey.USE_DYNAMIC_POWER); case UserDefineKey.WS2812_IS_GRB: return UserDefine.Boolean(UserDefineKey.WS2812_IS_GRB); // Network @@ -124,8 +90,6 @@ export default class TargetUserDefinesFactory { true ); break; - case UserDefineKey.AUTO_WIFI_ON_BOOT: - return UserDefine.Boolean(UserDefineKey.AUTO_WIFI_ON_BOOT); case UserDefineKey.AUTO_WIFI_ON_INTERVAL: return UserDefine.Text(UserDefineKey.AUTO_WIFI_ON_INTERVAL, '60', true); default: diff --git a/src/api/src/graphql/inputs/UserDefineInput.ts b/src/api/src/graphql/inputs/UserDefineInput.ts index 246562861..8af6a8871 100644 --- a/src/api/src/graphql/inputs/UserDefineInput.ts +++ b/src/api/src/graphql/inputs/UserDefineInput.ts @@ -21,7 +21,7 @@ export default class UserDefineInput { constructor() { this.type = UserDefineKind.Boolean; - this.key = UserDefineKey.FEATURE_OPENTX_SYNC; + this.key = UserDefineKey.AUTO_WIFI_ON_INTERVAL; this.enabled = false; } } diff --git a/src/api/src/graphql/resolvers/Firmware.resolver.ts b/src/api/src/graphql/resolvers/Firmware.resolver.ts index 965cee3e2..d2d0c3bce 100644 --- a/src/api/src/graphql/resolvers/Firmware.resolver.ts +++ b/src/api/src/graphql/resolvers/Firmware.resolver.ts @@ -7,7 +7,7 @@ import { Root, Subscription, } from 'type-graphql'; -import {Service} from 'typedi'; +import { Service } from 'typedi'; import UserDefine from '../../models/UserDefine'; import BuildFlashFirmwareInput from '../inputs/BuildFlashFirmwareInput'; import BuildFlashFirmwareResult from '../objects/BuildFlashFirmwareResult'; @@ -16,29 +16,25 @@ import PubSubTopic from '../../pubsub/enum/PubSubTopic'; import BuildLogUpdate from '../../models/BuildLogUpdate'; import ClearPlatformioCoreDirResult from '../objects/ClearPlatformioCoreDirResult'; import TargetDeviceOptionsArgs from '../args/TargetDeviceOptions'; -import UserDefinesBuilder from '../../services/UserDefinesBuilder'; import ClearFirmwareFilesResult from '../objects/ClearFirmwareFilesResult'; import TargetArgs from '../args/Target'; import Device from '../../models/Device'; import GitRepository from '../inputs/GitRepositoryInput'; import FlashingStrategyLocatorService from '../../services/FlashingStrategyLocator'; -import { - BuildProgressNotificationPayload -} from '../../services/FlashingStrategyLocator/BuildProgressNotificationPayload'; -import {BuildLogUpdatePayload} from '../../services/FlashingStrategyLocator/BuildLogUpdatePayload'; +import { BuildProgressNotificationPayload } from '../../services/FlashingStrategyLocator/BuildProgressNotificationPayload'; +import { BuildLogUpdatePayload } from '../../services/FlashingStrategyLocator/BuildLogUpdatePayload'; import Platformio from '../../library/Platformio'; import BuildUserDefinesTxtInput from '../inputs/BuildUserDefinesTxtInput'; import BuildUserDefinesTxtResult from '../objects/BuilduserDefinesTxtResult'; +import UserDefinesTxtFactory from '../../factories/UserDefinesTxtFactory'; @Service() @Resolver() export default class FirmwareResolver { constructor( private flashingStrategyLocatorService: FlashingStrategyLocatorService, - private platformio: Platformio, - private userDefinesBuilder: UserDefinesBuilder, - ) { - } + private platformio: Platformio + ) {} @Query(() => [Device]) async availableFirmwareTargets( @@ -87,9 +83,7 @@ export default class FirmwareResolver { async buildUserDefinesTxt( @Arg('input') input: BuildUserDefinesTxtInput ): Promise { - const userDefinesTxt = await this.userDefinesBuilder.buildUserDefinesTxt( - input.userDefines - ); + const userDefinesTxt = new UserDefinesTxtFactory().build(input.userDefines); return new BuildUserDefinesTxtResult(userDefinesTxt); } diff --git a/src/api/src/library/ConfigureFirmware/Config.ts b/src/api/src/library/ConfigureFirmware/Config.ts deleted file mode 100644 index 7a4e932fe..000000000 --- a/src/api/src/library/ConfigureFirmware/Config.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface Config { - product_name: string; - lua_name?: string; - layout_file?: string; - upload_methods: string[]; - platform: string; - firmware: string; - features?: string[]; - prior_target_name: string; - overlay?: { [key: string]: string }; -} diff --git a/src/api/src/library/ConfigureFirmware/Domain.ts b/src/api/src/library/ConfigureFirmware/Domain.ts deleted file mode 100644 index b7f44383c..000000000 --- a/src/api/src/library/ConfigureFirmware/Domain.ts +++ /dev/null @@ -1,10 +0,0 @@ -enum Domain { - AU915 = 0, - FCC915 = 1, - EU868 = 2, - IN866 = 3, - AU433 = 4, - EU433 = 5, -} - -export default Domain; diff --git a/src/api/src/library/ConfigureFirmware/Firmware.ts b/src/api/src/library/ConfigureFirmware/Firmware.ts deleted file mode 100644 index a19d3af19..000000000 --- a/src/api/src/library/ConfigureFirmware/Firmware.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Firmware { - data: Uint8Array; - address: number; -} diff --git a/src/api/src/library/ConfigureFirmware/FirmwareFiles.ts b/src/api/src/library/ConfigureFirmware/FirmwareFiles.ts deleted file mode 100644 index 3e6c1a651..000000000 --- a/src/api/src/library/ConfigureFirmware/FirmwareFiles.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Firmware } from './Firmware'; - -export interface FirmwareFiles { - bootloader?: Firmware; - partitions?: Firmware; - boot_app0?: Firmware; - firmware?: Firmware; -} diff --git a/src/api/src/library/ConfigureFirmware/Options.ts b/src/api/src/library/ConfigureFirmware/Options.ts deleted file mode 100644 index eb6faaeb3..000000000 --- a/src/api/src/library/ConfigureFirmware/Options.ts +++ /dev/null @@ -1,19 +0,0 @@ -import Domain from './Domain'; - -export interface Options { - uid?: number[]; - 'wifi-on-interval'?: number | undefined; - 'wifi-ssid'?: string | undefined; - 'wifi-password'?: string | undefined; - 'rcvr-uart-baud'?: number | undefined; - 'rcvr-invert-tx'?: boolean | undefined; - 'lock-on-first-connection'?: boolean | undefined; - 'tlm-interval'?: number | undefined; - 'fan-runtime'?: number | undefined; - 'uart-inverted'?: boolean | undefined; - 'unlock-higher-power'?: boolean | undefined; - 'r9mm-mini-sbus'?: boolean | undefined; - beeptype?: number | undefined; - domain?: Domain | undefined; - melody?: number[][] | undefined; -} diff --git a/src/api/src/library/ConfigureFirmware/Targets.ts b/src/api/src/library/ConfigureFirmware/Targets.ts deleted file mode 100644 index 195072dc4..000000000 --- a/src/api/src/library/ConfigureFirmware/Targets.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Config } from './Config'; - -export type Targets = { - [key: string]: { - name: string; - tx_2400: { - [key: string]: Config; - }; - rx_2400: { - [key: string]: Config; - }; - tx_900: { - [key: string]: Config; - }; - rx_900: { - [key: string]: Config; - }; - }; -}; diff --git a/src/api/src/library/ConfigureFirmware/index.ts b/src/api/src/library/ConfigureFirmware/index.ts deleted file mode 100644 index 7f2c28680..000000000 --- a/src/api/src/library/ConfigureFirmware/index.ts +++ /dev/null @@ -1,282 +0,0 @@ -/* eslint-disable no-param-reassign */ -/* eslint-disable no-bitwise */ -/* eslint-disable import/prefer-default-export */ -import fs from 'fs'; -import { Config } from './Config'; -import { Firmware } from './Firmware'; -import { FirmwareFiles } from './FirmwareFiles'; -import { Options } from './Options'; - -export default class Configure { - static #MAGIC = new Uint8Array([ - 0xbe, 0xef, 0xba, 0xbe, 0xca, 0xfe, 0xf0, 0x0d, - ]); - - static #find_patch_location(binary: Uint8Array) { - return binary.findIndex((_, i, a) => { - let j = 0; - while (j < Configure.#MAGIC.length && a[i + j] === Configure.#MAGIC[j]) { - j++; - } - return j === Configure.#MAGIC.length; - }); - } - - static #write32(binary: Uint8Array, pos: number, val: number) { - if (val !== undefined) { - binary[pos + 0] = (val >> 0) & 0xff; - binary[pos + 1] = (val >> 8) & 0xff; - binary[pos + 2] = (val >> 16) & 0xff; - binary[pos + 3] = (val >> 24) & 0xff; - } - return pos + 4; - } - - static #patch_buzzer(binary: Uint8Array, pos: number, options: Options) { - binary[pos] = options.beeptype ?? 2; - pos += 1; - for (let i = 0; i < 32 * 4; i++) { - binary[pos + i] = 0; - } - const { melody } = options; - if (melody) { - for (let i = 0; i < melody.length; i++) { - binary[pos + i * 4 + 0] = melody[i][0] & 0xff; - binary[pos + i * 4 + 1] = (melody[i][0] >> 8) & 0xff; - binary[pos + i * 4 + 2] = melody[i][1] & 0xff; - binary[pos + i * 4 + 3] = (melody[i][1] >> 8) & 0xff; - } - } - pos += 32 * 4; - return pos; - } - - static #patch_tx_params(binary: Uint8Array, pos: number, options: Options) { - pos = this.#write32(binary, pos, options['tlm-interval'] ?? 240); - pos = this.#write32(binary, pos, options['fan-runtime'] ?? 30); - let val = binary[pos]; - if (options['uart-inverted'] !== undefined) { - val &= ~1; - val |= options['uart-inverted'] ? 1 : 0; - } - if (options['unlock-higher-power'] !== undefined) { - val &= ~2; - val |= options['unlock-higher-power'] ? 2 : 0; - } - binary[pos] = val; - return pos + 1; - } - - static #patch_rx_params(binary: Uint8Array, pos: number, options: Options) { - pos = this.#write32(binary, pos, options['rcvr-uart-baud'] ?? 400000); - let val = binary[pos]; - if (options['rcvr-invert-tx'] !== undefined) { - val &= ~1; - val |= options['rcvr-invert-tx'] ? 1 : 0; - } - if (options['lock-on-first-connection'] !== undefined) { - val &= ~2; - val |= options['lock-on-first-connection'] ? 2 : 0; - } - if (options['r9mm-mini-sbus'] !== undefined) { - val &= ~4; - val |= options['r9mm-mini-sbus'] ? 4 : 0; - } - binary[pos] = val; - return pos + 1; - } - - static #configureSTM32( - binary: Uint8Array, - deviceType: string, - radioType: string, - options: Options - ) { - let pos = this.#find_patch_location(binary); - if (pos === -1) - throw new Error( - 'Configuration magic not found in firmware file. Is this a 3.x firmware?' - ); - - pos += 8; // Skip magic - const version = (binary[pos] + binary[pos + 1]) << 8; - pos += 2; // Skip version - if (version === 0) { - pos += 1; // Skip the (old) hardware flag - } - - // Poke in the domain - if (radioType === 'sx127x' && options.domain) { - binary[pos] = options.domain; - } - pos += 1; - - // Poke in the UID (if there is one) - if (options.uid) { - binary[pos] = 1; - for (let i = 0; i < 6; i++) { - binary[pos + 1 + i] = options.uid[i]; - } - } else { - binary[pos] = 0; - } - pos += 7; - - if (deviceType === 'TX') { - // TX target - pos = this.#patch_tx_params(binary, pos, options); - if (options.beeptype) { - // Has a Buzzer - pos = this.#patch_buzzer(binary, pos, options); - } - } else if (deviceType === 'RX') { - // RX target - pos = this.#patch_rx_params(binary, pos, options); - } - - return binary; - } - - static #fetch_file = async ( - file: string, - address: number, - transform = (e: Uint8Array) => e - ): Promise => { - const buffer = await fs.promises.readFile(file); - const dataArray = new Uint8Array(buffer); - const data = transform(dataArray); - return { data, address }; - }; - - static #findFirmwareEnd = (binary: Uint8Array, config: Config) => { - let pos = 0x0; - if (config.platform === 'esp8285') pos = 0x1000; - if (binary[pos] !== 0xe9) - throw new Error( - 'The file provided does not the right magic for a firmware file!' - ); - let segments = binary[pos + 1]; - if (config.platform === 'esp32') pos = 24; - else pos = 0x1008; - while (segments--) { - const size = - binary[pos + 4] + - (binary[pos + 5] << 8) + - (binary[pos + 6] << 16) + - (binary[pos + 7] << 24); - pos += 8 + size; - } - pos = (pos + 16) & ~15; - if (config.platform === 'esp32') pos += 32; - return pos; - }; - - static #appendArray = (arr1: Uint8Array, arr2: Uint8Array) => { - const c = new Uint8Array(arr1.length + arr2.length); - c.set(arr1, 0); - c.set(arr2, arr1.length); - return c; - }; - - static #ui8ToBstr = (u8Array: Uint8Array) => { - const len = u8Array.length; - let bStr = ''; - for (let i = 0; i < len; i++) { - bStr += String.fromCharCode(u8Array[i]); - } - return bStr; - }; - - static #bstrToUi8 = (bStr: string) => { - const len = bStr.length; - const u8array = new Uint8Array(len); - for (let i = 0; i < len; i++) { - u8array[i] = bStr.charCodeAt(i); - } - return u8array; - }; - - static #configureESP = ( - binary: Uint8Array, - config: Config, - options: Options - ) => { - const end = this.#findFirmwareEnd(binary, config); - binary = binary.slice(0, end); - binary = this.#appendArray( - binary, - this.#bstrToUi8(config.product_name.padEnd(128, '\x00')) - ); - binary = this.#appendArray( - binary, - this.#bstrToUi8(config.lua_name.padEnd(16, '\x00')) - ); - binary = this.#appendArray( - binary, - this.#bstrToUi8(JSON.stringify(options).padEnd(512, '\x00')) - ); - return binary; - }; - - static getFirmware = async ( - deviceType: string, - radioType: string, - config: Config, - options: Options, - folder: string, - fcclbt: string - ): Promise => { - const firmware = `${folder}/${fcclbt}/${config.firmware}/firmware.bin`; - if (config.platform === 'stm32') { - const entry = await this.#fetch_file(firmware, 0, (bin) => - this.#configureSTM32(bin, deviceType, radioType, options) - ); - return { firmware: { data: entry.data, address: 0 } }; - } - const hardwareLayoutFile = await this.#fetch_file( - `${folder}/hardware/${deviceType}/${config.layout_file}`, - 0 - ); - const firmwareFiles: FirmwareFiles = {}; - if (config.platform === 'esp32') { - firmwareFiles.bootloader = await Promise.any([ - this.#fetch_file(`${folder}/bootloader_dio_40m.bin`, 0x1000), - this.#fetch_file(`${folder}/bootloader_qio_40m.bin`, 0x1000), - ]); - firmwareFiles.partitions = await this.#fetch_file( - `${folder}/partitions.bin`, - 0x8000 - ); - firmwareFiles.boot_app0 = await this.#fetch_file( - `${folder}/boot_app0.bin`, - 0xe000 - ); - firmwareFiles.firmware = await this.#fetch_file( - firmware, - 0x10000, - (bin) => Configure.#configureESP(bin, config, options) - ); - } else if (config.platform === 'esp8285') { - firmwareFiles.firmware = await this.#fetch_file(firmware, 0x0, (bin) => - Configure.#configureESP(bin, config, options) - ); - } - - if (config.overlay) { - config.overlay = {}; - } - const hardwareLayoutData = this.#bstrToUi8( - JSON.stringify({ - ...JSON.parse(this.#ui8ToBstr(hardwareLayoutFile.data)), - ...config.overlay, - }) - ); - if (firmwareFiles.firmware) { - firmwareFiles.firmware.data = await this.#appendArray( - firmwareFiles.firmware.data, - this.#appendArray(hardwareLayoutData, new Uint8Array([0])) - ); - } - return firmwareFiles; - }; -} diff --git a/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts b/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts index 8a8bd6128..0d895a6ee 100644 --- a/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts +++ b/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts @@ -11,43 +11,25 @@ enum UserDefineKey { REGULATORY_DOMAIN_ISM_2400 = 'DRegulatory_Domain_ISM_2400', REGULATORY_DOMAIN_EU_CE_2400 = 'DRegulatory_Domain_EU_CE_2400', // Hybrid switches - HYBRID_SWITCHES_8 = 'DHYBRID_SWITCHES_8', - ENABLE_TELEMETRY = 'DENABLE_TELEMETRY', TLM_REPORT_INTERVAL_MS = 'DTLM_REPORT_INTERVAL_MS', - // Performance options - FAST_SYNC = 'DFAST_SYNC', // #unlocks >250mw output power for R9M (Fan mod suggested: https://github.com/AlessandroAU/ExpressLRS/wiki/R9M-Fan-Mod-Cover) - // deprecated in favour of DUNLOCK_HIGHER_POWER - R9M_UNLOCK_HIGHER_POWER = 'DR9M_UNLOCK_HIGHER_POWER', UNLOCK_HIGHER_POWER = 'DUNLOCK_HIGHER_POWER', - USE_DIVERSITY = 'DUSE_DIVERSITY', - NO_SYNC_ON_ARM = 'DNO_SYNC_ON_ARM', - ARM_CHANNEL = 'DARM_CHANNEL', - FEATURE_OPENTX_SYNC = 'DFEATURE_OPENTX_SYNC', - FEATURE_OPENTX_SYNC_AUTOTUNE = 'DFEATURE_OPENTX_SYNC_AUTOTUNE', LOCK_ON_FIRST_CONNECTION = 'DLOCK_ON_FIRST_CONNECTION', - LOCK_ON_50HZ = 'DLOCK_ON_50HZ', // Compatibility options - USE_UART2 = 'DUSE_UART2', UART_INVERTED = 'DUART_INVERTED', USE_R9MM_R9MINI_SBUS = 'DUSE_R9MM_R9MINI_SBUS', RCVR_UART_BAUD = 'DRCVR_UART_BAUD', RCVR_INVERT_TX = 'DRCVR_INVERT_TX', // Other options - BLE_HID_JOYSTICK = 'DBLE_HID_JOYSTICK', - USE_ESP8266_BACKPACK = 'DUSE_ESP8266_BACKPACK', USE_TX_BACKPACK = 'DUSE_TX_BACKPACK', JUST_BEEP_ONCE = 'DJUST_BEEP_ONCE', DISABLE_ALL_BEEPS = 'DDISABLE_ALL_BEEPS', DISABLE_STARTUP_BEEP = 'DDISABLE_STARTUP_BEEP', MY_STARTUP_MELODY = 'DMY_STARTUP_MELODY', - USE_500HZ = 'DUSE_500HZ', - USE_DYNAMIC_POWER = 'DUSE_DYNAMIC_POWER', WS2812_IS_GRB = 'DWS2812_IS_GRB', // Network HOME_WIFI_SSID = 'DHOME_WIFI_SSID', HOME_WIFI_PASSWORD = 'DHOME_WIFI_PASSWORD', - AUTO_WIFI_ON_BOOT = 'DAUTO_WIFI_ON_BOOT', AUTO_WIFI_ON_INTERVAL = 'DAUTO_WIFI_ON_INTERVAL', DEVICE_NAME = 'DDEVICE_NAME', } diff --git a/src/api/src/library/FirmwareBuilder/index.ts b/src/api/src/library/FirmwareBuilder/index.ts index 4068258b3..84a0dd654 100644 --- a/src/api/src/library/FirmwareBuilder/index.ts +++ b/src/api/src/library/FirmwareBuilder/index.ts @@ -4,6 +4,7 @@ import Platformio from '../Platformio'; import { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander'; import UserDefineKey from './Enum/UserDefineKey'; import UploadType from '../Platformio/Enum/UploadType'; +import { LoggerService } from '../../logger'; interface UserDefinesCompatibilityResult { compatible: boolean; @@ -11,7 +12,7 @@ interface UserDefinesCompatibilityResult { } export default class FirmwareBuilder { - constructor(private platformio: Platformio) {} + constructor(private platformio: Platformio, private logger: LoggerService) {} async checkDefaultUserDefinesCompatibilityAtPath( firmwarePath: string, @@ -72,7 +73,7 @@ export default class FirmwareBuilder { // eslint-disable-next-line no-restricted-syntax for (const location of paths) { if (fs.existsSync(location)) { - this.logger?.log('removing stale bin', { + this.logger.log('removing stale bin', { location, }); fs.unlinkSync(location); diff --git a/src/api/src/library/Platformio/index.ts b/src/api/src/library/Platformio/index.ts index 3d6f51c1c..eae9902d1 100644 --- a/src/api/src/library/Platformio/index.ts +++ b/src/api/src/library/Platformio/index.ts @@ -3,8 +3,8 @@ import path from 'path'; import child_process from 'child_process'; import rimraf from 'rimraf'; import * as os from 'os'; -import Commander, {CommandResult, NoOpFunc, OnOutputFunc} from '../Commander'; -import {LoggerService} from '../../logger'; +import Commander, { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander'; +import { LoggerService } from '../../logger'; import UploadType from './Enum/UploadType'; import Python from '../Python'; @@ -39,8 +39,7 @@ export default class Platformio { private env: NodeJS.ProcessEnv, private logger: LoggerService, private python: Python - ) { - } + ) {} async install(onUpdate: OnOutputFunc = NoOpFunc): Promise { return this.python.runPythonScript(this.getPlatformioPath, [], onUpdate); @@ -51,6 +50,17 @@ export default class Platformio { return this.python.runPythonScript(this.getPlatformioPath, cmdArgs); } + async checkPython(): Promise { + const pyExec = await this.python.findPythonExecutable(this.python.PATH); + return new Commander().runCommand( + pyExec, + [this.getPlatformioPath, 'check', 'python'], + { + env: this.env, + } + ); + } + async getPlatformioState(): Promise { const statePath = path.join( this.stateTempStoragePath, @@ -112,7 +122,7 @@ export default class Platformio { } // eslint-disable-next-line @typescript-eslint/naming-convention - let {platformio_exe} = state; + let { platformio_exe } = state; // if using shell, surround exe in quotes in case path has a space in it if (options.shell) { platformio_exe = `"${platformio_exe}"`; diff --git a/src/api/src/library/Python/index.ts b/src/api/src/library/Python/index.ts index ea9f9c306..143327ba8 100644 --- a/src/api/src/library/Python/index.ts +++ b/src/api/src/library/Python/index.ts @@ -1,17 +1,15 @@ /* eslint-disable no-await-in-loop */ import fs from 'fs'; import path from 'path'; -import Commander, {CommandResult, NoOpFunc, OnOutputFunc} from '../Commander'; -import {LoggerService} from '../../logger'; +import Commander, { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander'; +import { LoggerService } from '../../logger'; export default class Python { constructor( - private getPlatformioPath: string, - private PATH: string, + public PATH: string, private env: NodeJS.ProcessEnv, private logger: LoggerService - ) { - } + ) {} async runPythonScript( script: string, @@ -32,17 +30,6 @@ export default class Python { ); } - async checkPython(): Promise { - const pyExec = await this.findPythonExecutable(this.PATH); - return new Commander().runCommand( - pyExec, - [this.getPlatformioPath, 'check', 'python'], - { - env: this.env, - } - ); - } - async findPythonExecutable(envPath: string): Promise { const IS_WINDOWS = process.platform.startsWith('win'); const exenames = IS_WINDOWS diff --git a/src/api/src/models/enum/FlashingMethod.ts b/src/api/src/models/enum/FlashingMethod.ts index 9c87148c9..d0da5c0d3 100644 --- a/src/api/src/models/enum/FlashingMethod.ts +++ b/src/api/src/models/enum/FlashingMethod.ts @@ -8,7 +8,6 @@ enum FlashingMethod { UART = 'UART', WIFI = 'WIFI', EdgeTxPassthrough = 'EdgeTxPassthrough', - Radio = 'Radio', Passthrough = 'Passthrough', } diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts new file mode 100644 index 000000000..a68b68cb1 --- /dev/null +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts @@ -0,0 +1,296 @@ +import { Service } from 'typedi'; +import path from 'path'; +import FirmwareSource from '../../../models/enum/FirmwareSource'; +import TargetArgs from '../../../graphql/args/Target'; +import { LoggerService } from '../../../logger'; +import Device from '../../../models/Device'; +import { + findGitExecutable, + GitFirmwareDownloader, +} from '../../../library/FirmwareDownloader'; +import Mutex from '../../../library/Mutex'; +import { DeviceDescription, TargetsJSONLoader } from '../TargetsJSONLoader'; +import FlashingMethod from '../../../models/enum/FlashingMethod'; +import Target from '../../../models/Target'; +import DeviceType from '../../../models/enum/DeviceType'; +import { UserDefineFilters } from '../../UserDefinesLoader'; +import UserDefine from '../../../models/UserDefine'; +import TargetUserDefinesFactory from '../../../factories/TargetUserDefinesFactory'; +import UserDefineKey from '../../../library/FirmwareBuilder/Enum/UserDefineKey'; +import PullRequest from '../../../models/PullRequest'; + +export interface GitRepository { + url: string; + srcFolder: string; +} + +export interface FirmwareVersion { + source: FirmwareSource; + gitTag: string; + gitBranch: string; + gitCommit: string; + localPath: string; + gitPullRequest: PullRequest | null; +} + +@Service() +export default class DeviceDescriptionsLoader { + mutex: Mutex; + + constructor( + private logger: LoggerService, + private PATH: string, + private targetStoragePath: string + ) { + this.mutex = new Mutex(); + } + + private uploadMethodToFlashingMethod(uploadMethod: string): FlashingMethod { + switch (uploadMethod.toLowerCase()) { + case 'betaflight': + return FlashingMethod.BetaflightPassthrough; + case 'dfu': + return FlashingMethod.DFU; + case 'etx': + return FlashingMethod.EdgeTxPassthrough; + case 'stlink': + return FlashingMethod.STLink; + case 'uart': + return FlashingMethod.UART; + case 'wifi': + return FlashingMethod.WIFI; + default: + throw new Error(`Upload Method ${uploadMethod} Not Recognized!`); + } + } + + private configToDevice( + id: string, + category: string, + config: DeviceDescription + ): Device { + return new Device( + id, + config.product_name, + category, + config.upload_methods.map((uploadMethod) => { + const targetName = `${id}.${uploadMethod}`; + return new Target( + targetName, + targetName, + this.uploadMethodToFlashingMethod(uploadMethod) + ); + }), + [], + DeviceType.ExpressLRS, + true + ); + } + + async loadTargetsList( + args: TargetArgs, + gitRepository: GitRepository + ): Promise { + await this.mutex.tryLockWithTimeout(60000); + try { + const targetsDataDirectory = await this.loadTargetsData( + args, + gitRepository + ); + + const targetsJSONLoader = new TargetsJSONLoader(this.logger); + const targetsJSONPath = path.join(targetsDataDirectory, 'targets.json'); + const data = await targetsJSONLoader.loadDeviceDescriptions( + targetsJSONPath + ); + const devices: Device[] = []; + Object.keys(data).forEach((id) => { + devices.push( + this.configToDevice(id, data[id].category, data[id].config) + ); + }); + return devices; + } finally { + this.mutex.unlock(); + } + } + + private async loadTargetsData( + args: FirmwareVersion, + gitRepository: GitRepository + ): Promise { + let gitPath = ''; + try { + gitPath = await findGitExecutable(this.PATH); + } catch (e) { + this.logger?.error('failed to find git', undefined, { + PATH: this.PATH, + err: e, + }); + throw e; + } + this.logger?.log('git path', { + gitPath, + }); + + const firmwareDownload = new GitFirmwareDownloader( + { + baseDirectory: this.targetStoragePath, + gitBinaryLocation: gitPath, + }, + this.logger + ); + + const srcFolder = + gitRepository.srcFolder === '/' ? '' : `${gitRepository.srcFolder}/`; + switch (args.source) { + case FirmwareSource.GitBranch: + const branchResult = await firmwareDownload.checkoutBranch( + gitRepository.url, + `${srcFolder}hardware`, + args.gitBranch + ); + return branchResult.path; + case FirmwareSource.GitCommit: + const commitResult = await firmwareDownload.checkoutCommit( + gitRepository.url, + `${srcFolder}hardware`, + args.gitCommit + ); + return commitResult.path; + case FirmwareSource.GitTag: + const tagResult = await firmwareDownload.checkoutTag( + gitRepository.url, + `${srcFolder}hardware`, + args.gitTag + ); + return tagResult.path; + case FirmwareSource.GitPullRequest: + if (args.gitPullRequest === null) { + throw new Error('empty GitPullRequest head commit hash'); + } + const prResult = await firmwareDownload.checkoutCommit( + gitRepository.url, + `${srcFolder}hardware`, + args.gitPullRequest.headCommitHash + ); + return prResult.path; + case FirmwareSource.Local: + return path.join(args.localPath, 'hardware'); + default: + throw new Error( + `unsupported firmware source for the targets service: ${args.source}` + ); + } + } + + async getDeviceConfig( + args: UserDefineFilters, + gitRepository: GitRepository + ): Promise { + const targetsDataDirectory = await this.loadTargetsData( + args, + gitRepository + ); + const targetsJSONLoader = new TargetsJSONLoader(this.logger); + const targetsJSONPath = path.join(targetsDataDirectory, 'targets.json'); + const data = await targetsJSONLoader.loadDeviceDescriptions( + targetsJSONPath + ); + if (typeof data[args.target] === 'undefined') { + throw new Error(`failed to find device description for ${args.target}`); + } + const { config } = data[args.target]; + return config; + } + + async targetDeviceOptions( + args: UserDefineFilters, + gitRepository: GitRepository + ): Promise { + const config = await this.getDeviceConfig(args, gitRepository); + + const userDefines: UserDefine[] = []; + const targetUserDefinesFactory = new TargetUserDefinesFactory(); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.BINDING_PHRASE) + ); + + if (args.target.includes('_2400.')) { + userDefines.push( + targetUserDefinesFactory.build( + UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 + ) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_ISM_2400) + ); + } + if (args.target.includes('_900.')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_AU_915) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_EU_868) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_FCC_915) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_IN_866) + ); + } + if (['esp32', 'esp8285'].includes(config.platform)) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.AUTO_WIFI_ON_INTERVAL) + ); + } + if (config.features && config.features.includes('buzzer')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.DISABLE_ALL_BEEPS) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.JUST_BEEP_ONCE) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.MY_STARTUP_MELODY) + ); + } + if (config.features && config.features.includes('unlock-higher-power')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.UNLOCK_HIGHER_POWER) + ); + } + if (config.features && config.features.includes('sbus-uart')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.USE_R9MM_R9MINI_SBUS) + ); + } + if (args.target.includes('.tx_')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.TLM_REPORT_INTERVAL_MS) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.UART_INVERTED) + ); + } + if (args.target.includes('.rx_')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.RCVR_UART_BAUD) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.RCVR_INVERT_TX) + ); + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.LOCK_ON_FIRST_CONNECTION) + ); + } + return userDefines; + } +} diff --git a/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts new file mode 100644 index 000000000..f73ec5cd4 --- /dev/null +++ b/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts @@ -0,0 +1,109 @@ +import fs from 'fs'; +import { LoggerService } from '../../../logger'; + +export interface DeviceDescription { + product_name: string; + lua_name?: string; + layout_file?: string; + upload_methods: string[]; + platform: string; + firmware: string; + // @TODO: add this to the upstream firmware repository + verified_hardware: string; + features?: string[]; + prior_target_name: string; + overlay?: { [key: string]: string }; +} + +export type TargetsJSONRaw = { + [manufacturer: string]: { + [key in string]: + | { + [key: string]: DeviceDescription; + } + | string; + }; +}; + +export class TargetsJSONLoader { + constructor(private logger: LoggerService) {} + + private async getTargets(targetsJsonPath: string) { + return JSON.parse( + await fs.promises.readFile(targetsJsonPath, 'utf-8') + ) as TargetsJSONRaw; + } + + async loadDeviceDescriptions(targetsJsonPath: string) { + const data = await this.getTargets(targetsJsonPath); + const result: { + [id: string]: { + category: string; + config: DeviceDescription; + }; + } = {}; + Object.keys(data).forEach((categoryKey) => { + if (typeof data[categoryKey].name !== 'string') { + throw new Error(`no category name for ${categoryKey}`); + } + const categoryName: string = data[categoryKey].name as string; + + const subTypes = data[categoryKey]; + Object.keys(subTypes).forEach((subTypeKey) => { + if (subTypeKey === 'name') { + return; + } + const configs: { + [key: string]: DeviceDescription; + } = data[categoryKey][subTypeKey] as { + [key: string]: DeviceDescription; + }; + Object.keys(configs).forEach((deviceDescriptionKey) => { + const id = `${categoryKey}.${subTypeKey}.${deviceDescriptionKey}`; + const config = configs[deviceDescriptionKey]; + if (this.validConfig(id, config)) { + result[id] = { + category: categoryName, + config, + }; + } + }); + }); + }); + return result; + } + + private validConfig(id: string, config: DeviceDescription) { + let missingFields = ''; + const logMissingField = (field: string) => { + if (missingFields.length > 0) { + missingFields += ', '; + } + missingFields += field; + }; + let valid = true; + if (!config.firmware) { + logMissingField('firmware'); + valid = false; + } + if (!config.platform) { + logMissingField('platform'); + valid = false; + } + if (!config.product_name) { + logMissingField('product_name'); + valid = false; + } + if (!config.upload_methods) { + logMissingField('upload_methods'); + valid = false; + } + + if (!valid) { + this.logger.error( + `${id} in targets file is not a valid Config, missing fields ${missingFields}` + ); + } + return valid; + } +} diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 36f415c7e..df67ffac4 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -6,9 +6,7 @@ import * as fs from 'fs'; import * as path from 'path'; import rimraf from 'rimraf'; import cloneDeep from 'lodash/cloneDeep'; -import Crypto from 'crypto'; import semver from 'semver'; -import FormData from 'form-data'; import UserDefine from '../../models/UserDefine'; import FirmwareSource from '../../models/enum/FirmwareSource'; import Mutex from '../../library/Mutex'; @@ -23,22 +21,19 @@ import { FlashingStrategy, IsCompatibleArgs, } from '../FlashingStrategyLocator/FlashingStrategy'; -import ConfigureFirmware from '../../library/ConfigureFirmware'; -import { Options } from '../../library/ConfigureFirmware/Options'; -import { Config } from '../../library/ConfigureFirmware/Config'; -import Domain from '../../library/ConfigureFirmware/Domain'; -import Target from '../../models/Target'; -import { Targets } from '../../library/ConfigureFirmware/Targets'; import TargetArgs from '../../graphql/args/Target'; import GitRepository from '../../graphql/inputs/GitRepositoryInput'; import Device from '../../models/Device'; import { UserDefineFilters } from '../UserDefinesLoader'; -import FlashingMethod from '../../models/enum/FlashingMethod'; -import DeviceType from '../../models/enum/DeviceType'; -import TargetUserDefinesFactory from '../../factories/TargetUserDefinesFactory'; import BuildJobType from '../../models/enum/BuildJobType'; -import { FirmwareFiles } from '../../library/ConfigureFirmware/FirmwareFiles'; import BuildFlashFirmwareResult from '../../graphql/objects/BuildFlashFirmwareResult'; +import Python from '../../library/Python'; +import { + findGitExecutable, + GitFirmwareDownloader, +} from '../../library/FirmwareDownloader'; +import DeviceDescriptionsLoader from './DeviceDescriptionsLoader'; +import FlashingMethod from '../../models/enum/FlashingMethod'; const maskSensitiveData = (haystack: string): string => { const needles = [ @@ -83,12 +78,14 @@ const maskBuildFlashFirmwareParams = ( export default class BinaryFlashingStrategyService implements FlashingStrategy { readonly name: string = 'BinaryFlashingStrategy'; - mutex: Mutex; + private mutex: Mutex; constructor( private PATH: string, private firmwaresPath: string, private pubSub: PubSubEngine, + private python: Python, + private deviceDescriptionsLoader: DeviceDescriptionsLoader, private logger: LoggerService ) { this.mutex = new Mutex(); @@ -118,271 +115,21 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { }); } - async getTargets(workingDirectory: string) { - const targetsFile = path.join(workingDirectory, 'hardware', 'targets.json'); - this.logger.log(`Loading targets from ${targetsFile}`); - const targets = JSON.parse( - await fs.promises.readFile(targetsFile, 'utf-8') - ) as Targets; - return targets; - } - - async getConfig(workingDirectory: string, devicePath: string) { - const [manufacturer, type, product] = devicePath.split('.'); - const targets = await this.getTargets(workingDirectory); - let config: Config; - if (type === 'rx_2400') { - config = targets[manufacturer].rx_2400[product]; - } else if (type === 'rx_900') { - config = targets[manufacturer].rx_900[product]; - } else if (type === 'tx_2400') { - config = targets[manufacturer].tx_2400[product]; - } else if (type === 'tx_900') { - config = targets[manufacturer].tx_900[product]; - } else { - throw new Error(`Unknown type ${type} encountered`); - } - return config; - } - - uploadMethodToFlashingMethod(uploadMethod: string): FlashingMethod { - switch (uploadMethod.toLowerCase()) { - case 'betaflight': - return FlashingMethod.BetaflightPassthrough; - break; - case 'dfu': - return FlashingMethod.DFU; - break; - case 'etx': - return FlashingMethod.EdgeTxPassthrough; - break; - case 'stlink': - return FlashingMethod.STLink; - break; - case 'uart': - return FlashingMethod.UART; - break; - case 'wifi': - return FlashingMethod.WIFI; - break; - - default: - throw new Error(`Upload Method ${uploadMethod} Not Recognized!`); - break; - } - } - - configToDevice(id: string, category: string, config: Config): Device { - return new Device( - id, - config.product_name, - category, - config.upload_methods.map((uploadMethod) => { - const targetName = `${id}.${uploadMethod}`; - return new Target( - targetName, - targetName, - this.uploadMethodToFlashingMethod(uploadMethod) - ); - }), - [], - DeviceType.ExpressLRS, - true - ); - } - - verifyConfig(id: string, config: Config) { - let missingFields = ''; - const logMissingField = (field: string) => { - if (missingFields.length > 0) { - missingFields += ', '; - } - missingFields += field; - }; - let valid = true; - if (!config.firmware) { - logMissingField('firmware'); - valid = false; - } - if (!config.platform) { - logMissingField('platform'); - valid = false; - } - if (!config.product_name) { - logMissingField('product_name'); - valid = false; - } - if (!config.upload_methods) { - logMissingField('upload_methods'); - valid = false; - } - - if (!valid) { - this.logger.error( - `${id} in targets file is not a valid Config, missing fields ${missingFields}` - ); - } - return valid; - } - async availableFirmwareTargets( args: TargetArgs, gitRepository: GitRepository ): Promise { - const targets = await this.getTargets(this.firmwaresPath); - const devices: Device[] = []; - Object.keys(targets).forEach((manufacturerKey) => { - const manufacturerConfig = targets[manufacturerKey]; - if (manufacturerConfig.rx_2400) { - Object.keys(manufacturerConfig.rx_2400).forEach((deviceKey) => { - const deviceConfig = manufacturerConfig.rx_2400[deviceKey]; - const id = `${manufacturerKey}.rx_2400.${deviceKey}`; - - if (this.verifyConfig(id, deviceConfig)) { - const device = this.configToDevice( - id, - manufacturerConfig.name, - deviceConfig - ); - devices.push(device); - } - }); - } - if (manufacturerConfig.rx_900) { - Object.keys(manufacturerConfig.rx_900).forEach((deviceKey) => { - const deviceConfig = manufacturerConfig.rx_900[deviceKey]; - const id = `${manufacturerKey}.rx_900.${deviceKey}`; - - if (this.verifyConfig(id, deviceConfig)) { - const device = this.configToDevice( - id, - manufacturerConfig.name, - deviceConfig - ); - devices.push(device); - } - }); - } - if (manufacturerConfig.tx_2400) { - Object.keys(manufacturerConfig.tx_2400).forEach((deviceKey) => { - const deviceConfig = manufacturerConfig.tx_2400[deviceKey]; - const id = `${manufacturerKey}.tx_2400.${deviceKey}`; - - if (this.verifyConfig(id, deviceConfig)) { - const device = this.configToDevice( - id, - manufacturerConfig.name, - deviceConfig - ); - devices.push(device); - } - }); - } - if (manufacturerConfig.tx_900) { - Object.keys(manufacturerConfig.tx_900).forEach((deviceKey) => { - const deviceConfig = manufacturerConfig.tx_900[deviceKey]; - const id = `${manufacturerKey}.tx_900.${deviceKey}`; - - if (this.verifyConfig(id, deviceConfig)) { - const device = this.configToDevice( - id, - manufacturerConfig.name, - deviceConfig - ); - devices.push(device); - } - }); - } - }); - return devices; + return this.deviceDescriptionsLoader.loadTargetsList(args, gitRepository); } async targetDeviceOptions( args: UserDefineFilters, gitRepository: GitRepository ): Promise { - const config = await this.getConfig(this.firmwaresPath, args.target); - const userDefines: UserDefine[] = []; - const targetUserDefinesFactory = new TargetUserDefinesFactory(); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.BINDING_PHRASE) + return this.deviceDescriptionsLoader.targetDeviceOptions( + args, + gitRepository ); - if (args.target.includes('_2400.')) { - userDefines.push( - targetUserDefinesFactory.build( - UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 - ) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_ISM_2400) - ); - } - if (args.target.includes('_900.')) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_AU_915) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_EU_868) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_FCC_915) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.REGULATORY_DOMAIN_IN_866) - ); - } - if (['esp32', 'esp8285'].includes(config.platform)) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.AUTO_WIFI_ON_INTERVAL) - ); - } - if (config.features && config.features.includes('buzzer')) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.DISABLE_ALL_BEEPS) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.JUST_BEEP_ONCE) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.MY_STARTUP_MELODY) - ); - } - if (config.features && config.features.includes('unlock-higher-power')) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.UNLOCK_HIGHER_POWER) - ); - } - if (config.features && config.features.includes('sbus-uart')) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.USE_R9MM_R9MINI_SBUS) - ); - } - if (args.target.includes('.tx_')) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.TLM_REPORT_INTERVAL_MS) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.UART_INVERTED) - ); - } - if (args.target.includes('.rx_')) { - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.RCVR_UART_BAUD) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.RCVR_INVERT_TX) - ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.LOCK_ON_FIRST_CONNECTION) - ); - } - return userDefines; } private osUsernameContainsAmpersand(): boolean { @@ -395,14 +142,16 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return false; } + // @TODO: check remote build cache + // @TODO: checkout git repository for a full check async isCompatible( params: IsCompatibleArgs, gitRepositoryUrl: string, - gitRepositorySrcFolder: string + _gitRepositorySrcFolder: string ) { if ( gitRepositoryUrl.toLowerCase() === - 'https://github.com/ExpressLRS/ExpressLRS'.toLowerCase() && + 'https://github.com/expresslrs/expresslrs'.toLowerCase() && params.source === FirmwareSource.GitTag && semver.compare(params.gitTag, '3.0.0') >= 0 ) { @@ -412,6 +161,93 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return false; } + userDefinesToFlags(userDefines: UserDefine[]): string[][] { + const flags: string[][] = []; + userDefines + .filter(({ enabled }) => enabled) + .forEach((userDefine) => { + switch (userDefine.key) { + case UserDefineKey.BINDING_PHRASE: + flags.push(['--phrase', userDefine.value!]); + break; + case UserDefineKey.REGULATORY_DOMAIN_AU_433: + flags.push(['--domain', 'au_433']); + break; + case UserDefineKey.REGULATORY_DOMAIN_EU_433: + flags.push(['--domain', 'eu_433']); + break; + case UserDefineKey.REGULATORY_DOMAIN_AU_915: + flags.push(['--domain', 'au_915']); + break; + case UserDefineKey.REGULATORY_DOMAIN_FCC_915: + flags.push(['--domain', 'fcc_915']); + break; + case UserDefineKey.REGULATORY_DOMAIN_EU_868: + flags.push(['--domain', 'eu_868']); + break; + case UserDefineKey.REGULATORY_DOMAIN_IN_866: + flags.push(['--domain', 'in_866']); + break; + case UserDefineKey.REGULATORY_DOMAIN_ISM_2400: + break; + case UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400: + flags.push(['--lbt']); + break; + case UserDefineKey.AUTO_WIFI_ON_INTERVAL: + flags.push(['--auto-wifi', userDefine.value!]); + break; + case UserDefineKey.HOME_WIFI_SSID: + flags.push(['--ssid', userDefine.value!]); + break; + case UserDefineKey.HOME_WIFI_PASSWORD: + flags.push(['--password', userDefine.value!]); + break; + case UserDefineKey.LOCK_ON_FIRST_CONNECTION: + break; + case UserDefineKey.JUST_BEEP_ONCE: + flags.push(['--buzzer-mode', 'one-beep']); + break; + case UserDefineKey.DISABLE_ALL_BEEPS: + flags.push(['--buzzer-mode', 'quiet']); + break; + case UserDefineKey.DISABLE_STARTUP_BEEP: + flags.push(['--buzzer-mode', 'quiet']); + break; + case UserDefineKey.MY_STARTUP_MELODY: + flags.push(['--buzzer-mode', 'custom']); + flags.push(['--buzzer-melody', userDefine.key]); + break; + case UserDefineKey.RCVR_INVERT_TX: + flags.push(['--invert-tx']); + break; + case UserDefineKey.RCVR_UART_BAUD: + flags.push(['--rx-baud', userDefine.value!]); + break; + case UserDefineKey.TLM_REPORT_INTERVAL_MS: + flags.push(['--tlm-report', userDefine.value!]); + break; + case UserDefineKey.UART_INVERTED: + flags.push(['--uart-inverted']); + break; + case UserDefineKey.UNLOCK_HIGHER_POWER: + flags.push(['--unlock-higher-power']); + break; + case UserDefineKey.USE_R9MM_R9MINI_SBUS: + flags.push(['--r9mm-mini-sbus']); + break; + // not supported + case UserDefineKey.USE_TX_BACKPACK: + case UserDefineKey.WS2812_IS_GRB: + case UserDefineKey.DEVICE_NAME: + break; + default: + const exhaustiveCheck: never = userDefine.key; + throw new Error(`Unhandled color case: ${exhaustiveCheck}`); + } + }); + return flags; + } + async buildFlashFirmware( params: BuildFlashFirmwareParams, gitRepositoryUrl: string, @@ -452,203 +288,148 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildFirmwareStep.DOWNLOADING_FIRMWARE ); + let gitPath = ''; + try { + gitPath = await findGitExecutable(this.PATH); + } catch (e) { + this.logger?.error('failed to find git', undefined, { + PATH: this.PATH, + err: e, + }); + return new BuildFlashFirmwareResult( + false, + `${e}`, + BuildFirmwareErrorType.GitDependencyError + ); + } + this.logger?.log('git path', { + gitPath, + }); + + const firmwareDownload = new GitFirmwareDownloader( + { + baseDirectory: this.firmwaresPath, + gitBinaryLocation: gitPath, + }, + this.logger + ); + await this.updateProgress( BuildProgressNotificationType.Info, - BuildFirmwareStep.FLASHING_FIRMWARE + BuildFirmwareStep.DOWNLOADING_FIRMWARE ); - const [manufacturer, type, device, uploadMethod] = - params.target.split('.'); - const flashingMethod = this.uploadMethodToFlashingMethod(uploadMethod); - const config = await this.getConfig(this.firmwaresPath, params.target); - // need a better way to do this, it should really be included in the config - const deviceType = params.target.includes('.tx_') ? 'TX' : 'RX'; - const radioType = params.target.includes('_900.') ? 'sx127x' : 'sx128x'; - - // assign some default values - const options: Options = { - 'wifi-on-interval': 60, - }; - - if (deviceType === 'RX') { - options['rcvr-uart-baud'] = 420000; - options['rcvr-invert-tx'] = false; - options['lock-on-first-connection'] = false; - } else { - options['tlm-interval'] = 240; - } - - let fcclbt = 'FCC'; - params.userDefines.forEach((userDefine) => { - if ( - userDefine.key === UserDefineKey.AUTO_WIFI_ON_INTERVAL && - userDefine.value && - userDefine.enabled - ) { - const wifiOnInterval = Number.parseInt(userDefine.value, 10); - if (!Number.isNaN(wifiOnInterval)) { - options['wifi-on-interval'] = wifiOnInterval; - } - } else if ( - userDefine.key === UserDefineKey.BINDING_PHRASE && - userDefine.value && - userDefine.enabled - ) { - const bindingPhraseFull = `-DMY_BINDING_PHRASE="${userDefine.value}"`; - const bindingPhraseHashed = new Uint8Array( - Crypto.createHash('md5').update(bindingPhraseFull).digest() + let firmwarePath = ''; + switch (params.firmware.source) { + case FirmwareSource.GitTag: + const tagResult = await firmwareDownload.checkoutTag( + gitRepositoryUrl, + gitRepositorySrcFolder, + params.firmware.gitTag ); - options.uid = Array.from(bindingPhraseHashed.subarray(0, 6)); - } else if ( - userDefine.key === UserDefineKey.DISABLE_ALL_BEEPS && - userDefine.enabled - ) { - options.beeptype = 0; - } else if ( - userDefine.key === UserDefineKey.JUST_BEEP_ONCE && - userDefine.enabled - ) { - options.beeptype = 1; - } else if ( - userDefine.key === UserDefineKey.MY_STARTUP_MELODY && - userDefine.enabled && - userDefine.value - ) { - options.beeptype = 4; - // TODO: Fix melody parsing, importing module bluejay-rtttl-parse gives the error Unexpected token 'export' - // options.melody = MelodyParser.parseToArray(userDefine.value); - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_AU_433 && - userDefine.enabled - ) { - options.domain = Domain.AU433; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_AU_915 && - userDefine.enabled - ) { - options.domain = Domain.AU915; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_EU_433 && - userDefine.enabled - ) { - options.domain = Domain.EU433; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_EU_868 && - userDefine.enabled - ) { - options.domain = Domain.EU868; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_FCC_915 && - userDefine.enabled - ) { - options.domain = Domain.FCC915; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_IN_866 && - userDefine.enabled - ) { - options.domain = Domain.IN866; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 && - userDefine.enabled - ) { - fcclbt = 'LBT'; - } else if ( - userDefine.key === UserDefineKey.REGULATORY_DOMAIN_ISM_2400 && - userDefine.enabled - ) { - fcclbt = 'FCC'; - } else if (userDefine.key === UserDefineKey.LOCK_ON_FIRST_CONNECTION) { - options['lock-on-first-connection'] = userDefine.enabled; - } else if ( - userDefine.key === UserDefineKey.RCVR_UART_BAUD && - userDefine.enabled && - userDefine.value - ) { - const RCVR_UART_BAUD = Number.parseInt(userDefine.value, 10); - options['rcvr-uart-baud'] = RCVR_UART_BAUD; - } else if (userDefine.key === UserDefineKey.RCVR_INVERT_TX) { - options['rcvr-invert-tx'] = userDefine.enabled; - } else if ( - userDefine.key === UserDefineKey.TLM_REPORT_INTERVAL_MS && - userDefine.enabled && - userDefine.value - ) { - const TLM_REPORT_INTERVAL_MS = Number.parseInt(userDefine.value, 10); - options['tlm-interval'] = TLM_REPORT_INTERVAL_MS; - } else if (userDefine.key === UserDefineKey.UART_INVERTED) { - options['uart-inverted'] = userDefine.enabled; - } else if (userDefine.key === UserDefineKey.UNLOCK_HIGHER_POWER) { - options['unlock-higher-power'] = userDefine.enabled; - } else if ( - userDefine.key === UserDefineKey.HOME_WIFI_PASSWORD && - userDefine.enabled - ) { - options['wifi-password'] = userDefine.value; - } else if ( - userDefine.key === UserDefineKey.HOME_WIFI_SSID && - userDefine.enabled - ) { - options['wifi-ssid'] = userDefine.value; - } else if (userDefine.key === UserDefineKey.USE_R9MM_R9MINI_SBUS) { - options['r9mm-mini-sbus'] = userDefine.enabled; - } - }); - const folder = this.firmwaresPath; - const firmware = await ConfigureFirmware.getFirmware( - deviceType, - radioType, - config, - options, - folder, - fcclbt - ); - if (params.type === BuildJobType.Build) { - if (firmware.firmware) { - const firmwareBinPath = path.join( - this.firmwaresPath, - `${this.generateFirmwareName(config.product_name, params)}.bin` + firmwarePath = tagResult.path; + break; + case FirmwareSource.GitBranch: + const branchResult = await firmwareDownload.checkoutBranch( + gitRepositoryUrl, + gitRepositorySrcFolder, + params.firmware.gitBranch ); - fs.promises.writeFile( - firmwareBinPath, - Buffer.from(firmware.firmware.data.buffer) + firmwarePath = branchResult.path; + break; + case FirmwareSource.GitCommit: + const commitResult = await firmwareDownload.checkoutCommit( + gitRepositoryUrl, + gitRepositorySrcFolder, + params.firmware.gitCommit ); - return new BuildFlashFirmwareResult( - true, - undefined, - undefined, - firmwareBinPath + firmwarePath = commitResult.path; + break; + case FirmwareSource.Local: + firmwarePath = params.firmware.localPath; + break; + case FirmwareSource.GitPullRequest: + if (params.firmware.gitPullRequest) { + const pullRequestResult = await firmwareDownload.checkoutCommit( + gitRepositoryUrl, + gitRepositorySrcFolder, + params.firmware.gitPullRequest.headCommitHash + ); + firmwarePath = pullRequestResult.path; + } + break; + default: + throw new Error( + `unsupported firmware source: ${params.firmware.source}` ); + } + this.logger?.log('firmware path', { + firmwarePath, + gitRepositoryUrl, + }); + + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.FLASHING_FIRMWARE + ); + + const config = await this.deviceDescriptionsLoader.getDeviceConfig( + { + ...params.firmware, + target: params.target, + }, + { + url: gitRepositoryUrl, + srcFolder: gitRepositorySrcFolder, } - } else if ( + ); + + let regulatoryDomain: 'LBT' | 'FCC' = 'FCC'; + const regDomainCE2400 = params.userDefines.find( + ({ key }) => key === UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 + ); + if (regDomainCE2400?.enabled) { + regulatoryDomain = 'LBT'; + } + + const flags: string[][] = []; + const [manufacturer, subType, device, uploadMethod] = + params.target.split('.'); + flags.push(['--target', `${manufacturer}.${subType}.${device}`]); + if (subType.toLocaleLowerCase().includes('tx_')) { + flags.push(['--tx']); + } + flags.push(...this.userDefinesToFlags(params.userDefines)); + + if (params.type === BuildJobType.Build) { + const firmwareBinPath = path.join( + this.firmwaresPath, + `${this.generateFirmwareName(config.product_name, params)}.bin` + ); + fs.promises.writeFile(firmwareBinPath, Buffer.from([])); + return new BuildFlashFirmwareResult( + true, + undefined, + undefined, + firmwareBinPath + ); + } + + if (params.type === BuildJobType.ForceFlash) { + flags.push(['--force']); + } + + if ( params.type === BuildJobType.BuildAndFlash || params.type === BuildJobType.ForceFlash ) { - const forceFlash = params.type === BuildJobType.ForceFlash; - - if (flashingMethod === FlashingMethod.WIFI) { - try { - const response = await this.WIFI( - params.serialDevice ?? '10.0.0.1', - firmware, - forceFlash - ); + flags.push(['--flash', uploadMethod]); - return response; - } catch (error: any) { - this.logger.error(error); - this.updateLogs(error.message); - return new BuildFlashFirmwareResult(false, error); - } - } - const message = `Upload Method ${flashingMethod} is not currently supported`; - this.updateLogs(message); - return new BuildFlashFirmwareResult(false, message); - } else { - const message = `Build Job Type ${params.type} is not currently supported`; - this.updateLogs(message); - return new BuildFlashFirmwareResult(false, message); + return new BuildFlashFirmwareResult(true, ''); } - return new BuildFlashFirmwareResult(true); + const message = `Build Job Type ${params.type} is not currently supported`; + this.updateLogs(message); + return new BuildFlashFirmwareResult(false, message); } catch (e) { this.logger?.error('generic error', undefined, { err: e, @@ -688,152 +469,6 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } } - async WIFI( - host: string, - firmware: FirmwareFiles, - force: boolean - ): Promise { - /// https://wanago.io/2019/03/18/node-js-typescript-6-sending-http-requests-understanding-multipart-form-data/ - return new Promise((resolve) => { - const output: string[] = []; - - const form = new FormData(); - - if (force) { - console.log('forcing flash'); - form.append('force', '1'); - } - const buffer = Buffer.from(firmware.firmware!.data); - let uploadSize = 0; - let uploaded = 0; - - form.append('data', buffer); - - // get upload size - form.getLength((err, size) => { - console.log(`upload size: ${err} ${size}`); - uploadSize = size; - }); - - const options = { - host, - path: '/update', - headers: { - 'X-FileSize': buffer.byteLength, - }, - }; - - this.updateLogs(`starting upload to ${host}`); - const req = form.submit(options, (err, response) => { - if (err) { - this.logger.error(err.message); - resolve( - new BuildFlashFirmwareResult( - false, - err.message, - BuildFirmwareErrorType.FlashError - ) - ); - - return; - } - - const { statusCode, statusMessage, headers } = response; - console.log(req.getHeaders()); // 200 - console.log(req.path); // 200 - console.log(statusCode); // 200 - console.log(statusMessage); // 200 - const chunks: any = []; - response.on('data', (chunk) => { - chunks.push(chunk); - }); - response.on('end', () => { - console.log('response end'); - const data = Buffer.concat(chunks).toString(); - const result = { - data, - headers, - }; - - console.log(result); - this.updateLogs(`response from ${host}`); - this.updateLogs(result.data); - if (response.statusCode === 200) { - let json: any = {}; - try { - json = JSON.parse(data); - } catch (e) { - this.logger.error(e); - console.group(e); - } - - if (json.status === 'ok') { - resolve( - new BuildFlashFirmwareResult( - true, - data, - BuildFirmwareErrorType.FlashError - ) - ); - } else if (json.status === 'mismatch') { - resolve( - new BuildFlashFirmwareResult( - false, - data.replaceAll(//g, ''), - BuildFirmwareErrorType.TargetMismatch - ) - ); - } else { - resolve( - new BuildFlashFirmwareResult( - false, - data, - BuildFirmwareErrorType.FlashError - ) - ); - } - } else { - resolve( - new BuildFlashFirmwareResult( - false, - data, - BuildFirmwareErrorType.FlashError - ) - ); - } - }); - }); - - req.on('error', (error) => { - console.log('req error'); - this.logger.error(error.message); - this.updateLogs(error.message); - console.log(error); // 200 - resolve( - new BuildFlashFirmwareResult( - false, - error.message, - BuildFirmwareErrorType.FlashError - ) - ); - }); - - form.on('data', (data) => { - console.log('data'); - if (uploadSize > 0) { - uploaded += data.length; - const progress = (uploaded / uploadSize) * 100; - const progressMessage = `progress: ${progress.toLocaleString( - undefined, - { minimumFractionDigits: 0, maximumFractionDigits: 2 } - )}%`; - this.updateLogs(progressMessage); - // console.log(progressMessage); - } - }); - }); - } - async clearFirmwareFiles(): Promise { const rmrf = async (file: string): Promise => { return new Promise((resolve, reject) => { diff --git a/src/api/src/services/GitHubClient/index.ts b/src/api/src/services/GitHubClient/index.ts index 4bce12c7e..369dc7a9d 100644 --- a/src/api/src/services/GitHubClient/index.ts +++ b/src/api/src/services/GitHubClient/index.ts @@ -1,4 +1,3 @@ -import { RepeatOneSharp } from '@mui/icons-material'; import { Octokit } from '@octokit/rest'; import { Service } from 'typedi'; import PullRequest from '../../models/PullRequest'; @@ -79,10 +78,11 @@ export default class OctopusGitHubClient implements IGitHubClient { } async getRelease(owner: string, repository: string, tag: string) { - const response = await this.client.repos.listReleases({ + const response = await this.client.repos.getReleaseByTag({ owner, repo: repository, + tag, }); - return response.data.find((value) => value.tag_name === tag); + return response.data; } } diff --git a/src/api/src/services/MulticastDns/MulticastDnsSimulator.ts b/src/api/src/services/MulticastDns/MulticastDnsSimulator.ts index 06c29775c..8ce6e6ee3 100644 --- a/src/api/src/services/MulticastDns/MulticastDnsSimulator.ts +++ b/src/api/src/services/MulticastDns/MulticastDnsSimulator.ts @@ -20,7 +20,6 @@ export default class MulticastDnsSimulatorService { true, true ), - UserDefine.Boolean(UserDefineKey.FEATURE_OPENTX_SYNC, true), UserDefine.Boolean(UserDefineKey.UART_INVERTED, true), ], version: '2.0.0 (5c4808)', @@ -41,7 +40,6 @@ export default class MulticastDnsSimulatorService { true, true ), - UserDefine.Boolean(UserDefineKey.FEATURE_OPENTX_SYNC, true), UserDefine.Boolean(UserDefineKey.UART_INVERTED, true), ], version: '2.0.0 (5c4808)', diff --git a/src/api/src/services/PlatformioFlashingStrategy/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts index 20f5070e0..f7be487ef 100644 --- a/src/api/src/services/PlatformioFlashingStrategy/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -1,5 +1,5 @@ -import {Service} from 'typedi'; -import {PubSubEngine} from 'graphql-subscriptions'; +import { Service } from 'typedi'; +import { PubSubEngine } from 'graphql-subscriptions'; import * as os from 'os'; import * as fs from 'fs'; import * as path from 'path'; @@ -21,21 +21,21 @@ import { } from '../../library/FirmwareDownloader'; import Platformio from '../../library/Platformio'; import FirmwareBuilder from '../../library/FirmwareBuilder'; -import {LoggerService} from '../../logger'; +import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; import UploadType from '../../library/Platformio/Enum/UploadType'; -import {BuildFlashFirmwareParams} from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; import { FlashingStrategy, IsCompatibleArgs, } from '../FlashingStrategyLocator/FlashingStrategy'; -import Python from '../../library/Python'; import UserDefinesBuilder from '../UserDefinesBuilder'; import TargetsLoader from '../TargetsLoader'; import TargetArgs from '../../graphql/args/Target'; import Device from '../../models/Device'; -import {UserDefineFilters} from '../UserDefinesLoader'; +import { UserDefineFilters } from '../UserDefinesLoader'; import GitRepository from '../../graphql/inputs/GitRepositoryInput'; +import UserDefinesTxtFactory from '../../factories/UserDefinesTxtFactory'; const maskSensitiveData = (haystack: string): string => { const needles = [ @@ -78,8 +78,10 @@ const maskBuildFlashFirmwareParams = ( @Service() export default class PlatformioFlashingStrategyService - implements FlashingStrategy { + implements FlashingStrategy +{ readonly name: string = 'PlatformioFlashingStrategy'; + mutex: Mutex; constructor( @@ -89,7 +91,6 @@ export default class PlatformioFlashingStrategyService private builder: FirmwareBuilder, private pubSub: PubSubEngine, private logger: LoggerService, - private python: Python, private userDefinesBuilder: UserDefinesBuilder, private targetsLoader: TargetsLoader ) { @@ -137,7 +138,7 @@ export default class PlatformioFlashingStrategyService private osUsernameContainsAmpersand(): boolean { if ( os.platform() === 'win32' && - os.userInfo({encoding: 'utf8'}).username.indexOf('&') > -1 + os.userInfo({ encoding: 'utf8' }).username.indexOf('&') > -1 ) { return true; } @@ -187,7 +188,7 @@ export default class PlatformioFlashingStrategyService ); } - const pythonCheck = await this.python.checkPython(); + const pythonCheck = await this.platformio.checkPython(); if (!pythonCheck.success) { this.logger?.error('python dependency check error', undefined, { stderr: pythonCheck.stderr, @@ -323,7 +324,7 @@ export default class PlatformioFlashingStrategyService userDefine.enabled && userDefine.key !== UserDefineKey.DEVICE_NAME ) - .map(({key}) => key) + .map(({ key }) => key) ); if (!compatCheck.compatible) { return new BuildFlashFirmwareResult( @@ -340,7 +341,7 @@ export default class PlatformioFlashingStrategyService userDefines = params.userDefinesTxt; break; case UserDefinesMode.UserInterface: - userDefines = await this.userDefinesBuilder.buildUserDefinesTxt(params.userDefines); + userDefines = new UserDefinesTxtFactory().build(params.userDefines); break; default: throw new Error( @@ -499,7 +500,7 @@ export default class PlatformioFlashingStrategyService } generateFirmwareName(params: BuildFlashFirmwareParams): string { - const {source, gitBranch, gitCommit, gitTag, gitPullRequest} = + const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = params.firmware; const deviceName = params.userDefines.find( (userDefine) => userDefine.key === UserDefineKey.DEVICE_NAME diff --git a/src/api/src/services/TargetsLoader/HttpTargets.ts b/src/api/src/services/TargetsLoader/HttpTargets.ts deleted file mode 100644 index ca8eee9d8..000000000 --- a/src/api/src/services/TargetsLoader/HttpTargets.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { Service } from 'typedi'; -import { Octokit } from '@octokit/rest'; -import fetch from 'node-fetch'; -import path from 'path'; -import { LoggerService } from '../../logger'; -import DeviceService from '../Device'; -import extractTargetFiles from './extractTargetFiles'; -import extractTargets from './extractTargets'; -import TargetArgs from '../../graphql/args/Target'; -import Device from '../../models/Device'; -import FirmwareSource from '../../models/enum/FirmwareSource'; -import loadTargetsFromDirectory from './loadTargetsFromDirectory'; -import TargetsLoader, { GitRepository } from './index'; - -@Service() -export default class HttpTargetsService implements TargetsLoader { - client: Octokit; - - constructor( - private logger: LoggerService, - private deviceService: DeviceService - ) { - this.client = new Octokit(); - } - - async downloadFile(url: string): Promise { - const file = await fetch(url); - const body = await file.text(); - if (!file.ok) { - this.logger?.log(`failed to get ${file}`, { - status: file.status, - body, - }); - throw new Error(`failed to get ${url}: ${file.status}, ${body}`); - } - return body; - } - - async loadTargetsFromGitHub( - gitRepositoryOwner: string, - gitRepositoryName: string, - gitRepositorySrcFolder: string, - ref: string - ): Promise { - if (!ref || ref.length === 0) { - return []; - } - try { - const platformioFile = `https://raw.githubusercontent.com/${gitRepositoryOwner}/${gitRepositoryName}/${ref}${gitRepositorySrcFolder}/platformio.ini`; - const platformioFileContents = await this.downloadFile(platformioFile); - - const targetFiles = extractTargetFiles(platformioFileContents); - - const values = await Promise.all( - targetFiles.map(async (item) => { - const contents = await this.downloadFile( - `https://raw.githubusercontent.com/${gitRepositoryOwner}/${gitRepositoryName}/${ref}${gitRepositorySrcFolder}/${item}` - ); - const targets = extractTargets(contents); - return targets; - }) - ); - - const platformioFileTargets = extractTargets(platformioFileContents); - - values.push(platformioFileTargets); - - return values.reduce((prev, curr) => { - return prev.concat(curr); - }, []); - } catch (err) { - this.logger?.log(`failed to get targets from ref: ${ref}`, { err }); - throw new Error(`failed to get targets from ref: ${ref}. Error: ${err}`); - } - } - - async loadTargetsList( - args: TargetArgs, - gitRepository: GitRepository - ): Promise { - let availableTargets: string[] = []; - switch (args.source) { - case FirmwareSource.GitBranch: - availableTargets = await this.loadTargetsFromGitHub( - gitRepository.owner, - gitRepository.repositoryName, - gitRepository.srcFolder, - args.gitBranch - ); - break; - case FirmwareSource.GitCommit: - availableTargets = await this.loadTargetsFromGitHub( - gitRepository.owner, - gitRepository.repositoryName, - gitRepository.srcFolder, - args.gitCommit - ); - break; - case FirmwareSource.GitTag: - availableTargets = await this.loadTargetsFromGitHub( - gitRepository.owner, - gitRepository.repositoryName, - gitRepository.srcFolder, - args.gitTag - ); - break; - case FirmwareSource.GitPullRequest: - if (args.gitPullRequest) { - availableTargets = await this.loadTargetsFromGitHub( - gitRepository.owner, - gitRepository.repositoryName, - gitRepository.srcFolder, - args.gitPullRequest.headCommitHash - ); - } - break; - case FirmwareSource.Local: - availableTargets = await loadTargetsFromDirectory( - path.join(args.localPath, 'targets') - ); - break; - default: - throw new Error( - `unsupported firmware source for the targets service: ${args.source}` - ); - } - const devices = this.deviceService.getDevices(); - return devices - .map((value) => { - const device = { ...value }; - device.targets = value.targets.filter((item) => - availableTargets.find((target) => target === item.name) - ); - return device; - }) - .filter((item) => item.targets.length > 0); - } -} diff --git a/src/api/src/services/UserDefinesBuilder/index.ts b/src/api/src/services/UserDefinesBuilder/index.ts index e93b0bbb3..c4061a5a2 100644 --- a/src/api/src/services/UserDefinesBuilder/index.ts +++ b/src/api/src/services/UserDefinesBuilder/index.ts @@ -4,8 +4,6 @@ import UserDefine from '../../models/UserDefine'; import PullRequest from '../../models/PullRequest'; import UserDefinesLoader from '../UserDefinesLoader'; import DeviceService from '../Device'; -import UserDefinesTxtFactory from '../../factories/UserDefinesTxtFactory'; -import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; interface UserDefineFilters { target: string; @@ -30,41 +28,6 @@ export default class UserDefinesBuilder { private deviceService: DeviceService ) {} - async buildUserDefinesTxt(userDefines: UserDefine[]): Promise { - const userDefinesBuilder = new UserDefinesTxtFactory(); - return userDefinesBuilder.build(this.processUserDefines(userDefines)); - } - - private processUserDefines(userDefines: UserDefine[]): UserDefine[] { - const overrideUserDefineTo = ( - // eslint-disable-next-line @typescript-eslint/no-shadow - userDefines: UserDefine[], - defaultUserDefine: UserDefine - ): UserDefine[] => { - const exists = - userDefines.find(({ key }) => key === defaultUserDefine.key) !== - undefined; - if (exists) { - return userDefines.map((item) => { - if (item.key === defaultUserDefine.key) { - return defaultUserDefine; - } - return item; - }); - } - return [...userDefines, defaultUserDefine]; - }; - const overrides = [ - UserDefine.Boolean(UserDefineKey.FAST_SYNC, true), - UserDefine.Boolean(UserDefineKey.LOCK_ON_50HZ, false), - ]; - let result = userDefines; - overrides.forEach((override) => { - result = overrideUserDefineTo(result, override); - }); - return result; - } - async loadForDevice( input: UserDefineFilters, gitRepository: GitRepository diff --git a/src/api/src/services/UserDefinesLoader/HttpUserDefinesLoader.ts b/src/api/src/services/UserDefinesLoader/HttpUserDefinesLoader.ts deleted file mode 100644 index b873841f7..000000000 --- a/src/api/src/services/UserDefinesLoader/HttpUserDefinesLoader.ts +++ /dev/null @@ -1,77 +0,0 @@ -import fetch from 'node-fetch'; -import { Service } from 'typedi'; -import path from 'path'; -import fs from 'fs'; -import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; -import FirmwareSource from '../../models/enum/FirmwareSource'; -import { LoggerService } from '../../logger'; -import extractCompatibleKeys from './extractCompatibleKeys'; -import UserDefinesLoader, { GitRepository, UserDefineFilters } from './index'; - -@Service() -export default class HttpUserDefinesLoader implements UserDefinesLoader { - constructor(private logger: LoggerService) {} - - async loadUserDefinesTxt( - userDefineFilters: UserDefineFilters, - gitRepository: GitRepository - ): Promise { - this.logger?.log('http based loadUserDefinesTxt', { - userDefineFilters, - }); - const handleResponse = async (response: { - status: number; - ok: boolean; - text: () => Promise; - }): Promise => { - const body = await response.text(); - if (!response.ok) { - this.logger?.log('failed to get user_defines.txt', { - status: response.status, - body, - }); - throw new Error( - `failed to get user_defines.txt: ${response.status}, ${body}` - ); - } - return body; - }; - switch (userDefineFilters.source) { - case FirmwareSource.GitBranch: - return fetch( - `${gitRepository.rawRepoUrl}/${userDefineFilters.gitBranch}${gitRepository.srcFolder}/user_defines.txt` - ) - .then(handleResponse) - .then(extractCompatibleKeys); - case FirmwareSource.GitCommit: - return fetch( - `${gitRepository.rawRepoUrl}/${userDefineFilters.gitCommit}${gitRepository.srcFolder}/user_defines.txt` - ) - .then(handleResponse) - .then(extractCompatibleKeys); - case FirmwareSource.GitTag: - return fetch( - `${gitRepository.rawRepoUrl}/${userDefineFilters.gitTag}${gitRepository.srcFolder}/user_defines.txt` - ) - .then(handleResponse) - .then(extractCompatibleKeys); - case FirmwareSource.Local: - const userDefinesPath = path.join( - userDefineFilters.localPath, - 'user_defines.txt' - ); - const data = await fs.promises.readFile(userDefinesPath, 'utf8'); - return extractCompatibleKeys(data); - case FirmwareSource.GitPullRequest: - return fetch( - `${gitRepository.rawRepoUrl}/${userDefineFilters.gitPullRequest?.headCommitHash}${gitRepository.srcFolder}/user_defines.txt` - ) - .then(handleResponse) - .then(extractCompatibleKeys); - default: - throw new Error( - `unsupported firmware source: ${userDefineFilters.source}` - ); - } - } -} diff --git a/src/main.dev.ts b/src/main.dev.ts index 4ad1c9b76..94884d429 100644 --- a/src/main.dev.ts +++ b/src/main.dev.ts @@ -27,7 +27,6 @@ import { } from './ipc'; import Updater from './app/updater'; import WinstonLoggerService from './api/src/logger/WinstonLogger'; -import { FirmwareParamsLoaderType } from './api/src/config'; import packageJson from '../package.json'; @@ -176,32 +175,11 @@ const createWindow = async () => { 'firmwares', 'targets' ); - let targetsLoaderType: FirmwareParamsLoaderType = - FirmwareParamsLoaderType.Git; - if ( - process.env.FIRMWARE_TARGETS_LOADER_TYPE === FirmwareParamsLoaderType.Git - ) { - targetsLoaderType = FirmwareParamsLoaderType.Git; - } - if ( - process.env.FIRMWARE_TARGETS_LOADER_TYPE === FirmwareParamsLoaderType.Http - ) { - targetsLoaderType = FirmwareParamsLoaderType.Http; - } - const userDefinesStoragePath = path.join( app.getPath('userData'), 'firmwares', 'userDefines' ); - let userDefinesLoaderType: FirmwareParamsLoaderType = - FirmwareParamsLoaderType.Git; - if (process.env.USER_DEFINES_LOADER_TYPE === FirmwareParamsLoaderType.Git) { - userDefinesLoaderType = FirmwareParamsLoaderType.Git; - } - if (process.env.USER_DEFINES_LOADER_TYPE === FirmwareParamsLoaderType.Http) { - userDefinesLoaderType = FirmwareParamsLoaderType.Http; - } const dependenciesPath = app.isPackaged ? path.join(process.resourcesPath, '../dependencies') @@ -285,8 +263,6 @@ const createWindow = async () => { env: localApiServerEnv, devicesPath, targetsStoragePath, - targetsLoader: targetsLoaderType, - userDefinesLoader: userDefinesLoaderType, userDefinesStoragePath, userDataPath: app.getPath('userData'), }, diff --git a/src/ui/components/DeviceOptionsForm/index.tsx b/src/ui/components/DeviceOptionsForm/index.tsx index 0f5b953fc..b892e6533 100644 --- a/src/ui/components/DeviceOptionsForm/index.tsx +++ b/src/ui/components/DeviceOptionsForm/index.tsx @@ -97,46 +97,28 @@ const userDefinesToCategories = ( UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400, ], [UserDefineCategory.BindingPhrase]: [UserDefineKey.BINDING_PHRASE], - [UserDefineCategory.ExtraData]: [ - UserDefineKey.HYBRID_SWITCHES_8, - UserDefineKey.ENABLE_TELEMETRY, - UserDefineKey.TLM_REPORT_INTERVAL_MS, - ], + [UserDefineCategory.ExtraData]: [UserDefineKey.TLM_REPORT_INTERVAL_MS], [UserDefineCategory.PerformanceOptions]: [ - UserDefineKey.FAST_SYNC, - UserDefineKey.NO_SYNC_ON_ARM, - UserDefineKey.ARM_CHANNEL, - UserDefineKey.FEATURE_OPENTX_SYNC, - UserDefineKey.FEATURE_OPENTX_SYNC_AUTOTUNE, UserDefineKey.LOCK_ON_FIRST_CONNECTION, - UserDefineKey.LOCK_ON_50HZ, - UserDefineKey.USE_500HZ, - UserDefineKey.USE_DIVERSITY, ], [UserDefineCategory.CompatibilityOptions]: [ UserDefineKey.UART_INVERTED, - UserDefineKey.USE_UART2, - UserDefineKey.R9M_UNLOCK_HIGHER_POWER, // deprecated UserDefineKey.UNLOCK_HIGHER_POWER, UserDefineKey.USE_R9MM_R9MINI_SBUS, UserDefineKey.RCVR_UART_BAUD, UserDefineKey.RCVR_INVERT_TX, ], [UserDefineCategory.NetworkOptions]: [ - UserDefineKey.AUTO_WIFI_ON_BOOT, UserDefineKey.AUTO_WIFI_ON_INTERVAL, UserDefineKey.HOME_WIFI_SSID, UserDefineKey.HOME_WIFI_PASSWORD, ], [UserDefineCategory.OtherOptions]: [ - UserDefineKey.BLE_HID_JOYSTICK, - UserDefineKey.USE_ESP8266_BACKPACK, UserDefineKey.USE_TX_BACKPACK, UserDefineKey.JUST_BEEP_ONCE, UserDefineKey.DISABLE_STARTUP_BEEP, UserDefineKey.DISABLE_ALL_BEEPS, UserDefineKey.MY_STARTUP_MELODY, - UserDefineKey.USE_DYNAMIC_POWER, UserDefineKey.WS2812_IS_GRB, ], }; diff --git a/src/ui/components/FlashingMethodDescription/index.tsx b/src/ui/components/FlashingMethodDescription/index.tsx index 88e4938f5..a5eefef72 100644 --- a/src/ui/components/FlashingMethodDescription/index.tsx +++ b/src/ui/components/FlashingMethodDescription/index.tsx @@ -63,28 +63,6 @@ const FlashingMethodDescription: FunctionComponent<

); - case FlashingMethod.Radio: - return ( -
-

- This method allows you to build the firmware which can then be - copied to your radio and flashed to the transmitter using - EdgeTX/OpenTX -

-
    -
  1. Run Build
  2. -
  3. - Put the firmware on your radio's SD Card (recommended - location is inside the /FIRMWARE folder) -
  4. -
  5. - On your radio, navigate to where the firmware file was placed - (/FIRMWARE), select the firmware file, click-hold the Enter - button and select "Flash External ELRS" -
  6. -
-
- ); case FlashingMethod.DFU: return (
diff --git a/src/ui/components/UserDefineDescription/index.tsx b/src/ui/components/UserDefineDescription/index.tsx index 95a0076b4..d27f245ad 100644 --- a/src/ui/components/UserDefineDescription/index.tsx +++ b/src/ui/components/UserDefineDescription/index.tsx @@ -118,51 +118,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.ARM_CHANNEL: - return ( -
-

ARM channels

-

- AUX1 - Channel 5
- AUX2 - Channel 6
- AUX3 - Channel 7
- AUX4 - Channel 8
- AUX5 - Channel 9
- AUX6 - Channel 10 -
- AUX7 - Channel 11 -
- AUX8 - Channel 12 -

-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.AUTO_WIFI_ON_BOOT: - return ( -
-

- This will automatically turn the wifi on for any module that has - an ESP8285 on it if no connection is found after ~10 seconds - from boot. This enables pushing firmware updates to the RX by - connecting to its wifi network and visiting 10.0.0.1 -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.WS2812_IS_GRB: return (
@@ -203,223 +158,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.BLE_HID_JOYSTICK: - return ( -
-

- Use your transmitter module as a Bluetooth joystick and play - drone simulators wirelessly. -

-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.USE_DIVERSITY: - return ( -
-

- Enable antenna-switching diversity for RX that support it. Safe - to leave on for hardware that does not have diversity except DIY - builds, which did not populate the RF switch. -

-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.FAST_SYNC: - return ( -
-

Experimental option that adds faster initial syncing.

-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.FEATURE_OPENTX_SYNC: - return ( -
-

- This option lowers latency and{' '} - should be kept enabled. It requires{' '} - - OpenTX 2.3.12 or Newer - {' '} - or{' '} - - EdgeTX - - . -

-

- Check our{' '} - - Check our Wiki page for latest definition. - {' '} - and{' '} - - Wiki page for latest definition. - - . -

-
- ); - case UserDefineKey.FEATURE_OPENTX_SYNC_AUTOTUNE: - return ( -
-

- This option is more experimental and can lower the offset from - the radio by tuning it as close as possible to 0. It requires{' '} - - OpenTX 2.3.12 or Newer - {' '} - or{' '} - - EdgeTX - - . -

-

- Check our{' '} - - Check our Wiki page for latest definition. - {' '} - and{' '} - - Wiki page for latest definition. - - . -

-
- ); - case UserDefineKey.HYBRID_SWITCHES_8: - return ( -
-

- The switch mode MUST match on the transmitter and all receivers - that you use with it. Do not mix switch modes. Older versions of - the firmware (prior to November 30th 2020) did not check to make - sure the transmitter and receiver are using the same mode, so - the switch positions could be misinterpreted leading to - unexpected behaviour, possibly including unintentional arming. - Current versions include a check that should prevent receivers - and transmitters with different switch settings from - establishing a link, but it is still recommended to pick one - switch mode and use it for all your transmitters and receivers. -

-

There are 2 available choices here.

- -
    -
  1. - The default choice is to have 4 1bit switches (which - corresponds to 2 position switches). -
  2. -
  3. - The other option is DHYBRID_SWITCHES_8 where the first switch - is treated as a low-latency switch to be used for arm/disarm. - It is sent with every packet. For the remaining 7 switches the - first 3 bits are the switch ID, followed by 2 bits for the - switch value. Switches that have changed are given priority, - otherwise each switch value is sent in a round-robin. All - switches are encoded for 3 position support. All analog - channels are reduced to 10 bit resolution to free up space in - the RC packet for switches. For all use cases besides absolute - speed we recommend using hybrid switches over sequential or - 1-bit switches. The only "downside" is the reduced - channel resolution, but that turns out to not be important - because Betaflight only interprets channels as 10bit, so no - resolution is actually lost. -
  4. -
-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.ENABLE_TELEMETRY: - return ( -
-

- Enable advanced telemetry support. This option must be enabled - on both TX and RX. The - following telemetry messages are supported: -

-
    -
  • GPS
  • -
  • BATTERY_SENSOR
  • -
  • ATTITUDE
  • -
  • DEVICE_INFO
  • -
  • FLIGHT_MODE
  • -
-

- Note #1: Increase the telemetry rate with the - ExpressLRS lua script. Increase the rate until the sensor lost - warnings go away. It is normal to set it up to 1:16 with 200 Hz - refresh rate. -

-

- Note #2: It must be enabled together with{' '} - HYBRID_SWITCHES_8. -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.TLM_REPORT_INTERVAL_MS: return (
@@ -471,24 +209,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.LOCK_ON_50HZ: - return ( -
-

- This locks the RX at 50Hz mode from the power-up. Only - recommended for long range, and partly redundant with - LOCK_ON_FIRST_CONNECTION. -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.JUST_BEEP_ONCE: return (
@@ -592,40 +312,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.NO_SYNC_ON_ARM: - return ( -
-

- Do not transmit sync packets while armed. If you are using a - different channel than the default you need to edit (or you may - not be able to gain sync safely - default is listed in - ARM_CHANNEL as AUX1 which is Channel 5). This is useful for - racing as there is less time & packets wasted on sending sync - packets. HOWEVER if you are doing serious long range, keep this - turned off because in the case of a sustained failsafe, link can - not be regained while armed. -

-

- - This feature assumes that a low value of the arm switch is - disarmed, and a high value is armed - - . If you have the arm switch reversed it will not work - correctly, and the link will not be established. For this reason - it may be best not to enable no sync on arm when you are first - setting up ExpressLRS as it can be a source of confusion. -

-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.R9M_UNLOCK_HIGHER_POWER: case UserDefineKey.UNLOCK_HIGHER_POWER: return (
@@ -717,84 +403,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.USE_DYNAMIC_POWER: - return ( -
-

- Dynamic power feature uses LQ-based power boost up to detect - quick drops in link quality and RSSI-based power adjustments - which are working slowly, suitable for a general long-range - flights. -

-

- - Check our Wiki page for latest definition. - -

-
- ); - case UserDefineKey.USE_500HZ: - return ( -
-

- Enables 500Hz mode. It requires{' '} - - OpenTX 2.3.12 or Newer - {' '} - or{' '} - - EdgeTX - - . -

-

- Check our{' '} - - radio setup page - {' '} - and{' '} - - Wiki page for latest definition. - - . -

-
- ); - case UserDefineKey.USE_ESP8266_BACKPACK: - return ( -
-

- This enables communication with the ESP Backpack for - over-the-air updates (DeviceTarget: FrSky_TX_R9M_via_WiFi) 🖥️ - and debugging via WebSocket 🔍 -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.USE_TX_BACKPACK: return (
@@ -838,29 +446,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.USE_UART2: - return ( -
-

- This enables integration with Jye's{' '} - - FENIX rx5805 pro-diversity module - -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.HOME_WIFI_SSID: return (
diff --git a/src/ui/components/UserDefinesAdvisor/index.tsx b/src/ui/components/UserDefinesAdvisor/index.tsx index d4260f934..69937fa94 100644 --- a/src/ui/components/UserDefinesAdvisor/index.tsx +++ b/src/ui/components/UserDefinesAdvisor/index.tsx @@ -37,25 +37,6 @@ const UserDefinesAdvisor: FunctionComponent = ({ 'Disabling UART_INVERTED is uncommon. Please make sure that your transmitter supports that.' ); } - - if (isUserDefine(UserDefineKey.FEATURE_OPENTX_SYNC, false)) { - messages.push( - 'Disabling FEATURE_OPENTX_SYNC is uncommon. Keeping it disabled will prevent the ExpressLRS LUA script from communicating with the TX module properly.' - ); - } - - if (isUserDefine(UserDefineKey.NO_SYNC_ON_ARM, true)) { - messages.push( - 'NO_SYNC_ON_ARM is an advanced performance option. Make sure to read the documentation on how it works.' - ); - } - - if (isUserDefine(UserDefineKey.USE_DIVERSITY, true)) { - messages.push( - `USE_DIVERSITY requires hardware support. Make sure to attach both antennas to your device. Safe to leave on - for hardware that does not have diversity except DIY builds, which did not populate the RF switch.` - ); - } } return messages.length > 0 ? ( diff --git a/src/ui/components/UserDefinesList/index.tsx b/src/ui/components/UserDefinesList/index.tsx index 7615adf0b..47eff7bd8 100644 --- a/src/ui/components/UserDefinesList/index.tsx +++ b/src/ui/components/UserDefinesList/index.tsx @@ -82,8 +82,6 @@ const UserDefinesList: FunctionComponent = (props) => { const inputLabel = (key: UserDefineKey): string => { switch (key) { - case UserDefineKey.ARM_CHANNEL: - return 'Arm channel'; case UserDefineKey.BINDING_PHRASE: return 'Custom binding phrase'; case UserDefineKey.MY_STARTUP_MELODY: diff --git a/src/ui/gql/generated/types.ts b/src/ui/gql/generated/types.ts index f69a1d166..1f6eb0266 100644 --- a/src/ui/gql/generated/types.ts +++ b/src/ui/gql/generated/types.ts @@ -144,7 +144,6 @@ export enum FlashingMethod { DFU = 'DFU', EdgeTxPassthrough = 'EdgeTxPassthrough', Passthrough = 'Passthrough', - Radio = 'Radio', STLink = 'STLink', Stock_BL = 'Stock_BL', UART = 'UART', @@ -386,27 +385,16 @@ export type UserDefineInput = { }; export enum UserDefineKey { - ARM_CHANNEL = 'ARM_CHANNEL', - AUTO_WIFI_ON_BOOT = 'AUTO_WIFI_ON_BOOT', AUTO_WIFI_ON_INTERVAL = 'AUTO_WIFI_ON_INTERVAL', BINDING_PHRASE = 'BINDING_PHRASE', - BLE_HID_JOYSTICK = 'BLE_HID_JOYSTICK', DEVICE_NAME = 'DEVICE_NAME', DISABLE_ALL_BEEPS = 'DISABLE_ALL_BEEPS', DISABLE_STARTUP_BEEP = 'DISABLE_STARTUP_BEEP', - ENABLE_TELEMETRY = 'ENABLE_TELEMETRY', - FAST_SYNC = 'FAST_SYNC', - FEATURE_OPENTX_SYNC = 'FEATURE_OPENTX_SYNC', - FEATURE_OPENTX_SYNC_AUTOTUNE = 'FEATURE_OPENTX_SYNC_AUTOTUNE', HOME_WIFI_PASSWORD = 'HOME_WIFI_PASSWORD', HOME_WIFI_SSID = 'HOME_WIFI_SSID', - HYBRID_SWITCHES_8 = 'HYBRID_SWITCHES_8', JUST_BEEP_ONCE = 'JUST_BEEP_ONCE', - LOCK_ON_50HZ = 'LOCK_ON_50HZ', LOCK_ON_FIRST_CONNECTION = 'LOCK_ON_FIRST_CONNECTION', MY_STARTUP_MELODY = 'MY_STARTUP_MELODY', - NO_SYNC_ON_ARM = 'NO_SYNC_ON_ARM', - R9M_UNLOCK_HIGHER_POWER = 'R9M_UNLOCK_HIGHER_POWER', RCVR_INVERT_TX = 'RCVR_INVERT_TX', RCVR_UART_BAUD = 'RCVR_UART_BAUD', REGULATORY_DOMAIN_AU_433 = 'REGULATORY_DOMAIN_AU_433', @@ -420,13 +408,8 @@ export enum UserDefineKey { TLM_REPORT_INTERVAL_MS = 'TLM_REPORT_INTERVAL_MS', UART_INVERTED = 'UART_INVERTED', UNLOCK_HIGHER_POWER = 'UNLOCK_HIGHER_POWER', - USE_500HZ = 'USE_500HZ', - USE_DIVERSITY = 'USE_DIVERSITY', - USE_DYNAMIC_POWER = 'USE_DYNAMIC_POWER', - USE_ESP8266_BACKPACK = 'USE_ESP8266_BACKPACK', USE_R9MM_R9MINI_SBUS = 'USE_R9MM_R9MINI_SBUS', USE_TX_BACKPACK = 'USE_TX_BACKPACK', - USE_UART2 = 'USE_UART2', WS2812_IS_GRB = 'WS2812_IS_GRB', } @@ -825,6 +808,7 @@ export function useAvailableDevicesListQuery( AvailableDevicesListQueryVariables >(AvailableDevicesListDocument, options); } + export function useAvailableDevicesListLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< AvailableDevicesListQuery, @@ -837,6 +821,7 @@ export function useAvailableDevicesListLazyQuery( AvailableDevicesListQueryVariables >(AvailableDevicesListDocument, options); } + export type AvailableDevicesListQueryHookResult = ReturnType< typeof useAvailableDevicesListQuery >; @@ -917,6 +902,7 @@ export function useAvailableFirmwareTargetsQuery( AvailableFirmwareTargetsQueryVariables >(AvailableFirmwareTargetsDocument, options); } + export function useAvailableFirmwareTargetsLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< AvailableFirmwareTargetsQuery, @@ -929,6 +915,7 @@ export function useAvailableFirmwareTargetsLazyQuery( AvailableFirmwareTargetsQueryVariables >(AvailableFirmwareTargetsDocument, options); } + export type AvailableFirmwareTargetsQueryHookResult = ReturnType< typeof useAvailableFirmwareTargetsQuery >; @@ -990,6 +977,7 @@ export function useAvailableMulticastDnsDevicesListQuery( AvailableMulticastDnsDevicesListQueryVariables >(AvailableMulticastDnsDevicesListDocument, options); } + export function useAvailableMulticastDnsDevicesListLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< AvailableMulticastDnsDevicesListQuery, @@ -1002,6 +990,7 @@ export function useAvailableMulticastDnsDevicesListLazyQuery( AvailableMulticastDnsDevicesListQueryVariables >(AvailableMulticastDnsDevicesListDocument, options); } + export type AvailableMulticastDnsDevicesListQueryHookResult = ReturnType< typeof useAvailableMulticastDnsDevicesListQuery >; @@ -1060,6 +1049,7 @@ export function useBuildFlashFirmwareMutation( BuildFlashFirmwareMutationVariables >(BuildFlashFirmwareDocument, options); } + export type BuildFlashFirmwareMutationHookResult = ReturnType< typeof useBuildFlashFirmwareMutation >; @@ -1104,6 +1094,7 @@ export function useBuildLogUpdatesSubscription( BuildLogUpdatesSubscriptionVariables >(BuildLogUpdatesDocument, options); } + export type BuildLogUpdatesSubscriptionHookResult = ReturnType< typeof useBuildLogUpdatesSubscription >; @@ -1146,6 +1137,7 @@ export function useBuildProgressNotificationsSubscription( BuildProgressNotificationsSubscriptionVariables >(BuildProgressNotificationsDocument, options); } + export type BuildProgressNotificationsSubscriptionHookResult = ReturnType< typeof useBuildProgressNotificationsSubscription >; @@ -1192,6 +1184,7 @@ export function useBuildUserDefinesTxtMutation( BuildUserDefinesTxtMutationVariables >(BuildUserDefinesTxtDocument, options); } + export type BuildUserDefinesTxtMutationHookResult = ReturnType< typeof useBuildUserDefinesTxtMutation >; @@ -1239,6 +1232,7 @@ export function useCheckForUpdatesQuery( options ); } + export function useCheckForUpdatesLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< CheckForUpdatesQuery, @@ -1251,6 +1245,7 @@ export function useCheckForUpdatesLazyQuery( CheckForUpdatesQueryVariables >(CheckForUpdatesDocument, options); } + export type CheckForUpdatesQueryHookResult = ReturnType< typeof useCheckForUpdatesQuery >; @@ -1302,6 +1297,7 @@ export function useClearFirmwareFilesMutation( ClearFirmwareFilesMutationVariables >(ClearFirmwareFilesDocument, options); } + export type ClearFirmwareFilesMutationHookResult = ReturnType< typeof useClearFirmwareFilesMutation >; @@ -1352,6 +1348,7 @@ export function useClearPlatformioCoreDirMutation( ClearPlatformioCoreDirMutationVariables >(ClearPlatformioCoreDirDocument, options); } + export type ClearPlatformioCoreDirMutationHookResult = ReturnType< typeof useClearPlatformioCoreDirMutation >; @@ -1403,6 +1400,7 @@ export function useConnectToSerialDeviceMutation( ConnectToSerialDeviceMutationVariables >(ConnectToSerialDeviceDocument, options); } + export type ConnectToSerialDeviceMutationHookResult = ReturnType< typeof useConnectToSerialDeviceMutation >; @@ -1479,6 +1477,7 @@ export function useTargetDeviceOptionsQuery( TargetDeviceOptionsQueryVariables >(TargetDeviceOptionsDocument, options); } + export function useTargetDeviceOptionsLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< TargetDeviceOptionsQuery, @@ -1491,6 +1490,7 @@ export function useTargetDeviceOptionsLazyQuery( TargetDeviceOptionsQueryVariables >(TargetDeviceOptionsDocument, options); } + export type TargetDeviceOptionsQueryHookResult = ReturnType< typeof useTargetDeviceOptionsQuery >; @@ -1542,6 +1542,7 @@ export function useDisconnectFromSerialDeviceMutation( DisconnectFromSerialDeviceMutationVariables >(DisconnectFromSerialDeviceDocument, options); } + export type DisconnectFromSerialDeviceMutationHookResult = ReturnType< typeof useDisconnectFromSerialDeviceMutation >; @@ -1587,6 +1588,7 @@ export function useGetBranchesQuery( options ); } + export function useGetBranchesLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< GetBranchesQuery, @@ -1599,6 +1601,7 @@ export function useGetBranchesLazyQuery( options ); } + export type GetBranchesQueryHookResult = ReturnType; export type GetBranchesLazyQueryHookResult = ReturnType< typeof useGetBranchesLazyQuery @@ -1647,6 +1650,7 @@ export function useGetPullRequestsQuery( options ); } + export function useGetPullRequestsLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< GetPullRequestsQuery, @@ -1659,6 +1663,7 @@ export function useGetPullRequestsLazyQuery( GetPullRequestsQueryVariables >(GetPullRequestsDocument, options); } + export type GetPullRequestsQueryHookResult = ReturnType< typeof useGetPullRequestsQuery >; @@ -1707,6 +1712,7 @@ export function useGetReleasesQuery( options ); } + export function useGetReleasesLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< GetReleasesQuery, @@ -1719,6 +1725,7 @@ export function useGetReleasesLazyQuery( options ); } + export type GetReleasesQueryHookResult = ReturnType; export type GetReleasesLazyQueryHookResult = ReturnType< typeof useGetReleasesLazyQuery @@ -1782,6 +1789,7 @@ export function useLuaScriptQuery( options ); } + export function useLuaScriptLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions< LuaScriptQuery, @@ -1794,6 +1802,7 @@ export function useLuaScriptLazyQuery( options ); } + export type LuaScriptQueryHookResult = ReturnType; export type LuaScriptLazyQueryHookResult = ReturnType< typeof useLuaScriptLazyQuery @@ -1856,6 +1865,7 @@ export function useMulticastDnsMonitorUpdatesSubscription( MulticastDnsMonitorUpdatesSubscriptionVariables >(MulticastDnsMonitorUpdatesDocument, options); } + export type MulticastDnsMonitorUpdatesSubscriptionHookResult = ReturnType< typeof useMulticastDnsMonitorUpdatesSubscription >; @@ -1893,6 +1903,7 @@ export function useGetTagsQuery( options ); } + export function useGetTagsLazyQuery( baseOptions?: Apollo.LazyQueryHookOptions ) { @@ -1902,6 +1913,7 @@ export function useGetTagsLazyQuery( options ); } + export type GetTagsQueryHookResult = ReturnType; export type GetTagsLazyQueryHookResult = ReturnType; export type GetTagsQueryResult = Apollo.QueryResult< @@ -1943,6 +1955,7 @@ export function useSerialMonitorEventsSubscription( SerialMonitorEventsSubscriptionVariables >(SerialMonitorEventsDocument, options); } + export type SerialMonitorEventsSubscriptionHookResult = ReturnType< typeof useSerialMonitorEventsSubscription >; @@ -1983,6 +1996,7 @@ export function useSerialMonitorLogsSubscription( SerialMonitorLogsSubscriptionVariables >(SerialMonitorLogsDocument, options); } + export type SerialMonitorLogsSubscriptionHookResult = ReturnType< typeof useSerialMonitorLogsSubscription >; diff --git a/src/ui/views/ConfiguratorView/UserDefinesValidator.ts b/src/ui/views/ConfiguratorView/UserDefinesValidator.ts index 5007a59eb..af8e93e0a 100644 --- a/src/ui/views/ConfiguratorView/UserDefinesValidator.ts +++ b/src/ui/views/ConfiguratorView/UserDefinesValidator.ts @@ -78,24 +78,11 @@ export default class UserDefinesValidator { return results; } - validateArmChannel(data: UserDefine[]): Error[] { - const results: Error[] = []; - - const option = data.find(({ key }) => key === UserDefineKey.ARM_CHANNEL); - - if (option && option.enabled && option.value && option.value.length === 0) { - results.push(new Error('Arm channel selected, but not entered')); - } - - return results; - } - validate(data: UserDefine[]): Error[] { return [ ...this.validateRegulatoryDomains(data), ...this.validateBindingPhrase(data), ...this.validateStartupMelody(data), - ...this.validateArmChannel(data), ]; } } diff --git a/src/ui/views/ConfiguratorView/index.tsx b/src/ui/views/ConfiguratorView/index.tsx index a6b5a933e..32372cc6d 100644 --- a/src/ui/views/ConfiguratorView/index.tsx +++ b/src/ui/views/ConfiguratorView/index.tsx @@ -900,30 +900,28 @@ const ConfiguratorView: FunctionComponent = (props) => { > Build - {deviceTarget?.flashingMethod !== FlashingMethod.Radio && ( - { - if (value === BuildJobType.BuildAndFlash) { - onBuildAndFlash(); - } else if (value === BuildJobType.ForceFlash) { - onForceFlash(); - } - }} - /> - )} + { + if (value === BuildJobType.BuildAndFlash) { + onBuildAndFlash(); + } else if (value === BuildJobType.ForceFlash) { + onForceFlash(); + } + }} + />
@@ -1072,9 +1070,7 @@ const ConfiguratorView: FunctionComponent = (props) => { currentJobType === BuildJobType.Build && ( Build notice - {deviceTarget?.flashingMethod !== FlashingMethod.Radio - ? 'Firmware binary file was opened in the file explorer' - : "Firmware binary file was opened in the file explorer, copy the firmware file to your radios's SD card and flash it to the transmitter using EdgeTX/OpenTX"} + Firmware binary file was opened in the file explorer )} From c52e0981dd4d3bb7b168aaf3c43e4e275909aecd Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 13 Nov 2022 20:07:56 +0200 Subject: [PATCH 09/54] Remove more unused user defines --- devices/axis-2400.json | 1 - devices/betafpv-2400.json | 4 --- devices/betafpv-900.json | 3 -- devices/diy-2400.json | 12 ++----- devices/diy-900.json | 4 --- devices/foxeer-2400.json | 1 - devices/frsky-900.json | 5 +-- devices/happymodel-2400.json | 8 ++--- devices/happymodel-900.json | 1 - devices/hglrc-2400.json | 4 +-- devices/hglrc-900.json | 1 - devices/jumper-2400.json | 1 - devices/matek-2400.json | 3 -- devices/namimnorc-2400.json | 2 -- devices/namimnorc-900.json | 1 - devices/neutronrc-900.json | 1 - devices/quadcopters-2400.json | 4 +-- .../src/factories/TargetUserDefinesFactory.ts | 4 --- .../FirmwareBuilder/Enum/UserDefineKey.ts | 2 -- .../services/BinaryFlashingStrategy/index.ts | 2 -- src/ui/components/DeviceOptionsForm/index.tsx | 2 -- .../UserDefineDescription/index.tsx | 36 ------------------- src/ui/gql/generated/types.ts | 2 -- 23 files changed, 8 insertions(+), 96 deletions(-) diff --git a/devices/axis-2400.json b/devices/axis-2400.json index 8b77c95e3..e26d3a738 100644 --- a/devices/axis-2400.json +++ b/devices/axis-2400.json @@ -48,7 +48,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/betafpv-2400.json b/devices/betafpv-2400.json index 9cddb3623..9c06a3609 100644 --- a/devices/betafpv-2400.json +++ b/devices/betafpv-2400.json @@ -21,7 +21,6 @@ "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB", "UNLOCK_HIGHER_POWER" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/betafpv2400/", @@ -58,7 +57,6 @@ "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB", "UNLOCK_HIGHER_POWER" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/betafpv2400/", @@ -156,7 +154,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -210,7 +207,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/betafpv-900.json b/devices/betafpv-900.json index 19ce80751..2ff17cf18 100644 --- a/devices/betafpv-900.json +++ b/devices/betafpv-900.json @@ -20,7 +20,6 @@ "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", "UART_INVERTED", - "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -59,7 +58,6 @@ "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", "UART_INVERTED", - "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -93,7 +91,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/diy-2400.json b/devices/diy-2400.json index a326685ef..77f21766e 100644 --- a/devices/diy-2400.json +++ b/devices/diy-2400.json @@ -72,8 +72,7 @@ "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", "deviceType": "ExpressLRS", @@ -97,12 +96,10 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "USE_TX_BACKPACK", "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", "deviceType": "ExpressLRS", @@ -159,8 +156,7 @@ "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/diy2400/", "deviceType": "ExpressLRS", @@ -188,7 +184,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -296,7 +291,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" diff --git a/devices/diy-900.json b/devices/diy-900.json index 4ea1cc35c..0605d7702 100644 --- a/devices/diy-900.json +++ b/devices/diy-900.json @@ -78,7 +78,6 @@ "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", "UART_INVERTED", - "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -109,7 +108,6 @@ "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", "UART_INVERTED", - "WS2812_IS_GRB", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -142,7 +140,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -186,7 +183,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" diff --git a/devices/foxeer-2400.json b/devices/foxeer-2400.json index 6f0e0cf68..940bfb5f4 100644 --- a/devices/foxeer-2400.json +++ b/devices/foxeer-2400.json @@ -21,7 +21,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/frsky-900.json b/devices/frsky-900.json index 9c0a8804f..794177f71 100644 --- a/devices/frsky-900.json +++ b/devices/frsky-900.json @@ -23,14 +23,11 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "USE_ESP8266_BACKPACK", "JUST_BEEP_ONCE", "DISABLE_STARTUP_BEEP", "DISABLE_ALL_BEEPS", "MY_STARTUP_MELODY", - "R9M_UNLOCK_HIGHER_POWER", - "UNLOCK_HIGHER_POWER", - "USE_TX_BACKPACK" + "UNLOCK_HIGHER_POWER" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/frsky-r9modules/", "deviceType": "ExpressLRS", diff --git a/devices/happymodel-2400.json b/devices/happymodel-2400.json index cc6e9f034..614345326 100644 --- a/devices/happymodel-2400.json +++ b/devices/happymodel-2400.json @@ -20,8 +20,7 @@ "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/es24tx/", "deviceType": "ExpressLRS", @@ -48,8 +47,7 @@ "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/es24tx/", "deviceType": "ExpressLRS", @@ -84,7 +82,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -124,7 +121,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/happymodel-900.json b/devices/happymodel-900.json index 905940fa2..48076b182 100644 --- a/devices/happymodel-900.json +++ b/devices/happymodel-900.json @@ -53,7 +53,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/hglrc-2400.json b/devices/hglrc-2400.json index d41fa8256..47dc4fbd1 100644 --- a/devices/hglrc-2400.json +++ b/devices/hglrc-2400.json @@ -20,8 +20,7 @@ "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "", "deviceType": "ExpressLRS", @@ -49,7 +48,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/hglrc-900.json b/devices/hglrc-900.json index a5627d98b..90f636c4d 100644 --- a/devices/hglrc-900.json +++ b/devices/hglrc-900.json @@ -23,7 +23,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/jumper-2400.json b/devices/jumper-2400.json index 18a016cbf..236811e29 100644 --- a/devices/jumper-2400.json +++ b/devices/jumper-2400.json @@ -108,7 +108,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/matek-2400.json b/devices/matek-2400.json index 2d21d37d2..a59a05673 100644 --- a/devices/matek-2400.json +++ b/devices/matek-2400.json @@ -21,7 +21,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -50,7 +49,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD" @@ -81,7 +79,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/namimnorc-2400.json b/devices/namimnorc-2400.json index 0dfb94432..f4ce93e89 100644 --- a/devices/namimnorc-2400.json +++ b/devices/namimnorc-2400.json @@ -99,7 +99,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", @@ -132,7 +131,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/namimnorc-900.json b/devices/namimnorc-900.json index 74313229b..c49e4e887 100644 --- a/devices/namimnorc-900.json +++ b/devices/namimnorc-900.json @@ -103,7 +103,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/neutronrc-900.json b/devices/neutronrc-900.json index ac7f67c39..313cfe7a1 100644 --- a/devices/neutronrc-900.json +++ b/devices/neutronrc-900.json @@ -23,7 +23,6 @@ "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/devices/quadcopters-2400.json b/devices/quadcopters-2400.json index 2282b9b90..3a33d3ec4 100644 --- a/devices/quadcopters-2400.json +++ b/devices/quadcopters-2400.json @@ -20,8 +20,7 @@ "UART_INVERTED", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "WS2812_IS_GRB" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "", "deviceType": "ExpressLRS", @@ -56,7 +55,6 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "LOCK_ON_FIRST_CONNECTION", - "AUTO_WIFI_ON_BOOT", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", diff --git a/src/api/src/factories/TargetUserDefinesFactory.ts b/src/api/src/factories/TargetUserDefinesFactory.ts index da21ecfbe..727d8c1ae 100644 --- a/src/api/src/factories/TargetUserDefinesFactory.ts +++ b/src/api/src/factories/TargetUserDefinesFactory.ts @@ -67,8 +67,6 @@ export default class TargetUserDefinesFactory { case UserDefineKey.RCVR_INVERT_TX: return UserDefine.Boolean(UserDefineKey.RCVR_INVERT_TX); // Other options - case UserDefineKey.USE_TX_BACKPACK: - return UserDefine.Boolean(UserDefineKey.USE_TX_BACKPACK); case UserDefineKey.JUST_BEEP_ONCE: return UserDefine.Boolean(UserDefineKey.JUST_BEEP_ONCE); case UserDefineKey.DISABLE_ALL_BEEPS: @@ -77,8 +75,6 @@ export default class TargetUserDefinesFactory { return UserDefine.Boolean(UserDefineKey.DISABLE_STARTUP_BEEP); case UserDefineKey.MY_STARTUP_MELODY: return UserDefine.Text(UserDefineKey.MY_STARTUP_MELODY); - case UserDefineKey.WS2812_IS_GRB: - return UserDefine.Boolean(UserDefineKey.WS2812_IS_GRB); // Network case UserDefineKey.HOME_WIFI_SSID: return UserDefine.Text(UserDefineKey.HOME_WIFI_SSID, '', false, true); diff --git a/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts b/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts index 0d895a6ee..1ef6a788e 100644 --- a/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts +++ b/src/api/src/library/FirmwareBuilder/Enum/UserDefineKey.ts @@ -21,12 +21,10 @@ enum UserDefineKey { RCVR_UART_BAUD = 'DRCVR_UART_BAUD', RCVR_INVERT_TX = 'DRCVR_INVERT_TX', // Other options - USE_TX_BACKPACK = 'DUSE_TX_BACKPACK', JUST_BEEP_ONCE = 'DJUST_BEEP_ONCE', DISABLE_ALL_BEEPS = 'DDISABLE_ALL_BEEPS', DISABLE_STARTUP_BEEP = 'DDISABLE_STARTUP_BEEP', MY_STARTUP_MELODY = 'DMY_STARTUP_MELODY', - WS2812_IS_GRB = 'DWS2812_IS_GRB', // Network HOME_WIFI_SSID = 'DHOME_WIFI_SSID', HOME_WIFI_PASSWORD = 'DHOME_WIFI_PASSWORD', diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index df67ffac4..8d166f503 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -236,8 +236,6 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { flags.push(['--r9mm-mini-sbus']); break; // not supported - case UserDefineKey.USE_TX_BACKPACK: - case UserDefineKey.WS2812_IS_GRB: case UserDefineKey.DEVICE_NAME: break; default: diff --git a/src/ui/components/DeviceOptionsForm/index.tsx b/src/ui/components/DeviceOptionsForm/index.tsx index b892e6533..298b7e36a 100644 --- a/src/ui/components/DeviceOptionsForm/index.tsx +++ b/src/ui/components/DeviceOptionsForm/index.tsx @@ -114,12 +114,10 @@ const userDefinesToCategories = ( UserDefineKey.HOME_WIFI_PASSWORD, ], [UserDefineCategory.OtherOptions]: [ - UserDefineKey.USE_TX_BACKPACK, UserDefineKey.JUST_BEEP_ONCE, UserDefineKey.DISABLE_STARTUP_BEEP, UserDefineKey.DISABLE_ALL_BEEPS, UserDefineKey.MY_STARTUP_MELODY, - UserDefineKey.WS2812_IS_GRB, ], }; diff --git a/src/ui/components/UserDefineDescription/index.tsx b/src/ui/components/UserDefineDescription/index.tsx index d27f245ad..bd8532aa9 100644 --- a/src/ui/components/UserDefineDescription/index.tsx +++ b/src/ui/components/UserDefineDescription/index.tsx @@ -118,23 +118,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.WS2812_IS_GRB: - return ( -
-

- Enable this if your LED is GRB. If this is disabled your led - will be used in RGB configuration. -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.AUTO_WIFI_ON_INTERVAL: return (
@@ -403,25 +386,6 @@ const UserDefineDescription: FunctionComponent =

); - case UserDefineKey.USE_TX_BACKPACK: - return ( -
-

- Enables code for talking to a connected backpack on the TX - module, and associated Lua params The device target should - enable this automatically for devices that come with this, but - can be added to any device. -

-

- - Check our Wiki page for latest definition. - -

-
- ); case UserDefineKey.USE_R9MM_R9MINI_SBUS: return (
diff --git a/src/ui/gql/generated/types.ts b/src/ui/gql/generated/types.ts index 1f6eb0266..addf38c3c 100644 --- a/src/ui/gql/generated/types.ts +++ b/src/ui/gql/generated/types.ts @@ -409,8 +409,6 @@ export enum UserDefineKey { UART_INVERTED = 'UART_INVERTED', UNLOCK_HIGHER_POWER = 'UNLOCK_HIGHER_POWER', USE_R9MM_R9MINI_SBUS = 'USE_R9MM_R9MINI_SBUS', - USE_TX_BACKPACK = 'USE_TX_BACKPACK', - WS2812_IS_GRB = 'WS2812_IS_GRB', } export enum UserDefineKind { From 4d9cc8b5703250a9807c6b97b3c03fc38b539553 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 19 Nov 2022 21:43:18 +0200 Subject: [PATCH 10/54] wip --- .../services/BinaryFlashingStrategy/CloudBinariesCache/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts diff --git a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts new file mode 100644 index 000000000..eeb888b5c --- /dev/null +++ b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts @@ -0,0 +1 @@ +export class CloudBinariesCache {} From 1767db7f4e85ba71ca746d25b97d0d5f93cc8283 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Tue, 10 Jan 2023 18:15:46 +0200 Subject: [PATCH 11/54] Fix startup issues from legacy user defines --- devices/radiomaster-2400.json | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/devices/radiomaster-2400.json b/devices/radiomaster-2400.json index 43fb5e791..5e2db59cc 100644 --- a/devices/radiomaster-2400.json +++ b/devices/radiomaster-2400.json @@ -87,8 +87,6 @@ "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "UART_INVERTED" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-external/", @@ -115,8 +113,6 @@ "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "UART_INVERTED" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-external/", @@ -144,8 +140,6 @@ "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER", "UART_INVERTED" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-external/", @@ -174,13 +168,9 @@ "REGULATORY_DOMAIN_EU_CE_2400", "BINDING_PHRASE", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "FEATURE_OPENTX_SYNC", "AUTO_WIFI_ON_INTERVAL", "HOME_WIFI_SSID", - "HOME_WIFI_PASSWORD", - "BLE_HID_JOYSTICK", - "USE_DYNAMIC_POWER" + "HOME_WIFI_PASSWORD" ], "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-internal/", "deviceType": "ExpressLRS", From 01a7d2020b1171cd1e0f8e6da80099569dff40ff Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Wed, 11 Jan 2023 01:53:49 +0200 Subject: [PATCH 12/54] wip --- src/api/index.ts | 9 +- src/api/src/library/Python/index.ts | 17 +- .../BinaryConfigurator/index.ts | 173 ++++++ .../CloudBinariesCache/index.ts | 2 +- .../DeviceDescriptionsLoader/index.ts | 30 +- .../services/BinaryFlashingStrategy/index.ts | 552 ++++++++---------- .../FlashingStrategy.ts | 4 +- .../FlashingStrategyLocator/artefacts.ts | 110 ++++ .../services/FlashingStrategyLocator/index.ts | 13 +- .../services/FlashingStrategyLocator/masks.ts | 42 ++ .../PlatformioFlashingStrategy/index.ts | 165 +----- 11 files changed, 653 insertions(+), 464 deletions(-) create mode 100644 src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/artefacts.ts create mode 100644 src/api/src/services/FlashingStrategyLocator/masks.ts diff --git a/src/api/index.ts b/src/api/index.ts index c08992326..c0e691686 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -41,6 +41,7 @@ import FlashingStrategyLocatorService from './src/services/FlashingStrategyLocat import Python from './src/library/Python'; import BinaryFlashingStrategyService from './src/services/BinaryFlashingStrategy'; import DeviceDescriptionsLoader from './src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader'; +import BinaryConfigurator from './src/services/BinaryFlashingStrategy/BinaryConfigurator'; export default class ApiServer { app: Express | undefined; @@ -123,12 +124,13 @@ export default class ApiServer { ); Container.set(TargetsLoader, targetsLoader); + const firmwareBuilder = new FirmwareBuilder(platformio, logger); const platformioFlashingStrategyService = new PlatformioFlashingStrategyService( config.PATH, config.firmwaresPath, platformio, - new FirmwareBuilder(platformio, logger), + firmwareBuilder, pubSub, logger, userDefinesBuilder, @@ -140,11 +142,14 @@ export default class ApiServer { config.PATH, path.join(config.userDataPath, 'firmwares', 'binary-targets') ); + const binaryConfigurator = new BinaryConfigurator(python, logger); const binaryFlashingStrategyService = new BinaryFlashingStrategyService( config.PATH, path.join(config.userDataPath, 'firmwares', 'binary'), pubSub, - python, + binaryConfigurator, + platformio, + firmwareBuilder, deviceDescriptionsLoader, logger ); diff --git a/src/api/src/library/Python/index.ts b/src/api/src/library/Python/index.ts index 143327ba8..35a61cc21 100644 --- a/src/api/src/library/Python/index.ts +++ b/src/api/src/library/Python/index.ts @@ -1,6 +1,7 @@ /* eslint-disable no-await-in-loop */ import fs from 'fs'; import path from 'path'; +import child_process from 'child_process'; import Commander, { CommandResult, NoOpFunc, OnOutputFunc } from '../Commander'; import { LoggerService } from '../../logger'; @@ -14,18 +15,26 @@ export default class Python { async runPythonScript( script: string, args: string[], - onUpdate: OnOutputFunc = NoOpFunc + onUpdate: OnOutputFunc = NoOpFunc, + options: child_process.SpawnOptions = {} ): Promise { const pyExec = await this.findPythonExecutable(this.PATH); if (pyExec === null) { throw new Error('python executable not found'); } + let spawnOptions = { + env: this.env, + }; + if (options !== undefined) { + spawnOptions = { + env: this.env, + ...options, + }; + } return new Commander().runCommand( pyExec, [script, ...args], - { - env: this.env, - }, + spawnOptions, onUpdate ); } diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts new file mode 100644 index 000000000..9990074cc --- /dev/null +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -0,0 +1,173 @@ +import path from 'path'; +import { BuildFlashFirmwareParams } from '../../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import BuildJobType from '../../../models/enum/BuildJobType'; +import UserDefine from '../../../models/UserDefine'; +import UserDefineKey from '../../../library/FirmwareBuilder/Enum/UserDefineKey'; +import { NoOpFunc, OnOutputFunc } from '../../../library/Commander'; +import Python from '../../../library/Python'; +import { LoggerService } from '../../../logger'; + +const maskSensitiveFlags = (data: string[][]): string[][] => { + const sensitiveData = ['--phrase', '--ssid', '--password']; + const result = [...data]; + for (let i = 0; i < data.length; i++) { + if (sensitiveData.includes(data[i][0])) { + result[i][1] = '***'; + } + } + return result; +}; + +export default class BinaryConfigurator { + constructor(private python: Python, private logger: LoggerService) {} + + private stringifyFlags(flags: string[][]): string[] { + return flags.map((flag) => { + if (flag.length === 2) { + return `${flag[0]}=${flag[1]}`; + } + return flag[0]; + }); + } + + buildBinaryConfigFlags(params: BuildFlashFirmwareParams): string[][] { + const flags: string[][] = []; + const [manufacturer, subType, device, uploadMethod] = + params.target.split('.'); + flags.push(['--target', `${manufacturer}.${subType}.${device}`]); + if (subType.toLocaleLowerCase().includes('tx_')) { + flags.push(['--tx']); + } + flags.push(...this.userDefinesToFlags(params.userDefines)); + + if (params.type === BuildJobType.ForceFlash) { + flags.push(['--force']); + } + + if ( + params.type === BuildJobType.BuildAndFlash || + params.type === BuildJobType.ForceFlash + ) { + flags.push(['--flash', uploadMethod]); + } + + if ( + params.serialDevice?.length !== undefined && + params.serialDevice?.length > 0 + ) { + flags.push(['--port', params.serialDevice]); + } + + return flags; + } + + userDefinesToFlags(userDefines: UserDefine[]): string[][] { + const flags: string[][] = []; + userDefines + .filter(({ enabled }) => enabled) + .forEach((userDefine) => { + switch (userDefine.key) { + case UserDefineKey.BINDING_PHRASE: + flags.push(['--phrase', userDefine.value!]); + break; + case UserDefineKey.REGULATORY_DOMAIN_AU_433: + flags.push(['--domain', 'au_433']); + break; + case UserDefineKey.REGULATORY_DOMAIN_EU_433: + flags.push(['--domain', 'eu_433']); + break; + case UserDefineKey.REGULATORY_DOMAIN_AU_915: + flags.push(['--domain', 'au_915']); + break; + case UserDefineKey.REGULATORY_DOMAIN_FCC_915: + flags.push(['--domain', 'fcc_915']); + break; + case UserDefineKey.REGULATORY_DOMAIN_EU_868: + flags.push(['--domain', 'eu_868']); + break; + case UserDefineKey.REGULATORY_DOMAIN_IN_866: + flags.push(['--domain', 'in_866']); + break; + case UserDefineKey.REGULATORY_DOMAIN_ISM_2400: + break; + case UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400: + flags.push(['--lbt']); + break; + case UserDefineKey.AUTO_WIFI_ON_INTERVAL: + flags.push(['--auto-wifi', userDefine.value!]); + break; + case UserDefineKey.HOME_WIFI_SSID: + flags.push(['--ssid', userDefine.value!]); + break; + case UserDefineKey.HOME_WIFI_PASSWORD: + flags.push(['--password', userDefine.value!]); + break; + case UserDefineKey.LOCK_ON_FIRST_CONNECTION: + break; + case UserDefineKey.JUST_BEEP_ONCE: + flags.push(['--buzzer-mode', 'one-beep']); + break; + case UserDefineKey.DISABLE_ALL_BEEPS: + flags.push(['--buzzer-mode', 'quiet']); + break; + case UserDefineKey.DISABLE_STARTUP_BEEP: + flags.push(['--buzzer-mode', 'quiet']); + break; + case UserDefineKey.MY_STARTUP_MELODY: + flags.push(['--buzzer-mode', 'custom']); + flags.push(['--buzzer-melody', userDefine.key]); + break; + case UserDefineKey.RCVR_INVERT_TX: + flags.push(['--invert-tx']); + break; + case UserDefineKey.RCVR_UART_BAUD: + flags.push(['--rx-baud', userDefine.value!]); + break; + case UserDefineKey.TLM_REPORT_INTERVAL_MS: + const tlmReportIntervalMs = userDefine.value!.replaceAll('LU', ''); + flags.push(['--tlm-report', tlmReportIntervalMs]); + break; + case UserDefineKey.UART_INVERTED: + flags.push(['--uart-inverted']); + break; + case UserDefineKey.UNLOCK_HIGHER_POWER: + flags.push(['--unlock-higher-power']); + break; + case UserDefineKey.USE_R9MM_R9MINI_SBUS: + flags.push(['--r9mm-mini-sbus']); + break; + // not supported + case UserDefineKey.DEVICE_NAME: + break; + default: + const exhaustiveCheck: never = userDefine.key; + throw new Error(`Unhandled color case: ${exhaustiveCheck}`); + } + }); + return flags; + } + + async run( + firmwareSourcePath: string, + firmwareBinaryPath: string, + flags: string[][], + onUpdate: OnOutputFunc = NoOpFunc + ) { + this.logger.log('flags', { + flags: maskSensitiveFlags(flags), + firmwareBinaryPath, + }); + const binaryConfiguratorArgs = [ + ...this.stringifyFlags(flags), + firmwareBinaryPath, + ]; + return this.python.runPythonScript( + path.join(firmwareSourcePath, 'python', 'binary_configurator.py'), + binaryConfiguratorArgs, + onUpdate, + { + cwd: firmwareSourcePath, + } + ); + } +} diff --git a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts index eeb888b5c..237c85c52 100644 --- a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts @@ -1 +1 @@ -export class CloudBinariesCache {} +export default class CloudBinariesCache {} diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts index a68b68cb1..5a7df4117 100644 --- a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts @@ -184,6 +184,19 @@ export default class DeviceDescriptionsLoader { } } + private splitLastOccurrence( + str: string, + substring: string + ): [string, string] { + const lastIndex = str.lastIndexOf(substring); + + const before = str.slice(0, lastIndex); + + const after = str.slice(lastIndex + 1); + + return [before, after]; + } + async getDeviceConfig( args: UserDefineFilters, gitRepository: GitRepository @@ -197,10 +210,21 @@ export default class DeviceDescriptionsLoader { const data = await targetsJSONLoader.loadDeviceDescriptions( targetsJSONPath ); - if (typeof data[args.target] === 'undefined') { + + /* + axis.tx_2400.thor.uart split at last index gives us axis.tx_2400.thor. + */ + const [target] = this.splitLastOccurrence(args.target, '.'); + + if (typeof data[target] === 'undefined') { throw new Error(`failed to find device description for ${args.target}`); } - const { config } = data[args.target]; + const { config } = data[target]; + this.logger.log('device config', { + target, + config, + }); + return config; } @@ -245,7 +269,7 @@ export default class DeviceDescriptionsLoader { targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) ); userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_SSID) + targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_PASSWORD) ); userDefines.push( targetUserDefinesFactory.build(UserDefineKey.AUTO_WIFI_ON_INTERVAL) diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 8d166f503..c3475f576 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -2,10 +2,6 @@ import { Service } from 'typedi'; import { PubSubEngine } from 'graphql-subscriptions'; import * as os from 'os'; -import * as fs from 'fs'; -import * as path from 'path'; -import rimraf from 'rimraf'; -import cloneDeep from 'lodash/cloneDeep'; import semver from 'semver'; import UserDefine from '../../models/UserDefine'; import FirmwareSource from '../../models/enum/FirmwareSource'; @@ -17,6 +13,10 @@ import BuildFirmwareStep from '../../models/enum/FirmwareBuildStep'; import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import { + removeDirectoryContents, + createBinaryCopyWithCanonicalName, +} from '../FlashingStrategyLocator/artefacts'; import { FlashingStrategy, IsCompatibleArgs, @@ -27,52 +27,21 @@ import Device from '../../models/Device'; import { UserDefineFilters } from '../UserDefinesLoader'; import BuildJobType from '../../models/enum/BuildJobType'; import BuildFlashFirmwareResult from '../../graphql/objects/BuildFlashFirmwareResult'; -import Python from '../../library/Python'; import { findGitExecutable, GitFirmwareDownloader, } from '../../library/FirmwareDownloader'; import DeviceDescriptionsLoader from './DeviceDescriptionsLoader'; -import FlashingMethod from '../../models/enum/FlashingMethod'; - -const maskSensitiveData = (haystack: string): string => { - const needles = [ - 'HOME_WIFI_SSID', - 'HOME_WIFI_PASSWORD', - 'MY_BINDING_PHRASE', - 'MY_UID', - ]; - for (let i = 0; i < needles.length; i++) { - if (haystack.includes(needles[i])) { - return '***'; - } - } - return haystack; -}; - -const maskBuildFlashFirmwareParams = ( - params: BuildFlashFirmwareParams -): BuildFlashFirmwareParams => { - const result = cloneDeep(params); - if (result.userDefinesTxt?.length > 0) { - result.userDefinesTxt = '******'; - } - result.userDefines = result.userDefines.map((userDefine) => { - const sensitiveDataKeys = [ - UserDefineKey.BINDING_PHRASE, - UserDefineKey.HOME_WIFI_SSID, - UserDefineKey.HOME_WIFI_PASSWORD, - ]; - if (sensitiveDataKeys.includes(userDefine.key)) { - return { - ...userDefine, - value: '***', - }; - } - return userDefine; - }); - return result; -}; +import { FirmwareVersionData } from '../FlashingStrategyLocator/FirmwareVersionData'; +import Platformio from '../../library/Platformio'; +import FirmwareBuilder from '../../library/FirmwareBuilder'; +import UserDefinesMode from '../../models/enum/UserDefinesMode'; +import UserDefinesTxtFactory from '../../factories/UserDefinesTxtFactory'; +import BinaryConfigurator from './BinaryConfigurator'; +import { + maskBuildFlashFirmwareParams, + maskSensitiveData, +} from '../FlashingStrategyLocator/masks'; @Service() export default class BinaryFlashingStrategyService implements FlashingStrategy { @@ -84,7 +53,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { private PATH: string, private firmwaresPath: string, private pubSub: PubSubEngine, - private python: Python, + private binaryConfigurator: BinaryConfigurator, + private platformio: Platformio, + private builder: FirmwareBuilder, private deviceDescriptionsLoader: DeviceDescriptionsLoader, private logger: LoggerService ) { @@ -142,18 +113,19 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return false; } - // @TODO: check remote build cache - // @TODO: checkout git repository for a full check async isCompatible( params: IsCompatibleArgs, gitRepositoryUrl: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars _gitRepositorySrcFolder: string ) { if ( - gitRepositoryUrl.toLowerCase() === + (gitRepositoryUrl.toLowerCase() === 'https://github.com/expresslrs/expresslrs'.toLowerCase() && - params.source === FirmwareSource.GitTag && - semver.compare(params.gitTag, '3.0.0') >= 0 + params.source === FirmwareSource.GitTag && + semver.compare(params.gitTag, '3.0.0') >= 0) || + (params.source === FirmwareSource.GitBranch && + params.gitBranch === 'master') ) { return true; } @@ -161,89 +133,187 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return false; } - userDefinesToFlags(userDefines: UserDefine[]): string[][] { - const flags: string[][] = []; - userDefines - .filter(({ enabled }) => enabled) - .forEach((userDefine) => { - switch (userDefine.key) { - case UserDefineKey.BINDING_PHRASE: - flags.push(['--phrase', userDefine.value!]); - break; - case UserDefineKey.REGULATORY_DOMAIN_AU_433: - flags.push(['--domain', 'au_433']); - break; - case UserDefineKey.REGULATORY_DOMAIN_EU_433: - flags.push(['--domain', 'eu_433']); - break; - case UserDefineKey.REGULATORY_DOMAIN_AU_915: - flags.push(['--domain', 'au_915']); - break; - case UserDefineKey.REGULATORY_DOMAIN_FCC_915: - flags.push(['--domain', 'fcc_915']); - break; - case UserDefineKey.REGULATORY_DOMAIN_EU_868: - flags.push(['--domain', 'eu_868']); - break; - case UserDefineKey.REGULATORY_DOMAIN_IN_866: - flags.push(['--domain', 'in_866']); - break; - case UserDefineKey.REGULATORY_DOMAIN_ISM_2400: - break; - case UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400: - flags.push(['--lbt']); - break; - case UserDefineKey.AUTO_WIFI_ON_INTERVAL: - flags.push(['--auto-wifi', userDefine.value!]); - break; - case UserDefineKey.HOME_WIFI_SSID: - flags.push(['--ssid', userDefine.value!]); - break; - case UserDefineKey.HOME_WIFI_PASSWORD: - flags.push(['--password', userDefine.value!]); - break; - case UserDefineKey.LOCK_ON_FIRST_CONNECTION: - break; - case UserDefineKey.JUST_BEEP_ONCE: - flags.push(['--buzzer-mode', 'one-beep']); - break; - case UserDefineKey.DISABLE_ALL_BEEPS: - flags.push(['--buzzer-mode', 'quiet']); - break; - case UserDefineKey.DISABLE_STARTUP_BEEP: - flags.push(['--buzzer-mode', 'quiet']); - break; - case UserDefineKey.MY_STARTUP_MELODY: - flags.push(['--buzzer-mode', 'custom']); - flags.push(['--buzzer-melody', userDefine.key]); - break; - case UserDefineKey.RCVR_INVERT_TX: - flags.push(['--invert-tx']); - break; - case UserDefineKey.RCVR_UART_BAUD: - flags.push(['--rx-baud', userDefine.value!]); - break; - case UserDefineKey.TLM_REPORT_INTERVAL_MS: - flags.push(['--tlm-report', userDefine.value!]); - break; - case UserDefineKey.UART_INVERTED: - flags.push(['--uart-inverted']); - break; - case UserDefineKey.UNLOCK_HIGHER_POWER: - flags.push(['--unlock-higher-power']); - break; - case UserDefineKey.USE_R9MM_R9MINI_SBUS: - flags.push(['--r9mm-mini-sbus']); - break; - // not supported - case UserDefineKey.DEVICE_NAME: - break; - default: - const exhaustiveCheck: never = userDefine.key; - throw new Error(`Unhandled color case: ${exhaustiveCheck}`); + async downloadSource( + firmware: FirmwareVersionData, + gitRepositoryUrl: string, + gitRepositorySrcFolder: string + ): Promise { + let gitPath = ''; + try { + gitPath = await findGitExecutable(this.PATH); + } catch (e) { + this.logger?.error('failed to find git', undefined, { + PATH: this.PATH, + err: e, + }); + throw e; + } + this.logger?.log('git path', { + gitPath, + }); + + const firmwareDownload = new GitFirmwareDownloader( + { + baseDirectory: this.firmwaresPath, + gitBinaryLocation: gitPath, + }, + this.logger + ); + + let firmwarePath = ''; + switch (firmware.source) { + case FirmwareSource.GitTag: + const tagResult = await firmwareDownload.checkoutTag( + gitRepositoryUrl, + gitRepositorySrcFolder, + firmware.gitTag + ); + firmwarePath = tagResult.path; + break; + case FirmwareSource.GitBranch: + const branchResult = await firmwareDownload.checkoutBranch( + gitRepositoryUrl, + gitRepositorySrcFolder, + firmware.gitBranch + ); + firmwarePath = branchResult.path; + break; + case FirmwareSource.GitCommit: + const commitResult = await firmwareDownload.checkoutCommit( + gitRepositoryUrl, + gitRepositorySrcFolder, + firmware.gitCommit + ); + firmwarePath = commitResult.path; + break; + case FirmwareSource.Local: + firmwarePath = firmware.localPath; + break; + case FirmwareSource.GitPullRequest: + if (firmware.gitPullRequest) { + const pullRequestResult = await firmwareDownload.checkoutCommit( + gitRepositoryUrl, + gitRepositorySrcFolder, + firmware.gitPullRequest.headCommitHash + ); + firmwarePath = pullRequestResult.path; } + break; + default: + throw new Error(`unsupported firmware source: ${firmware.source}`); + } + this.logger?.log('firmware path', { + firmwarePath, + gitRepositoryUrl, + }); + return firmwarePath; + } + + async isCachedBinariesAvailable(): Promise { + return false; + } + + async compileBinary( + target: string, + firmwareSourcePath: string, + userDefinesMode: UserDefinesMode, + userDefinesTxt: string, + userDefines: UserDefine[] + ): Promise { + const pythonCheck = await this.platformio.checkPython(); + if (!pythonCheck.success) { + this.logger?.error('python dependency check error', undefined, { + stderr: pythonCheck.stderr, + stdout: pythonCheck.stdout, + }); + throw new Error( + `Python dependency error: ${pythonCheck.stderr} ${pythonCheck.stdout}` + ); + } + + const coreCheck = await this.platformio.checkCore(); + if (!coreCheck.success) { + await this.updateLogs( + 'Failed to find Platformio on your computer. Trying to install it automatically...' + ); + this.logger?.error('platformio dependency check error', undefined, { + stderr: coreCheck.stderr, + stdout: coreCheck.stdout, + }); + const platformioInstallResult = await this.platformio.install( + (output) => { + this.updateLogs(output); + } + ); + if (!platformioInstallResult.success) { + this.logger?.error('platformio installation error', undefined, { + stderr: platformioInstallResult.stderr, + stdout: platformioInstallResult.stdout, + }); + throw new Error( + `platformio error: ${platformioInstallResult.stderr} ${platformioInstallResult.stdout}` + ); + } + } + + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.BUILDING_USER_DEFINES + ); + + let buildUserDefines = ''; + switch (userDefinesMode) { + case UserDefinesMode.Manual: + buildUserDefines = userDefinesTxt; + break; + case UserDefinesMode.UserInterface: + buildUserDefines = new UserDefinesTxtFactory().build(userDefines); + break; + default: + throw new Error(`unsupported user defines mode: ${userDefinesMode}`); + } + + const platformioStateJson = await this.platformio.getPlatformioState(); + this.logger?.log('platformio state json', { + platformioStateJson, + }); + + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.BUILDING_FIRMWARE + ); + const compileResult = await this.builder.build( + target, + buildUserDefines, + firmwareSourcePath, + (output) => { + this.updateLogs(output); + } + ); + if (!compileResult.success) { + this.logger?.error('compile error', undefined, { + code: compileResult.code, + stderr: compileResult.stderr, + stdout: compileResult.stdout, }); - return flags; + throw new Error(`failed to compile firmware: ${compileResult.stderr}`); + } + + return this.builder.getFirmwareBinPath(target, firmwareSourcePath); + } + + async getCachedBuildPath( + platformioTarget: string, + userDefines: UserDefine[] + ): Promise { + let regulatoryDomain: 'LBT' | 'FCC' = 'FCC'; + const regDomainCE2400 = userDefines.find( + ({ key }) => key === UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 + ); + if (regDomainCE2400?.enabled) { + regulatoryDomain = 'LBT'; + } + return `${regulatoryDomain}/${platformioTarget}/firmware.bin`; } async buildFlashFirmware( @@ -285,89 +355,10 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildProgressNotificationType.Info, BuildFirmwareStep.DOWNLOADING_FIRMWARE ); - - let gitPath = ''; - try { - gitPath = await findGitExecutable(this.PATH); - } catch (e) { - this.logger?.error('failed to find git', undefined, { - PATH: this.PATH, - err: e, - }); - return new BuildFlashFirmwareResult( - false, - `${e}`, - BuildFirmwareErrorType.GitDependencyError - ); - } - this.logger?.log('git path', { - gitPath, - }); - - const firmwareDownload = new GitFirmwareDownloader( - { - baseDirectory: this.firmwaresPath, - gitBinaryLocation: gitPath, - }, - this.logger - ); - - await this.updateProgress( - BuildProgressNotificationType.Info, - BuildFirmwareStep.DOWNLOADING_FIRMWARE - ); - let firmwarePath = ''; - switch (params.firmware.source) { - case FirmwareSource.GitTag: - const tagResult = await firmwareDownload.checkoutTag( - gitRepositoryUrl, - gitRepositorySrcFolder, - params.firmware.gitTag - ); - firmwarePath = tagResult.path; - break; - case FirmwareSource.GitBranch: - const branchResult = await firmwareDownload.checkoutBranch( - gitRepositoryUrl, - gitRepositorySrcFolder, - params.firmware.gitBranch - ); - firmwarePath = branchResult.path; - break; - case FirmwareSource.GitCommit: - const commitResult = await firmwareDownload.checkoutCommit( - gitRepositoryUrl, - gitRepositorySrcFolder, - params.firmware.gitCommit - ); - firmwarePath = commitResult.path; - break; - case FirmwareSource.Local: - firmwarePath = params.firmware.localPath; - break; - case FirmwareSource.GitPullRequest: - if (params.firmware.gitPullRequest) { - const pullRequestResult = await firmwareDownload.checkoutCommit( - gitRepositoryUrl, - gitRepositorySrcFolder, - params.firmware.gitPullRequest.headCommitHash - ); - firmwarePath = pullRequestResult.path; - } - break; - default: - throw new Error( - `unsupported firmware source: ${params.firmware.source}` - ); - } - this.logger?.log('firmware path', { - firmwarePath, + const firmwareSourcePath = await this.downloadSource( + params.firmware, gitRepositoryUrl, - }); - - await this.updateProgress( - BuildProgressNotificationType.Info, - BuildFirmwareStep.FLASHING_FIRMWARE + gitRepositorySrcFolder ); const config = await this.deviceDescriptionsLoader.getDeviceConfig( @@ -381,53 +372,75 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } ); - let regulatoryDomain: 'LBT' | 'FCC' = 'FCC'; - const regDomainCE2400 = params.userDefines.find( - ({ key }) => key === UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 - ); - if (regDomainCE2400?.enabled) { - regulatoryDomain = 'LBT'; + const isCachedBinariesAvailable = await this.isCachedBinariesAvailable(); + let firmwareBinaryPath = ''; + if (isCachedBinariesAvailable) { + this.logger.log('cached binaries are available'); + } else { + const target = `${config.firmware}_via_UART`; + firmwareBinaryPath = await this.compileBinary( + target, + firmwareSourcePath, + params.userDefinesMode, + params.userDefinesTxt, + params.userDefines + ); } - const flags: string[][] = []; - const [manufacturer, subType, device, uploadMethod] = - params.target.split('.'); - flags.push(['--target', `${manufacturer}.${subType}.${device}`]); - if (subType.toLocaleLowerCase().includes('tx_')) { - flags.push(['--tx']); + await this.updateProgress( + BuildProgressNotificationType.Info, + BuildFirmwareStep.BUILDING_FIRMWARE + ); + + // TODO: pip3 install pyserial esptool + // python-serial + const flags: string[][] = + this.binaryConfigurator.buildBinaryConfigFlags(params); + const binaryConfiguratorResult = await this.binaryConfigurator.run( + firmwareSourcePath, + firmwareBinaryPath, + flags, + (output) => { + this.updateLogs(output); + } + ); + if (!binaryConfiguratorResult.success) { + this.logger?.error('compile error', undefined, { + code: binaryConfiguratorResult.code, + stderr: binaryConfiguratorResult.stderr, + stdout: binaryConfiguratorResult.stdout, + }); + return new BuildFlashFirmwareResult( + false, + binaryConfiguratorResult.stderr, + BuildFirmwareErrorType.BuildError + ); } - flags.push(...this.userDefinesToFlags(params.userDefines)); if (params.type === BuildJobType.Build) { - const firmwareBinPath = path.join( - this.firmwaresPath, - `${this.generateFirmwareName(config.product_name, params)}.bin` + const canonicalBinPath = await createBinaryCopyWithCanonicalName( + params, + firmwareBinaryPath ); - fs.promises.writeFile(firmwareBinPath, Buffer.from([])); return new BuildFlashFirmwareResult( true, undefined, undefined, - firmwareBinPath + canonicalBinPath ); } - if (params.type === BuildJobType.ForceFlash) { - flags.push(['--force']); - } - if ( params.type === BuildJobType.BuildAndFlash || params.type === BuildJobType.ForceFlash ) { - flags.push(['--flash', uploadMethod]); - return new BuildFlashFirmwareResult(true, ''); } - const message = `Build Job Type ${params.type} is not currently supported`; - this.updateLogs(message); - return new BuildFlashFirmwareResult(false, message); + return new BuildFlashFirmwareResult( + false, + `Build Job Type ${params.type} is not currently supported` + ); } catch (e) { this.logger?.error('generic error', undefined, { err: e, @@ -442,62 +455,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } } - generateFirmwareName( - deviceName: string, - params: BuildFlashFirmwareParams - ): string { - const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = - params.firmware; - - const firmwareName = deviceName - ?.replace(/([^a-z0-9. ]+)/gi, '-') - .replaceAll(' ', '_'); - - switch (source) { - case FirmwareSource.GitTag: - return `${firmwareName}-${gitTag}`; - case FirmwareSource.GitBranch: - return `${firmwareName}-${gitBranch}`; - case FirmwareSource.GitCommit: - return `${firmwareName}-${gitCommit}`; - case FirmwareSource.GitPullRequest: - return `${firmwareName}-PR_${gitPullRequest?.number}`; - default: - return `${firmwareName}`; - } - } - async clearFirmwareFiles(): Promise { - const rmrf = async (file: string): Promise => { - return new Promise((resolve, reject) => { - rimraf(file, (err) => { - if (err) { - reject(); - } else { - resolve(); - } - }); - }); - }; - const listFiles = async (directory: string): Promise => { - return new Promise((resolve, reject) => { - fs.readdir(directory, (err, files) => { - if (err) { - reject(err); - } else { - resolve(files.map((file) => path.join(directory, file))); - } - }); - }); - }; - const files = await listFiles(this.firmwaresPath); - this.logger?.log('removing firmware files', { - firmwaresPath: this.firmwaresPath, - files, - }); - if (files.length > 4) { - throw new Error(`unexpected number of files to remove: ${files}`); - } - await Promise.all(files.map((item) => rmrf(item))); + return removeDirectoryContents(this.firmwaresPath); } } diff --git a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts index 06aa8e1f2..57e237337 100644 --- a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts +++ b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts @@ -1,8 +1,8 @@ import TargetArgs from '../../graphql/args/Target'; import GitRepository from '../../graphql/inputs/GitRepositoryInput'; -import {BuildFlashFirmwareParams} from './BuildFlashFirmwareParams'; +import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; import Device from '../../models/Device'; -import {UserDefineFilters} from '../UserDefinesLoader'; +import { UserDefineFilters } from '../UserDefinesLoader'; import UserDefine from '../../models/UserDefine'; import FirmwareSource from '../../models/enum/FirmwareSource'; import PullRequest from '../../models/PullRequest'; diff --git a/src/api/src/services/FlashingStrategyLocator/artefacts.ts b/src/api/src/services/FlashingStrategyLocator/artefacts.ts new file mode 100644 index 000000000..be372f474 --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/artefacts.ts @@ -0,0 +1,110 @@ +import sanitize from 'sanitize-filename'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import rimraf from 'rimraf'; +import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; +import FirmwareSource from '../../models/enum/FirmwareSource'; +import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; + +export const generateFirmwareFileName = ( + params: BuildFlashFirmwareParams +): string => { + const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = + params.firmware; + const deviceName = params.userDefines.find( + (userDefine) => userDefine.key === UserDefineKey.DEVICE_NAME + )?.value; + let target = params.target.toString(); + + const viaIndex = params.target?.lastIndexOf('_via'); + if (viaIndex > 0) { + target = target.substring(0, viaIndex); + } + + const replacement = '_'; + + const firmwareName = deviceName + ? sanitize(deviceName, { replacement }).replaceAll(' ', replacement) + : target; + + switch (source) { + case FirmwareSource.GitTag: + return `${firmwareName}-${gitTag}`; + case FirmwareSource.GitBranch: + return `${firmwareName}-${gitBranch}`; + case FirmwareSource.GitCommit: + return `${firmwareName}-${gitCommit}`; + case FirmwareSource.GitPullRequest: + return `${firmwareName}-PR_${gitPullRequest?.number}`; + default: + return `${firmwareName}`; + } +}; + +export const createBinaryCopyWithCanonicalName = async ( + params: BuildFlashFirmwareParams, + firmwareBinPath: string +): Promise => { + if (fs.existsSync(firmwareBinPath)) { + const newFirmwareBaseName = generateFirmwareFileName(params); + const firmwareExtension = path.extname(firmwareBinPath); + + const tmpPath = await fs.promises.mkdtemp( + path.join(os.tmpdir(), `${params.target}_`) + ); + + // copy with original filename to tmpPath + const tmpFirmwareBinPathOriginalName = path.join( + tmpPath, + path.basename(firmwareBinPath) + ); + await fs.promises.copyFile(firmwareBinPath, tmpFirmwareBinPathOriginalName); + + const tmpFirmwareBinPath = path.join( + tmpPath, + `${newFirmwareBaseName}${firmwareExtension}` + ); + + try { + await fs.promises.copyFile(firmwareBinPath, tmpFirmwareBinPath); + return tmpFirmwareBinPath; + } catch (err) { + return firmwareBinPath; + } + } + + return firmwareBinPath; +}; + +const rmrf = async (file: string): Promise => { + return new Promise((resolve, reject) => { + rimraf(file, (err) => { + if (err) { + reject(); + } else { + resolve(); + } + }); + }); +}; + +const listFiles = async (directory: string): Promise => { + return new Promise((resolve, reject) => { + fs.readdir(directory, (err, files) => { + if (err) { + reject(err); + } else { + resolve(files.map((file) => path.join(directory, file))); + } + }); + }); +}; + +export const removeDirectoryContents = async (firmwaresPath: string) => { + const files = await listFiles(firmwaresPath); + if (files.length > 4) { + throw new Error(`unexpected number of files to remove: ${files}`); + } + await Promise.all(files.map((item) => rmrf(item))); +}; diff --git a/src/api/src/services/FlashingStrategyLocator/index.ts b/src/api/src/services/FlashingStrategyLocator/index.ts index d0931771c..b549cc4d5 100644 --- a/src/api/src/services/FlashingStrategyLocator/index.ts +++ b/src/api/src/services/FlashingStrategyLocator/index.ts @@ -1,15 +1,14 @@ /* eslint-disable no-await-in-loop */ -import {Service} from 'typedi'; -import {FlashingStrategy, IsCompatibleArgs} from './FlashingStrategy'; -import {LoggerService} from '../../logger'; +import { Service } from 'typedi'; +import { FlashingStrategy, IsCompatibleArgs } from './FlashingStrategy'; +import { LoggerService } from '../../logger'; @Service() export default class FlashingStrategyLocatorService { constructor( private flashingStrategies: FlashingStrategy[], private logger: LoggerService - ) { - } + ) {} async locate( params: IsCompatibleArgs, @@ -36,7 +35,9 @@ export default class FlashingStrategyLocatorService { } async clearFirmwareFiles(): Promise { - const jobs = this.flashingStrategies.map((strategy) => strategy.clearFirmwareFiles()); + const jobs = this.flashingStrategies.map((strategy) => + strategy.clearFirmwareFiles() + ); await Promise.all(jobs); } } diff --git a/src/api/src/services/FlashingStrategyLocator/masks.ts b/src/api/src/services/FlashingStrategyLocator/masks.ts new file mode 100644 index 000000000..b386e345c --- /dev/null +++ b/src/api/src/services/FlashingStrategyLocator/masks.ts @@ -0,0 +1,42 @@ +import cloneDeep from 'lodash/cloneDeep'; +import { BuildFlashFirmwareParams } from './BuildFlashFirmwareParams'; +import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; + +export const maskSensitiveData = (haystack: string): string => { + const needles = [ + 'HOME_WIFI_SSID', + 'HOME_WIFI_PASSWORD', + 'MY_BINDING_PHRASE', + 'MY_UID', + ]; + for (let i = 0; i < needles.length; i++) { + if (haystack.includes(needles[i])) { + return '***'; + } + } + return haystack; +}; + +export const maskBuildFlashFirmwareParams = ( + params: BuildFlashFirmwareParams +): BuildFlashFirmwareParams => { + const result = cloneDeep(params); + if (result.userDefinesTxt?.length > 0) { + result.userDefinesTxt = '******'; + } + result.userDefines = result.userDefines.map((userDefine) => { + const sensitiveDataKeys = [ + UserDefineKey.BINDING_PHRASE, + UserDefineKey.HOME_WIFI_SSID, + UserDefineKey.HOME_WIFI_PASSWORD, + ]; + if (sensitiveDataKeys.includes(userDefine.key)) { + return { + ...userDefine, + value: '***', + }; + } + return userDefine; + }); + return result; +}; diff --git a/src/api/src/services/PlatformioFlashingStrategy/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts index 55ab47821..b9cfd8aae 100644 --- a/src/api/src/services/PlatformioFlashingStrategy/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -1,11 +1,6 @@ import { Service } from 'typedi'; import { PubSubEngine } from 'graphql-subscriptions'; import * as os from 'os'; -import * as fs from 'fs'; -import * as path from 'path'; -import rimraf from 'rimraf'; -import cloneDeep from 'lodash.clonedeep'; -import sanitize from 'sanitize-filename'; import BuildJobType from '../../models/enum/BuildJobType'; import UserDefinesMode from '../../models/enum/UserDefinesMode'; import UserDefine from '../../models/UserDefine'; @@ -26,6 +21,10 @@ import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; import UploadType from '../../library/Platformio/Enum/UploadType'; import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; +import { + createBinaryCopyWithCanonicalName, + removeDirectoryContents, +} from '../FlashingStrategyLocator/artefacts'; import { FlashingStrategy, IsCompatibleArgs, @@ -37,45 +36,10 @@ import Device from '../../models/Device'; import { UserDefineFilters } from '../UserDefinesLoader'; import GitRepository from '../../graphql/inputs/GitRepositoryInput'; import UserDefinesTxtFactory from '../../factories/UserDefinesTxtFactory'; - -const maskSensitiveData = (haystack: string): string => { - const needles = [ - 'HOME_WIFI_SSID', - 'HOME_WIFI_PASSWORD', - 'MY_BINDING_PHRASE', - 'MY_UID', - ]; - for (let i = 0; i < needles.length; i++) { - if (haystack.includes(needles[i])) { - return '***'; - } - } - return haystack; -}; - -const maskBuildFlashFirmwareParams = ( - params: BuildFlashFirmwareParams -): BuildFlashFirmwareParams => { - const result = cloneDeep(params); - if (result.userDefinesTxt?.length > 0) { - result.userDefinesTxt = '******'; - } - result.userDefines = result.userDefines.map((userDefine) => { - const sensitiveDataKeys = [ - UserDefineKey.BINDING_PHRASE, - UserDefineKey.HOME_WIFI_SSID, - UserDefineKey.HOME_WIFI_PASSWORD, - ]; - if (sensitiveDataKeys.includes(userDefine.key)) { - return { - ...userDefine, - value: '***', - }; - } - return userDefine; - }); - return result; -}; +import { + maskBuildFlashFirmwareParams, + maskSensitiveData, +} from '../FlashingStrategyLocator/masks'; @Service() export default class PlatformioFlashingStrategyService @@ -147,8 +111,11 @@ export default class PlatformioFlashingStrategyService } async isCompatible( + // eslint-disable-next-line @typescript-eslint/no-unused-vars _params: IsCompatibleArgs, + // eslint-disable-next-line @typescript-eslint/no-unused-vars _gitRepositoryUrl: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars _gitRepositorySrcFolder: string ) { return true; @@ -381,55 +348,18 @@ export default class PlatformioFlashingStrategyService ); } - let firmwareBinPath = this.builder.getFirmwareBinPath( + const firmwareBinPath = this.builder.getFirmwareBinPath( params.target, firmwarePath ); - - if (fs.existsSync(firmwareBinPath)) { - const newFirmwareBaseName = this.generateFirmwareName(params); - const firmwareExtension = path.extname(firmwareBinPath); - - const tmpPath = await fs.promises.mkdtemp( - path.join(os.tmpdir(), `${params.target}_`) - ); - - // copy with original filename to tmpPath - const tmpFirmwareBinPathOriginalName = path.join( - tmpPath, - path.basename(firmwareBinPath) - ); - try { - await fs.promises.copyFile( - firmwareBinPath, - tmpFirmwareBinPathOriginalName - ); - } catch (err) { - this.logger?.error( - `error copying file from ${firmwareBinPath} to ${tmpFirmwareBinPathOriginalName}: ${err}` - ); - } - - const tmpFirmwareBinPath = path.join( - tmpPath, - `${newFirmwareBaseName}${firmwareExtension}` - ); - - try { - await fs.promises.copyFile(firmwareBinPath, tmpFirmwareBinPath); - firmwareBinPath = tmpFirmwareBinPath; - } catch (err) { - this.logger?.error( - `error copying file from ${firmwareBinPath} to ${tmpFirmwareBinPath}: ${err}` - ); - } - } + const canonicalFirmwareBinPath = + await createBinaryCopyWithCanonicalName(params, firmwareBinPath); return new BuildFlashFirmwareResult( true, undefined, undefined, - firmwareBinPath + canonicalFirmwareBinPath ); } @@ -500,70 +430,7 @@ export default class PlatformioFlashingStrategyService } } - generateFirmwareName(params: BuildFlashFirmwareParams): string { - const { source, gitBranch, gitCommit, gitTag, gitPullRequest } = - params.firmware; - const deviceName = params.userDefines.find( - (userDefine) => userDefine.key === UserDefineKey.DEVICE_NAME - )?.value; - let target = params.target.toString(); - - const viaIndex = params.target?.lastIndexOf('_via'); - if (viaIndex > 0) { - target = target.substring(0, viaIndex); - } - - const replacement = '_'; - - const firmwareName = deviceName - ? sanitize(deviceName, { replacement }).replaceAll(' ', replacement) - : target; - - switch (source) { - case FirmwareSource.GitTag: - return `${firmwareName}-${gitTag}`; - case FirmwareSource.GitBranch: - return `${firmwareName}-${gitBranch}`; - case FirmwareSource.GitCommit: - return `${firmwareName}-${gitCommit}`; - case FirmwareSource.GitPullRequest: - return `${firmwareName}-PR_${gitPullRequest?.number}`; - default: - return `${firmwareName}`; - } - } - async clearFirmwareFiles(): Promise { - const rmrf = async (file: string): Promise => { - return new Promise((resolve, reject) => { - rimraf(file, (err) => { - if (err) { - reject(); - } else { - resolve(); - } - }); - }); - }; - const listFiles = async (directory: string): Promise => { - return new Promise((resolve, reject) => { - fs.readdir(directory, (err, files) => { - if (err) { - reject(err); - } else { - resolve(files.map((file) => path.join(directory, file))); - } - }); - }); - }; - const files = await listFiles(this.firmwaresPath); - this.logger?.log('removing firmware files', { - firmwaresPath: this.firmwaresPath, - files, - }); - if (files.length > 4) { - throw new Error(`unexpected number of files to remove: ${files}`); - } - await Promise.all(files.map((item) => rmrf(item))); + return removeDirectoryContents(this.firmwaresPath); } } From 88da8f202e117ab968aa036d91798be12703d8d7 Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:25:22 -0500 Subject: [PATCH 13/54] Include esptool in the bundled Windows python --- dependencies/windows_amd64/portablepy.ps1 | 4 +- .../_cffi_backend.cp310-win_amd64.pyd | Bin 0 -> 181248 bytes .../bitstring-3.1.9.dist-info/INSTALLER | 1 + .../bitstring-3.1.9.dist-info/LICENSE | 21 + .../bitstring-3.1.9.dist-info/METADATA | 118 + .../bitstring-3.1.9.dist-info/RECORD | 8 + .../bitstring-3.1.9.dist-info/WHEEL | 5 + .../bitstring-3.1.9.dist-info/top_level.txt | 1 + .../python/Lib/site-packages/bitstring.py | 4469 +++++++++++++++++ .../cffi-1.15.1.dist-info/INSTALLER | 1 + .../cffi-1.15.1.dist-info/LICENSE | 26 + .../cffi-1.15.1.dist-info/METADATA | 34 + .../cffi-1.15.1.dist-info/RECORD | 44 + .../site-packages/cffi-1.15.1.dist-info/WHEEL | 5 + .../cffi-1.15.1.dist-info/entry_points.txt | 2 + .../cffi-1.15.1.dist-info/top_level.txt | 2 + .../python/Lib/site-packages/cffi/__init__.py | 14 + .../Lib/site-packages/cffi/_cffi_errors.h | 149 + .../Lib/site-packages/cffi/_cffi_include.h | 385 ++ .../Lib/site-packages/cffi/_embedding.h | 528 ++ .../python/Lib/site-packages/cffi/api.py | 965 ++++ .../Lib/site-packages/cffi/backend_ctypes.py | 1121 +++++ .../Lib/site-packages/cffi/cffi_opcode.py | 187 + .../Lib/site-packages/cffi/commontypes.py | 80 + .../python/Lib/site-packages/cffi/cparser.py | 1006 ++++ .../python/Lib/site-packages/cffi/error.py | 31 + .../Lib/site-packages/cffi/ffiplatform.py | 127 + .../python/Lib/site-packages/cffi/lock.py | 30 + .../python/Lib/site-packages/cffi/model.py | 617 +++ .../Lib/site-packages/cffi/parse_c_type.h | 181 + .../Lib/site-packages/cffi/pkgconfig.py | 121 + .../Lib/site-packages/cffi/recompiler.py | 1581 ++++++ .../Lib/site-packages/cffi/setuptools_ext.py | 219 + .../Lib/site-packages/cffi/vengine_cpy.py | 1076 ++++ .../Lib/site-packages/cffi/vengine_gen.py | 675 +++ .../python/Lib/site-packages/cffi/verifier.py | 307 ++ .../cryptography-39.0.0.dist-info/INSTALLER | 1 + .../cryptography-39.0.0.dist-info/LICENSE | 6 + .../LICENSE.APACHE | 202 + .../cryptography-39.0.0.dist-info/LICENSE.BSD | 27 + .../cryptography-39.0.0.dist-info/LICENSE.PSF | 41 + .../cryptography-39.0.0.dist-info/METADATA | 134 + .../cryptography-39.0.0.dist-info/RECORD | 180 + .../cryptography-39.0.0.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/cryptography/__about__.py | 15 + .../site-packages/cryptography/__init__.py | 25 + .../site-packages/cryptography/exceptions.py | 68 + .../Lib/site-packages/cryptography/fernet.py | 220 + .../cryptography/hazmat/__init__.py | 10 + .../site-packages/cryptography/hazmat/_oid.py | 293 ++ .../cryptography/hazmat/backends/__init__.py | 10 + .../hazmat/backends/openssl/__init__.py | 8 + .../hazmat/backends/openssl/aead.py | 306 ++ .../hazmat/backends/openssl/backend.py | 2514 ++++++++++ .../hazmat/backends/openssl/ciphers.py | 281 ++ .../hazmat/backends/openssl/cmac.py | 87 + .../hazmat/backends/openssl/decode_asn1.py | 31 + .../hazmat/backends/openssl/dh.py | 317 ++ .../hazmat/backends/openssl/dsa.py | 236 + .../hazmat/backends/openssl/ec.py | 315 ++ .../hazmat/backends/openssl/ed25519.py | 155 + .../hazmat/backends/openssl/ed448.py | 156 + .../hazmat/backends/openssl/hashes.py | 86 + .../hazmat/backends/openssl/hmac.py | 84 + .../hazmat/backends/openssl/poly1305.py | 67 + .../hazmat/backends/openssl/rsa.py | 588 +++ .../hazmat/backends/openssl/utils.py | 52 + .../hazmat/backends/openssl/x25519.py | 132 + .../hazmat/backends/openssl/x448.py | 117 + .../cryptography/hazmat/bindings/__init__.py | 3 + .../cryptography/hazmat/bindings/_openssl.pyd | Bin 0 -> 3964416 bytes .../cryptography/hazmat/bindings/_openssl.pyi | 8 + .../cryptography/hazmat/bindings/_rust.pyd | Bin 0 -> 1686528 bytes .../hazmat/bindings/_rust/__init__.pyi | 34 + .../hazmat/bindings/_rust/asn1.pyi | 16 + .../hazmat/bindings/_rust/ocsp.pyi | 25 + .../hazmat/bindings/_rust/pkcs7.pyi | 15 + .../hazmat/bindings/_rust/x509.pyi | 42 + .../hazmat/bindings/openssl/__init__.py | 3 + .../hazmat/bindings/openssl/_conditional.py | 360 ++ .../hazmat/bindings/openssl/binding.py | 244 + .../hazmat/primitives/__init__.py | 3 + .../hazmat/primitives/_asymmetric.py | 17 + .../hazmat/primitives/_cipheralgorithm.py | 43 + .../hazmat/primitives/_serialization.py | 168 + .../hazmat/primitives/asymmetric/__init__.py | 3 + .../hazmat/primitives/asymmetric/dh.py | 251 + .../hazmat/primitives/asymmetric/dsa.py | 288 ++ .../hazmat/primitives/asymmetric/ec.py | 483 ++ .../hazmat/primitives/asymmetric/ed25519.py | 91 + .../hazmat/primitives/asymmetric/ed448.py | 87 + .../hazmat/primitives/asymmetric/padding.py | 101 + .../hazmat/primitives/asymmetric/rsa.py | 432 ++ .../hazmat/primitives/asymmetric/types.py | 68 + .../hazmat/primitives/asymmetric/utils.py | 23 + .../hazmat/primitives/asymmetric/x25519.py | 81 + .../hazmat/primitives/asymmetric/x448.py | 81 + .../hazmat/primitives/ciphers/__init__.py | 26 + .../hazmat/primitives/ciphers/aead.py | 374 ++ .../hazmat/primitives/ciphers/algorithms.py | 227 + .../hazmat/primitives/ciphers/base.py | 269 + .../hazmat/primitives/ciphers/modes.py | 275 + .../cryptography/hazmat/primitives/cmac.py | 64 + .../hazmat/primitives/constant_time.py | 13 + .../cryptography/hazmat/primitives/hashes.py | 261 + .../cryptography/hazmat/primitives/hmac.py | 70 + .../hazmat/primitives/kdf/__init__.py | 22 + .../hazmat/primitives/kdf/concatkdf.py | 127 + .../hazmat/primitives/kdf/hkdf.py | 100 + .../hazmat/primitives/kdf/kbkdf.py | 297 ++ .../hazmat/primitives/kdf/pbkdf2.py | 65 + .../hazmat/primitives/kdf/scrypt.py | 73 + .../hazmat/primitives/kdf/x963kdf.py | 62 + .../cryptography/hazmat/primitives/keywrap.py | 176 + .../cryptography/hazmat/primitives/padding.py | 224 + .../hazmat/primitives/poly1305.py | 60 + .../primitives/serialization/__init__.py | 46 + .../hazmat/primitives/serialization/base.py | 72 + .../hazmat/primitives/serialization/pkcs12.py | 226 + .../hazmat/primitives/serialization/pkcs7.py | 219 + .../hazmat/primitives/serialization/ssh.py | 758 +++ .../hazmat/primitives/twofactor/__init__.py | 7 + .../hazmat/primitives/twofactor/hotp.py | 91 + .../hazmat/primitives/twofactor/totp.py | 48 + .../Lib/site-packages/cryptography/py.typed | 0 .../Lib/site-packages/cryptography/utils.py | 132 + .../cryptography/x509/__init__.py | 250 + .../site-packages/cryptography/x509/base.py | 1131 +++++ .../x509/certificate_transparency.py | 96 + .../cryptography/x509/extensions.py | 2113 ++++++++ .../cryptography/x509/general_name.py | 284 ++ .../site-packages/cryptography/x509/name.py | 460 ++ .../site-packages/cryptography/x509/ocsp.py | 621 +++ .../site-packages/cryptography/x509/oid.py | 31 + .../ecdsa-0.18.0.dist-info/INSTALLER | 1 + .../ecdsa-0.18.0.dist-info/LICENSE | 24 + .../ecdsa-0.18.0.dist-info/METADATA | 675 +++ .../ecdsa-0.18.0.dist-info/RECORD | 64 + .../ecdsa-0.18.0.dist-info/WHEEL | 6 + .../ecdsa-0.18.0.dist-info/top_level.txt | 1 + .../Lib/site-packages/ecdsa/__init__.py | 90 + .../python/Lib/site-packages/ecdsa/_compat.py | 153 + .../python/Lib/site-packages/ecdsa/_rwlock.py | 86 + .../python/Lib/site-packages/ecdsa/_sha3.py | 181 + .../Lib/site-packages/ecdsa/_version.py | 21 + .../python/Lib/site-packages/ecdsa/curves.py | 513 ++ .../python/Lib/site-packages/ecdsa/der.py | 409 ++ .../python/Lib/site-packages/ecdsa/ecdh.py | 336 ++ .../python/Lib/site-packages/ecdsa/ecdsa.py | 859 ++++ .../python/Lib/site-packages/ecdsa/eddsa.py | 252 + .../Lib/site-packages/ecdsa/ellipticcurve.py | 1584 ++++++ .../python/Lib/site-packages/ecdsa/errors.py | 4 + .../python/Lib/site-packages/ecdsa/keys.py | 1612 ++++++ .../Lib/site-packages/ecdsa/numbertheory.py | 825 +++ .../python/Lib/site-packages/ecdsa/rfc6979.py | 113 + .../Lib/site-packages/ecdsa/test_curves.py | 361 ++ .../Lib/site-packages/ecdsa/test_der.py | 476 ++ .../Lib/site-packages/ecdsa/test_ecdh.py | 441 ++ .../Lib/site-packages/ecdsa/test_ecdsa.py | 661 +++ .../Lib/site-packages/ecdsa/test_eddsa.py | 1079 ++++ .../site-packages/ecdsa/test_ellipticcurve.py | 199 + .../Lib/site-packages/ecdsa/test_jacobi.py | 657 +++ .../Lib/site-packages/ecdsa/test_keys.py | 959 ++++ .../ecdsa/test_malformed_sigs.py | 370 ++ .../site-packages/ecdsa/test_numbertheory.py | 433 ++ .../Lib/site-packages/ecdsa/test_pyecdsa.py | 2267 +++++++++ .../Lib/site-packages/ecdsa/test_rw_lock.py | 180 + .../Lib/site-packages/ecdsa/test_sha3.py | 111 + .../python/Lib/site-packages/ecdsa/util.py | 433 ++ .../Lib/site-packages/espefuse/__init__.py | 284 ++ .../Lib/site-packages/espefuse/__main__.py | 10 + .../site-packages/espefuse/efuse/__init__.py | 0 .../espefuse/efuse/base_fields.py | 725 +++ .../espefuse/efuse/base_operations.py | 675 +++ .../efuse/emulate_efuse_controller_base.py | 229 + .../espefuse/efuse/esp32/__init__.py | 3 + .../efuse/esp32/emulate_efuse_controller.py | 143 + .../espefuse/efuse/esp32/fields.py | 500 ++ .../espefuse/efuse/esp32/mem_definition.py | 168 + .../espefuse/efuse/esp32/operations.py | 350 ++ .../espefuse/efuse/esp32c2/__init__.py | 3 + .../efuse/esp32c2/emulate_efuse_controller.py | 142 + .../espefuse/efuse/esp32c2/fields.py | 417 ++ .../espefuse/efuse/esp32c2/mem_definition.py | 158 + .../espefuse/efuse/esp32c2/operations.py | 349 ++ .../espefuse/efuse/esp32c3/__init__.py | 3 + .../efuse/esp32c3/emulate_efuse_controller.py | 94 + .../espefuse/efuse/esp32c3/fields.py | 466 ++ .../espefuse/efuse/esp32c3/mem_definition.py | 249 + .../espefuse/efuse/esp32c3/operations.py | 415 ++ .../espefuse/efuse/esp32c6/__init__.py | 3 + .../efuse/esp32c6/emulate_efuse_controller.py | 94 + .../espefuse/efuse/esp32c6/fields.py | 466 ++ .../espefuse/efuse/esp32c6/mem_definition.py | 263 + .../espefuse/efuse/esp32c6/operations.py | 415 ++ .../espefuse/efuse/esp32h2beta1/__init__.py | 3 + .../esp32h2beta1/emulate_efuse_controller.py | 94 + .../espefuse/efuse/esp32h2beta1/fields.py | 454 ++ .../efuse/esp32h2beta1/mem_definition.py | 229 + .../espefuse/efuse/esp32h2beta1/operations.py | 414 ++ .../espefuse/efuse/esp32s2/__init__.py | 3 + .../efuse/esp32s2/emulate_efuse_controller.py | 94 + .../espefuse/efuse/esp32s2/fields.py | 524 ++ .../espefuse/efuse/esp32s2/mem_definition.py | 310 ++ .../espefuse/efuse/esp32s2/operations.py | 525 ++ .../espefuse/efuse/esp32s3/__init__.py | 3 + .../efuse/esp32s3/emulate_efuse_controller.py | 94 + .../espefuse/efuse/esp32s3/fields.py | 478 ++ .../espefuse/efuse/esp32s3/mem_definition.py | 256 + .../espefuse/efuse/esp32s3/operations.py | 523 ++ .../espefuse/efuse/esp32s3beta2/__init__.py | 3 + .../esp32s3beta2/emulate_efuse_controller.py | 94 + .../espefuse/efuse/esp32s3beta2/fields.py | 478 ++ .../efuse/esp32s3beta2/mem_definition.py | 250 + .../espefuse/efuse/esp32s3beta2/operations.py | 523 ++ .../espefuse/efuse/mem_definition_base.py | 59 + .../Lib/site-packages/espefuse/efuse/util.py | 49 + .../Lib/site-packages/espsecure/__init__.py | 1558 ++++++ .../Lib/site-packages/espsecure/__main__.py | 10 + .../esptool-4.4.dist-info/INSTALLER | 1 + .../esptool-4.4.dist-info/LICENSE | 339 ++ .../esptool-4.4.dist-info/METADATA | 57 + .../esptool-4.4.dist-info/RECORD | 158 + .../esptool-4.4.dist-info/REQUESTED | 0 .../site-packages/esptool-4.4.dist-info/WHEEL | 5 + .../esptool-4.4.dist-info/entry_points.txt | 4 + .../esptool-4.4.dist-info/top_level.txt | 3 + .../Lib/site-packages/esptool/__init__.py | 1045 ++++ .../Lib/site-packages/esptool/__main__.py | 11 + .../Lib/site-packages/esptool/bin_image.py | 1221 +++++ .../python/Lib/site-packages/esptool/cmds.py | 1160 +++++ .../Lib/site-packages/esptool/loader.py | 1573 ++++++ .../site-packages/esptool/targets/__init__.py | 29 + .../site-packages/esptool/targets/esp32.py | 360 ++ .../site-packages/esptool/targets/esp32c2.py | 164 + .../site-packages/esptool/targets/esp32c3.py | 177 + .../site-packages/esptool/targets/esp32c6.py | 175 + .../esptool/targets/esp32c6beta.py | 23 + .../esptool/targets/esp32h2beta1.py | 154 + .../esptool/targets/esp32h2beta2.py | 42 + .../site-packages/esptool/targets/esp32s2.py | 300 ++ .../site-packages/esptool/targets/esp32s3.py | 275 + .../esptool/targets/esp32s3beta2.py | 41 + .../site-packages/esptool/targets/esp8266.py | 190 + .../targets/stub_flasher/stub_flasher_32.json | 7 + .../stub_flasher/stub_flasher_32c2.json | 7 + .../stub_flasher/stub_flasher_32c3.json | 7 + .../stub_flasher/stub_flasher_32c6.json | 7 + .../stub_flasher/stub_flasher_32c6beta.json | 7 + .../stub_flasher/stub_flasher_32h2beta1.json | 7 + .../stub_flasher/stub_flasher_32h2beta2.json | 7 + .../stub_flasher/stub_flasher_32s2.json | 7 + .../stub_flasher/stub_flasher_32s3.json | 7 + .../stub_flasher/stub_flasher_32s3beta2.json | 7 + .../stub_flasher/stub_flasher_8266.json | 7 + .../python/Lib/site-packages/esptool/util.py | 157 + .../site-packages/pip-22.3.1.dist-info/RECORD | 6 +- .../pycparser-2.21.dist-info/INSTALLER | 1 + .../pycparser-2.21.dist-info/LICENSE | 27 + .../pycparser-2.21.dist-info/METADATA | 31 + .../pycparser-2.21.dist-info/RECORD | 41 + .../pycparser-2.21.dist-info/WHEEL | 6 + .../pycparser-2.21.dist-info/top_level.txt | 1 + .../Lib/site-packages/pycparser/__init__.py | 90 + .../Lib/site-packages/pycparser/_ast_gen.py | 336 ++ .../site-packages/pycparser/_build_tables.py | 37 + .../Lib/site-packages/pycparser/_c_ast.cfg | 195 + .../site-packages/pycparser/ast_transforms.py | 164 + .../Lib/site-packages/pycparser/c_ast.py | 1125 +++++ .../site-packages/pycparser/c_generator.py | 502 ++ .../Lib/site-packages/pycparser/c_lexer.py | 554 ++ .../Lib/site-packages/pycparser/c_parser.py | 1936 +++++++ .../Lib/site-packages/pycparser/lextab.py | 10 + .../site-packages/pycparser/ply/__init__.py | 5 + .../Lib/site-packages/pycparser/ply/cpp.py | 905 ++++ .../site-packages/pycparser/ply/ctokens.py | 133 + .../Lib/site-packages/pycparser/ply/lex.py | 1099 ++++ .../Lib/site-packages/pycparser/ply/yacc.py | 3494 +++++++++++++ .../Lib/site-packages/pycparser/ply/ygen.py | 74 + .../Lib/site-packages/pycparser/plyparser.py | 133 + .../Lib/site-packages/pycparser/yacctab.py | 366 ++ .../pyserial-3.5.dist-info/RECORD | 4 +- .../reedsolo-1.5.4.dist-info/INSTALLER | 1 + .../reedsolo-1.5.4.dist-info/LICENSE | 2 + .../reedsolo-1.5.4.dist-info/METADATA | 355 ++ .../reedsolo-1.5.4.dist-info/RECORD | 8 + .../reedsolo-1.5.4.dist-info/WHEEL | 5 + .../reedsolo-1.5.4.dist-info/top_level.txt | 1 + .../python/Lib/site-packages/reedsolo.py | 974 ++++ .../six-1.16.0.dist-info/INSTALLER | 1 + .../six-1.16.0.dist-info/LICENSE | 18 + .../six-1.16.0.dist-info/METADATA | 49 + .../site-packages/six-1.16.0.dist-info/RECORD | 8 + .../site-packages/six-1.16.0.dist-info/WHEEL | 6 + .../six-1.16.0.dist-info/top_level.txt | 1 + .../python/Lib/site-packages/six.py | 998 ++++ .../wheel-0.38.4.dist-info/RECORD | 2 +- .../windows_amd64/python/Scripts/espefuse.exe | Bin 0 -> 108448 bytes .../python/Scripts/espsecure.exe | Bin 0 -> 108449 bytes .../windows_amd64/python/Scripts/esptool.exe | Bin 0 -> 108447 bytes .../windows_amd64/python/Scripts/pip.exe | Bin 108451 -> 108451 bytes .../windows_amd64/python/Scripts/pip3.10.exe | Bin 108451 -> 108451 bytes .../windows_amd64/python/Scripts/pip3.exe | Bin 108451 -> 108451 bytes .../python/Scripts/pyserial-miniterm.exe | Bin 108450 -> 108450 bytes .../python/Scripts/pyserial-ports.exe | Bin 108452 -> 108452 bytes .../windows_amd64/python/Scripts/wheel.exe | Bin 108438 -> 108438 bytes 307 files changed, 84777 insertions(+), 7 deletions(-) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/_cffi_backend.cp310-win_amd64.pyd create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyd create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyi create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust.pyd create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/py.typed create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/REQUESTED create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c2.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c3.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six.py create mode 100644 dependencies/windows_amd64/python/Scripts/espefuse.exe create mode 100644 dependencies/windows_amd64/python/Scripts/espsecure.exe create mode 100644 dependencies/windows_amd64/python/Scripts/esptool.exe diff --git a/dependencies/windows_amd64/portablepy.ps1 b/dependencies/windows_amd64/portablepy.ps1 index 9643bb086..c0e0cdd8d 100644 --- a/dependencies/windows_amd64/portablepy.ps1 +++ b/dependencies/windows_amd64/portablepy.ps1 @@ -15,7 +15,7 @@ if (!$pythonVersion) { } if (!$destination) { - $destination = '.\' + $destination = '.\python\' } @@ -58,6 +58,8 @@ Invoke-WebRequest -OutFile "$($destination)get-pip.py" "https://bootstrap.pypa.i "Installing pyserial" & "$($destination)python.exe" -m pip install pyserial +"Installing esptool" +& "$($destination)python.exe" -m pip install esptool "Cleaning up" Remove-Item -Path "$($package)" -Confirm:$false -Force diff --git a/dependencies/windows_amd64/python/Lib/site-packages/_cffi_backend.cp310-win_amd64.pyd b/dependencies/windows_amd64/python/Lib/site-packages/_cffi_backend.cp310-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..b4cfe5120de1803b278c8216854e3888868af2db GIT binary patch literal 181248 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 z

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%0DXYJ0|)Oy-DY9q#s(P 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 Bt4ja? literal 0 HcmV?d00001 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE new file mode 100644 index 000000000..a5ea96cb2 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA new file mode 100644 index 000000000..9ec1098ad --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA @@ -0,0 +1,118 @@ +Metadata-Version: 2.1 +Name: bitstring +Version: 3.1.9 +Summary: Simple construction, analysis and modification of binary data. +Home-page: https://github.com/scott-griffiths/bitstring +Author: Scott Griffiths +Author-email: dr.scottgriffiths@gmail.com +License: The MIT License: http://www.opensource.org/licenses/mit-license.php +Download-URL: https://pypi.python.org/pypi/bitstring/ +Platform: all +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Topic :: Software Development :: Libraries :: Python Modules + + + +.. image:: /doc/bitstring_logo_small.png + +**bitstring** is a pure Python module designed to help make +the creation and analysis of binary data as simple and natural as possible. + +Bitstrings can be constructed from integers (big and little endian), hex, +octal, binary, strings or files. They can be sliced, joined, reversed, +inserted into, overwritten, etc. with simple functions or slice notation. +They can also be read from, searched and replaced, and navigated in, +similar to a file or stream. + +bitstring is open source software, and has been released under the MIT +licence. + +This module works in both Python 2.7 and Python 3.6+. + +Installation +------------ + +Probably all you need to do is:: + + pip install bitstring + + +Documentation +------------- +The manual for the bitstring module is available here +. It contains a walk-through of all +the features and a complete reference section. + +It is also available as a PDF at . + + +Simple Examples +--------------- +Creation:: + + >>> a = BitArray(bin='00101') + >>> b = Bits(a_file_object) + >>> c = BitArray('0xff, 0b101, 0o65, uint:6=22') + >>> d = pack('intle:16, hex=a, 0b1', 100, a='0x34f') + >>> e = pack('<16h', *range(16)) + +Different interpretations, slicing and concatenation:: + + >>> a = BitArray('0x1af') + >>> a.hex, a.bin, a.uint + ('1af', '000110101111', 431) + >>> a[10:3:-1].bin + '1110101' + >>> '0b100' + 3*a + BitArray('0x835e35e35, 0b111') + +Reading data sequentially:: + + >>> b = BitStream('0x160120f') + >>> b.read(12).hex + '160' + >>> b.pos = 0 + >>> b.read('uint:12') + 352 + >>> b.readlist('uint:12, bin:3') + [288, '111'] + +Searching, inserting and deleting:: + + >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' + >>> c.find('0x48') + (8,) + >>> c.replace('0b001', '0xabc') + >>> c.insert('0b0000', pos=3) + >>> del c[12:16] + +Unit Tests +---------- + +The 500+ unit tests should all pass for Python 2.7 and later. To run them, from the `test` +directory run:: + + python -m unittest discover + +---- + +The bitstring module has been released as open source under the MIT License. +Copyright (c) 2006 Scott Griffiths + +For more information see the project's homepage on GitHub: + + + + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD new file mode 100644 index 000000000..e6f631a67 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/bitstring.cpython-310.pyc,, +bitstring-3.1.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +bitstring-3.1.9.dist-info/LICENSE,sha256=qenFhBBtT5aGyaaqA25Qe15wRGM7istDZbwHTY0ARxE,1127 +bitstring-3.1.9.dist-info/METADATA,sha256=o79a_RpHIO_5JhwVLILTLS48OLLS5_fOHkzO37cGM-M,3501 +bitstring-3.1.9.dist-info/RECORD,, +bitstring-3.1.9.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 +bitstring-3.1.9.dist-info/top_level.txt,sha256=9Xh4qfKH0fMhwxzzkSBk3uzTRGiYPPM1FuYFAgCB-ks,10 +bitstring.py,sha256=3WSVGMf9K8qpzIU8_rvVEzL8L7uNoKapOikGxsOxd-0,179627 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL new file mode 100644 index 000000000..385faab05 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt new file mode 100644 index 000000000..3ef76dc10 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt @@ -0,0 +1 @@ +bitstring diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring.py b/dependencies/windows_amd64/python/Lib/site-packages/bitstring.py new file mode 100644 index 000000000..22bd0dd40 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/bitstring.py @@ -0,0 +1,4469 @@ +#!/usr/bin/env python +r""" +This package defines classes that simplify bit-wise creation, manipulation and +interpretation of data. + +Classes: + +Bits -- An immutable container for binary data. +BitArray -- A mutable container for binary data. +ConstBitStream -- An immutable container with streaming methods. +BitStream -- A mutable container with streaming methods. + + Bits (base class) + / \ + + mutating methods / \ + streaming methods + / \ + BitArray ConstBitStream + \ / + \ / + \ / + BitStream + +Functions: + +pack -- Create a BitStream from a format string. + +Exceptions: + +Error -- Module exception base class. +CreationError -- Error during creation. +InterpretError -- Inappropriate interpretation of binary data. +ByteAlignError -- Whole byte position or length needed. +ReadError -- Reading or peeking past the end of a bitstring. + +https://github.com/scott-griffiths/bitstring +""" + +__licence__ = """ +The MIT License + +Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +__version__ = "3.1.9" + +__author__ = "Scott Griffiths" + +import numbers +import copy +import sys +import re +import binascii +import mmap +import os +import struct +import operator +import array +import io +import collections + +try: + collectionsAbc = collections.abc +except AttributeError: # Python 2.7 + collectionsAbc = collections + +byteorder = sys.byteorder + +bytealigned = False +"""Determines whether a number of methods default to working only on byte boundaries.""" + + +# Maximum number of digits to use in __str__ and __repr__. +MAX_CHARS = 250 + +# Maximum size of caches used for speed optimisations. +CACHE_SIZE = 1000 + +# Set this to True for extra assertions for debugging. +_debug = False + + +class Error(Exception): + """Base class for errors in the bitstring module.""" + + def __init__(self, *params): + self.msg = params[0] if params else '' + self.params = params[1:] + + def __str__(self): + if self.params: + return self.msg.format(*self.params) + return self.msg + + +class ReadError(Error, IndexError): + """Reading or peeking past the end of a bitstring.""" + + def __init__(self, *params): + Error.__init__(self, *params) + + +class InterpretError(Error, ValueError): + """Inappropriate interpretation of binary data.""" + + def __init__(self, *params): + Error.__init__(self, *params) + + +class ByteAlignError(Error): + """Whole-byte position or length needed.""" + + def __init__(self, *params): + Error.__init__(self, *params) + + +class CreationError(Error, ValueError): + """Inappropriate argument during bitstring creation.""" + + def __init__(self, *params): + Error.__init__(self, *params) + + +class ConstByteStore(object): + """Stores raw bytes together with a bit offset and length. + + Used internally - not part of public interface. + """ + + __slots__ = ('offset', '_rawarray', 'bitlength') + + def __init__(self, data, bitlength=None, offset=None): + """data is either a bytearray or a MmapByteArray""" + self._rawarray = data + if offset is None: + offset = 0 + if bitlength is None: + bitlength = 8 * len(data) - offset + self.offset = offset + self.bitlength = bitlength + + def __iter__(self): + start_byte, start_bit = divmod(self.offset, 8) + end_byte, end_bit = divmod(self.offset + self.bitlength, 8) + + for byte_index in xrange(start_byte, end_byte): + byte = self._rawarray[byte_index] + for bit in range(start_bit, 8): + yield bool(byte & (128 >> bit)) + start_bit = 0 + + if end_bit: + byte = self._rawarray[end_byte] + for bit in range(start_bit, end_bit): + yield bool(byte & (128 >> bit)) + + def _getbit_lsb0(self, pos): + assert 0 <= pos < self.bitlength + pos = self.bitlength - pos - 1 + byte, bit = divmod(self.offset + pos, 8) + return bool(self._rawarray[byte] & (128 >> bit)) + + def _getbit_msb0(self, pos): + assert 0 <= pos < self.bitlength + byte, bit = divmod(self.offset + pos, 8) + return bool(self._rawarray[byte] & (128 >> bit)) + + def getbyte(self, pos): + """Direct access to byte data.""" + return self._rawarray[pos] + + def getbyteslice(self, start, end): + """Direct access to byte data.""" + c = self._rawarray[start:end] + return c + + @property + def bytelength(self): + if not self.bitlength: + return 0 + sb = self.offset // 8 + eb = (self.offset + self.bitlength - 1) // 8 + return eb - sb + 1 + + def __copy__(self): + return ByteStore(self._rawarray[:], self.bitlength, self.offset) + + def _appendstore(self, store): + """Join another store on to the end of this one.""" + if not store.bitlength: + return + # Set new array offset to the number of bits in the final byte of current array. + store = offsetcopy(store, (self.offset + self.bitlength) % 8) + if store.offset: + # first do the byte with the join. + joinval = (self._rawarray.pop() & (255 ^ (255 >> store.offset)) | + (store.getbyte(0) & (255 >> store.offset))) + self._rawarray.append(joinval) + self._rawarray.extend(store._rawarray[1:]) + else: + self._rawarray.extend(store._rawarray) + self.bitlength += store.bitlength + + def _prependstore(self, store): + """Join another store on to the start of this one.""" + if not store.bitlength: + return + # Set the offset of copy of store so that it's final byte + # ends in a position that matches the offset of self, + # then join self on to the end of it. + store = offsetcopy(store, (self.offset - store.bitlength) % 8) + assert (store.offset + store.bitlength) % 8 == self.offset % 8 + bit_offset = self.offset % 8 + if bit_offset: + # first do the byte with the join. + joinval = (store.getbyte(-1) & (255 ^ (255 >> bit_offset)) | + (self._rawarray[self.byteoffset] & (255 >> bit_offset))) + store._rawarray[-1] = joinval + store._rawarray.extend(self._rawarray[self.byteoffset + 1: self.byteoffset + self.bytelength]) + else: + store._rawarray.extend(self._rawarray[self.byteoffset: self.byteoffset + self.bytelength]) + self._rawarray = store._rawarray + self.offset = store.offset + self.bitlength += store.bitlength + + @property + def byteoffset(self): + return self.offset // 8 + + @property + def rawbytes(self): + return self._rawarray + + +class ByteStore(ConstByteStore): + """Adding mutating methods to ConstByteStore + + Used internally - not part of public interface. + """ + __slots__ = () + + def _setbit_lsb0(self, pos): + assert 0 <= pos < self.bitlength + pos = self.bitlength - pos - 1 + byte, bit = divmod(self.offset + pos, 8) + self._rawarray[byte] |= (128 >> bit) + + def _setbit_msb0(self, pos): + assert 0 <= pos < self.bitlength + byte, bit = divmod(self.offset + pos, 8) + self._rawarray[byte] |= (128 >> bit) + + def _unsetbit_lsb0(self, pos): + assert 0 <= pos < self.bitlength + pos = self.bitlength - pos - 1 + byte, bit = divmod(self.offset + pos, 8) + self._rawarray[byte] &= ~(128 >> bit) + + def _unsetbit_msb0(self, pos): + assert 0 <= pos < self.bitlength + byte, bit = divmod(self.offset + pos, 8) + self._rawarray[byte] &= ~(128 >> bit) + + def _invertbit_lsb0(self, pos): + assert 0 <= pos < self.bitlength + pos = self.bitlength - pos - 1 + byte, bit = divmod(self.offset + pos, 8) + self._rawarray[byte] ^= (128 >> bit) + + def _invertbit_msb0(self, pos): + assert 0 <= pos < self.bitlength + byte, bit = divmod(self.offset + pos, 8) + self._rawarray[byte] ^= (128 >> bit) + + def setbyte(self, pos, value): + self._rawarray[pos] = value + + def setbyteslice(self, start, end, value): + self._rawarray[start:end] = value + + +def offsetcopy(s, newoffset): + """Return a copy of a ByteStore with the newoffset. + + Not part of public interface. + """ + assert 0 <= newoffset < 8 + if not s.bitlength: + return copy.copy(s) + else: + if newoffset == s.offset % 8: + return type(s)(s.getbyteslice(s.byteoffset, s.byteoffset + s.bytelength), s.bitlength, newoffset) + newdata = [] + d = s._rawarray + assert newoffset != s.offset % 8 + if newoffset < s.offset % 8: + # We need to shift everything left + shiftleft = s.offset % 8 - newoffset + # First deal with everything except for the final byte + for x in range(s.byteoffset, s.byteoffset + s.bytelength - 1): + newdata.append(((d[x] << shiftleft) & 0xff) + (d[x + 1] >> (8 - shiftleft))) + bits_in_last_byte = (s.offset + s.bitlength) % 8 + if not bits_in_last_byte: + bits_in_last_byte = 8 + if bits_in_last_byte > shiftleft: + newdata.append((d[s.byteoffset + s.bytelength - 1] << shiftleft) & 0xff) + else: # newoffset > s._offset % 8 + shiftright = newoffset - s.offset % 8 + newdata.append(s.getbyte(0) >> shiftright) + for x in range(s.byteoffset + 1, s.byteoffset + s.bytelength): + newdata.append(((d[x - 1] << (8 - shiftright)) & 0xff) + (d[x] >> shiftright)) + bits_in_last_byte = (s.offset + s.bitlength) % 8 + if not bits_in_last_byte: + bits_in_last_byte = 8 + if bits_in_last_byte + shiftright > 8: + newdata.append((d[s.byteoffset + s.bytelength - 1] << (8 - shiftright)) & 0xff) + new_s = type(s)(bytearray(newdata), s.bitlength, newoffset) + assert new_s.offset == newoffset + return new_s + + +def equal(a, b): + """Return True if ByteStores a == b. + + Not part of public interface. + """ + # We want to return False for inequality as soon as possible, which + # means we get lots of special cases. + # First the easy one - compare lengths: + a_bitlength = a.bitlength + b_bitlength = b.bitlength + if a_bitlength != b_bitlength: + return False + if not a_bitlength: + assert b_bitlength == 0 + return True + # Make 'a' the one with the smaller offset + if (a.offset % 8) > (b.offset % 8): + a, b = b, a + # and create some aliases + a_bitoff = a.offset % 8 + b_bitoff = b.offset % 8 + a_byteoffset = a.byteoffset + b_byteoffset = b.byteoffset + a_bytelength = a.bytelength + b_bytelength = b.bytelength + da = a._rawarray + db = b._rawarray + + # If they are pointing to the same data, they must be equal + if da is db and a.offset == b.offset: + return True + + if a_bitoff == b_bitoff: + bits_spare_in_last_byte = 8 - (a_bitoff + a_bitlength) % 8 + if bits_spare_in_last_byte == 8: + bits_spare_in_last_byte = 0 + # Special case for a, b contained in a single byte + if a_bytelength == 1: + a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength) + b_val = ((db[b_byteoffset] << b_bitoff) & 0xff) >> (8 - b_bitlength) + return a_val == b_val + # Otherwise check first byte + if da[a_byteoffset] & (0xff >> a_bitoff) != db[b_byteoffset] & (0xff >> b_bitoff): + return False + # then everything up to the last + b_a_offset = b_byteoffset - a_byteoffset + for x in range(1 + a_byteoffset, a_byteoffset + a_bytelength - 1): + if da[x] != db[b_a_offset + x]: + return False + # and finally the last byte + return (da[a_byteoffset + a_bytelength - 1] >> bits_spare_in_last_byte == + db[b_byteoffset + b_bytelength - 1] >> bits_spare_in_last_byte) + + assert a_bitoff != b_bitoff + # This is how much we need to shift a to the right to compare with b: + shift = b_bitoff - a_bitoff + # Special case for b only one byte long + if b_bytelength == 1: + assert a_bytelength == 1 + a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength) + b_val = ((db[b_byteoffset] << b_bitoff) & 0xff) >> (8 - b_bitlength) + return a_val == b_val + # Special case for a only one byte long + if a_bytelength == 1: + assert b_bytelength == 2 + a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength) + b_val = ((db[b_byteoffset] << 8) + db[b_byteoffset + 1]) << b_bitoff + b_val &= 0xffff + b_val >>= 16 - b_bitlength + return a_val == b_val + + # Compare first byte of b with bits from first byte of a + if (da[a_byteoffset] & (0xff >> a_bitoff)) >> shift != db[b_byteoffset] & (0xff >> b_bitoff): + return False + # Now compare every full byte of b with bits from 2 bytes of a + for x in range(1, b_bytelength - 1): + # Construct byte from 2 bytes in a to compare to byte in b + b_val = db[b_byteoffset + x] + a_val = ((da[a_byteoffset + x - 1] << 8) + da[a_byteoffset + x]) >> shift + a_val &= 0xff + if a_val != b_val: + return False + + # Now check bits in final byte of b + final_b_bits = (b.offset + b_bitlength) % 8 + if not final_b_bits: + final_b_bits = 8 + b_val = db[b_byteoffset + b_bytelength - 1] >> (8 - final_b_bits) + final_a_bits = (a.offset + a_bitlength) % 8 + if not final_a_bits: + final_a_bits = 8 + if b.bytelength > a_bytelength: + assert b_bytelength == a_bytelength + 1 + a_val = da[a_byteoffset + a_bytelength - 1] >> (8 - final_a_bits) + a_val &= 0xff >> (8 - final_b_bits) + return a_val == b_val + assert a_bytelength == b_bytelength + a_val = da[a_byteoffset + a_bytelength - 2] << 8 + a_val += da[a_byteoffset + a_bytelength - 1] + a_val >>= (8 - final_a_bits) + a_val &= 0xff >> (8 - final_b_bits) + return a_val == b_val + + +class MmapByteArray(object): + """Looks like a bytearray, but from an mmap. + + Not part of public interface. + """ + + __slots__ = ('filemap', 'filelength', 'source', 'byteoffset', 'bytelength') + + def __init__(self, source, bytelength=None, byteoffset=None): + self.source = source + source.seek(0, os.SEEK_END) + self.filelength = source.tell() + if byteoffset is None: + byteoffset = 0 + if bytelength is None: + bytelength = self.filelength - byteoffset + self.byteoffset = byteoffset + self.bytelength = bytelength + self.filemap = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ) + + def __getitem__(self, key): + try: + start = key.start + stop = key.stop + except AttributeError: + try: + assert 0 <= key < self.bytelength + return ord(self.filemap[key + self.byteoffset]) + except TypeError: + # for Python 3 + return self.filemap[key + self.byteoffset] + else: + if start is None: + start = 0 + if stop is None: + stop = self.bytelength + assert key.step is None + assert 0 <= start < self.bytelength + assert 0 <= stop <= self.bytelength + s = slice(start + self.byteoffset, stop + self.byteoffset) + return bytearray(self.filemap.__getitem__(s)) + + def __len__(self): + return self.bytelength + + +# This creates a dictionary for every possible byte with the value being +# the key with its bits reversed. +BYTE_REVERSAL_DICT = dict() + +# For Python 2.7/ 3.x coexistence +# Yes this is very very hacky. +if sys.version_info[0] == 2: + for i in range(256): + BYTE_REVERSAL_DICT[i] = chr(int("{0:08b}".format(i)[::-1], 2)) +else: + for i in range(256): + BYTE_REVERSAL_DICT[i] = bytes([int("{0:08b}".format(i)[::-1], 2)]) + from io import IOBase as file + xrange = range + basestring = str + +# Python 2.x octals start with '0', in Python 3 it's '0o' +LEADING_OCT_CHARS = len(oct(1)) - 1 + + +def tidy_input_string(s): + """Return string made lowercase and with all whitespace removed.""" + s = ''.join(s.split()).lower() + return s + + +INIT_NAMES = ('uint', 'int', 'ue', 'se', 'sie', 'uie', 'hex', 'oct', 'bin', 'bits', + 'uintbe', 'intbe', 'uintle', 'intle', 'uintne', 'intne', + 'float', 'floatbe', 'floatle', 'floatne', 'bytes', 'bool', 'pad') + +TOKEN_RE = re.compile(r'(?P' + '|'.join(INIT_NAMES) + + r')(:(?P[^=]+))?(=(?P.*))?$', re.IGNORECASE) +DEFAULT_UINT = re.compile(r'(?P[^=]+)?(=(?P.*))?$', re.IGNORECASE) + +MULTIPLICATIVE_RE = re.compile(r'(?P.*)\*(?P.+)') + +# Hex, oct or binary literals +LITERAL_RE = re.compile(r'(?P0([xob]))(?P.+)', re.IGNORECASE) + +# An endianness indicator followed by one or more struct.pack codes +STRUCT_PACK_RE = re.compile(r'(?P[<>@])?(?P(?:\d*[bBhHlLqQfd])+)$') + +# A number followed by a single character struct.pack code +STRUCT_SPLIT_RE = re.compile(r'\d*[bBhHlLqQfd]') + +# These replicate the struct.pack codes +# Big-endian +REPLACEMENTS_BE = {'b': 'intbe:8', 'B': 'uintbe:8', + 'h': 'intbe:16', 'H': 'uintbe:16', + 'l': 'intbe:32', 'L': 'uintbe:32', + 'q': 'intbe:64', 'Q': 'uintbe:64', + 'f': 'floatbe:32', 'd': 'floatbe:64'} +# Little-endian +REPLACEMENTS_LE = {'b': 'intle:8', 'B': 'uintle:8', + 'h': 'intle:16', 'H': 'uintle:16', + 'l': 'intle:32', 'L': 'uintle:32', + 'q': 'intle:64', 'Q': 'uintle:64', + 'f': 'floatle:32', 'd': 'floatle:64'} + +# Size in bytes of all the pack codes. +PACK_CODE_SIZE = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4, 'L': 4, + 'q': 8, 'Q': 8, 'f': 4, 'd': 8} + +_tokenname_to_initialiser = {'hex': 'hex', '0x': 'hex', '0X': 'hex', 'oct': 'oct', + '0o': 'oct', '0O': 'oct', 'bin': 'bin', '0b': 'bin', + '0B': 'bin', 'bits': 'auto', 'bytes': 'bytes', 'pad': 'pad'} + + +def structparser(token): + """Parse struct-like format string token into sub-token list.""" + m = STRUCT_PACK_RE.match(token) + if not m: + return [token] + else: + endian = m.group('endian') + if endian is None: + return [token] + # Split the format string into a list of 'q', '4h' etc. + formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt')) + # Now deal with multiplicative factors, 4h -> hhhh etc. + fmt = ''.join([f[-1] * int(f[:-1]) if len(f) != 1 else + f for f in formatlist]) + if endian == '@': + # Native endianness + if byteorder == 'little': + endian = '<' + else: + assert byteorder == 'big' + endian = '>' + if endian == '<': + tokens = [REPLACEMENTS_LE[c] for c in fmt] + else: + assert endian == '>' + tokens = [REPLACEMENTS_BE[c] for c in fmt] + return tokens + + +def tokenparser(fmt, keys=None, token_cache={}): + """Divide the format string into tokens and parse them. + + Return stretchy token and list of [initialiser, length, value] + initialiser is one of: hex, oct, bin, uint, int, se, ue, 0x, 0o, 0b etc. + length is None if not known, as is value. + + If the token is in the keyword dictionary (keys) then it counts as a + special case and isn't messed with. + + tokens must be of the form: [factor*][initialiser][:][length][=value] + + """ + try: + return token_cache[(fmt, keys)] + except KeyError: + token_key = (fmt, keys) + # Very inefficient expanding of brackets. + fmt = expand_brackets(fmt) + # Split tokens by ',' and remove whitespace + # The meta_tokens can either be ordinary single tokens or multiple + # struct-format token strings. + meta_tokens = (''.join(f.split()) for f in fmt.split(',')) + return_values = [] + stretchy_token = False + for meta_token in meta_tokens: + # See if it has a multiplicative factor + m = MULTIPLICATIVE_RE.match(meta_token) + if not m: + factor = 1 + else: + factor = int(m.group('factor')) + meta_token = m.group('token') + # See if it's a struct-like format + tokens = structparser(meta_token) + ret_vals = [] + for token in tokens: + if keys and token in keys: + # Don't bother parsing it, it's a keyword argument + ret_vals.append([token, None, None]) + continue + value = length = None + if token == '': + continue + # Match literal tokens of the form 0x... 0o... and 0b... + m = LITERAL_RE.match(token) + if m: + name = m.group('name') + value = m.group('value') + ret_vals.append([name, length, value]) + continue + # Match everything else: + m1 = TOKEN_RE.match(token) + if not m1: + # and if you don't specify a 'name' then the default is 'uint': + m2 = DEFAULT_UINT.match(token) + if not m2: + raise ValueError("Don't understand token '{0}'.".format(token)) + if m1: + name = m1.group('name') + length = m1.group('len') + if m1.group('value'): + value = m1.group('value') + else: + assert m2 + name = 'uint' + length = m2.group('len') + if m2.group('value'): + value = m2.group('value') + if name == 'bool': + if length is not None and length != '1': + raise ValueError("You can only specify one bit sized bool tokens or leave unspecified.") + length = 1 + if length is None and name not in ('se', 'ue', 'sie', 'uie'): + stretchy_token = True + if length is not None: + # Try converting length to int, otherwise check it's a key. + try: + length = int(length) + if length < 0: + raise Error + # For the 'bytes' token convert length to bits. + if name == 'bytes': + length *= 8 + except Error: + raise ValueError("Can't read a token with a negative length.") + except ValueError: + if not keys or length not in keys: + raise ValueError("Don't understand length '{0}' of token.".format(length)) + ret_vals.append([name, length, value]) + # This multiplies by the multiplicative factor, but this means that + # we can't allow keyword values as multipliers (e.g. n*uint:8). + # The only way to do this would be to return the factor in some fashion + # (we can't use the key's value here as it would mean that we couldn't + # sensibly continue to cache the function's results. (TODO). + return_values.extend(ret_vals * factor) + return_values = [tuple(x) for x in return_values] + if len(token_cache) < CACHE_SIZE: + token_cache[token_key] = stretchy_token, return_values + return stretchy_token, return_values + + +# Looks for first number*( +BRACKET_RE = re.compile(r'(?P\d+)\*\(') + + +def expand_brackets(s): + """Remove whitespace and expand all brackets.""" + s = ''.join(s.split()) + while True: + start = s.find('(') + if start == -1: + break + count = 1 # Number of hanging open brackets + p = start + 1 + while p < len(s): + if s[p] == '(': + count += 1 + if s[p] == ')': + count -= 1 + if not count: + break + p += 1 + if count: + raise ValueError("Unbalanced parenthesis in '{0}'.".format(s)) + if start == 0 or s[start - 1] != '*': + s = s[0:start] + s[start + 1:p] + s[p + 1:] + else: + m = BRACKET_RE.search(s) + if m: + factor = int(m.group('factor')) + matchstart = m.start('factor') + s = s[0:matchstart] + (factor - 1) * (s[start + 1:p] + ',') + s[start + 1:p] + s[p + 1:] + else: + raise ValueError("Failed to parse '{0}'.".format(s)) + return s + + +# This converts a single octal digit to 3 bits. +OCT_TO_BITS = ['{0:03b}'.format(i) for i in xrange(8)] + +# A dictionary of number of 1 bits contained in binary representation of any byte +BIT_COUNT = dict(zip(xrange(256), [bin(i).count('1') for i in xrange(256)])) + + +class Bits(object): + """A container holding an immutable sequence of bits. + + For a mutable container use the BitArray class instead. + + Methods: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + join() -- Join bitstrings together using current bitstring. + rfind() -- Seek backwards to find a sub-bitstring. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Special methods: + + Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. + + Properties: + + bin -- The bitstring as a binary string. + bool -- For single bit bitstrings, interpret as True or False. + bytes -- The bitstring as a bytes object. + float -- Interpret as a floating point number. + floatbe -- Interpret as a big-endian floating point number. + floatle -- Interpret as a little-endian floating point number. + floatne -- Interpret as a native-endian floating point number. + hex -- The bitstring as a hexadecimal string. + int -- Interpret as a two's complement signed integer. + intbe -- Interpret as a big-endian signed integer. + intle -- Interpret as a little-endian signed integer. + intne -- Interpret as a native-endian signed integer. + len -- Length of the bitstring in bits. + oct -- The bitstring as an octal string. + se -- Interpret as a signed exponential-Golomb code. + ue -- Interpret as an unsigned exponential-Golomb code. + sie -- Interpret as a signed interleaved exponential-Golomb code. + uie -- Interpret as an unsigned interleaved exponential-Golomb code. + uint -- Interpret as a two's complement unsigned integer. + uintbe -- Interpret as a big-endian unsigned integer. + uintle -- Interpret as a little-endian unsigned integer. + uintne -- Interpret as a native-endian unsigned integer. + + """ + + __slots__ = ('_datastore') + + def __init__(self, auto=None, length=None, offset=None, **kwargs): + """Either specify an 'auto' initialiser: + auto -- a string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable, an array or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bytes -- raw data as a string, for example read from a binary file. + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + uint -- an unsigned integer. + int -- a signed integer. + float -- a floating point number. + uintbe -- an unsigned big-endian whole byte integer. + intbe -- a signed big-endian whole byte integer. + floatbe - a big-endian floating point number. + uintle -- an unsigned little-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + floatle -- a little-endian floating point number. + uintne -- an unsigned native-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + floatne -- a native-endian floating point number. + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + bool -- a boolean (True or False). + filename -- a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is mainly intended for use when + initialising using 'bytes' or 'filename'. + + """ + pass + + def __new__(cls, auto=None, length=None, offset=None, _cache={}, **kwargs): + # For instances auto-initialised with a string we intern the + # instance for re-use. + try: + if isinstance(auto, basestring): + try: + return _cache[auto] + except KeyError: + x = object.__new__(Bits) + try: + _, tokens = tokenparser(auto) + except ValueError as e: + raise CreationError(*e.args) + if offset is not None: + raise CreationError("offset should not be specified when using string initialisation.") + if length is not None: + raise CreationError("length should not be specified when using string initialisation.") + x._datastore = ConstByteStore(bytearray(0), 0, 0) + for token in tokens: + x._datastore._appendstore(Bits._init_with_token(*token)._datastore) + assert x._assertsanity() + if len(_cache) < CACHE_SIZE: + _cache[auto] = x + return x + if type(auto) == Bits: + return auto + except TypeError: + pass + x = super(Bits, cls).__new__(cls) + x._datastore = ConstByteStore(b'') + x._initialise(auto, length, offset, **kwargs) + return x + + def _initialise(self, auto, length, offset, **kwargs): + if length is not None and length < 0: + raise CreationError("bitstring length cannot be negative.") + if offset is not None and offset < 0: + raise CreationError("offset must be >= 0.") + if auto is not None: + self._initialise_from_auto(auto, length, offset) + return + if not kwargs: + # No initialisers, so initialise with nothing or zero bits + if length is not None and length != 0: + data = bytearray((length + 7) // 8) + self._setbytes_unsafe(data, length, 0) + return + self._setbytes_unsafe(bytearray(0), 0, 0) + return + k, v = kwargs.popitem() + try: + init_without_length_or_offset[k](self, v) + if length is not None or offset is not None: + raise CreationError("Cannot use length or offset with this initialiser.") + except KeyError: + try: + init_with_length_only[k](self, v, length) + if offset is not None: + raise CreationError("Cannot use offset with this initialiser.") + except KeyError: + if offset is None: + offset = 0 + try: + init_with_length_and_offset[k](self, v, length, offset) + except KeyError: + raise CreationError("Unrecognised keyword '{0}' used to initialise.", k) + + def _initialise_from_auto(self, auto, length, offset): + if offset is None: + offset = 0 + self._setauto(auto, length, offset) + return + + def __iter__(self): + return iter(self._datastore) + + def __copy__(self): + """Return a new copy of the Bits for the copy module.""" + # Note that if you want a new copy (different ID), use _copy instead. + # The copy can return self as it's immutable. + return self + + def __lt__(self, other): + raise TypeError("unorderable type: {0}".format(type(self).__name__)) + + def __gt__(self, other): + raise TypeError("unorderable type: {0}".format(type(self).__name__)) + + def __le__(self, other): + raise TypeError("unorderable type: {0}".format(type(self).__name__)) + + def __ge__(self, other): + raise TypeError("unorderable type: {0}".format(type(self).__name__)) + + def __add__(self, bs): + """Concatenate bitstrings and return new bitstring. + + bs -- the bitstring to append. + + """ + bs = Bits(bs) + if bs.len <= self.len: + s = self._copy() + s._addright(bs) + else: + s = bs._copy() + s = self.__class__(s) + s._addleft(self) + return s + + def __radd__(self, bs): + """Append current bitstring to bs and return new bitstring. + + bs -- the string for the 'auto' initialiser that will be appended to. + + """ + bs = self._converttobitstring(bs) + return bs.__add__(self) + + def __getitem__(self, key): + """Return a new bitstring representing a slice of the current bitstring. + + Indices are in units of the step parameter (default 1 bit). + Stepping is used to specify the number of bits in each item. + + >>> print BitArray('0b00110')[1:4] + '0b011' + >>> print BitArray('0x00112233')[1:3:8] + '0x1122' + + """ + length = self.len + if isinstance(key, slice): + step = key.step if key.step is not None else 1 + if step != 1: + # convert to binary string and use string slicing + bs = self.__class__() + if _lsb0: + start = length - key.start - 1 if key.start is not None else None + stop = length - key.stop - 1 if key.stop is not None else None + bs._setbin_unsafe(self._getbin().__getitem__(slice(start, stop, -step))[::-1]) + else: + bs._setbin_unsafe(self._getbin().__getitem__(key)) + return bs + start, stop = 0, length + if key.start is not None: + start = key.start + if key.start < 0: + start += stop + if key.stop is not None: + stop = key.stop + if key.stop < 0: + stop += length + start = max(start, 0) + stop = min(stop, length) + if start < stop: + return self._slice(start, stop) + else: + return self.__class__() + else: + # single element + if key < 0: + key += length + if not 0 <= key < length: + raise IndexError("Slice index out of range.") + # Single bit, return True or False + return self._datastore.getbit(key) + + def __len__(self): + """Return the length of the bitstring in bits.""" + return self._getlength() + + def __str__(self): + """Return approximate string representation of bitstring for printing. + + Short strings will be given wholly in hexadecimal or binary. Longer + strings may be part hexadecimal and part binary. Very long strings will + be truncated with '...'. + + """ + length = self.len + if not length: + return '' + if length > MAX_CHARS * 4: + # Too long for hex. Truncate... + return ''.join(('0x', self._readhex(MAX_CHARS * 4, 0), '...')) + # If it's quite short and we can't do hex then use bin + if length < 32 and length % 4 != 0: + return '0b' + self.bin + # If we can use hex then do so + if not length % 4: + return '0x' + self.hex + # Otherwise first we do as much as we can in hex + # then add on 1, 2 or 3 bits on at the end + bits_at_end = length % 4 + return ''.join(('0x', self._readhex(length - bits_at_end, 0), + ', ', '0b', + self._readbin(bits_at_end, length - bits_at_end))) + + def __repr__(self): + """Return representation that could be used to recreate the bitstring. + + If the returned string is too long it will be truncated. See __str__(). + + """ + length = self.len + try: + pos = self._pos + pos_string = "" if pos == 0 else ", pos={0}".format(pos) + except AttributeError: + pos_string = "" + if isinstance(self._datastore._rawarray, MmapByteArray): + offsetstring = '' + if self._datastore.byteoffset or self._offset: + offsetstring = ", offset=%d" % (self._datastore._rawarray.byteoffset * 8 + self._offset) + lengthstring = ", length=%d" % length + return "{0}(filename='{1}'{2}{3}{4})".format(self.__class__.__name__, + self._datastore._rawarray.source.name, + lengthstring, offsetstring, pos_string) + else: + s = self.__str__() + lengthstring = '' + if s.endswith('...'): + lengthstring = " # length={0}".format(length) + return "{0}('{1}'{2}){3}".format(self.__class__.__name__, s, pos_string, lengthstring) + + def __eq__(self, bs): + """Return True if two bitstrings have the same binary representation. + + >>> BitArray('0b1110') == '0xe' + True + + """ + try: + bs = Bits(bs) + except TypeError: + return False + return equal(self._datastore, bs._datastore) + + def __ne__(self, bs): + """Return False if two bitstrings have the same binary representation. + + >>> BitArray('0b111') == '0x7' + False + + """ + return not self.__eq__(bs) + + def __invert__(self): + """Return bitstring with every bit inverted. + + Raises Error if the bitstring is empty. + + """ + if not self.len: + raise Error("Cannot invert empty bitstring.") + s = self._copy() + s._invert_all() + return s + + def __lshift__(self, n): + """Return bitstring with bits shifted by n to the left. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if not self.len: + raise ValueError("Cannot shift an empty bitstring.") + n = min(n, self.len) + s = self._slice(n, self.len) + s._addright(Bits(n)) + return s + + def __rshift__(self, n): + """Return bitstring with bits shifted by n to the right. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if not self.len: + raise ValueError("Cannot shift an empty bitstring.") + if not n: + return self._copy() + s = self.__class__(length=min(n, self.len)) + s._addright(self[:-n]) + return s + + def __mul__(self, n): + """Return bitstring consisting of n concatenations of self. + + Called for expression of the form 'a = b*3'. + n -- The number of concatenations. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot multiply by a negative integer.") + if not n: + return self.__class__() + s = self._copy() + s._imul(n) + return s + + def __rmul__(self, n): + """Return bitstring consisting of n concatenations of self. + + Called for expressions of the form 'a = 3*b'. + n -- The number of concatenations. Must be >= 0. + + """ + return self.__mul__(n) + + def __and__(self, bs): + """Bit-wise 'and' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '&' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + bs = Bits(bs) + if self.len != bs.len: + raise ValueError("Bitstrings must have the same length " + "for & operator.") + s = self._copy() + s._iand(bs) + return s + + def __rand__(self, bs): + """Bit-wise 'and' between two bitstrings. Returns new bitstring. + + bs -- the bitstring to '&' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + return self.__and__(bs) + + def __or__(self, bs): + """Bit-wise 'or' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '|' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + bs = Bits(bs) + if self.len != bs.len: + raise ValueError("Bitstrings must have the same length " + "for | operator.") + s = self._copy() + s._ior(bs) + return s + + def __ror__(self, bs): + """Bit-wise 'or' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '|' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + return self.__or__(bs) + + def __xor__(self, bs): + """Bit-wise 'xor' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '^' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + bs = Bits(bs) + if self.len != bs.len: + raise ValueError("Bitstrings must have the same length " + "for ^ operator.") + s = self._copy() + s._ixor(bs) + return s + + def __rxor__(self, bs): + """Bit-wise 'xor' between two bitstrings. Returns new bitstring. + + bs -- The bitstring to '^' with. + + Raises ValueError if the two bitstrings have differing lengths. + + """ + return self.__xor__(bs) + + def __contains__(self, bs): + """Return whether bs is contained in the current bitstring. + + bs -- The bitstring to search for. + + """ + # Don't want to change pos + try: + pos = self._pos + except AttributeError: + pass + found = Bits.find(self, bs, bytealigned=False) + try: + self._pos = pos + except UnboundLocalError: + pass + return bool(found) + + def __hash__(self): + """Return an integer hash of the object.""" + # We can't in general hash the whole bitstring (it could take hours!) + # So instead take some bits from the start and end. + if self.len <= 160: + # Use the whole bitstring. + shorter = self + else: + # Take 10 bytes from start and end + shorter = self[:80] + self[-80:] + h = 0 + for byte in shorter.tobytes(): + try: + h = (h << 4) + ord(byte) + except TypeError: + # Python 3 + h = (h << 4) + byte + g = h & 0xf0000000 + if g & (1 << 31): + h ^= (g >> 24) + h ^= g + return h % 1442968193 + + # This is only used in Python 2.x... + def __nonzero__(self): + """Return True if any bits are set to 1, otherwise return False.""" + return self.any(True) + + # ...whereas this is used in Python 3.x + __bool__ = __nonzero__ + + if _debug is True: + def _assertsanity(self): + """Check internal self consistency as a debugging aid.""" + assert self.len >= 0 + assert 0 <= self._offset, "offset={0}".format(self._offset) + assert (self.len + self._offset + 7) // 8 == self._datastore.bytelength + self._datastore.byteoffset, "len={0}, offset={1}, bytelength={2}, byteoffset={3}".format(self.len, self._offset, self._datastore.bytelength, self._datastore.byteoffset) + return True + else: + @staticmethod + def _assertsanity(): + return True + + @classmethod + def _init_with_token(cls, name, token_length, value): + if token_length is not None: + token_length = int(token_length) + if token_length == 0: + return cls() + # For pad token just return the length in zero bits + if name == 'pad': + return cls(token_length) + + if value is None: + if token_length is None: + error = "Token has no value ({0}=???).".format(name) + else: + error = "Token has no value ({0}:{1}=???).".format(name, token_length) + raise ValueError(error) + try: + b = cls(**{_tokenname_to_initialiser[name]: value}) + except KeyError: + if name in ('se', 'ue', 'sie', 'uie'): + b = cls(**{name: int(value)}) + elif name in ('uint', 'int', 'uintbe', 'intbe', 'uintle', 'intle', 'uintne', 'intne'): + b = cls(**{name: int(value), 'length': token_length}) + elif name in ('float', 'floatbe', 'floatle', 'floatne'): + b = cls(**{name: float(value), 'length': token_length}) + elif name == 'bool': + if value in (1, 'True', '1'): + b = cls(bool=True) + elif value in (0, 'False', '0'): + b = cls(bool=False) + else: + raise CreationError("bool token can only be 'True' or 'False'.") + else: + raise CreationError("Can't parse token name {0}.", name) + if token_length is not None and b.len != token_length: + msg = "Token with length {0} packed with value of length {1} ({2}:{3}={4})." + raise CreationError(msg, token_length, b.len, name, token_length, value) + return b + + def _clear(self): + """Reset the bitstring to an empty state.""" + self._datastore = ByteStore(bytearray(0)) + + def _setauto(self, s, length, offset): + """Set bitstring from a bitstring, file, bool, integer, array, iterable or string.""" + # As s can be so many different things it's important to do the checks + # in the correct order, as some types are also other allowed types. + # So basestring must be checked before Iterable + # and bytes/bytearray before Iterable but after basestring! + if isinstance(s, Bits): + if length is None: + length = s.len - offset + self._setbytes_unsafe(s._datastore.rawbytes, length, s._offset + offset) + return + + if isinstance(s, io.BytesIO): + if offset is None: + offset = 0 + if length is None: + length = s.seek(0, 2) * 8 - offset + byteoffset, offset = divmod(offset, 8) + bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset + if length + byteoffset * 8 + offset > s.seek(0, 2) * 8: + raise CreationError("BytesIO object is not long enough for specified " + "length and offset.") + self._datastore = ConstByteStore(bytearray(s.getvalue()[byteoffset: byteoffset + bytelength]), length, offset) + return + + if isinstance(s, file): + if offset is None: + offset = 0 + if length is None: + length = os.path.getsize(s.name) * 8 - offset + byteoffset, offset = divmod(offset, 8) + bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset + m = MmapByteArray(s, bytelength, byteoffset) + if length + byteoffset * 8 + offset > m.filelength * 8: + raise CreationError("File is not long enough for specified " + "length and offset.") + self._datastore = ConstByteStore(m, length, offset) + return + + if length is not None: + raise CreationError("The length keyword isn't applicable to this initialiser.") + if offset: + raise CreationError("The offset keyword isn't applicable to this initialiser.") + if isinstance(s, basestring): + bs = self._converttobitstring(s) + assert bs._offset == 0 + self._setbytes_unsafe(bs._datastore.rawbytes, bs.length, 0) + return + if isinstance(s, (bytes, bytearray)): + self._setbytes_unsafe(bytearray(s), len(s) * 8, 0) + return + if isinstance(s, array.array): + try: + b = s.tobytes() + except AttributeError: + b = s.tostring() # Python 2.7 + self._setbytes_unsafe(bytearray(b), len(b) * 8, 0) + return + if isinstance(s, numbers.Integral): + # Initialise with s zero bits. + if s < 0: + msg = "Can't create bitstring of negative length {0}." + raise CreationError(msg, s) + data = bytearray((s + 7) // 8) + self._datastore = ByteStore(data, s, 0) + return + if isinstance(s, collectionsAbc.Iterable): + # Evaluate each item as True or False and set bits to 1 or 0. + self._setbin_unsafe(''.join(str(int(bool(x))) for x in s)) + return + raise TypeError("Cannot initialise bitstring from {0}.".format(type(s))) + + def _setfile(self, filename, length, offset): + """Use file as source of bits.""" + with open(filename, 'rb') as source: + if offset is None: + offset = 0 + if length is None: + length = os.path.getsize(source.name) * 8 - offset + byteoffset, offset = divmod(offset, 8) + bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset + m = MmapByteArray(source, bytelength, byteoffset) + if length + byteoffset * 8 + offset > m.filelength * 8: + raise CreationError("File is not long enough for specified " + "length and offset.") + self._datastore = ConstByteStore(m, length, offset) + + def _setbytes_safe(self, data, length=None, offset=0): + """Set the data from a string.""" + data = bytearray(data) + if length is None: + # Use to the end of the data + length = len(data)*8 - offset + self._datastore = ByteStore(data, length, offset) + else: + if length + offset > len(data) * 8: + msg = "Not enough data present. Need {0} bits, have {1}." + raise CreationError(msg, length + offset, len(data) * 8) + if length == 0: + self._datastore = ByteStore(bytearray(0)) + else: + self._datastore = ByteStore(data, length, offset) + + def _setbytes_unsafe(self, data, length, offset): + """Unchecked version of _setbytes_safe.""" + self._datastore = type(self._datastore)(data[:], length, offset) + assert self._assertsanity() + + def _readbytes(self, length, start): + """Read bytes and return them. Note that length is in bits.""" + assert length % 8 == 0 + assert start + length <= self.len + if not (start + self._offset) % 8: + return bytes(self._datastore.getbyteslice((start + self._offset) // 8, + (start + self._offset + length) // 8)) + return self._slice(start, start + length).tobytes() + + def _getbytes(self): + """Return the data as an ordinary string.""" + if self.len % 8: + raise InterpretError("Cannot interpret as bytes unambiguously - " + "not multiple of 8 bits.") + return self._readbytes(self.len, 0) + + def _setuint(self, uint, length=None): + """Reset the bitstring to have given unsigned int interpretation.""" + try: + if length is None: + # Use the whole length. Deliberately not using .len here. + length = self._datastore.bitlength + except AttributeError: + # bitstring doesn't have a _datastore as it hasn't been created! + pass + if length is None or length == 0: + raise CreationError("A non-zero length must be specified with a " + "uint initialiser.") + if uint >= (1 << length): + msg = "{0} is too large an unsigned integer for a bitstring of length {1}. "\ + "The allowed range is [0, {2}]." + raise CreationError(msg, uint, length, (1 << length) - 1) + if uint < 0: + raise CreationError("uint cannot be initialised by a negative number.") + s = hex(uint)[2:] + s = s.rstrip('L') + if len(s) & 1: + s = '0' + s + try: + data = bytes.fromhex(s) + except AttributeError: + # the Python 2.x way + data = binascii.unhexlify(s) + # Now add bytes as needed to get the right length. + extrabytes = ((length + 7) // 8) - len(data) + if extrabytes > 0: + data = b'\x00' * extrabytes + data + offset = 8 - (length % 8) + if offset == 8: + offset = 0 + self._setbytes_unsafe(bytearray(data), length, offset) + + def _readuint_lsb0(self, length, start): + # TODO: This needs a complete rewrite - can't delegate to _readuint_msb0 + return self._readuint_msb0(length, self.len - start - length) + + def _readuint_msb0(self, length, start): + """Read bits and interpret as an unsigned int.""" + if not length: + raise InterpretError("Cannot interpret a zero length bitstring " + "as an integer.") + offset = self._offset + startbyte = (start + offset) // 8 + endbyte = (start + offset + length - 1) // 8 + + b = binascii.hexlify(bytes(self._datastore.getbyteslice(startbyte, endbyte + 1))) + assert b + i = int(b, 16) + final_bits = 8 - ((start + offset + length) % 8) + if final_bits != 8: + i >>= final_bits + i &= (1 << length) - 1 + return i + + def _getuint(self): + """Return data as an unsigned int.""" + return self._readuint(self.len, 0) + + def _setint(self, int_, length=None): + """Reset the bitstring to have given signed int interpretation.""" + # If no length given, and we've previously been given a length, use it. + if length is None and hasattr(self, 'len') and self.len != 0: + length = self.len + if length is None or length == 0: + raise CreationError("A non-zero length must be specified with an int initialiser.") + if int_ >= (1 << (length - 1)) or int_ < -(1 << (length - 1)): + raise CreationError("{0} is too large a signed integer for a bitstring of length {1}. " + "The allowed range is [{2}, {3}].", int_, length, -(1 << (length - 1)), + (1 << (length - 1)) - 1) + if int_ >= 0: + self._setuint(int_, length) + return + # Do the 2's complement thing. Add one, set to minus number, then flip bits. + self._setuint((-int_ - 1) ^ ((1 << length) - 1), length) + + def _readint(self, length, start): + """Read bits and interpret as a signed int""" + ui = self._readuint(length, start) + if not ui >> (length - 1): + # Top bit not set, number is positive + return ui + # Top bit is set, so number is negative + tmp = (~(ui - 1)) & ((1 << length) - 1) + return -tmp + + def _getint(self): + """Return data as a two's complement signed int.""" + return self._readint(self.len, 0) + + def _setuintbe(self, uintbe, length=None): + """Set the bitstring to a big-endian unsigned int interpretation.""" + if length is not None and length % 8 != 0: + raise CreationError("Big-endian integers must be whole-byte. " + "Length = {0} bits.", length) + self._setuint(uintbe, length) + + def _readuintbe(self, length, start): + """Read bits and interpret as a big-endian unsigned int.""" + if length % 8: + raise InterpretError("Big-endian integers must be whole-byte. " + "Length = {0} bits.", length) + return self._readuint(length, start) + + def _getuintbe(self): + """Return data as a big-endian two's complement unsigned int.""" + return self._readuintbe(self.len, 0) + + def _setintbe(self, intbe, length=None): + """Set bitstring to a big-endian signed int interpretation.""" + if length is not None and length % 8 != 0: + raise CreationError("Big-endian integers must be whole-byte. " + "Length = {0} bits.", length) + self._setint(intbe, length) + + def _readintbe(self, length, start): + """Read bits and interpret as a big-endian signed int.""" + if length % 8: + raise InterpretError("Big-endian integers must be whole-byte. " + "Length = {0} bits.", length) + return self._readint(length, start) + + def _getintbe(self): + """Return data as a big-endian two's complement signed int.""" + return self._readintbe(self.len, 0) + + def _setuintle(self, uintle, length=None): + if length is not None and length % 8 != 0: + raise CreationError("Little-endian integers must be whole-byte. " + "Length = {0} bits.", length) + self._setuint(uintle, length) + self._datastore._rawarray = self._datastore._rawarray[::-1] + + def _readuintle(self, length, start): + """Read bits and interpret as a little-endian unsigned int.""" + if length % 8: + raise InterpretError("Little-endian integers must be whole-byte. " + "Length = {0} bits.", length) + assert start + length <= self.len + absolute_pos = start + self._offset + startbyte, offset = divmod(absolute_pos, 8) + val = 0 + if not offset: + endbyte = (absolute_pos + length - 1) // 8 + chunksize = 4 # for 'L' format + while endbyte - chunksize + 1 >= startbyte: + val <<= 8 * chunksize + val += struct.unpack('> (length - 1): + # Top bit not set, number is positive + return ui + # Top bit is set, so number is negative + tmp = (~(ui - 1)) & ((1 << length) - 1) + return -tmp + + def _getintle(self): + return self._readintle(self.len, 0) + + def _setfloat(self, f, length=None): + # If no length given, and we've previously been given a length, use it. + if length is None and hasattr(self, 'len') and self.len != 0: + length = self.len + if length is None or length == 0: + raise CreationError("A non-zero length must be specified with a " + "float initialiser.") + if length == 32: + b = struct.pack('>f', f) + elif length == 64: + b = struct.pack('>d', f) + else: + raise CreationError("floats can only be 32 or 64 bits long, " + "not {0} bits", length) + self._setbytes_unsafe(bytearray(b), length, 0) + + def _readfloat(self, length, start): + """Read bits and interpret as a float.""" + if not (start + self._offset) % 8: + startbyte = (start + self._offset) // 8 + if length == 32: + f, = struct.unpack('>f', bytes(self._datastore.getbyteslice(startbyte, startbyte + 4))) + elif length == 64: + f, = struct.unpack('>d', bytes(self._datastore.getbyteslice(startbyte, startbyte + 8))) + else: + if length == 32: + f, = struct.unpack('>f', self._readbytes(32, start)) + elif length == 64: + f, = struct.unpack('>d', self._readbytes(64, start)) + try: + return f + except NameError: + raise InterpretError("floats can only be 32 or 64 bits long, not {0} bits", length) + + def _getfloat(self): + """Interpret the whole bitstring as a float.""" + return self._readfloat(self.len, 0) + + def _setfloatle(self, f, length=None): + # If no length given, and we've previously been given a length, use it. + if length is None and hasattr(self, 'len') and self.len != 0: + length = self.len + if length is None or length == 0: + raise CreationError("A non-zero length must be specified with a " + "float initialiser.") + if length == 32: + b = struct.pack(' 0: + tmp >>= 1 + leadingzeros += 1 + remainingpart = i + 1 - (1 << leadingzeros) + binstring = '0' * leadingzeros + '1' + Bits(uint=remainingpart, + length=leadingzeros).bin + self._setbin_unsafe(binstring) + + def _readue(self, pos): + """Return interpretation of next bits as unsigned exponential-Golomb code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + oldpos = pos + try: + while not self[pos]: + pos += 1 + except IndexError: + raise ReadError("Read off end of bitstring trying to read code.") + leadingzeros = pos - oldpos + codenum = (1 << leadingzeros) - 1 + if leadingzeros > 0: + if pos + leadingzeros + 1 > self.len: + raise ReadError("Read off end of bitstring trying to read code.") + codenum += self._readuint(leadingzeros, pos + 1) + pos += leadingzeros + 1 + else: + assert codenum == 0 + pos += 1 + return codenum, pos + + def _getue(self): + """Return data as unsigned exponential-Golomb code. + + Raises InterpretError if bitstring is not a single exponential-Golomb code. + + """ + try: + value, newpos = self._readue(0) + if value is None or newpos != self.len: + raise ReadError + except ReadError: + raise InterpretError("Bitstring is not a single exponential-Golomb code.") + return value + + def _setse(self, i): + """Initialise bitstring with signed exponential-Golomb code for integer i.""" + if i > 0: + u = (i * 2) - 1 + else: + u = -2 * i + self._setue(u) + + def _getse(self): + """Return data as signed exponential-Golomb code. + + Raises InterpretError if bitstring is not a single exponential-Golomb code. + + """ + try: + value, newpos = self._readse(0) + if value is None or newpos != self.len: + raise ReadError + except ReadError: + raise InterpretError("Bitstring is not a single exponential-Golomb code.") + return value + + def _readse(self, pos): + """Return interpretation of next bits as a signed exponential-Golomb code. + + Advances position to after the read code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + codenum, pos = self._readue(pos) + m = (codenum + 1) // 2 + if not codenum % 2: + return -m, pos + else: + return m, pos + + def _setuie(self, i): + """Initialise bitstring with unsigned interleaved exponential-Golomb code for integer i. + + Raises CreationError if i < 0. + + """ + if i < 0: + raise CreationError("Cannot use negative initialiser for unsigned " + "interleaved exponential-Golomb.") + self._setbin_unsafe('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1') + + def _readuie(self, pos): + """Return interpretation of next bits as unsigned interleaved exponential-Golomb code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + try: + codenum = 1 + while not self[pos]: + pos += 1 + codenum <<= 1 + codenum += self[pos] + pos += 1 + pos += 1 + except IndexError: + raise ReadError("Read off end of bitstring trying to read code.") + codenum -= 1 + return codenum, pos + + def _getuie(self): + """Return data as unsigned interleaved exponential-Golomb code. + + Raises InterpretError if bitstring is not a single exponential-Golomb code. + + """ + try: + value, newpos = self._readuie(0) + if value is None or newpos != self.len: + raise ReadError + except ReadError: + raise InterpretError("Bitstring is not a single interleaved exponential-Golomb code.") + return value + + def _setsie(self, i): + """Initialise bitstring with signed interleaved exponential-Golomb code for integer i.""" + if not i: + self._setbin_unsafe('1') + else: + self._setuie(abs(i)) + self._addright(Bits([i < 0])) + + def _getsie(self): + """Return data as signed interleaved exponential-Golomb code. + + Raises InterpretError if bitstring is not a single exponential-Golomb code. + + """ + try: + value, newpos = self._readsie(0) + if value is None or newpos != self.len: + raise ReadError + except ReadError: + raise InterpretError("Bitstring is not a single interleaved exponential-Golomb code.") + return value + + def _readsie(self, pos): + """Return interpretation of next bits as a signed interleaved exponential-Golomb code. + + Advances position to after the read code. + + Raises ReadError if the end of the bitstring is encountered while + reading the code. + + """ + codenum, pos = self._readuie(pos) + if not codenum: + return 0, pos + try: + if self[pos]: + return -codenum, pos + 1 + else: + return codenum, pos + 1 + except IndexError: + raise ReadError("Read off end of bitstring trying to read code.") + + def _setbool(self, value): + # We deliberately don't want to have implicit conversions to bool here. + # If we did then it would be difficult to deal with the 'False' string. + if value in (1, 'True'): + self._setbytes_unsafe(bytearray(b'\x80'), 1, 0) + elif value in (0, 'False'): + self._setbytes_unsafe(bytearray(b'\x00'), 1, 0) + else: + raise CreationError('Cannot initialise boolean with {0}.', value) + + def _getbool(self): + if self.length != 1: + msg = "For a bool interpretation a bitstring must be 1 bit long, not {0} bits." + raise InterpretError(msg, self.length) + return self[0] + + def _readbool(self, pos): + return self[pos], pos + 1 + + def _setbin_safe(self, binstring): + """Reset the bitstring to the value given in binstring.""" + binstring = tidy_input_string(binstring) + # remove any 0b if present + binstring = binstring.replace('0b', '') + self._setbin_unsafe(binstring) + + def _setbin_unsafe(self, binstring): + """Same as _setbin_safe, but input isn't sanity checked. binstring mustn't start with '0b'.""" + length = len(binstring) + # pad with zeros up to byte boundary if needed + boundary = ((length + 7) // 8) * 8 + padded_binstring = binstring + '0' * (boundary - length)\ + if len(binstring) < boundary else binstring + try: + bytelist = [int(padded_binstring[x:x + 8], 2) + for x in xrange(0, len(padded_binstring), 8)] + except ValueError: + raise CreationError("Invalid character in bin initialiser {0}.", binstring) + self._setbytes_unsafe(bytearray(bytelist), length, 0) + + def _readbin(self, length, start): + """Read bits and interpret as a binary string.""" + if not length: + return '' + # Get the byte slice containing our bit slice + startbyte, startoffset = divmod(start + self._offset, 8) + endbyte = (start + self._offset + length - 1) // 8 + b = self._datastore.getbyteslice(startbyte, endbyte + 1) + # Convert to a string of '0' and '1's (via a hex string an and int!) + c = "{:0{}b}".format(int(binascii.hexlify(b), 16), 8*len(b)) + # Finally chop off any extra bits. + return c[startoffset:startoffset + length] + + def _getbin(self): + """Return interpretation as a binary string.""" + return self._readbin(self.len, 0) + + def _setoct(self, octstring): + """Reset the bitstring to have the value given in octstring.""" + octstring = tidy_input_string(octstring) + # remove any 0o if present + octstring = octstring.replace('0o', '') + binlist = [] + for i in octstring: + try: + binlist.append(OCT_TO_BITS[int(i)]) + except (ValueError, IndexError): + raise CreationError("Invalid symbol '{0}' in oct initialiser.", i) + + self._setbin_unsafe(''.join(binlist)) + + def _readoct(self, length, start): + """Read bits and interpret as an octal string.""" + if length % 3: + raise InterpretError("Cannot convert to octal unambiguously - " + "not multiple of 3 bits.") + if not length: + return '' + # Get main octal bit by converting from int. + # Strip starting 0 or 0o depending on Python version. + end = oct(self._readuint(length, start))[LEADING_OCT_CHARS:] + if end.endswith('L'): + end = end[:-1] + middle = '0' * (length // 3 - len(end)) + return middle + end + + def _getoct(self): + """Return interpretation as an octal string.""" + return self._readoct(self.len, 0) + + def _sethex(self, hexstring): + """Reset the bitstring to have the value given in hexstring.""" + hexstring = tidy_input_string(hexstring) + # remove any 0x if present + hexstring = hexstring.replace('0x', '') + length = len(hexstring) + if length % 2: + hexstring += '0' + try: + data = bytearray.fromhex(hexstring) + except ValueError: + raise CreationError("Invalid symbol in hex initialiser.") + self._setbytes_unsafe(data, length * 4, 0) + + def _readhex(self, length, start): + """Read bits and interpret as a hex string.""" + if length % 4: + raise InterpretError("Cannot convert to hex unambiguously - " + "not multiple of 4 bits.") + if not length: + return '' + s = self._slice(start, start + length).tobytes() + try: + s = s.hex() # Available in Python 3.5+ + except AttributeError: + # This monstrosity is the only thing I could get to work for both 2.6 and 3.1. + s = str(binascii.hexlify(s).decode('utf-8')) + # If there's one nibble too many then cut it off + return s[:-1] if (length // 4) % 2 else s + + def _gethex(self): + """Return the hexadecimal representation as a string prefixed with '0x'. + + Raises an InterpretError if the bitstring's length is not a multiple of 4. + + """ + return self._readhex(self.len, 0) + + def _getoffset(self): + return self._datastore.offset + + def _getlength(self): + """Return the length of the bitstring in bits.""" + return self._datastore.bitlength + + def _ensureinmemory(self): + """Ensure the data is held in memory, not in a file.""" + self._setbytes_unsafe(self._datastore.getbyteslice(0, self._datastore.bytelength), + self.len, self._offset) + + @classmethod + def _converttobitstring(cls, bs, offset=0, cache={}): + """Convert bs to a bitstring and return it. + + offset gives the suggested bit offset of first significant + bit, to optimise append etc. + + """ + if isinstance(bs, Bits): + return bs + try: + return cache[(bs, offset)] + except KeyError: + if isinstance(bs, basestring): + b = cls() + try: + _, tokens = tokenparser(bs) + except ValueError as e: + raise CreationError(*e.args) + if tokens: + b._addright(Bits._init_with_token(*tokens[0])) + b._datastore = offsetcopy(b._datastore, offset) + for token in tokens[1:]: + b._addright(Bits._init_with_token(*token)) + assert b._assertsanity() + assert b.len == 0 or b._offset == offset + if len(cache) < CACHE_SIZE: + cache[(bs, offset)] = b + return b + except TypeError: + # Unhashable type + pass + return cls(bs) + + def _copy(self): + """Create and return a new copy of the Bits (always in memory).""" + s_copy = self.__class__() + s_copy._setbytes_unsafe(self._datastore.getbyteslice(0, self._datastore.bytelength), + self.len, self._offset) + return s_copy + + def _slice_lsb0(self, start, end): + """Used internally to get a slice, without error checking (LSB0).""" + return self._slice_msb0(self.length - end, self.length - start) + + def _slice_msb0(self, start, end): + """Used internally to get a slice, without error checking.""" + if end == start: + return self.__class__() + assert start < end, "start={0}, end={1}".format(start, end) + offset = self._offset + startbyte, newoffset = divmod(start + offset, 8) + endbyte = (end + offset - 1) // 8 + bs = self.__class__() + bs._setbytes_unsafe(self._datastore.getbyteslice(startbyte, endbyte + 1), end - start, newoffset) + return bs + + def _readtoken(self, name, pos, length): + """Reads a token from the bitstring and returns the result.""" + if length is not None and int(length) > self.length - pos: + raise ReadError("Reading off the end of the data. " + "Tried to read {0} bits when only {1} available.".format(int(length), self.length - pos)) + try: + val = name_to_read[name](self, length, pos) + return val, pos + length + except KeyError: + if name == 'pad': + return None, pos + length + raise ValueError("Can't parse token {0}:{1}".format(name, length)) + except TypeError: + # This is for the 'ue', 'se' and 'bool' tokens. They will also return the new pos. + return name_to_read[name](self, pos) + + def _addright(self, bs): + """Add a bitstring to the RHS of the current bitstring.""" + self._datastore._appendstore(bs._datastore) + + def _addleft(self, bs): + """Prepend a bitstring to the current bitstring.""" + self._datastore._prependstore(bs._datastore) + + def _reverse(self): + """Reverse all bits in-place.""" + # Reverse the contents of each byte + n = [BYTE_REVERSAL_DICT[b] for b in self._datastore.rawbytes] + # Then reverse the order of the bytes + n.reverse() + # The new offset is the number of bits that were unused at the end. + newoffset = 8 - (self._offset + self.len) % 8 + if newoffset == 8: + newoffset = 0 + self._setbytes_unsafe(bytearray().join(n), self.length, newoffset) + + def _truncateleft(self, bits): + """Truncate bits from the start of the bitstring.""" + assert 0 <= bits <= self.len + if not bits: + return Bits() + truncated_bits = self._slice_msb0(0, bits) + if bits == self.len: + self._clear() + return truncated_bits + bytepos, offset = divmod(self._offset + bits, 8) + self._setbytes_unsafe(self._datastore.getbyteslice(bytepos, self._datastore.bytelength), self.len - bits, + offset) + assert self._assertsanity() + return truncated_bits + + def _truncateright(self, bits): + """Truncate bits from the end of the bitstring.""" + assert 0 <= bits <= self.len + if not bits: + return Bits() + truncated_bits = self._slice_lsb0(0, bits) + if bits == self.len: + self._clear() + return truncated_bits + newlength_in_bytes = (self._offset + self.len - bits + 7) // 8 + self._setbytes_unsafe(self._datastore.getbyteslice(0, newlength_in_bytes), self.len - bits, + self._offset) + assert self._assertsanity() + return truncated_bits + + def _insert_lsb0(self, bs, pos): + """Insert bs at pos (LSB0).""" + self._insert_msb0(bs, self.len - pos) + + def _insert_msb0(self, bs, pos): + """Insert bs at pos.""" + assert 0 <= pos <= self.len + if pos > self.len // 2: + # Inserting nearer end, so cut off end. + # end = self._slice(pos, self.len) + end = self._truncateright(self.len - pos) + self._addright(bs) + self._addright(end) + else: + # Inserting nearer start, so cut off start. + start = self._slice(0, pos) + self._truncateleft(pos) + self._addleft(bs) + self._addleft(start) + try: + self._pos = pos + bs.len + except AttributeError: + pass + assert self._assertsanity() + + def _overwrite_lsb0(self, bs, pos): + """Overwrite with bs at pos (LSB0).""" + self._overwrite_msb0(bs, self.len - pos - bs.len) + + def _overwrite_msb0(self, bs, pos): + """Overwrite with bs at pos.""" + assert 0 <= pos < self.len + if bs is self: + # Just overwriting with self, so do nothing. + assert pos == 0 + return + firstbytepos = (self._offset + pos) // 8 + lastbytepos = (self._offset + pos + bs.len - 1) // 8 + bytepos, bitoffset = divmod(self._offset + pos, 8) + if firstbytepos == lastbytepos: + mask = ((1 << bs.len) - 1) << (8 - bs.len - bitoffset) + self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) & (~mask)) + d = offsetcopy(bs._datastore, bitoffset) + self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) | (d.getbyte(0) & mask)) + else: + # Do first byte + mask = (1 << (8 - bitoffset)) - 1 + self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) & (~mask)) + d = offsetcopy(bs._datastore, bitoffset) + self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) | (d.getbyte(0) & mask)) + # Now do all the full bytes + self._datastore.setbyteslice(firstbytepos + 1, lastbytepos, d.getbyteslice(1, lastbytepos - firstbytepos)) + # and finally the last byte + bitsleft = (self._offset + pos + bs.len) % 8 + if not bitsleft: + bitsleft = 8 + mask = (1 << (8 - bitsleft)) - 1 + self._datastore.setbyte(lastbytepos, self._datastore.getbyte(lastbytepos) & mask) + self._datastore.setbyte(lastbytepos, + self._datastore.getbyte(lastbytepos) | (d.getbyte(d.bytelength - 1) & ~mask)) + assert self._assertsanity() + + def _delete_lsb0(self, bits, pos): + """Delete bits at pos (LSB0).""" + self._delete_msb0(bits, self.len - pos - bits) + + def _delete_msb0(self, bits, pos): + """Delete bits at pos.""" + assert 0 <= pos <= self.len + assert pos + bits <= self.len, "pos={}, bits={}, len={}".format(pos, bits, self.len) + if not pos: + # Cutting bits off at the start. + self._truncateleft(bits) + return + if pos + bits == self.len: + # Cutting bits off at the end. + self._truncateright(bits) + return + if pos > self.len - pos - bits: + # More bits before cut point than after it, so do bit shifting + # on the final bits. + end = self._slice_msb0(pos + bits, self.len) + assert self.len - pos > 0 + self._truncateright(self.len - pos) + self._addright(end) + return + # More bits after the cut point than before it. + start = self._slice_msb0(0, pos) + self._truncateleft(pos + bits) + self._addleft(start) + return + + def _reversebytes(self, start, end): + """Reverse bytes in-place.""" + # Make the start occur on a byte boundary + # TODO: We could be cleverer here to avoid changing the offset. + newoffset = 8 - (start % 8) + if newoffset == 8: + newoffset = 0 + self._datastore = offsetcopy(self._datastore, newoffset) + # Now just reverse the byte data + toreverse = bytearray(self._datastore.getbyteslice((newoffset + start) // 8, (newoffset + end) // 8)) + toreverse.reverse() + self._datastore.setbyteslice((newoffset + start) // 8, (newoffset + end) // 8, toreverse) + + def _set(self, pos): + """Set bit at pos to 1.""" + assert 0 <= pos < self.len + self._datastore.setbit(pos) + + def _unset(self, pos): + """Set bit at pos to 0.""" + assert 0 <= pos < self.len + self._datastore.unsetbit(pos) + + def _invert(self, pos): + """Flip bit at pos 1<->0.""" + assert 0 <= pos < self.len + self._datastore.invertbit(pos) + + def _invert_all(self): + """Invert every bit.""" + for p in xrange(self._datastore.byteoffset, self._datastore.byteoffset + self._datastore.bytelength): + self._datastore._rawarray[p] = 256 + ~self._datastore._rawarray[p] + + def _ilshift(self, n): + """Shift bits by n to the left in place. Return self.""" + assert 0 < n <= self.len + self._addright(Bits(n)) + self._truncateleft(n) + return self + + def _irshift(self, n): + """Shift bits by n to the right in place. Return self.""" + assert 0 < n <= self.len + self._addleft(Bits(n)) + self._truncateright(n) + return self + + def _imul(self, n): + """Concatenate n copies of self in place. Return self.""" + assert n >= 0 + if not n: + self._clear() + return self + m = 1 + old_len = self.len + while m * 2 < n: + self._addright(self) + m *= 2 + self._addright(self[0:(n - m) * old_len]) + return self + + def _inplace_logical_helper(self, bs, f): + """Helper function containing most of the __ior__, __iand__, __ixor__ code.""" + # Give the two bitstrings the same offset (modulo 8) + self_byteoffset, self_bitoffset = divmod(self._offset, 8) + bs_byteoffset, bs_bitoffset = divmod(bs._offset, 8) + if bs_bitoffset != self_bitoffset: + if not self_bitoffset: + bs._datastore = offsetcopy(bs._datastore, 0) + else: + self._datastore = offsetcopy(self._datastore, bs_bitoffset) + a = self._datastore.rawbytes + b = bs._datastore.rawbytes + for i in xrange(len(a)): + a[i] = f(a[i + self_byteoffset], b[i + bs_byteoffset]) + return self + + def _ior(self, bs): + return self._inplace_logical_helper(bs, operator.ior) + + def _iand(self, bs): + return self._inplace_logical_helper(bs, operator.iand) + + def _ixor(self, bs): + return self._inplace_logical_helper(bs, operator.xor) + + def _readbits(self, length, start): + """Read some bits from the bitstring and return newly constructed bitstring.""" + return self._slice(start, start + length) + + def _validate_slice_msb0(self, start, end): + """Validate start and end and return them as positive bit positions.""" + if start is None: + start = 0 + elif start < 0: + start += self.len + if end is None: + end = self.len + elif end < 0: + end += self.len + if not 0 <= end <= self.len: + raise ValueError("end is not a valid position in the bitstring.") + if not 0 <= start <= self.len: + raise ValueError("start is not a valid position in the bitstring.") + if end < start: + raise ValueError("end must not be less than start.") + return start, end + + def _validate_slice_lsb0(self, start, end): + start, end = self._validate_slice_msb0(start, end) + return self.len - end, self.len - start + + def unpack(self, fmt, **kwargs): + """Interpret the whole bitstring using fmt and return list. + + fmt -- A single string or a list of strings with comma separated tokens + describing how to interpret the bits in the bitstring. Items + can also be integers, for reading new bitstring of the given length. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + Raises ValueError if the format is not understood. If not enough bits + are available then all bits to the end of the bitstring will be used. + + See the docstring for 'read' for token examples. + + """ + return self._readlist(fmt, 0, **kwargs)[0] + + def _readlist(self, fmt, pos, **kwargs): + tokens = [] + stretchy_token = None + if isinstance(fmt, basestring): + fmt = [fmt] + # Replace integers with 'bits' tokens + for i, f in enumerate(fmt): + if isinstance(f, numbers.Integral): + fmt[i] = "bits:{0}".format(f) + for f_item in fmt: + stretchy, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys()))) + if stretchy: + if stretchy_token: + raise Error("It's not possible to have more than one 'filler' token.") + stretchy_token = stretchy + tokens.extend(tkns) + if not stretchy_token: + lst = [] + for name, length, _ in tokens: + if length in kwargs: + length = kwargs[length] + if name == 'bytes': + length *= 8 + if name in kwargs and length is None: + # Using default 'uint' - the name is really the length. + value, pos = self._readtoken('uint', pos, kwargs[name]) + lst.append(value) + continue + value, pos = self._readtoken(name, pos, length) + if value is not None: # Don't append pad tokens + lst.append(value) + return lst, pos + stretchy_token = False + bits_after_stretchy_token = 0 + for token in tokens: + name, length, _ = token + if length in kwargs: + length = kwargs[length] + if name == 'bytes': + length *= 8 + if name in kwargs and length is None: + # Default 'uint'. + length = kwargs[name] + if stretchy_token: + if name in ('se', 'ue', 'sie', 'uie'): + raise Error("It's not possible to parse a variable" + "length token after a 'filler' token.") + else: + if length is None: + raise Error("It's not possible to have more than " + "one 'filler' token.") + bits_after_stretchy_token += length + if length is None and name not in ('se', 'ue', 'sie', 'uie'): + assert not stretchy_token + stretchy_token = token + bits_left = self.len - pos + return_values = [] + for token in tokens: + name, length, _ = token + if token is stretchy_token: + # Set length to the remaining bits + length = max(bits_left - bits_after_stretchy_token, 0) + if length in kwargs: + length = kwargs[length] + if name == 'bytes': + length *= 8 + if name in kwargs and length is None: + # Default 'uint' + length = kwargs[name] + if length is not None: + bits_left -= length + value, pos = self._readtoken(name, pos, length) + if value is not None: + return_values.append(value) + return return_values, pos + + def _findbytes(self, bytes_, start, end, bytealigned): + """Quicker version of find when everything's whole byte + and byte aligned. + + """ + assert self._datastore.offset == 0 + assert bytealigned is True + # Extract data bytes from bitstring to be found. + bytepos = (start + 7) // 8 + found = False + p = bytepos + finalpos = end // 8 + increment = max(1024, len(bytes_) * 10) + buffersize = increment + len(bytes_) + while p < finalpos: + # Read in file or from memory in overlapping chunks and search the chunks. + buf = bytearray(self._datastore.getbyteslice(p, min(p + buffersize, finalpos))) + pos = buf.find(bytes_) + if pos != -1: + found = True + p += pos + break + p += increment + if not found: + return () + return (p * 8,) + + def _findregex(self, reg_ex, start, end, bytealigned): + """Find first occurrence of a compiled regular expression. + + Note that this doesn't support arbitrary regexes, in particular they + must match a known length. + + """ + p = start + length = len(reg_ex.pattern) + # We grab overlapping chunks of the binary representation and + # do an ordinary string search within that. + increment = max(4096, length * 10) + buffersize = increment + length + while p < end: + buf = self._readbin(min(buffersize, end - p), p) + # Test using regular expressions... + m = reg_ex.search(buf) + if m: + pos = m.start() + # pos = buf.find(targetbin) + # if pos != -1: + # if bytealigned then we only accept byte aligned positions. + if not bytealigned or (p + pos) % 8 == 0: + return (p + pos,) + if bytealigned: + # Advance to just beyond the non-byte-aligned match and try again... + p += pos + 1 + continue + p += increment + # Not found, return empty tuple + return () + + def find(self, bs, start=None, end=None, bytealigned=None): + """Find first occurrence of substring bs. + + Returns a single item tuple with the bit position if found, or an + empty tuple if not found. The bit position (pos property) will + also be set to the start of the substring if it is found. + + bs -- The bitstring to find. + start -- The bit position to start the search. Defaults to 0. + end -- The bit position one past the last bit to search. + Defaults to self.len. + bytealigned -- If True the bitstring will only be + found on byte boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > self.len or + if end < start. + + >>> BitArray('0xc3e').find('0b1111') + (6,) + + """ + return self._find(bs, start, end, bytealigned) + + def _find_lsb0(self, bs, start=None, end=None, bytealigned=None): + bs = Bits(bs) + start, end = self._validate_slice_lsb0(start, end) + p = self.rfind(bs, start, end, bytealigned) + if p: + return (self.len - p[0] - bs.length,) + + def _find_msb0(self, bs, start=None, end=None, bytealigned=None): + bs = Bits(bs) + if not bs.len: + raise ValueError("Cannot find an empty bitstring.") + start, end = self._validate_slice(start, end) + if bytealigned is None: + bytealigned = globals()['bytealigned'] + if bytealigned and not bs.len % 8 and not self._datastore.offset: + p = self._findbytes(bs.bytes, start, end, bytealigned) + else: + p = self._findregex(re.compile(bs._getbin()), start, end, bytealigned) + # If called from a class that has a pos, set it + try: + self._pos = p[0] + except (AttributeError, IndexError): + pass + return p + + def findall(self, bs, start=None, end=None, count=None, bytealigned=None): + """Find all occurrences of bs. Return generator of bit positions. + + bs -- The bitstring to find. + start -- The bit position to start the search. Defaults to 0. + end -- The bit position one past the last bit to search. + Defaults to self.len. + count -- The maximum number of occurrences to find. + bytealigned -- If True the bitstring will only be found on + byte boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > self.len or + if end < start. + + Note that all occurrences of bs are found, even if they overlap. + + """ + if count is not None and count < 0: + raise ValueError("In findall, count must be >= 0.") + bs = Bits(bs) + start, end = self._validate_slice(start, end) + if bytealigned is None: + bytealigned = globals()['bytealigned'] + c = 0 + if bytealigned and not bs.len % 8 and not self._datastore.offset: + # Use the quick find method + f = self._findbytes + x = bs._getbytes() + else: + f = self._findregex + x = re.compile(bs._getbin()) + while True: + + p = f(x, start, end, bytealigned) + if not p: + break + if count is not None and c >= count: + return + c += 1 + try: + self._pos = p[0] + except AttributeError: + pass + yield p[0] + if bytealigned: + start = p[0] + 8 + else: + start = p[0] + 1 + if start >= end: + break + return + + def rfind(self, bs, start=None, end=None, bytealigned=None): + """Find final occurrence of substring bs. + + Returns a single item tuple with the bit position if found, or an + empty tuple if not found. The bit position (pos property) will + also be set to the start of the substring if it is found. + + bs -- The bitstring to find. + start -- The bit position to end the reverse search. Defaults to 0. + end -- The bit position one past the first bit to reverse search. + Defaults to self.len. + bytealigned -- If True the bitstring will only be found on byte + boundaries. + + Raises ValueError if bs is empty, if start < 0, if end > self.len or + if end < start. + + """ + bs = Bits(bs) + start, end = self._validate_slice(start, end) + if bytealigned is None: + bytealigned = globals()['bytealigned'] + if not bs.len: + raise ValueError("Cannot find an empty bitstring.") + # Search chunks starting near the end and then moving back + # until we find bs. + increment = max(8192, bs.len * 80) + buffersize = min(increment + bs.len, end - start) + pos = max(start, end - buffersize) + while True: + found = list(self.findall(bs, start=pos, end=pos + buffersize, + bytealigned=bytealigned)) + if not found: + if pos == start: + return () + pos = max(start, pos - increment) + continue + return (found[-1],) + + def cut(self, bits, start=None, end=None, count=None): + """Return bitstring generator by cutting into bits sized chunks. + + bits -- The size in bits of the bitstring chunks to generate. + start -- The bit position to start the first cut. Defaults to 0. + end -- The bit position one past the last bit to use in the cut. + Defaults to self.len. + count -- If specified then at most count items are generated. + Default is to cut as many times as possible. + + """ + start, end = self._validate_slice(start, end) + if count is not None and count < 0: + raise ValueError("Cannot cut - count must be >= 0.") + if bits <= 0: + raise ValueError("Cannot cut - bits must be >= 0.") + c = 0 + while count is None or c < count: + c += 1 + nextchunk = self._slice(start, min(start + bits, end)) + if nextchunk.len != bits: + return + assert nextchunk._assertsanity() + yield nextchunk + start += bits + return + + def split(self, delimiter, start=None, end=None, count=None, + bytealigned=None): + """Return bitstring generator by splittling using a delimiter. + + The first item returned is the initial bitstring before the delimiter, + which may be an empty bitstring. + + delimiter -- The bitstring used as the divider. + start -- The bit position to start the split. Defaults to 0. + end -- The bit position one past the last bit to use in the split. + Defaults to self.len. + count -- If specified then at most count items are generated. + Default is to split as many times as possible. + bytealigned -- If True splits will only occur on byte boundaries. + + Raises ValueError if the delimiter is empty. + + """ + delimiter = Bits(delimiter) + if not delimiter.len: + raise ValueError("split delimiter cannot be empty.") + start, end = self._validate_slice(start, end) + if bytealigned is None: + bytealigned = globals()['bytealigned'] + if count is not None and count < 0: + raise ValueError("Cannot split - count must be >= 0.") + if count == 0: + return + if bytealigned and not delimiter.len % 8 and not self._datastore.offset: + # Use the quick find method + f = self._findbytes + x = delimiter._getbytes() + else: + f = self._findregex + x = re.compile(delimiter._getbin()) + found = f(x, start, end, bytealigned) + if not found: + # Initial bits are the whole bitstring being searched + yield self._slice(start, end) + return + # yield the bytes before the first occurrence of the delimiter, even if empty + yield self._slice(start, found[0]) + startpos = pos = found[0] + c = 1 + while count is None or c < count: + pos += delimiter.len + found = f(x, pos, end, bytealigned) + if not found: + # No more occurrences, so return the rest of the bitstring + yield self._slice(startpos, end) + return + c += 1 + yield self._slice(startpos, found[0]) + startpos = pos = found[0] + # Have generated count bitstrings, so time to quit. + return + + def join(self, sequence): + """Return concatenation of bitstrings joined by self. + + sequence -- A sequence of bitstrings. + + """ + s = self.__class__() + i = iter(sequence) + try: + s._addright(Bits(next(i))) + while True: + n = next(i) + s._addright(self) + s._addright(Bits(n)) + except StopIteration: + pass + return s + + def tobytes(self): + """Return the bitstring as bytes, padding with zero bits if needed. + + Up to seven zero bits will be added at the end to byte align. + + """ + d = offsetcopy(self._datastore, 0).rawbytes + # Need to ensure that unused bits at end are set to zero + unusedbits = 8 - self.len % 8 + if unusedbits != 8: + d[-1] &= (0xff << unusedbits) + return bytes(d) + + def tofile(self, f): + """Write the bitstring to a file object, padding with zero bits if needed. + + Up to seven zero bits will be added at the end to byte align. + + """ + # If the bitstring is file based then we don't want to read it all + # in to memory. + chunksize = 1024 * 1024 # 1 MiB chunks + if not self._offset: + a = 0 + bytelen = self._datastore.bytelength + p = self._datastore.getbyteslice(a, min(a + chunksize, bytelen - 1)) + while len(p) == chunksize: + f.write(p) + a += chunksize + p = self._datastore.getbyteslice(a, min(a + chunksize, bytelen - 1)) + f.write(p) + # Now the final byte, ensuring that unused bits at end are set to 0. + bits_in_final_byte = self.len % 8 + if not bits_in_final_byte: + bits_in_final_byte = 8 + f.write(self[-bits_in_final_byte:].tobytes()) + else: + # Really quite inefficient... + a = 0 + b = a + chunksize * 8 + while b <= self.len: + f.write(self._slice(a, b)._getbytes()) + a += chunksize * 8 + b += chunksize * 8 + if a != self.len: + f.write(self._slice(a, self.len).tobytes()) + + def startswith(self, prefix, start=None, end=None): + """Return whether the current bitstring starts with prefix. + + prefix -- The bitstring to search for. + start -- The bit position to start from. Defaults to 0. + end -- The bit position to end at. Defaults to self.len. + + """ + prefix = Bits(prefix) + start, end = self._validate_slice_msb0(start, end) # the _slice deals with msb0/lsb0 + if end < start + prefix.len: + return False + end = start + prefix.len + return self._slice(start, end) == prefix + + def endswith(self, suffix, start=None, end=None): + """Return whether the current bitstring ends with suffix. + + suffix -- The bitstring to search for. + start -- The bit position to start from. Defaults to 0. + end -- The bit position to end at. Defaults to self.len. + + """ + suffix = Bits(suffix) + start, end = self._validate_slice(start, end) + if start + suffix.len > end: + return False + start = end - suffix.len + return self._slice(start, end) == suffix + + def all(self, value, pos=None): + """Return True if one or many bits are all set to value. + + value -- If value is True then checks for bits set to 1, otherwise + checks for bits set to 0. + pos -- An iterable of bit positions. Negative numbers are treated in + the same way as slice indices. Defaults to the whole bitstring. + + """ + value = bool(value) + length = self.len + if pos is None: + pos = xrange(self.len) + for p in pos: + if p < 0: + p += length + if not 0 <= p < length: + raise IndexError("Bit position {0} out of range.".format(p)) + if not self._datastore.getbit(p) is value: + return False + return True + + def any(self, value, pos=None): + """Return True if any of one or many bits are set to value. + + value -- If value is True then checks for bits set to 1, otherwise + checks for bits set to 0. + pos -- An iterable of bit positions. Negative numbers are treated in + the same way as slice indices. Defaults to the whole bitstring. + + """ + value = bool(value) + length = self.len + if pos is None: + pos = xrange(self.len) + for p in pos: + if p < 0: + p += length + if not 0 <= p < length: + raise IndexError("Bit position {0} out of range.".format(p)) + if self._datastore.getbit(p) is value: + return True + return False + + def count(self, value): + """Return count of total number of either zero or one bits. + + value -- If True then bits set to 1 are counted, otherwise bits set + to 0 are counted. + + >>> Bits('0xef').count(1) + 7 + + """ + if not self.len: + return 0 + # count the number of 1s (from which it's easy to work out the 0s). + # Don't count the final byte yet. + count = sum(BIT_COUNT[self._datastore.getbyte(i)] for i in xrange(self._datastore.bytelength - 1)) + # adjust for bits at start that aren't part of the bitstring + if self._offset: + count -= BIT_COUNT[self._datastore.getbyte(0) >> (8 - self._offset)] + # and count the last 1 - 8 bits at the end. + endbits = self._datastore.bytelength * 8 - (self._offset + self.len) + count += BIT_COUNT[self._datastore.getbyte(self._datastore.bytelength - 1) >> endbits] + return count if value else self.len - count + + # Create native-endian functions as aliases depending on the byteorder + if byteorder == 'little': + _setfloatne = _setfloatle + _readfloatne = _readfloatle + _getfloatne = _getfloatle + _setuintne = _setuintle + _readuintne = _readuintle + _getuintne = _getuintle + _setintne = _setintle + _readintne = _readintle + _getintne = _getintle + else: + _setfloatne = _setfloat + _readfloatne = _readfloat + _getfloatne = _getfloat + _setuintne = _setuintbe + _readuintne = _readuintbe + _getuintne = _getuintbe + _setintne = _setintbe + _readintne = _readintbe + _getintne = _getintbe + + _offset = property(_getoffset) + + len = property(_getlength, + doc="""The length of the bitstring in bits. Read only. + """) + length = property(_getlength, + doc="""The length of the bitstring in bits. Read only. + """) + bool = property(_getbool, + doc="""The bitstring as a bool (True or False). Read only. + """) + hex = property(_gethex, + doc="""The bitstring as a hexadecimal string. Read only. + """) + bin = property(_getbin, + doc="""The bitstring as a binary string. Read only. + """) + oct = property(_getoct, + doc="""The bitstring as an octal string. Read only. + """) + bytes = property(_getbytes, + doc="""The bitstring as a bytes object. Read only. + """) + int = property(_getint, + doc="""The bitstring as a two's complement signed int. Read only. + """) + uint = property(_getuint, + doc="""The bitstring as a two's complement unsigned int. Read only. + """) + float = property(_getfloat, + doc="""The bitstring as a floating point number. Read only. + """) + intbe = property(_getintbe, + doc="""The bitstring as a two's complement big-endian signed int. Read only. + """) + uintbe = property(_getuintbe, + doc="""The bitstring as a two's complement big-endian unsigned int. Read only. + """) + floatbe = property(_getfloat, + doc="""The bitstring as a big-endian floating point number. Read only. + """) + intle = property(_getintle, + doc="""The bitstring as a two's complement little-endian signed int. Read only. + """) + uintle = property(_getuintle, + doc="""The bitstring as a two's complement little-endian unsigned int. Read only. + """) + floatle = property(_getfloatle, + doc="""The bitstring as a little-endian floating point number. Read only. + """) + intne = property(_getintne, + doc="""The bitstring as a two's complement native-endian signed int. Read only. + """) + uintne = property(_getuintne, + doc="""The bitstring as a two's complement native-endian unsigned int. Read only. + """) + floatne = property(_getfloatne, + doc="""The bitstring as a native-endian floating point number. Read only. + """) + ue = property(_getue, + doc="""The bitstring as an unsigned exponential-Golomb code. Read only. + """) + se = property(_getse, + doc="""The bitstring as a signed exponential-Golomb code. Read only. + """) + uie = property(_getuie, + doc="""The bitstring as an unsigned interleaved exponential-Golomb code. Read only. + """) + sie = property(_getsie, + doc="""The bitstring as a signed interleaved exponential-Golomb code. Read only. + """) + + + + + +class BitArray(Bits): + """A container holding a mutable sequence of bits. + + Subclass of the immutable Bits class. Inherits all of its + methods (except __hash__) and adds mutating methods. + + Mutating methods: + + append() -- Append a bitstring. + byteswap() -- Change byte endianness in-place. + insert() -- Insert a bitstring. + invert() -- Flip bit(s) between one and zero. + overwrite() -- Overwrite a section with a new bitstring. + prepend() -- Prepend a bitstring. + replace() -- Replace occurrences of one bitstring with another. + reverse() -- Reverse bits in-place. + rol() -- Rotate bits to the left. + ror() -- Rotate bits to the right. + set() -- Set bit(s) to 1 or 0. + + Methods inherited from Bits: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + join() -- Join bitstrings together using current bitstring. + rfind() -- Seek backwards to find a sub-bitstring. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Special methods: + + Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= + in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^. + + Properties: + + bin -- The bitstring as a binary string. + bool -- For single bit bitstrings, interpret as True or False. + bytepos -- The current byte position in the bitstring. + bytes -- The bitstring as a bytes object. + float -- Interpret as a floating point number. + floatbe -- Interpret as a big-endian floating point number. + floatle -- Interpret as a little-endian floating point number. + floatne -- Interpret as a native-endian floating point number. + hex -- The bitstring as a hexadecimal string. + int -- Interpret as a two's complement signed integer. + intbe -- Interpret as a big-endian signed integer. + intle -- Interpret as a little-endian signed integer. + intne -- Interpret as a native-endian signed integer. + len -- Length of the bitstring in bits. + oct -- The bitstring as an octal string. + pos -- The current bit position in the bitstring. + se -- Interpret as a signed exponential-Golomb code. + ue -- Interpret as an unsigned exponential-Golomb code. + sie -- Interpret as a signed interleaved exponential-Golomb code. + uie -- Interpret as an unsigned interleaved exponential-Golomb code. + uint -- Interpret as a two's complement unsigned integer. + uintbe -- Interpret as a big-endian unsigned integer. + uintle -- Interpret as a little-endian unsigned integer. + uintne -- Interpret as a native-endian unsigned integer. + + """ + + __slots__ = () + + # As BitArray objects are mutable, we shouldn't allow them to be hashed. + __hash__ = None + + def __init__(self, auto=None, length=None, offset=None, **kwargs): + """Either specify an 'auto' initialiser: + auto -- a string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bytes -- raw data as a string, for example read from a binary file. + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + uint -- an unsigned integer. + int -- a signed integer. + float -- a floating point number. + uintbe -- an unsigned big-endian whole byte integer. + intbe -- a signed big-endian whole byte integer. + floatbe - a big-endian floating point number. + uintle -- an unsigned little-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + floatle -- a little-endian floating point number. + uintne -- an unsigned native-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + floatne -- a native-endian floating point number. + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + bool -- a boolean (True or False). + filename -- a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is intended for use when + initialising using 'bytes' or 'filename'. + + """ + # For mutable BitArrays we always read in files to memory: + if not isinstance(self._datastore, ByteStore): + self._ensureinmemory() + + def __new__(cls, auto=None, length=None, offset=None, **kwargs): + x = super(BitArray, cls).__new__(cls) + y = Bits.__new__(BitArray, auto, length, offset, **kwargs) + x._datastore = ByteStore(y._datastore._rawarray[:], + y._datastore.bitlength, + y._datastore.offset) + return x + + def __iadd__(self, bs): + """Append bs to current bitstring. Return self. + + bs -- the bitstring to append. + + """ + self._append(bs) + return self + + def __copy__(self): + """Return a new copy of the BitArray.""" + s_copy = BitArray() + if not isinstance(self._datastore, ByteStore): + # Let them both point to the same (invariant) array. + # If either gets modified then at that point they'll be read into memory. + s_copy._datastore = self._datastore + else: + s_copy._datastore = copy.copy(self._datastore) + return s_copy + + def __setitem__(self, key, value): + try: + # A slice + start, step = 0, 1 + if key.step is not None: + step = key.step + except AttributeError: + # single element + if key < 0: + key += self.len + if not 0 <= key < self.len: + raise IndexError("Slice index out of range.") + if isinstance(value, numbers.Integral): + if not value: + self._unset(key) + return + if value in (1, -1): + self._set(key) + return + raise ValueError("Cannot set a single bit with integer {0}.".format(value)) + value = Bits(value) + if value.len == 1: + if value[0]: + self._set(key) + else: + self._unset(key) + else: + self._delete(1, key) + self._insert(value, key) + return + else: + if step != 1: + # convert to binary string and use string slicing + # TODO: Horribly inefficient + temp = list(self._getbin()) + v = list(Bits(value)._getbin()) + temp.__setitem__(key, v) + self._setbin_unsafe(''.join(temp)) + return + + # If value is an integer then we want to set the slice to that + # value rather than initialise a new bitstring of that length. + if not isinstance(value, numbers.Integral): + try: + value = Bits(value) + except TypeError: + raise TypeError("Bitstring, integer or string expected. " + "Got {0}.".format(type(value))) + if key.start is not None: + start = key.start + if key.start < 0: + start += self.len + if start < 0: + start = 0 + stop = self.len + if key.stop is not None: + stop = key.stop + if key.stop < 0: + stop += self.len + if start > stop: + # The standard behaviour for lists is to just insert at the + # start position if stop < start and step == 1. + stop = start + if isinstance(value, numbers.Integral): + if value >= 0: + value = self.__class__(uint=value, length=stop - start) + else: + value = self.__class__(int=value, length=stop - start) + stop = min(stop, self.len) + start = max(start, 0) + start = min(start, stop) + if (stop - start) == value.len: + if not value.len: + return + if step >= 0: + self._overwrite(value, start) + else: + self._overwrite(value.__getitem__(slice(None, None, 1)), start) + else: + # TODO: A delete then insert is wasteful - it could do unneeded shifts. + # Could be either overwrite + insert or overwrite + delete. + self._delete(stop - start, start) + if step >= 0: + self._insert(value, start) + else: + self._insert(value.__getitem__(slice(None, None, 1)), start) + # pos is now after the inserted piece. + return + + def __delitem__(self, key): + """Delete item or range. + + Indices are in units of the step parameter (default 1 bit). + Stepping is used to specify the number of bits in each item. + + >>> a = BitArray('0x001122') + >>> del a[1:2:8] + >>> print a + 0x0022 + + """ + try: + # A slice + start = 0 + step = key.step if key.step is not None else 1 + except AttributeError: + # single element + if key < 0: + key += self.len + if not 0 <= key < self.len: + raise IndexError("Slice index out of range.") + self._delete(1, key) + return + else: + if step != 1: + # convert to binary string and use string slicing + # TODO: Horribly inefficient + temp = list(self._getbin()) + temp.__delitem__(key) + self._setbin_unsafe(''.join(temp)) + return + if key.start is not None: + start = key.start + if key.start < 0: + start += self.len + if start < 0: + start = 0 + stop = self.len + if key.stop is not None: + stop = key.stop + if key.stop < 0: + stop += self.len + if start > stop: + return + stop = min(stop, self.len) + start = max(start, 0) + start = min(start, stop) + self._delete(stop - start, start) + return + + def __ilshift__(self, n): + """Shift bits by n to the left in place. Return self. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if not self.len: + raise ValueError("Cannot shift an empty bitstring.") + if not n: + return self + n = min(n, self.len) + return self._ilshift(n) + + def __irshift__(self, n): + """Shift bits by n to the right in place. Return self. + + n -- the number of bits to shift. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot shift by a negative amount.") + if not self.len: + raise ValueError("Cannot shift an empty bitstring.") + if not n: + return self + n = min(n, self.len) + return self._irshift(n) + + def __imul__(self, n): + """Concatenate n copies of self in place. Return self. + + Called for expressions of the form 'a *= 3'. + n -- The number of concatenations. Must be >= 0. + + """ + if n < 0: + raise ValueError("Cannot multiply by a negative integer.") + return self._imul(n) + + def __ior__(self, bs): + bs = Bits(bs) + if self.len != bs.len: + raise ValueError("Bitstrings must have the same length " + "for |= operator.") + return self._ior(bs) + + def __iand__(self, bs): + bs = Bits(bs) + if self.len != bs.len: + raise ValueError("Bitstrings must have the same length " + "for &= operator.") + return self._iand(bs) + + def __ixor__(self, bs): + bs = Bits(bs) + if self.len != bs.len: + raise ValueError("Bitstrings must have the same length " + "for ^= operator.") + return self._ixor(bs) + + def replace(self, old, new, start=None, end=None, count=None, + bytealigned=None): + """Replace all occurrences of old with new in place. + + Returns number of replacements made. + + old -- The bitstring to replace. + new -- The replacement bitstring. + start -- Any occurrences that start before this will not be replaced. + Defaults to 0. + end -- Any occurrences that finish after this will not be replaced. + Defaults to self.len. + count -- The maximum number of replacements to make. Defaults to + replace all occurrences. + bytealigned -- If True replacements will only be made on byte + boundaries. + + Raises ValueError if old is empty or if start or end are + out of range. + + """ + old = Bits(old) + new = Bits(new) + if not old.len: + raise ValueError("Empty bitstring cannot be replaced.") + start, end = self._validate_slice(start, end) + if bytealigned is None: + bytealigned = globals()['bytealigned'] + # Adjust count for use in split() + if count is not None: + count += 1 + sections = self.split(old, start, end, count, bytealigned) + lengths = [s.len for s in sections] + if len(lengths) == 1: + # Didn't find anything to replace. + return 0 # no replacements done + if new is self: + # Prevent self assignment woes + new = copy.copy(self) + positions = [lengths[0] + start] + for l in lengths[1:-1]: + # Next position is the previous one plus the length of the next section. + positions.append(positions[-1] + l) + # We have all the positions that need replacements. We do them + # in reverse order so that they won't move around as we replace. + positions.reverse() + try: + # Need to calculate new pos, if this is a bitstream + newpos = self._pos + for p in positions: + self[p:p + old.len] = new + if old.len != new.len: + diff = new.len - old.len + for p in positions: + if p >= newpos: + continue + if p + old.len <= newpos: + newpos += diff + else: + newpos = p + self._pos = newpos + except AttributeError: + for p in positions: + self[p:p + old.len] = new + assert self._assertsanity() + return len(lengths) - 1 + + def insert(self, bs, pos=None): + """Insert bs at bit position pos. + + bs -- The bitstring to insert. + pos -- The bit position to insert at. + + Raises ValueError if pos < 0 or pos > self.len. + + """ + bs = Bits(bs) + if not bs.len: + return self + if bs is self: + bs = self.__copy__() + if pos is None: + try: + pos = self._pos + except AttributeError: + raise TypeError("insert needs a bit position specified when used on a BitArray.") + if pos < 0: + pos += self.len + if not 0 <= pos <= self.len: + raise ValueError("Invalid insert position.") + self._insert(bs, pos) + + def overwrite(self, bs, pos=None): + """Overwrite with bs at bit position pos. + + bs -- The bitstring to overwrite with. + pos -- The bit position to begin overwriting from. + + Raises ValueError if pos < 0 or pos + bs.len > self.len + + """ + bs = Bits(bs) + if not bs.len: + return + if pos is None: + try: + pos = self._pos + except AttributeError: + raise TypeError("overwrite needs a bit position specified when used on a BitArray.") + if pos < 0: + pos += self.len + if pos < 0 or pos + bs.len > self.len: + raise ValueError("Overwrite exceeds boundary of bitstring.") + self._overwrite(bs, pos) + try: + self._pos = pos + bs.len + except AttributeError: + pass + + def append(self, bs): + """Append a bitstring to the current bitstring. + + bs -- The bitstring to append. + + """ + self._append(bs) + + def prepend(self, bs): + """Prepend a bitstring to the current bitstring. + + bs -- The bitstring to prepend. + + """ + self._prepend(bs) + + def _append_msb0(self, bs): + # The offset is a hint to make bs easily appendable. + bs = self._converttobitstring(bs, offset=(self.len + self._offset) % 8) + self._addright(bs) + + def _append_lsb0(self, bs): + bs = Bits(bs) + self._addleft(bs) + + def reverse(self, start=None, end=None): + """Reverse bits in-place. + + start -- Position of first bit to reverse. Defaults to 0. + end -- One past the position of the last bit to reverse. + Defaults to self.len. + + Using on an empty bitstring will have no effect. + + Raises ValueError if start < 0, end > self.len or end < start. + + """ + start, end = self._validate_slice(start, end) + if start == 0 and end == self.len: + self._reverse() + return + s = self._slice(start, end) + s._reverse() + self[start:end] = s + + def set(self, value, pos=None): + """Set one or many bits to 1 or 0. + + value -- If True bits are set to 1, otherwise they are set to 0. + pos -- Either a single bit position or an iterable of bit positions. + Negative numbers are treated in the same way as slice indices. + Defaults to the entire bitstring. + + Raises IndexError if pos < -self.len or pos >= self.len. + + """ + f = self._set if value else self._unset + if pos is None: + pos = xrange(self.len) + try: + length = self.len + for p in pos: + if p < 0: + p += length + if not 0 <= p < length: + raise IndexError("Bit position {0} out of range.".format(p)) + f(p) + except TypeError: + # Single pos + if pos < 0: + pos += self.len + if not 0 <= pos < length: + raise IndexError("Bit position {0} out of range.".format(pos)) + f(pos) + + def invert(self, pos=None): + """Invert one or many bits from 0 to 1 or vice versa. + + pos -- Either a single bit position or an iterable of bit positions. + Negative numbers are treated in the same way as slice indices. + + Raises IndexError if pos < -self.len or pos >= self.len. + + """ + if pos is None: + self._invert_all() + return + if not isinstance(pos, collectionsAbc.Iterable): + pos = (pos,) + length = self.len + + for p in pos: + if p < 0: + p += length + if not 0 <= p < length: + raise IndexError("Bit position {0} out of range.".format(p)) + self._invert(p) + + def ror(self, bits, start=None, end=None): + """Rotate bits to the right in-place. + + bits -- The number of bits to rotate by. + start -- Start of slice to rotate. Defaults to 0. + end -- End of slice to rotate. Defaults to self.len. + + Raises ValueError if bits < 0. + + """ + if not self.len: + raise Error("Cannot rotate an empty bitstring.") + if bits < 0: + raise ValueError("Cannot rotate by negative amount.") + self._ror(bits, start, end) + + def _ror_msb0(self, bits, start=None, end=None): + start, end = self._validate_slice_msb0(start, end) # the _slice deals with msb0/lsb0 + bits %= (end - start) + if not bits: + return + rhs = self._slice(end - bits, end) + self._delete(bits, end - bits) + self._insert(rhs, start) + + def rol(self, bits, start=None, end=None): + """Rotate bits to the left in-place. + + bits -- The number of bits to rotate by. + start -- Start of slice to rotate. Defaults to 0. + end -- End of slice to rotate. Defaults to self.len. + + Raises ValueError if bits < 0. + + """ + if not self.len: + raise Error("Cannot rotate an empty bitstring.") + if bits < 0: + raise ValueError("Cannot rotate by negative amount.") + self._rol(bits, start, end) + + def _rol_msb0(self, bits, start=None, end=None): + start, end = self._validate_slice_msb0(start, end) + bits %= (end - start) + if not bits: + return + lhs = self._slice(start, start + bits) + self._delete(bits, start) + self._insert(lhs, end - bits) + + def byteswap(self, fmt=None, start=None, end=None, repeat=True): + """Change the endianness in-place. Return number of repeats of fmt done. + + fmt -- A compact structure string, an integer number of bytes or + an iterable of integers. Defaults to 0, which byte reverses the + whole bitstring. + start -- Start bit position, defaults to 0. + end -- End bit position, defaults to self.len. + repeat -- If True (the default) the byte swapping pattern is repeated + as much as possible. + + """ + start, end = self._validate_slice(start, end) + if fmt is None or fmt == 0: + # reverse all of the whole bytes. + bytesizes = [(end - start) // 8] + elif isinstance(fmt, numbers.Integral): + if fmt < 0: + raise ValueError("Improper byte length {0}.".format(fmt)) + bytesizes = [fmt] + elif isinstance(fmt, basestring): + m = STRUCT_PACK_RE.match(fmt) + if not m: + raise ValueError("Cannot parse format string {0}.".format(fmt)) + # Split the format string into a list of 'q', '4h' etc. + formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt')) + # Now deal with multiplicative factors, 4h -> hhhh etc. + bytesizes = [] + for f in formatlist: + if len(f) == 1: + bytesizes.append(PACK_CODE_SIZE[f]) + else: + bytesizes.extend([PACK_CODE_SIZE[f[-1]]] * int(f[:-1])) + elif isinstance(fmt, collectionsAbc.Iterable): + bytesizes = fmt + for bytesize in bytesizes: + if not isinstance(bytesize, numbers.Integral) or bytesize < 0: + raise ValueError("Improper byte length {0}.".format(bytesize)) + else: + raise TypeError("Format must be an integer, string or iterable.") + + repeats = 0 + totalbitsize = 8 * sum(bytesizes) + if not totalbitsize: + return 0 + if repeat: + # Try to repeat up to the end of the bitstring. + finalbit = end + else: + # Just try one (set of) byteswap(s). + finalbit = start + totalbitsize + for patternend in xrange(start + totalbitsize, finalbit + 1, totalbitsize): + bytestart = patternend - totalbitsize + for bytesize in bytesizes: + byteend = bytestart + bytesize * 8 + self._reversebytes(bytestart, byteend) + bytestart += bytesize * 8 + repeats += 1 + return repeats + + def clear(self): + """Remove all bits, reset to zero length.""" + self._clear() + + def copy(self): + """Return a copy of the bitstring.""" + return self._copy() + + int = property(Bits._getint, Bits._setint, + doc="""The bitstring as a two's complement signed int. Read and write. + """) + uint = property(Bits._getuint, Bits._setuint, + doc="""The bitstring as a two's complement unsigned int. Read and write. + """) + float = property(Bits._getfloat, Bits._setfloat, + doc="""The bitstring as a floating point number. Read and write. + """) + intbe = property(Bits._getintbe, Bits._setintbe, + doc="""The bitstring as a two's complement big-endian signed int. Read and write. + """) + uintbe = property(Bits._getuintbe, Bits._setuintbe, + doc="""The bitstring as a two's complement big-endian unsigned int. Read and write. + """) + floatbe = property(Bits._getfloat, Bits._setfloat, + doc="""The bitstring as a big-endian floating point number. Read and write. + """) + intle = property(Bits._getintle, Bits._setintle, + doc="""The bitstring as a two's complement little-endian signed int. Read and write. + """) + uintle = property(Bits._getuintle, Bits._setuintle, + doc="""The bitstring as a two's complement little-endian unsigned int. Read and write. + """) + floatle = property(Bits._getfloatle, Bits._setfloatle, + doc="""The bitstring as a little-endian floating point number. Read and write. + """) + intne = property(Bits._getintne, Bits._setintne, + doc="""The bitstring as a two's complement native-endian signed int. Read and write. + """) + uintne = property(Bits._getuintne, Bits._setuintne, + doc="""The bitstring as a two's complement native-endian unsigned int. Read and write. + """) + floatne = property(Bits._getfloatne, Bits._setfloatne, + doc="""The bitstring as a native-endian floating point number. Read and write. + """) + ue = property(Bits._getue, Bits._setue, + doc="""The bitstring as an unsigned exponential-Golomb code. Read and write. + """) + se = property(Bits._getse, Bits._setse, + doc="""The bitstring as a signed exponential-Golomb code. Read and write. + """) + uie = property(Bits._getuie, Bits._setuie, + doc="""The bitstring as an unsigned interleaved exponential-Golomb code. Read and write. + """) + sie = property(Bits._getsie, Bits._setsie, + doc="""The bitstring as a signed interleaved exponential-Golomb code. Read and write. + """) + hex = property(Bits._gethex, Bits._sethex, + doc="""The bitstring as a hexadecimal string. Read and write. + """) + bin = property(Bits._getbin, Bits._setbin_safe, + doc="""The bitstring as a binary string. Read and write. + """) + oct = property(Bits._getoct, Bits._setoct, + doc="""The bitstring as an octal string. Read and write. + """) + bool = property(Bits._getbool, Bits._setbool, + doc="""The bitstring as a bool (True or False). Read and write. + """) + bytes = property(Bits._getbytes, Bits._setbytes_safe, + doc="""The bitstring as a ordinary string. Read and write. + """) + + + +class ConstBitStream(Bits): + """A container or stream holding an immutable sequence of bits. + + For a mutable container use the BitStream class instead. + + Methods inherited from Bits: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + join() -- Join bitstrings together using current bitstring. + rfind() -- Seek backwards to find a sub-bitstring. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Other methods: + + bytealign() -- Align to next byte boundary. + peek() -- Peek at and interpret next bits as a single item. + peeklist() -- Peek at and interpret next bits as a list of items. + read() -- Read and interpret next bits as a single item. + readlist() -- Read and interpret next bits as a list of items. + + Special methods: + + Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. + + Properties: + + bin -- The bitstring as a binary string. + bool -- For single bit bitstrings, interpret as True or False. + bytepos -- The current byte position in the bitstring. + bytes -- The bitstring as a bytes object. + float -- Interpret as a floating point number. + floatbe -- Interpret as a big-endian floating point number. + floatle -- Interpret as a little-endian floating point number. + floatne -- Interpret as a native-endian floating point number. + hex -- The bitstring as a hexadecimal string. + int -- Interpret as a two's complement signed integer. + intbe -- Interpret as a big-endian signed integer. + intle -- Interpret as a little-endian signed integer. + intne -- Interpret as a native-endian signed integer. + len -- Length of the bitstring in bits. + oct -- The bitstring as an octal string. + pos -- The current bit position in the bitstring. + se -- Interpret as a signed exponential-Golomb code. + ue -- Interpret as an unsigned exponential-Golomb code. + sie -- Interpret as a signed interleaved exponential-Golomb code. + uie -- Interpret as an unsigned interleaved exponential-Golomb code. + uint -- Interpret as a two's complement unsigned integer. + uintbe -- Interpret as a big-endian unsigned integer. + uintle -- Interpret as a little-endian unsigned integer. + uintne -- Interpret as a native-endian unsigned integer. + + """ + + __slots__ = ('_pos',) + + def __init__(self, auto=None, length=None, offset=None, pos=0, **kwargs): + """Either specify an 'auto' initialiser: + auto -- a string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bytes -- raw data as a string, for example read from a binary file. + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + uint -- an unsigned integer. + int -- a signed integer. + float -- a floating point number. + uintbe -- an unsigned big-endian whole byte integer. + intbe -- a signed big-endian whole byte integer. + floatbe - a big-endian floating point number. + uintle -- an unsigned little-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + floatle -- a little-endian floating point number. + uintne -- an unsigned native-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + floatne -- a native-endian floating point number. + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + bool -- a boolean (True or False). + filename -- a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is intended for use when + initialising using 'bytes' or 'filename'. + pos -- Initial bit position, defaults to 0. + + """ + pass + + def __new__(cls, auto=None, length=None, offset=None, pos=0, **kwargs): + x = super(ConstBitStream, cls).__new__(cls) + x._initialise(auto, length, offset, **kwargs) + x._pos = x._datastore.bitlength + pos if pos < 0 else pos + if x._pos < 0 or x._pos > x._datastore.bitlength: + raise CreationError("Cannot set pos to {0} when length is {1}".format(pos, x._datastore.bitlength)) + return x + + def _setbytepos(self, bytepos): + """Move to absolute byte-aligned position in stream.""" + self._setbitpos(bytepos * 8) + + def _getbytepos(self): + """Return the current position in the stream in bytes. Must be byte aligned.""" + if self._pos % 8: + raise ByteAlignError("Not byte aligned in _getbytepos().") + return self._pos // 8 + + def _setbitpos(self, pos): + """Move to absolute position bit in bitstream.""" + if pos < 0: + raise ValueError("Bit position cannot be negative.") + if pos > self.len: + raise ValueError("Cannot seek past the end of the data.") + self._pos = pos + + def _getbitpos(self): + """Return the current position in the stream in bits.""" + return self._pos + + def _clear(self): + Bits._clear(self) + self._pos = 0 + + def __copy__(self): + """Return a new copy of the ConstBitStream for the copy module.""" + # Note that if you want a new copy (different ID), use _copy instead. + # The copy can use the same datastore as it's immutable. + s = ConstBitStream() + s._datastore = self._datastore + # Reset the bit position, don't copy it. + s._pos = 0 + return s + + def __add__(self, bs): + """Concatenate bitstrings and return new bitstring. + + bs -- the bitstring to append. + + """ + s = Bits.__add__(self, bs) + s._pos = 0 + return s + + def read(self, fmt): + """Interpret next bits according to the format string and return result. + + fmt -- Token string describing how to interpret the next bits. + + Token examples: 'int:12' : 12 bits as a signed integer + 'uint:8' : 8 bits as an unsigned integer + 'float:64' : 8 bytes as a big-endian float + 'intbe:16' : 2 bytes as a big-endian signed integer + 'uintbe:16' : 2 bytes as a big-endian unsigned integer + 'intle:32' : 4 bytes as a little-endian signed integer + 'uintle:32' : 4 bytes as a little-endian unsigned integer + 'floatle:64': 8 bytes as a little-endian float + 'intne:24' : 3 bytes as a native-endian signed integer + 'uintne:24' : 3 bytes as a native-endian unsigned integer + 'floatne:32': 4 bytes as a native-endian float + 'hex:80' : 80 bits as a hex string + 'oct:9' : 9 bits as an octal string + 'bin:1' : single bit binary string + 'ue' : next bits as unsigned exp-Golomb code + 'se' : next bits as signed exp-Golomb code + 'uie' : next bits as unsigned interleaved exp-Golomb code + 'sie' : next bits as signed interleaved exp-Golomb code + 'bits:5' : 5 bits as a bitstring + 'bytes:10' : 10 bytes as a bytes object + 'bool' : 1 bit as a bool + 'pad:3' : 3 bits of padding to ignore - returns None + + fmt may also be an integer, which will be treated like the 'bits' token. + + The position in the bitstring is advanced to after the read items. + + Raises ReadError if not enough bits are available. + Raises ValueError if the format is not understood. + + """ + if isinstance(fmt, numbers.Integral): + if fmt < 0: + raise ValueError("Cannot read negative amount.") + if fmt > self.len - self._pos: + raise ReadError("Cannot read {0} bits, only {1} available.", + fmt, self.len - self._pos) + bs = self._slice(self._pos, self._pos + fmt) + self._pos += fmt + return bs + p = self._pos + _, token = tokenparser(fmt) + if len(token) != 1: + self._pos = p + raise ValueError("Format string should be a single token, not {0} " + "tokens - use readlist() instead.".format(len(token))) + name, length, _ = token[0] + if length is None: + length = self.len - self._pos + value, self._pos = self._readtoken(name, self._pos, length) + return value + + def readlist(self, fmt, **kwargs): + """Interpret next bits according to format string(s) and return list. + + fmt -- A single string or list of strings with comma separated tokens + describing how to interpret the next bits in the bitstring. Items + can also be integers, for reading new bitstring of the given length. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + The position in the bitstring is advanced to after the read items. + + Raises ReadError is not enough bits are available. + Raises ValueError if the format is not understood. + + See the docstring for 'read' for token examples. 'pad' tokens are skipped + and not added to the returned list. + + >>> h, b1, b2 = s.readlist('hex:20, bin:5, bin:3') + >>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10]) + + """ + value, self._pos = self._readlist(fmt, self._pos, **kwargs) + return value + + def readto(self, bs, bytealigned=None): + """Read up to and including next occurrence of bs and return result. + + bs -- The bitstring to find. An integer is not permitted. + bytealigned -- If True the bitstring will only be + found on byte boundaries. + + Raises ValueError if bs is empty. + Raises ReadError if bs is not found. + + """ + if isinstance(bs, numbers.Integral): + raise ValueError("Integers cannot be searched for") + bs = Bits(bs) + oldpos = self._pos + p = self.find(bs, self._pos, bytealigned=bytealigned) + if not p: + raise ReadError("Substring not found") + self._pos += bs.len + return self._slice(oldpos, self._pos) + + def peek(self, fmt): + """Interpret next bits according to format string and return result. + + fmt -- Token string describing how to interpret the next bits. + + The position in the bitstring is not changed. If not enough bits are + available then all bits to the end of the bitstring will be used. + + Raises ReadError if not enough bits are available. + Raises ValueError if the format is not understood. + + See the docstring for 'read' for token examples. + + """ + pos_before = self._pos + value = self.read(fmt) + self._pos = pos_before + return value + + def peeklist(self, fmt, **kwargs): + """Interpret next bits according to format string(s) and return list. + + fmt -- One or more strings with comma separated tokens describing + how to interpret the next bits in the bitstring. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + The position in the bitstring is not changed. If not enough bits are + available then all bits to the end of the bitstring will be used. + + Raises ReadError if not enough bits are available. + Raises ValueError if the format is not understood. + + See the docstring for 'read' for token examples. + + """ + pos = self._pos + return_values = self.readlist(fmt, **kwargs) + self._pos = pos + return return_values + + def bytealign(self): + """Align to next byte and return number of skipped bits. + + Raises ValueError if the end of the bitstring is reached before + aligning to the next byte. + + """ + skipped = (8 - (self._pos % 8)) % 8 + self.pos += self._offset + skipped + assert self._assertsanity() + return skipped + + pos = property(_getbitpos, _setbitpos, + doc="""The position in the bitstring in bits. Read and write. + """) + bitpos = property(_getbitpos, _setbitpos, + doc="""The position in the bitstring in bits. Read and write. + """) + bytepos = property(_getbytepos, _setbytepos, + doc="""The position in the bitstring in bytes. Read and write. + """) + + +class BitStream(ConstBitStream, BitArray): + """A container or stream holding a mutable sequence of bits + + Subclass of the ConstBitStream and BitArray classes. Inherits all of + their methods. + + Methods: + + all() -- Check if all specified bits are set to 1 or 0. + any() -- Check if any of specified bits are set to 1 or 0. + append() -- Append a bitstring. + bytealign() -- Align to next byte boundary. + byteswap() -- Change byte endianness in-place. + count() -- Count the number of bits set to 1 or 0. + cut() -- Create generator of constant sized chunks. + endswith() -- Return whether the bitstring ends with a sub-string. + find() -- Find a sub-bitstring in the current bitstring. + findall() -- Find all occurrences of a sub-bitstring in the current bitstring. + insert() -- Insert a bitstring. + invert() -- Flip bit(s) between one and zero. + join() -- Join bitstrings together using current bitstring. + overwrite() -- Overwrite a section with a new bitstring. + peek() -- Peek at and interpret next bits as a single item. + peeklist() -- Peek at and interpret next bits as a list of items. + prepend() -- Prepend a bitstring. + read() -- Read and interpret next bits as a single item. + readlist() -- Read and interpret next bits as a list of items. + replace() -- Replace occurrences of one bitstring with another. + reverse() -- Reverse bits in-place. + rfind() -- Seek backwards to find a sub-bitstring. + rol() -- Rotate bits to the left. + ror() -- Rotate bits to the right. + set() -- Set bit(s) to 1 or 0. + split() -- Create generator of chunks split by a delimiter. + startswith() -- Return whether the bitstring starts with a sub-bitstring. + tobytes() -- Return bitstring as bytes, padding if needed. + tofile() -- Write bitstring to file, padding if needed. + unpack() -- Interpret bits using format string. + + Special methods: + + Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= + in addition to [], ==, !=, +, *, ~, <<, >>, &, | and ^. + + Properties: + + bin -- The bitstring as a binary string. + bool -- For single bit bitstrings, interpret as True or False. + bytepos -- The current byte position in the bitstring. + bytes -- The bitstring as a bytes object. + float -- Interpret as a floating point number. + floatbe -- Interpret as a big-endian floating point number. + floatle -- Interpret as a little-endian floating point number. + floatne -- Interpret as a native-endian floating point number. + hex -- The bitstring as a hexadecimal string. + int -- Interpret as a two's complement signed integer. + intbe -- Interpret as a big-endian signed integer. + intle -- Interpret as a little-endian signed integer. + intne -- Interpret as a native-endian signed integer. + len -- Length of the bitstring in bits. + oct -- The bitstring as an octal string. + pos -- The current bit position in the bitstring. + se -- Interpret as a signed exponential-Golomb code. + ue -- Interpret as an unsigned exponential-Golomb code. + sie -- Interpret as a signed interleaved exponential-Golomb code. + uie -- Interpret as an unsigned interleaved exponential-Golomb code. + uint -- Interpret as a two's complement unsigned integer. + uintbe -- Interpret as a big-endian unsigned integer. + uintle -- Interpret as a little-endian unsigned integer. + uintne -- Interpret as a native-endian unsigned integer. + + """ + + __slots__ = () + + # As BitStream objects are mutable, we shouldn't allow them to be hashed. + __hash__ = None + + def __init__(self, auto=None, length=None, offset=None, pos=0, **kwargs): + """Either specify an 'auto' initialiser: + auto -- a string of comma separated tokens, an integer, a file object, + a bytearray, a boolean iterable or another bitstring. + + Or initialise via **kwargs with one (and only one) of: + bytes -- raw data as a string, for example read from a binary file. + bin -- binary string representation, e.g. '0b001010'. + hex -- hexadecimal string representation, e.g. '0x2ef' + oct -- octal string representation, e.g. '0o777'. + uint -- an unsigned integer. + int -- a signed integer. + float -- a floating point number. + uintbe -- an unsigned big-endian whole byte integer. + intbe -- a signed big-endian whole byte integer. + floatbe - a big-endian floating point number. + uintle -- an unsigned little-endian whole byte integer. + intle -- a signed little-endian whole byte integer. + floatle -- a little-endian floating point number. + uintne -- an unsigned native-endian whole byte integer. + intne -- a signed native-endian whole byte integer. + floatne -- a native-endian floating point number. + se -- a signed exponential-Golomb code. + ue -- an unsigned exponential-Golomb code. + sie -- a signed interleaved exponential-Golomb code. + uie -- an unsigned interleaved exponential-Golomb code. + bool -- a boolean (True or False). + filename -- a file which will be opened in binary read-only mode. + + Other keyword arguments: + length -- length of the bitstring in bits, if needed and appropriate. + It must be supplied for all integer and float initialisers. + offset -- bit offset to the data. These offset bits are + ignored and this is intended for use when + initialising using 'bytes' or 'filename'. + pos -- Initial bit position, defaults to 0. + + """ + # For mutable BitStreams we always read in files to memory: + if not isinstance(self._datastore, (ByteStore, ConstByteStore)): + self._ensureinmemory() + + def __new__(cls, auto=None, length=None, offset=None, pos=0, **kwargs): + x = super(BitStream, cls).__new__(cls) + y = ConstBitStream.__new__(BitStream, auto, length, offset, pos, **kwargs) + x._datastore = ByteStore(y._datastore._rawarray[:], + y._datastore.bitlength, + y._datastore.offset) + x._pos = y._pos + return x + + def __copy__(self): + """Return a new copy of the BitStream.""" + s_copy = BitStream() + s_copy._pos = 0 + if not isinstance(self._datastore, ByteStore): + # Let them both point to the same (invariant) array. + # If either gets modified then at that point they'll be read into memory. + s_copy._datastore = self._datastore + else: + s_copy._datastore = ByteStore(self._datastore._rawarray[:], + self._datastore.bitlength, + self._datastore.offset) + return s_copy + + def prepend(self, bs): + """Prepend a bitstring to the current bitstring. + + bs -- The bitstring to prepend. + + """ + bs = self._converttobitstring(bs) + self._addleft(bs) + self._pos += bs.len + + +def pack(fmt, *values, **kwargs): + """Pack the values according to the format string and return a new BitStream. + + fmt -- A single string or a list of strings with comma separated tokens + describing how to create the BitStream. + values -- Zero or more values to pack according to the format. + kwargs -- A dictionary or keyword-value pairs - the keywords used in the + format string will be replaced with their given value. + + Token examples: 'int:12' : 12 bits as a signed integer + 'uint:8' : 8 bits as an unsigned integer + 'float:64' : 8 bytes as a big-endian float + 'intbe:16' : 2 bytes as a big-endian signed integer + 'uintbe:16' : 2 bytes as a big-endian unsigned integer + 'intle:32' : 4 bytes as a little-endian signed integer + 'uintle:32' : 4 bytes as a little-endian unsigned integer + 'floatle:64': 8 bytes as a little-endian float + 'intne:24' : 3 bytes as a native-endian signed integer + 'uintne:24' : 3 bytes as a native-endian unsigned integer + 'floatne:32': 4 bytes as a native-endian float + 'hex:80' : 80 bits as a hex string + 'oct:9' : 9 bits as an octal string + 'bin:1' : single bit binary string + 'ue' / 'uie': next bits as unsigned exp-Golomb code + 'se' / 'sie': next bits as signed exp-Golomb code + 'bits:5' : 5 bits as a bitstring object + 'bytes:10' : 10 bytes as a bytes object + 'bool' : 1 bit as a bool + 'pad:3' : 3 zero bits as padding + + >>> s = pack('uint:12, bits', 100, '0xffe') + >>> t = pack(['bits', 'bin:3'], s, '111') + >>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44) + + """ + tokens = [] + if isinstance(fmt, basestring): + fmt = [fmt] + try: + for f_item in fmt: + _, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys()))) + tokens.extend(tkns) + except ValueError as e: + raise CreationError(*e.args) + value_iter = iter(values) + s = BitStream() + try: + for name, length, value in tokens: + # If the value is in the kwd dictionary then it takes precedence. + if value in kwargs: + value = kwargs[value] + # If the length is in the kwd dictionary then use that too. + if length in kwargs: + length = kwargs[length] + # Also if we just have a dictionary name then we want to use it + if name in kwargs and length is None and value is None: + s._append(kwargs[name]) + continue + if length is not None: + length = int(length) + if value is None and name != 'pad': + # Take the next value from the ones provided + value = next(value_iter) + s._addright(BitStream._init_with_token(name, length, value)) + except StopIteration: + raise CreationError("Not enough parameters present to pack according to the " + "format. {0} values are needed.", len(tokens)) + try: + next(value_iter) + except StopIteration: + # Good, we've used up all the *values. + return s + raise CreationError("Too many parameters present to pack according to the format.") + + +# Whether to label the Least Significant Bit as bit 0. Default is False. Experimental feature. +_lsb0 = False + +# Dictionary that maps token names to the function that reads them. Is set in next function. +name_to_read = {} + + +def _switch_lsb0_methods(lsb0): + global _lsb0 + _lsb0 = lsb0 + if lsb0: + ConstByteStore.getbit = ConstByteStore._getbit_lsb0 + Bits._find = Bits._find_lsb0 + Bits._slice = Bits._slice_lsb0 + BitArray._overwrite = BitArray._overwrite_lsb0 + BitArray._insert = BitArray._insert_lsb0 + BitArray._delete = BitArray._delete_lsb0 + BitArray._ror = BitArray._rol_msb0 + BitArray._rol = BitArray._ror_msb0 + ByteStore.setbit = ByteStore._setbit_lsb0 + ByteStore.unsetbit = ByteStore._unsetbit_lsb0 + ByteStore.invertbit = ByteStore._invertbit_lsb0 + BitArray._append = BitArray._append_lsb0 + BitArray._prepend = BitArray._append_msb0 # An LSB0 prepend is an MSB0 append + Bits._readuint = Bits._readuint_lsb0 + Bits._truncatestart = Bits._truncateright + Bits._truncateend = Bits._truncateleft + Bits._validate_slice = Bits._validate_slice_lsb0 + else: + ConstByteStore.getbit = ConstByteStore._getbit_msb0 + Bits._find = Bits._find_msb0 + Bits._slice = Bits._slice_msb0 + BitArray._overwrite = BitArray._overwrite_msb0 + BitArray._insert = BitArray._insert_msb0 + BitArray._delete = BitArray._delete_msb0 + BitArray._ror = BitArray._ror_msb0 + BitArray._rol = BitArray._rol_msb0 + ByteStore.setbit = ByteStore._setbit_msb0 + ByteStore.unsetbit = ByteStore._unsetbit_msb0 + ByteStore.invertbit = ByteStore._invertbit_msb0 + BitArray._append = BitArray._append_msb0 + BitArray._prepend = BitArray._append_lsb0 + Bits._readuint = Bits._readuint_msb0 + Bits._truncatestart = Bits._truncateleft + Bits._truncateend = Bits._truncateright + Bits._validate_slice = Bits._validate_slice_msb0 + + global name_to_read + name_to_read = {'uint': Bits._readuint, + 'uintle': Bits._readuintle, + 'uintbe': Bits._readuintbe, + 'uintne': Bits._readuintne, + 'int': Bits._readint, + 'intle': Bits._readintle, + 'intbe': Bits._readintbe, + 'intne': Bits._readintne, + 'float': Bits._readfloat, + 'floatbe': Bits._readfloat, # floatbe is a synonym for float + 'floatle': Bits._readfloatle, + 'floatne': Bits._readfloatne, + 'hex': Bits._readhex, + 'oct': Bits._readoct, + 'bin': Bits._readbin, + 'bits': Bits._readbits, + 'bytes': Bits._readbytes, + 'ue': Bits._readue, + 'se': Bits._readse, + 'uie': Bits._readuie, + 'sie': Bits._readsie, + 'bool': Bits._readbool, + } + + +def set_lsb0(v=True): + """Experimental method changing the bit numbering so that the least significant bit is bit 0""" + _switch_lsb0_methods(v) + + +def set_msb0(v=True): + """Experimental method to reset the bit numbering so that the most significant bit is bit 0""" + set_lsb0(not v) + + +# Initialise the default behaviour +set_msb0() + + +# Dictionaries for mapping init keywords with init functions. +init_with_length_and_offset = {'bytes': Bits._setbytes_safe, + 'filename': Bits._setfile, + } + +init_with_length_only = {'uint': Bits._setuint, + 'int': Bits._setint, + 'float': Bits._setfloat, + 'uintbe': Bits._setuintbe, + 'intbe': Bits._setintbe, + 'floatbe': Bits._setfloat, + 'uintle': Bits._setuintle, + 'intle': Bits._setintle, + 'floatle': Bits._setfloatle, + 'uintne': Bits._setuintne, + 'intne': Bits._setintne, + 'floatne': Bits._setfloatne, + } + +init_without_length_or_offset = {'bin': Bits._setbin_safe, + 'hex': Bits._sethex, + 'oct': Bits._setoct, + 'ue': Bits._setue, + 'se': Bits._setse, + 'uie': Bits._setuie, + 'sie': Bits._setsie, + 'bool': Bits._setbool, + } + + +# Aliases for backward compatibility +ConstBitArray = Bits +BitString = BitStream + +__all__ = ['ConstBitArray', 'ConstBitStream', 'BitStream', 'BitArray', + 'Bits', 'BitString', 'pack', 'Error', 'ReadError', 'InterpretError', + 'ByteAlignError', 'CreationError', 'bytealigned', 'set_lsb0', 'set_msb0'] + + +def main(): + # check if final parameter is an interpretation string + fp = sys.argv[-1] + if fp == '--help' or len(sys.argv) == 1: + print("""Create and interpret a bitstring from command-line parameters. + +Command-line parameters are concatenated and a bitstring created +from them. If the final parameter is either an interpretation string +or ends with a '.' followed by an interpretation string then that +interpretation of the bitstring will be used when printing it. + +Typical usage might be invoking the Python module from a console +as a one-off calculation: + +$ python -m bitstring int:16=-400 +0xfe70 +$ python -m bitstring float:32=0.2 bin +00111110010011001100110011001101 +$ python -m bitstring 0xff 3*0b01,0b11 uint +65367 +$ python -m bitstring hex=01, uint:12=352.hex +01160 + +This feature is experimental and is subject to change or removal. + """) + elif fp in name_to_read.keys(): + # concatenate all other parameters and interpret using the final one + b1 = Bits(','.join(sys.argv[1: -1])) + print(b1._readtoken(fp, 0, b1.__len__())[0]) + else: + # does final parameter end with a dot then an interpretation string? + interp = fp[fp.rfind('.') + 1:] + if interp in name_to_read.keys(): + sys.argv[-1] = fp[:fp.rfind('.')] + b1 = Bits(','.join(sys.argv[1:])) + print(b1._readtoken(interp, 0, b1.__len__())[0]) + else: + # No interpretation - just use default print + b1 = Bits(','.join(sys.argv[1:])) + print(b1) + + +if __name__ == '__main__': + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE new file mode 100644 index 000000000..e7057454d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE @@ -0,0 +1,26 @@ + +Except when otherwise stated (look for LICENSE files in directories or +information at the beginning of each file) all software and +documentation is licensed as follows: + + The MIT License + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA new file mode 100644 index 000000000..538e67914 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA @@ -0,0 +1,34 @@ +Metadata-Version: 2.1 +Name: cffi +Version: 1.15.1 +Summary: Foreign Function Interface for Python calling C code. +Home-page: http://cffi.readthedocs.org +Author: Armin Rigo, Maciej Fijalkowski +Author-email: python-cffi@googlegroups.com +License: MIT +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: License :: OSI Approved :: MIT License +License-File: LICENSE +Requires-Dist: pycparser + + +CFFI +==== + +Foreign Function Interface for Python calling C code. +Please see the `Documentation `_. + +Contact +------- + +`Mailing list `_ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD new file mode 100644 index 000000000..1ca4bfb0f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD @@ -0,0 +1,44 @@ +_cffi_backend.cp310-win_amd64.pyd,sha256=IJPn5PU1mzjwgZve-DFP2jMqFCfyLgmvxBbh7dWRD-E,181248 +cffi-1.15.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cffi-1.15.1.dist-info/LICENSE,sha256=esEZUOct9bRcUXFqeyLnuzSzJNZ_Bl4pOBUt1HLEgV8,1320 +cffi-1.15.1.dist-info/METADATA,sha256=KP4G3WmavRgDGwD2b8Y_eDsM1YeV6ckcG6Alz3-D8VY,1144 +cffi-1.15.1.dist-info/RECORD,, +cffi-1.15.1.dist-info/WHEEL,sha256=W26pYN7HLsBT1jrDSL9udgf_mdNKJmYmL23sIP-FcgM,102 +cffi-1.15.1.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75 +cffi-1.15.1.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 +cffi/__init__.py,sha256=uABQQ4lgzvhAvVhd1_ZA_oSO9T-O93qMod-rs0Ihjb8,527 +cffi/__pycache__/__init__.cpython-310.pyc,, +cffi/__pycache__/api.cpython-310.pyc,, +cffi/__pycache__/backend_ctypes.cpython-310.pyc,, +cffi/__pycache__/cffi_opcode.cpython-310.pyc,, +cffi/__pycache__/commontypes.cpython-310.pyc,, +cffi/__pycache__/cparser.cpython-310.pyc,, +cffi/__pycache__/error.cpython-310.pyc,, +cffi/__pycache__/ffiplatform.cpython-310.pyc,, +cffi/__pycache__/lock.cpython-310.pyc,, +cffi/__pycache__/model.cpython-310.pyc,, +cffi/__pycache__/pkgconfig.cpython-310.pyc,, +cffi/__pycache__/recompiler.cpython-310.pyc,, +cffi/__pycache__/setuptools_ext.cpython-310.pyc,, +cffi/__pycache__/vengine_cpy.cpython-310.pyc,, +cffi/__pycache__/vengine_gen.cpython-310.pyc,, +cffi/__pycache__/verifier.cpython-310.pyc,, +cffi/_cffi_errors.h,sha256=G0bGOb-6SNIO0UY8KEN3cM40Yd1JuR5bETQ8Ni5PxWY,4057 +cffi/_cffi_include.h,sha256=H7cgdZR-POwmUFrIup4jOGzmje8YoQHhN99gVFg7w08,15185 +cffi/_embedding.h,sha256=zo5hCU0uCLgUeTOPdfXbXc5ABXjOffCMHyfUKBjCQ5E,18208 +cffi/api.py,sha256=Xs_dAN5x1ehfnn_F9ZTdA3Ce0bmPrqeIOkO4Ya1tfbQ,43029 +cffi/backend_ctypes.py,sha256=BHN3q2giL2_Y8wMDST2CIcc_qoMrs65qV9Ob5JvxBZ4,43575 +cffi/cffi_opcode.py,sha256=57P2NHLZkuTWueZybu5iosWljb6ocQmUXzGrCplrnyE,5911 +cffi/commontypes.py,sha256=mEZD4g0qtadnv6O6CEXvMQaJ1K6SRbG5S1h4YvVZHOU,2769 +cffi/cparser.py,sha256=CwVk2V3ATYlCoywG6zN35w6UQ7zj2EWX68KjoJp2Mzk,45237 +cffi/error.py,sha256=Bka7fSV22aIglTQDPIDfpnxTc1aWZLMQdQOJY-h_PUA,908 +cffi/ffiplatform.py,sha256=qioydJeC63dEvrQ3ht5_BPmSs7wzzzuWnZAJtfhic7I,4173 +cffi/lock.py,sha256=vnbsel7392Ib8gGBifIfAfc7MHteSwd3nP725pvc25Q,777 +cffi/model.py,sha256=HRD0WEYHF2Vr6RjS-4wyncElrZxU2256zY0fbMkSKec,22385 +cffi/parse_c_type.h,sha256=fKYNqWNX5f9kZNNhbXcRLTOlpRGRhh8eCLyHmTXIZnQ,6157 +cffi/pkgconfig.py,sha256=9zDcDf0XKIJaxFHLg7e-W8-Xb8Yq5hdhqH7kLg-ugRo,4495 +cffi/recompiler.py,sha256=lV6Hz-F1RXPk8YjafaIe3a-UNNVfLIeEQk0FwmXqg3s,66179 +cffi/setuptools_ext.py,sha256=8y14TOlRAkgdczmwtPOahyFXJHNyIqhLjUHMYQmjOHs,9150 +cffi/vengine_cpy.py,sha256=ukugKCIsURxJzHxlxS265tGjQfPTFDbThwsqBrwKh-A,44396 +cffi/vengine_gen.py,sha256=mykUhLFJIcV6AyQ5cMJ3n_7dbqw0a9WEjXW0E-WfgiI,27359 +cffi/verifier.py,sha256=AZuuR7MxjMYZc8IsZjGsF8mdGajCsOY60AZLwZZ_Z2Y,11560 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL new file mode 100644 index 000000000..93f1ef46a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.1) +Root-Is-Purelib: false +Tag: cp310-cp310-win_amd64 + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt new file mode 100644 index 000000000..4b0274f23 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[distutils.setup_keywords] +cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt new file mode 100644 index 000000000..f64577957 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_cffi_backend +cffi diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py new file mode 100644 index 000000000..b95553706 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py @@ -0,0 +1,14 @@ +__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', + 'FFIError'] + +from .api import FFI +from .error import CDefError, FFIError, VerificationError, VerificationMissing +from .error import PkgConfigError + +__version__ = "1.15.1" +__version_info__ = (1, 15, 1) + +# The verifier module file names are based on the CRC32 of a string that +# contains the following version number. It may be older than __version__ +# if nothing is clearly incompatible. +__version_verifier_modules__ = "0.8.6" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h new file mode 100644 index 000000000..d9f7ad955 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h @@ -0,0 +1,149 @@ +#ifndef CFFI_MESSAGEBOX +# ifdef _MSC_VER +# define CFFI_MESSAGEBOX 1 +# else +# define CFFI_MESSAGEBOX 0 +# endif +#endif + + +#if CFFI_MESSAGEBOX +/* Windows only: logic to take the Python-CFFI embedding logic + initialization errors and display them in a background thread + with MessageBox. The idea is that if the whole program closes + as a result of this problem, then likely it is already a console + program and you can read the stderr output in the console too. + If it is not a console program, then it will likely show its own + dialog to complain, or generally not abruptly close, and for this + case the background thread should stay alive. +*/ +static void *volatile _cffi_bootstrap_text; + +static PyObject *_cffi_start_error_capture(void) +{ + PyObject *result = NULL; + PyObject *x, *m, *bi; + + if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, + (void *)1, NULL) != NULL) + return (PyObject *)1; + + m = PyImport_AddModule("_cffi_error_capture"); + if (m == NULL) + goto error; + + result = PyModule_GetDict(m); + if (result == NULL) + goto error; + +#if PY_MAJOR_VERSION >= 3 + bi = PyImport_ImportModule("builtins"); +#else + bi = PyImport_ImportModule("__builtin__"); +#endif + if (bi == NULL) + goto error; + PyDict_SetItemString(result, "__builtins__", bi); + Py_DECREF(bi); + + x = PyRun_String( + "import sys\n" + "class FileLike:\n" + " def write(self, x):\n" + " try:\n" + " of.write(x)\n" + " except: pass\n" + " self.buf += x\n" + " def flush(self):\n" + " pass\n" + "fl = FileLike()\n" + "fl.buf = ''\n" + "of = sys.stderr\n" + "sys.stderr = fl\n" + "def done():\n" + " sys.stderr = of\n" + " return fl.buf\n", /* make sure the returned value stays alive */ + Py_file_input, + result, result); + Py_XDECREF(x); + + error: + if (PyErr_Occurred()) + { + PyErr_WriteUnraisable(Py_None); + PyErr_Clear(); + } + return result; +} + +#pragma comment(lib, "user32.lib") + +static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) +{ + Sleep(666); /* may be interrupted if the whole process is closing */ +#if PY_MAJOR_VERSION >= 3 + MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, + L"Python-CFFI error", + MB_OK | MB_ICONERROR); +#else + MessageBoxA(NULL, (char *)_cffi_bootstrap_text, + "Python-CFFI error", + MB_OK | MB_ICONERROR); +#endif + _cffi_bootstrap_text = NULL; + return 0; +} + +static void _cffi_stop_error_capture(PyObject *ecap) +{ + PyObject *s; + void *text; + + if (ecap == (PyObject *)1) + return; + + if (ecap == NULL) + goto error; + + s = PyRun_String("done()", Py_eval_input, ecap, ecap); + if (s == NULL) + goto error; + + /* Show a dialog box, but in a background thread, and + never show multiple dialog boxes at once. */ +#if PY_MAJOR_VERSION >= 3 + text = PyUnicode_AsWideCharString(s, NULL); +#else + text = PyString_AsString(s); +#endif + + _cffi_bootstrap_text = text; + + if (text != NULL) + { + HANDLE h; + h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, + NULL, 0, NULL); + if (h != NULL) + CloseHandle(h); + } + /* decref the string, but it should stay alive as 'fl.buf' + in the small module above. It will really be freed only if + we later get another similar error. So it's a leak of at + most one copy of the small module. That's fine for this + situation which is usually a "fatal error" anyway. */ + Py_DECREF(s); + PyErr_Clear(); + return; + + error: + _cffi_bootstrap_text = NULL; + PyErr_Clear(); +} + +#else + +static PyObject *_cffi_start_error_capture(void) { return NULL; } +static void _cffi_stop_error_capture(PyObject *ecap) { } + +#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h new file mode 100644 index 000000000..e0033e5f6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h @@ -0,0 +1,385 @@ +#define _CFFI_ + +/* We try to define Py_LIMITED_API before including Python.h. + + Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and + Py_REF_DEBUG are not defined. This is a best-effort approximation: + we can learn about Py_DEBUG from pyconfig.h, but it is unclear if + the same works for the other two macros. Py_DEBUG implies them, + but not the other way around. + + The implementation is messy (issue #350): on Windows, with _MSC_VER, + we have to define Py_LIMITED_API even before including pyconfig.h. + In that case, we guess what pyconfig.h will do to the macros above, + and check our guess after the #include. + + Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv + version >= 16.0.0. With older versions of either, you don't get a + copy of PYTHON3.DLL in the virtualenv. We can't check the version of + CPython *before* we even include pyconfig.h. ffi.set_source() puts + a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is + running on Windows < 3.5, as an attempt at fixing it, but that's + arguably wrong because it may not be the target version of Python. + Still better than nothing I guess. As another workaround, you can + remove the definition of Py_LIMITED_API here. + + See also 'py_limited_api' in cffi/setuptools_ext.py. +*/ +#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) +# ifdef _MSC_VER +# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) +# define Py_LIMITED_API +# endif +# include + /* sanity-check: Py_LIMITED_API will cause crashes if any of these + are also defined. Normally, the Python file PC/pyconfig.h does not + cause any of these to be defined, with the exception that _DEBUG + causes Py_DEBUG. Double-check that. */ +# ifdef Py_LIMITED_API +# if defined(Py_DEBUG) +# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" +# endif +# if defined(Py_TRACE_REFS) +# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" +# endif +# if defined(Py_REF_DEBUG) +# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" +# endif +# endif +# else +# include +# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) +# define Py_LIMITED_API +# endif +# endif +#endif + +#include +#ifdef __cplusplus +extern "C" { +#endif +#include +#include "parse_c_type.h" + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +#endif + +#ifdef __GNUC__ +# define _CFFI_UNUSED_FN __attribute__((unused)) +#else +# define _CFFI_UNUSED_FN /* nothing */ +#endif + +#ifdef __cplusplus +# ifndef _Bool + typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ +# endif +#endif + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + + +#if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong PyLong_FromLong +#endif + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyInt_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong +#define _cffi_from_c__Bool PyBool_FromLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + not used any more +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ + PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) +#define _CFFI_CPIDX 25 +#define _cffi_call_python \ + ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) +#define _cffi_to_c_wchar3216_t \ + ((int(*)(PyObject *))_cffi_exports[26]) +#define _cffi_from_c_wchar3216_t \ + ((PyObject *(*)(int))_cffi_exports[27]) +#define _CFFI_NUM_EXPORTS 28 + +struct _cffi_ctypedescr; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; + +#define _cffi_type(index) ( \ + assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ + (struct _cffi_ctypedescr *)_cffi_types[index]) + +static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, + const struct _cffi_type_context_s *ctx) +{ + PyObject *module, *o_arg, *new_module; + void *raw[] = { + (void *)module_name, + (void *)version, + (void *)_cffi_exports, + (void *)ctx, + }; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + o_arg = PyLong_FromVoidPtr((void *)raw); + if (o_arg == NULL) + goto failure; + + new_module = PyObject_CallMethod( + module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); + + Py_DECREF(o_arg); + Py_DECREF(module); + return new_module; + + failure: + Py_XDECREF(module); + return NULL; +} + + +#ifdef HAVE_WCHAR_H +typedef wchar_t _cffi_wchar_t; +#else +typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ +#endif + +_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) +{ + if (sizeof(_cffi_wchar_t) == 2) + return (uint16_t)_cffi_to_c_wchar_t(o); + else + return (uint16_t)_cffi_to_c_wchar3216_t(o); +} + +_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) +{ + if (sizeof(_cffi_wchar_t) == 2) + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); + else + return _cffi_from_c_wchar3216_t((int)x); +} + +_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) +{ + if (sizeof(_cffi_wchar_t) == 4) + return (int)_cffi_to_c_wchar_t(o); + else + return (int)_cffi_to_c_wchar3216_t(o); +} + +_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) +{ + if (sizeof(_cffi_wchar_t) == 4) + return _cffi_from_c_wchar_t((_cffi_wchar_t)x); + else + return _cffi_from_c_wchar3216_t((int)x); +} + +union _cffi_union_alignment_u { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +}; + +struct _cffi_freeme_s { + struct _cffi_freeme_s *next; + union _cffi_union_alignment_u alignment; +}; + +_CFFI_UNUSED_FN static int +_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, + char **output_data, Py_ssize_t datasize, + struct _cffi_freeme_s **freeme) +{ + char *p; + if (datasize < 0) + return -1; + + p = *output_data; + if (p == NULL) { + struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( + offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); + if (fp == NULL) + return -1; + fp->next = *freeme; + *freeme = fp; + p = *output_data = (char *)&fp->alignment; + } + memset((void *)p, 0, (size_t)datasize); + return _cffi_convert_array_from_object(p, ctptr, arg); +} + +_CFFI_UNUSED_FN static void +_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) +{ + do { + void *p = (void *)freeme; + freeme = freeme->next; + PyObject_Free(p); + } while (freeme != NULL); +} + +/********** end CPython-specific section **********/ +#else +_CFFI_UNUSED_FN +static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); +# define _cffi_call_python _cffi_call_python_org +#endif + + +#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) + +#define _cffi_prim_int(size, sign) \ + ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ + (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ + (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ + (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ + _CFFI__UNKNOWN_PRIM) + +#define _cffi_prim_float(size) \ + ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ + (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ + (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ + _CFFI__UNKNOWN_FLOAT_PRIM) + +#define _cffi_check_int(got, got_nonpos, expected) \ + ((got_nonpos) == (expected <= 0) && \ + (got) == (unsigned long long)expected) + +#ifdef MS_WIN32 +# define _cffi_stdcall __stdcall +#else +# define _cffi_stdcall /* nothing */ +#endif + +#ifdef __cplusplus +} +#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h new file mode 100644 index 000000000..6238f7284 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h @@ -0,0 +1,528 @@ + +/***** Support code for embedding *****/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#if defined(_WIN32) +# define CFFI_DLLEXPORT __declspec(dllexport) +#elif defined(__GNUC__) +# define CFFI_DLLEXPORT __attribute__((visibility("default"))) +#else +# define CFFI_DLLEXPORT /* nothing */ +#endif + + +/* There are two global variables of type _cffi_call_python_fnptr: + + * _cffi_call_python, which we declare just below, is the one called + by ``extern "Python"`` implementations. + + * _cffi_call_python_org, which on CPython is actually part of the + _cffi_exports[] array, is the function pointer copied from + _cffi_backend. If _cffi_start_python() fails, then this is set + to NULL; otherwise, it should never be NULL. + + After initialization is complete, both are equal. However, the + first one remains equal to &_cffi_start_and_call_python until the + very end of initialization, when we are (or should be) sure that + concurrent threads also see a completely initialized world, and + only then is it changed. +*/ +#undef _cffi_call_python +typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); +static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); +static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; + + +#ifndef _MSC_VER + /* --- Assuming a GCC not infinitely old --- */ +# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) +# define cffi_write_barrier() __sync_synchronize() +# if !defined(__amd64__) && !defined(__x86_64__) && \ + !defined(__i386__) && !defined(__i386) +# define cffi_read_barrier() __sync_synchronize() +# else +# define cffi_read_barrier() (void)0 +# endif +#else + /* --- Windows threads version --- */ +# include +# define cffi_compare_and_swap(l,o,n) \ + (InterlockedCompareExchangePointer(l,n,o) == (o)) +# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) +# define cffi_read_barrier() (void)0 +static volatile LONG _cffi_dummy; +#endif + +#ifdef WITH_THREAD +# ifndef _MSC_VER +# include + static pthread_mutex_t _cffi_embed_startup_lock; +# else + static CRITICAL_SECTION _cffi_embed_startup_lock; +# endif + static char _cffi_embed_startup_lock_ready = 0; +#endif + +static void _cffi_acquire_reentrant_mutex(void) +{ + static void *volatile lock = NULL; + + while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: pthread_mutex_init() should be very fast, and + this is only run at start-up anyway. */ + } + +#ifdef WITH_THREAD + if (!_cffi_embed_startup_lock_ready) { +# ifndef _MSC_VER + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&_cffi_embed_startup_lock, &attr); +# else + InitializeCriticalSection(&_cffi_embed_startup_lock); +# endif + _cffi_embed_startup_lock_ready = 1; + } +#endif + + while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) + ; + +#ifndef _MSC_VER + pthread_mutex_lock(&_cffi_embed_startup_lock); +#else + EnterCriticalSection(&_cffi_embed_startup_lock); +#endif +} + +static void _cffi_release_reentrant_mutex(void) +{ +#ifndef _MSC_VER + pthread_mutex_unlock(&_cffi_embed_startup_lock); +#else + LeaveCriticalSection(&_cffi_embed_startup_lock); +#endif +} + + +/********** CPython-specific section **********/ +#ifndef PYPY_VERSION + +#include "_cffi_errors.h" + + +#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] + +PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ + +static void _cffi_py_initialize(void) +{ + /* XXX use initsigs=0, which "skips initialization registration of + signal handlers, which might be useful when Python is + embedded" according to the Python docs. But review and think + if it should be a user-controllable setting. + + XXX we should also give a way to write errors to a buffer + instead of to stderr. + + XXX if importing 'site' fails, CPython (any version) calls + exit(). Should we try to work around this behavior here? + */ + Py_InitializeEx(0); +} + +static int _cffi_initialize_python(void) +{ + /* This initializes Python, imports _cffi_backend, and then the + present .dll/.so is set up as a CPython C extension module. + */ + int result; + PyGILState_STATE state; + PyObject *pycode=NULL, *global_dict=NULL, *x; + PyObject *builtins; + + state = PyGILState_Ensure(); + + /* Call the initxxx() function from the present module. It will + create and initialize us as a CPython extension module, instead + of letting the startup Python code do it---it might reimport + the same .dll/.so and get maybe confused on some platforms. + It might also have troubles locating the .dll/.so again for all + I know. + */ + (void)_CFFI_PYTHON_STARTUP_FUNC(); + if (PyErr_Occurred()) + goto error; + + /* Now run the Python code provided to ffi.embedding_init_code(). + */ + pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, + "", + Py_file_input); + if (pycode == NULL) + goto error; + global_dict = PyDict_New(); + if (global_dict == NULL) + goto error; + builtins = PyEval_GetBuiltins(); + if (builtins == NULL) + goto error; + if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) + goto error; + x = PyEval_EvalCode( +#if PY_MAJOR_VERSION < 3 + (PyCodeObject *) +#endif + pycode, global_dict, global_dict); + if (x == NULL) + goto error; + Py_DECREF(x); + + /* Done! Now if we've been called from + _cffi_start_and_call_python() in an ``extern "Python"``, we can + only hope that the Python code did correctly set up the + corresponding @ffi.def_extern() function. Otherwise, the + general logic of ``extern "Python"`` functions (inside the + _cffi_backend module) will find that the reference is still + missing and print an error. + */ + result = 0; + done: + Py_XDECREF(pycode); + Py_XDECREF(global_dict); + PyGILState_Release(state); + return result; + + error:; + { + /* Print as much information as potentially useful. + Debugging load-time failures with embedding is not fun + */ + PyObject *ecap; + PyObject *exception, *v, *tb, *f, *modules, *mod; + PyErr_Fetch(&exception, &v, &tb); + ecap = _cffi_start_error_capture(); + f = PySys_GetObject((char *)"stderr"); + if (f != NULL && f != Py_None) { + PyFile_WriteString( + "Failed to initialize the Python-CFFI embedding logic:\n\n", f); + } + + if (exception != NULL) { + PyErr_NormalizeException(&exception, &v, &tb); + PyErr_Display(exception, v, tb); + } + Py_XDECREF(exception); + Py_XDECREF(v); + Py_XDECREF(tb); + + if (f != NULL && f != Py_None) { + PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME + "\ncompiled with cffi version: 1.15.1" + "\n_cffi_backend module: ", f); + modules = PyImport_GetModuleDict(); + mod = PyDict_GetItemString(modules, "_cffi_backend"); + if (mod == NULL) { + PyFile_WriteString("not loaded", f); + } + else { + v = PyObject_GetAttrString(mod, "__file__"); + PyFile_WriteObject(v, f, 0); + Py_XDECREF(v); + } + PyFile_WriteString("\nsys.path: ", f); + PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); + PyFile_WriteString("\n\n", f); + } + _cffi_stop_error_capture(ecap); + } + result = -1; + goto done; +} + +#if PY_VERSION_HEX < 0x03080000 +PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ +#endif + +static int _cffi_carefully_make_gil(void) +{ + /* This does the basic initialization of Python. It can be called + completely concurrently from unrelated threads. It assumes + that we don't hold the GIL before (if it exists), and we don't + hold it afterwards. + + (What it really does used to be completely different in Python 2 + and Python 3, with the Python 2 solution avoiding the spin-lock + around the Py_InitializeEx() call. However, after recent changes + to CPython 2.7 (issue #358) it no longer works. So we use the + Python 3 solution everywhere.) + + This initializes Python by calling Py_InitializeEx(). + Important: this must not be called concurrently at all. + So we use a global variable as a simple spin lock. This global + variable must be from 'libpythonX.Y.so', not from this + cffi-based extension module, because it must be shared from + different cffi-based extension modules. + + In Python < 3.8, we choose + _PyParser_TokenNames[0] as a completely arbitrary pointer value + that is never written to. The default is to point to the + string "ENDMARKER". We change it temporarily to point to the + next character in that string. (Yes, I know it's REALLY + obscure.) + + In Python >= 3.8, this string array is no longer writable, so + instead we pick PyCapsuleType.tp_version_tag. We can't change + Python < 3.8 because someone might use a mixture of cffi + embedded modules, some of which were compiled before this file + changed. + */ + +#ifdef WITH_THREAD +# if PY_VERSION_HEX < 0x03080000 + char *volatile *lock = (char *volatile *)_PyParser_TokenNames; + char *old_value, *locked_value; + + while (1) { /* spin loop */ + old_value = *lock; + locked_value = old_value + 1; + if (old_value[0] == 'E') { + assert(old_value[1] == 'N'); + if (cffi_compare_and_swap(lock, old_value, locked_value)) + break; + } + else { + assert(old_value[0] == 'N'); + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: PyEval_InitThreads() should be very fast, and + this is only run at start-up anyway. */ + } + } +# else + int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; + int old_value, locked_value; + assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); + + while (1) { /* spin loop */ + old_value = *lock; + locked_value = -42; + if (old_value == 0) { + if (cffi_compare_and_swap(lock, old_value, locked_value)) + break; + } + else { + assert(old_value == locked_value); + /* should ideally do a spin loop instruction here, but + hard to do it portably and doesn't really matter I + think: PyEval_InitThreads() should be very fast, and + this is only run at start-up anyway. */ + } + } +# endif +#endif + + /* call Py_InitializeEx() */ + if (!Py_IsInitialized()) { + _cffi_py_initialize(); +#if PY_VERSION_HEX < 0x03070000 + PyEval_InitThreads(); +#endif + PyEval_SaveThread(); /* release the GIL */ + /* the returned tstate must be the one that has been stored into the + autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ + } + else { +#if PY_VERSION_HEX < 0x03070000 + /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ + PyGILState_STATE state = PyGILState_Ensure(); + PyEval_InitThreads(); + PyGILState_Release(state); +#endif + } + +#ifdef WITH_THREAD + /* release the lock */ + while (!cffi_compare_and_swap(lock, locked_value, old_value)) + ; +#endif + + return 0; +} + +/********** end CPython-specific section **********/ + + +#else + + +/********** PyPy-specific section **********/ + +PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ + +static struct _cffi_pypy_init_s { + const char *name; + void *func; /* function pointer */ + const char *code; +} _cffi_pypy_init = { + _CFFI_MODULE_NAME, + _CFFI_PYTHON_STARTUP_FUNC, + _CFFI_PYTHON_STARTUP_CODE, +}; + +extern int pypy_carefully_make_gil(const char *); +extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); + +static int _cffi_carefully_make_gil(void) +{ + return pypy_carefully_make_gil(_CFFI_MODULE_NAME); +} + +static int _cffi_initialize_python(void) +{ + return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); +} + +/********** end PyPy-specific section **********/ + + +#endif + + +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static _cffi_call_python_fnptr _cffi_start_python(void) +{ + /* Delicate logic to initialize Python. This function can be + called multiple times concurrently, e.g. when the process calls + its first ``extern "Python"`` functions in multiple threads at + once. It can also be called recursively, in which case we must + ignore it. We also have to consider what occurs if several + different cffi-based extensions reach this code in parallel + threads---it is a different copy of the code, then, and we + can't have any shared global variable unless it comes from + 'libpythonX.Y.so'. + + Idea: + + * _cffi_carefully_make_gil(): "carefully" call + PyEval_InitThreads() (possibly with Py_InitializeEx() first). + + * then we use a (local) custom lock to make sure that a call to this + cffi-based extension will wait if another call to the *same* + extension is running the initialization in another thread. + It is reentrant, so that a recursive call will not block, but + only one from a different thread. + + * then we grab the GIL and (Python 2) we call Py_InitializeEx(). + At this point, concurrent calls to Py_InitializeEx() are not + possible: we have the GIL. + + * do the rest of the specific initialization, which may + temporarily release the GIL but not the custom lock. + Only release the custom lock when we are done. + */ + static char called = 0; + + if (_cffi_carefully_make_gil() != 0) + return NULL; + + _cffi_acquire_reentrant_mutex(); + + /* Here the GIL exists, but we don't have it. We're only protected + from concurrency by the reentrant mutex. */ + + /* This file only initializes the embedded module once, the first + time this is called, even if there are subinterpreters. */ + if (!called) { + called = 1; /* invoke _cffi_initialize_python() only once, + but don't set '_cffi_call_python' right now, + otherwise concurrent threads won't call + this function at all (we need them to wait) */ + if (_cffi_initialize_python() == 0) { + /* now initialization is finished. Switch to the fast-path. */ + + /* We would like nobody to see the new value of + '_cffi_call_python' without also seeing the rest of the + data initialized. However, this is not possible. But + the new value of '_cffi_call_python' is the function + 'cffi_call_python()' from _cffi_backend. So: */ + cffi_write_barrier(); + /* ^^^ we put a write barrier here, and a corresponding + read barrier at the start of cffi_call_python(). This + ensures that after that read barrier, we see everything + done here before the write barrier. + */ + + assert(_cffi_call_python_org != NULL); + _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; + } + else { + /* initialization failed. Reset this to NULL, even if it was + already set to some other value. Future calls to + _cffi_start_python() are still forced to occur, and will + always return NULL from now on. */ + _cffi_call_python_org = NULL; + } + } + + _cffi_release_reentrant_mutex(); + + return (_cffi_call_python_fnptr)_cffi_call_python_org; +} + +static +void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) +{ + _cffi_call_python_fnptr fnptr; + int current_err = errno; +#ifdef _MSC_VER + int current_lasterr = GetLastError(); +#endif + fnptr = _cffi_start_python(); + if (fnptr == NULL) { + fprintf(stderr, "function %s() called, but initialization code " + "failed. Returning 0.\n", externpy->name); + memset(args, 0, externpy->size_of_result); + } +#ifdef _MSC_VER + SetLastError(current_lasterr); +#endif + errno = current_err; + + if (fnptr != NULL) + fnptr(externpy, args); +} + + +/* The cffi_start_python() function makes sure Python is initialized + and our cffi module is set up. It can be called manually from the + user C code. The same effect is obtained automatically from any + dll-exported ``extern "Python"`` function. This function returns + -1 if initialization failed, 0 if all is OK. */ +_CFFI_UNUSED_FN +static int cffi_start_python(void) +{ + if (_cffi_call_python == &_cffi_start_and_call_python) { + if (_cffi_start_python() == NULL) + return -1; + } + cffi_read_barrier(); + return 0; +} + +#undef cffi_compare_and_swap +#undef cffi_write_barrier +#undef cffi_read_barrier + +#ifdef __cplusplus +} +#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py new file mode 100644 index 000000000..10090fe8f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py @@ -0,0 +1,965 @@ +import sys, types +from .lock import allocate_lock +from .error import CDefError +from . import model + +try: + callable +except NameError: + # Python 3.1 + from collections import Callable + callable = lambda x: isinstance(x, Callable) + +try: + basestring +except NameError: + # Python 3.x + basestring = str + +_unspecified = object() + + + +class FFI(object): + r''' + The main top-level class that you instantiate once, or once per module. + + Example usage: + + ffi = FFI() + ffi.cdef(""" + int printf(const char *, ...); + """) + + C = ffi.dlopen(None) # standard library + -or- + C = ffi.verify() # use a C compiler: verify the decl above is right + + C.printf("hello, %s!\n", ffi.new("char[]", "world")) + ''' + + def __init__(self, backend=None): + """Create an FFI instance. The 'backend' argument is used to + select a non-default backend, mostly for tests. + """ + if backend is None: + # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with + # _cffi_backend.so compiled. + import _cffi_backend as backend + from . import __version__ + if backend.__version__ != __version__: + # bad version! Try to be as explicit as possible. + if hasattr(backend, '__file__'): + # CPython + raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( + __version__, __file__, + backend.__version__, backend.__file__)) + else: + # PyPy + raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( + __version__, __file__, backend.__version__)) + # (If you insist you can also try to pass the option + # 'backend=backend_ctypes.CTypesBackend()', but don't + # rely on it! It's probably not going to work well.) + + from . import cparser + self._backend = backend + self._lock = allocate_lock() + self._parser = cparser.Parser() + self._cached_btypes = {} + self._parsed_types = types.ModuleType('parsed_types').__dict__ + self._new_types = types.ModuleType('new_types').__dict__ + self._function_caches = [] + self._libraries = [] + self._cdefsources = [] + self._included_ffis = [] + self._windows_unicode = None + self._init_once_cache = {} + self._cdef_version = None + self._embedding = None + self._typecache = model.get_typecache(backend) + if hasattr(backend, 'set_ffi'): + backend.set_ffi(self) + for name in list(backend.__dict__): + if name.startswith('RTLD_'): + setattr(self, name, getattr(backend, name)) + # + with self._lock: + self.BVoidP = self._get_cached_btype(model.voidp_type) + self.BCharA = self._get_cached_btype(model.char_array_type) + if isinstance(backend, types.ModuleType): + # _cffi_backend: attach these constants to the class + if not hasattr(FFI, 'NULL'): + FFI.NULL = self.cast(self.BVoidP, 0) + FFI.CData, FFI.CType = backend._get_types() + else: + # ctypes backend: attach these constants to the instance + self.NULL = self.cast(self.BVoidP, 0) + self.CData, self.CType = backend._get_types() + self.buffer = backend.buffer + + def cdef(self, csource, override=False, packed=False, pack=None): + """Parse the given C source. This registers all declared functions, + types, and global variables. The functions and global variables can + then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. + The types can be used in 'ffi.new()' and other functions. + If 'packed' is specified as True, all structs declared inside this + cdef are packed, i.e. laid out without any field alignment at all. + Alternatively, 'pack' can be a small integer, and requests for + alignment greater than that are ignored (pack=1 is equivalent to + packed=True). + """ + self._cdef(csource, override=override, packed=packed, pack=pack) + + def embedding_api(self, csource, packed=False, pack=None): + self._cdef(csource, packed=packed, pack=pack, dllexport=True) + if self._embedding is None: + self._embedding = '' + + def _cdef(self, csource, override=False, **options): + if not isinstance(csource, str): # unicode, on Python 2 + if not isinstance(csource, basestring): + raise TypeError("cdef() argument must be a string") + csource = csource.encode('ascii') + with self._lock: + self._cdef_version = object() + self._parser.parse(csource, override=override, **options) + self._cdefsources.append(csource) + if override: + for cache in self._function_caches: + cache.clear() + finishlist = self._parser._recomplete + if finishlist: + self._parser._recomplete = [] + for tp in finishlist: + tp.finish_backend_type(self, finishlist) + + def dlopen(self, name, flags=0): + """Load and return a dynamic library identified by 'name'. + The standard C library can be loaded by passing None. + Note that functions and types declared by 'ffi.cdef()' are not + linked to a particular library, just like C headers; in the + library we only look for the actual (untyped) symbols. + """ + if not (isinstance(name, basestring) or + name is None or + isinstance(name, self.CData)): + raise TypeError("dlopen(name): name must be a file name, None, " + "or an already-opened 'void *' handle") + with self._lock: + lib, function_cache = _make_ffi_library(self, name, flags) + self._function_caches.append(function_cache) + self._libraries.append(lib) + return lib + + def dlclose(self, lib): + """Close a library obtained with ffi.dlopen(). After this call, + access to functions or variables from the library will fail + (possibly with a segmentation fault). + """ + type(lib).__cffi_close__(lib) + + def _typeof_locked(self, cdecl): + # call me with the lock! + key = cdecl + if key in self._parsed_types: + return self._parsed_types[key] + # + if not isinstance(cdecl, str): # unicode, on Python 2 + cdecl = cdecl.encode('ascii') + # + type = self._parser.parse_type(cdecl) + really_a_function_type = type.is_raw_function + if really_a_function_type: + type = type.as_function_pointer() + btype = self._get_cached_btype(type) + result = btype, really_a_function_type + self._parsed_types[key] = result + return result + + def _typeof(self, cdecl, consider_function_as_funcptr=False): + # string -> ctype object + try: + result = self._parsed_types[cdecl] + except KeyError: + with self._lock: + result = self._typeof_locked(cdecl) + # + btype, really_a_function_type = result + if really_a_function_type and not consider_function_as_funcptr: + raise CDefError("the type %r is a function type, not a " + "pointer-to-function type" % (cdecl,)) + return btype + + def typeof(self, cdecl): + """Parse the C type given as a string and return the + corresponding object. + It can also be used on 'cdata' instance to get its C type. + """ + if isinstance(cdecl, basestring): + return self._typeof(cdecl) + if isinstance(cdecl, self.CData): + return self._backend.typeof(cdecl) + if isinstance(cdecl, types.BuiltinFunctionType): + res = _builtin_function_type(cdecl) + if res is not None: + return res + if (isinstance(cdecl, types.FunctionType) + and hasattr(cdecl, '_cffi_base_type')): + with self._lock: + return self._get_cached_btype(cdecl._cffi_base_type) + raise TypeError(type(cdecl)) + + def sizeof(self, cdecl): + """Return the size in bytes of the argument. It can be a + string naming a C type, or a 'cdata' instance. + """ + if isinstance(cdecl, basestring): + BType = self._typeof(cdecl) + return self._backend.sizeof(BType) + else: + return self._backend.sizeof(cdecl) + + def alignof(self, cdecl): + """Return the natural alignment size in bytes of the C type + given as a string. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.alignof(cdecl) + + def offsetof(self, cdecl, *fields_or_indexes): + """Return the offset of the named field inside the given + structure or array, which must be given as a C type name. + You can give several field names in case of nested structures. + You can also give numeric values which correspond to array + items, in case of an array type. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._typeoffsetof(cdecl, *fields_or_indexes)[1] + + def new(self, cdecl, init=None): + """Allocate an instance according to the specified C type and + return a pointer to it. The specified C type must be either a + pointer or an array: ``new('X *')`` allocates an X and returns + a pointer to it, whereas ``new('X[n]')`` allocates an array of + n X'es and returns an array referencing it (which works + mostly like a pointer, like in C). You can also use + ``new('X[]', n)`` to allocate an array of a non-constant + length n. + + The memory is initialized following the rules of declaring a + global variable in C: by default it is zero-initialized, but + an explicit initializer can be given which can be used to + fill all or part of the memory. + + When the returned object goes out of scope, the memory + is freed. In other words the returned object has + ownership of the value of type 'cdecl' that it points to. This + means that the raw data can be used as long as this object is + kept alive, but must not be used for a longer time. Be careful + about that when copying the pointer to the memory somewhere + else, e.g. into another structure. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.newp(cdecl, init) + + def new_allocator(self, alloc=None, free=None, + should_clear_after_alloc=True): + """Return a new allocator, i.e. a function that behaves like ffi.new() + but uses the provided low-level 'alloc' and 'free' functions. + + 'alloc' is called with the size as argument. If it returns NULL, a + MemoryError is raised. 'free' is called with the result of 'alloc' + as argument. Both can be either Python function or directly C + functions. If 'free' is None, then no free function is called. + If both 'alloc' and 'free' are None, the default is used. + + If 'should_clear_after_alloc' is set to False, then the memory + returned by 'alloc' is assumed to be already cleared (or you are + fine with garbage); otherwise CFFI will clear it. + """ + compiled_ffi = self._backend.FFI() + allocator = compiled_ffi.new_allocator(alloc, free, + should_clear_after_alloc) + def allocate(cdecl, init=None): + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return allocator(cdecl, init) + return allocate + + def cast(self, cdecl, source): + """Similar to a C cast: returns an instance of the named C + type initialized with the given 'source'. The source is + casted between integers or pointers of any type. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.cast(cdecl, source) + + def string(self, cdata, maxlen=-1): + """Return a Python string (or unicode string) from the 'cdata'. + If 'cdata' is a pointer or array of characters or bytes, returns + the null-terminated string. The returned string extends until + the first null character, or at most 'maxlen' characters. If + 'cdata' is an array then 'maxlen' defaults to its length. + + If 'cdata' is a pointer or array of wchar_t, returns a unicode + string following the same rules. + + If 'cdata' is a single character or byte or a wchar_t, returns + it as a string or unicode string. + + If 'cdata' is an enum, returns the value of the enumerator as a + string, or 'NUMBER' if the value is out of range. + """ + return self._backend.string(cdata, maxlen) + + def unpack(self, cdata, length): + """Unpack an array of C data of the given length, + returning a Python string/unicode/list. + + If 'cdata' is a pointer to 'char', returns a byte string. + It does not stop at the first null. This is equivalent to: + ffi.buffer(cdata, length)[:] + + If 'cdata' is a pointer to 'wchar_t', returns a unicode string. + 'length' is measured in wchar_t's; it is not the size in bytes. + + If 'cdata' is a pointer to anything else, returns a list of + 'length' items. This is a faster equivalent to: + [cdata[i] for i in range(length)] + """ + return self._backend.unpack(cdata, length) + + #def buffer(self, cdata, size=-1): + # """Return a read-write buffer object that references the raw C data + # pointed to by the given 'cdata'. The 'cdata' must be a pointer or + # an array. Can be passed to functions expecting a buffer, or directly + # manipulated with: + # + # buf[:] get a copy of it in a regular string, or + # buf[idx] as a single character + # buf[:] = ... + # buf[idx] = ... change the content + # """ + # note that 'buffer' is a type, set on this instance by __init__ + + def from_buffer(self, cdecl, python_buffer=_unspecified, + require_writable=False): + """Return a cdata of the given type pointing to the data of the + given Python object, which must support the buffer interface. + Note that this is not meant to be used on the built-in types + str or unicode (you can build 'char[]' arrays explicitly) + but only on objects containing large quantities of raw data + in some other format, like 'array.array' or numpy arrays. + + The first argument is optional and default to 'char[]'. + """ + if python_buffer is _unspecified: + cdecl, python_buffer = self.BCharA, cdecl + elif isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + return self._backend.from_buffer(cdecl, python_buffer, + require_writable) + + def memmove(self, dest, src, n): + """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. + + Like the C function memmove(), the memory areas may overlap; + apart from that it behaves like the C function memcpy(). + + 'src' can be any cdata ptr or array, or any Python buffer object. + 'dest' can be any cdata ptr or array, or a writable Python buffer + object. The size to copy, 'n', is always measured in bytes. + + Unlike other methods, this one supports all Python buffer including + byte strings and bytearrays---but it still does not support + non-contiguous buffers. + """ + return self._backend.memmove(dest, src, n) + + def callback(self, cdecl, python_callable=None, error=None, onerror=None): + """Return a callback object or a decorator making such a + callback object. 'cdecl' must name a C function pointer type. + The callback invokes the specified 'python_callable' (which may + be provided either directly or via a decorator). Important: the + callback object must be manually kept alive for as long as the + callback may be invoked from the C level. + """ + def callback_decorator_wrap(python_callable): + if not callable(python_callable): + raise TypeError("the 'python_callable' argument " + "is not callable") + return self._backend.callback(cdecl, python_callable, + error, onerror) + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) + if python_callable is None: + return callback_decorator_wrap # decorator mode + else: + return callback_decorator_wrap(python_callable) # direct mode + + def getctype(self, cdecl, replace_with=''): + """Return a string giving the C type 'cdecl', which may be itself + a string or a object. If 'replace_with' is given, it gives + extra text to append (or insert for more complicated C types), like + a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. + """ + if isinstance(cdecl, basestring): + cdecl = self._typeof(cdecl) + replace_with = replace_with.strip() + if (replace_with.startswith('*') + and '&[' in self._backend.getcname(cdecl, '&')): + replace_with = '(%s)' % replace_with + elif replace_with and not replace_with[0] in '[(': + replace_with = ' ' + replace_with + return self._backend.getcname(cdecl, replace_with) + + def gc(self, cdata, destructor, size=0): + """Return a new cdata object that points to the same + data. Later, when this new cdata object is garbage-collected, + 'destructor(old_cdata_object)' will be called. + + The optional 'size' gives an estimate of the size, used to + trigger the garbage collection more eagerly. So far only used + on PyPy. It tells the GC that the returned object keeps alive + roughly 'size' bytes of external memory. + """ + return self._backend.gcp(cdata, destructor, size) + + def _get_cached_btype(self, type): + assert self._lock.acquire(False) is False + # call me with the lock! + try: + BType = self._cached_btypes[type] + except KeyError: + finishlist = [] + BType = type.get_cached_btype(self, finishlist) + for type in finishlist: + type.finish_backend_type(self, finishlist) + return BType + + def verify(self, source='', tmpdir=None, **kwargs): + """Verify that the current ffi signatures compile on this + machine, and return a dynamic library object. The dynamic + library can be used to call functions and access global + variables declared in this 'ffi'. The library is compiled + by the C compiler: it gives you C-level API compatibility + (including calling macros). This is unlike 'ffi.dlopen()', + which requires binary compatibility in the signatures. + """ + from .verifier import Verifier, _caller_dir_pycache + # + # If set_unicode(True) was called, insert the UNICODE and + # _UNICODE macro declarations + if self._windows_unicode: + self._apply_windows_unicode(kwargs) + # + # Set the tmpdir here, and not in Verifier.__init__: it picks + # up the caller's directory, which we want to be the caller of + # ffi.verify(), as opposed to the caller of Veritier(). + tmpdir = tmpdir or _caller_dir_pycache() + # + # Make a Verifier() and use it to load the library. + self.verifier = Verifier(self, source, tmpdir, **kwargs) + lib = self.verifier.load_library() + # + # Save the loaded library for keep-alive purposes, even + # if the caller doesn't keep it alive itself (it should). + self._libraries.append(lib) + return lib + + def _get_errno(self): + return self._backend.get_errno() + def _set_errno(self, errno): + self._backend.set_errno(errno) + errno = property(_get_errno, _set_errno, None, + "the value of 'errno' from/to the C calls") + + def getwinerror(self, code=-1): + return self._backend.getwinerror(code) + + def _pointer_to(self, ctype): + with self._lock: + return model.pointer_cache(self, ctype) + + def addressof(self, cdata, *fields_or_indexes): + """Return the address of a . + If 'fields_or_indexes' are given, returns the address of that + field or array item in the structure or array, recursively in + case of nested structures. + """ + try: + ctype = self._backend.typeof(cdata) + except TypeError: + if '__addressof__' in type(cdata).__dict__: + return type(cdata).__addressof__(cdata, *fields_or_indexes) + raise + if fields_or_indexes: + ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) + else: + if ctype.kind == "pointer": + raise TypeError("addressof(pointer)") + offset = 0 + ctypeptr = self._pointer_to(ctype) + return self._backend.rawaddressof(ctypeptr, cdata, offset) + + def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): + ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) + for field1 in fields_or_indexes: + ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) + offset += offset1 + return ctype, offset + + def include(self, ffi_to_include): + """Includes the typedefs, structs, unions and enums defined + in another FFI instance. Usage is similar to a #include in C, + where a part of the program might include types defined in + another part for its own usage. Note that the include() + method has no effect on functions, constants and global + variables, which must anyway be accessed directly from the + lib object returned by the original FFI instance. + """ + if not isinstance(ffi_to_include, FFI): + raise TypeError("ffi.include() expects an argument that is also of" + " type cffi.FFI, not %r" % ( + type(ffi_to_include).__name__,)) + if ffi_to_include is self: + raise ValueError("self.include(self)") + with ffi_to_include._lock: + with self._lock: + self._parser.include(ffi_to_include._parser) + self._cdefsources.append('[') + self._cdefsources.extend(ffi_to_include._cdefsources) + self._cdefsources.append(']') + self._included_ffis.append(ffi_to_include) + + def new_handle(self, x): + return self._backend.newp_handle(self.BVoidP, x) + + def from_handle(self, x): + return self._backend.from_handle(x) + + def release(self, x): + self._backend.release(x) + + def set_unicode(self, enabled_flag): + """Windows: if 'enabled_flag' is True, enable the UNICODE and + _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR + to be (pointers to) wchar_t. If 'enabled_flag' is False, + declare these types to be (pointers to) plain 8-bit characters. + This is mostly for backward compatibility; you usually want True. + """ + if self._windows_unicode is not None: + raise ValueError("set_unicode() can only be called once") + enabled_flag = bool(enabled_flag) + if enabled_flag: + self.cdef("typedef wchar_t TBYTE;" + "typedef wchar_t TCHAR;" + "typedef const wchar_t *LPCTSTR;" + "typedef const wchar_t *PCTSTR;" + "typedef wchar_t *LPTSTR;" + "typedef wchar_t *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + else: + self.cdef("typedef char TBYTE;" + "typedef char TCHAR;" + "typedef const char *LPCTSTR;" + "typedef const char *PCTSTR;" + "typedef char *LPTSTR;" + "typedef char *PTSTR;" + "typedef TBYTE *PTBYTE;" + "typedef TCHAR *PTCHAR;") + self._windows_unicode = enabled_flag + + def _apply_windows_unicode(self, kwds): + defmacros = kwds.get('define_macros', ()) + if not isinstance(defmacros, (list, tuple)): + raise TypeError("'define_macros' must be a list or tuple") + defmacros = list(defmacros) + [('UNICODE', '1'), + ('_UNICODE', '1')] + kwds['define_macros'] = defmacros + + def _apply_embedding_fix(self, kwds): + # must include an argument like "-lpython2.7" for the compiler + def ensure(key, value): + lst = kwds.setdefault(key, []) + if value not in lst: + lst.append(value) + # + if '__pypy__' in sys.builtin_module_names: + import os + if sys.platform == "win32": + # we need 'libpypy-c.lib'. Current distributions of + # pypy (>= 4.1) contain it as 'libs/python27.lib'. + pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'libs')) + else: + # we need 'libpypy-c.{so,dylib}', which should be by + # default located in 'sys.prefix/bin' for installed + # systems. + if sys.version_info < (3,): + pythonlib = "pypy-c" + else: + pythonlib = "pypy3-c" + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'bin')) + # On uninstalled pypy's, the libpypy-c is typically found in + # .../pypy/goal/. + if hasattr(sys, 'prefix'): + ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) + else: + if sys.platform == "win32": + template = "python%d%d" + if hasattr(sys, 'gettotalrefcount'): + template += '_d' + else: + try: + import sysconfig + except ImportError: # 2.6 + from distutils import sysconfig + template = "python%d.%d" + if sysconfig.get_config_var('DEBUG_EXT'): + template += sysconfig.get_config_var('DEBUG_EXT') + pythonlib = (template % + (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) + if hasattr(sys, 'abiflags'): + pythonlib += sys.abiflags + ensure('libraries', pythonlib) + if sys.platform == "win32": + ensure('extra_link_args', '/MANIFEST') + + def set_source(self, module_name, source, source_extension='.c', **kwds): + import os + if hasattr(self, '_assigned_source'): + raise ValueError("set_source() cannot be called several times " + "per ffi object") + if not isinstance(module_name, basestring): + raise TypeError("'module_name' must be a string") + if os.sep in module_name or (os.altsep and os.altsep in module_name): + raise ValueError("'module_name' must not contain '/': use a dotted " + "name to make a 'package.module' location") + self._assigned_source = (str(module_name), source, + source_extension, kwds) + + def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, + source_extension='.c', **kwds): + from . import pkgconfig + if not isinstance(pkgconfig_libs, list): + raise TypeError("the pkgconfig_libs argument must be a list " + "of package names") + kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) + pkgconfig.merge_flags(kwds, kwds2) + self.set_source(module_name, source, source_extension, **kwds) + + def distutils_extension(self, tmpdir='build', verbose=True): + from distutils.dir_util import mkpath + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored + return self.verifier.get_extension() + raise ValueError("set_source() must be called before" + " distutils_extension()") + module_name, source, source_extension, kwds = self._assigned_source + if source is None: + raise TypeError("distutils_extension() is only for C extension " + "modules, not for dlopen()-style pure Python " + "modules") + mkpath(tmpdir) + ext, updated = recompile(self, module_name, + source, tmpdir=tmpdir, extradir=tmpdir, + source_extension=source_extension, + call_c_compiler=False, **kwds) + if verbose: + if updated: + sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) + else: + sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) + return ext + + def emit_c_code(self, filename): + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before emit_c_code()") + module_name, source, source_extension, kwds = self._assigned_source + if source is None: + raise TypeError("emit_c_code() is only for C extension modules, " + "not for dlopen()-style pure Python modules") + recompile(self, module_name, source, + c_file=filename, call_c_compiler=False, **kwds) + + def emit_python_code(self, filename): + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before emit_c_code()") + module_name, source, source_extension, kwds = self._assigned_source + if source is not None: + raise TypeError("emit_python_code() is only for dlopen()-style " + "pure Python modules, not for C extension modules") + recompile(self, module_name, source, + c_file=filename, call_c_compiler=False, **kwds) + + def compile(self, tmpdir='.', verbose=0, target=None, debug=None): + """The 'target' argument gives the final file name of the + compiled DLL. Use '*' to force distutils' choice, suitable for + regular CPython C API modules. Use a file name ending in '.*' + to ask for the system's default extension for dynamic libraries + (.so/.dll/.dylib). + + The default is '*' when building a non-embedded C API extension, + and (module_name + '.*') when building an embedded library. + """ + from .recompiler import recompile + # + if not hasattr(self, '_assigned_source'): + raise ValueError("set_source() must be called before compile()") + module_name, source, source_extension, kwds = self._assigned_source + return recompile(self, module_name, source, tmpdir=tmpdir, + target=target, source_extension=source_extension, + compiler_verbose=verbose, debug=debug, **kwds) + + def init_once(self, func, tag): + # Read _init_once_cache[tag], which is either (False, lock) if + # we're calling the function now in some thread, or (True, result). + # Don't call setdefault() in most cases, to avoid allocating and + # immediately freeing a lock; but still use setdefaut() to avoid + # races. + try: + x = self._init_once_cache[tag] + except KeyError: + x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) + # Common case: we got (True, result), so we return the result. + if x[0]: + return x[1] + # Else, it's a lock. Acquire it to serialize the following tests. + with x[1]: + # Read again from _init_once_cache the current status. + x = self._init_once_cache[tag] + if x[0]: + return x[1] + # Call the function and store the result back. + result = func() + self._init_once_cache[tag] = (True, result) + return result + + def embedding_init_code(self, pysource): + if self._embedding: + raise ValueError("embedding_init_code() can only be called once") + # fix 'pysource' before it gets dumped into the C file: + # - remove empty lines at the beginning, so it starts at "line 1" + # - dedent, if all non-empty lines are indented + # - check for SyntaxErrors + import re + match = re.match(r'\s*\n', pysource) + if match: + pysource = pysource[match.end():] + lines = pysource.splitlines() or [''] + prefix = re.match(r'\s*', lines[0]).group() + for i in range(1, len(lines)): + line = lines[i] + if line.rstrip(): + while not line.startswith(prefix): + prefix = prefix[:-1] + i = len(prefix) + lines = [line[i:]+'\n' for line in lines] + pysource = ''.join(lines) + # + compile(pysource, "cffi_init", "exec") + # + self._embedding = pysource + + def def_extern(self, *args, **kwds): + raise ValueError("ffi.def_extern() is only available on API-mode FFI " + "objects") + + def list_types(self): + """Returns the user type names known to this FFI instance. + This returns a tuple containing three lists of names: + (typedef_names, names_of_structs, names_of_unions) + """ + typedefs = [] + structs = [] + unions = [] + for key in self._parser._declarations: + if key.startswith('typedef '): + typedefs.append(key[8:]) + elif key.startswith('struct '): + structs.append(key[7:]) + elif key.startswith('union '): + unions.append(key[6:]) + typedefs.sort() + structs.sort() + unions.sort() + return (typedefs, structs, unions) + + +def _load_backend_lib(backend, name, flags): + import os + if not isinstance(name, basestring): + if sys.platform != "win32" or name is not None: + return backend.load_library(name, flags) + name = "c" # Windows: load_library(None) fails, but this works + # on Python 2 (backward compatibility hack only) + first_error = None + if '.' in name or '/' in name or os.sep in name: + try: + return backend.load_library(name, flags) + except OSError as e: + first_error = e + import ctypes.util + path = ctypes.util.find_library(name) + if path is None: + if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): + raise OSError("dlopen(None) cannot work on Windows for Python 3 " + "(see http://bugs.python.org/issue23606)") + msg = ("ctypes.util.find_library() did not manage " + "to locate a library called %r" % (name,)) + if first_error is not None: + msg = "%s. Additionally, %s" % (first_error, msg) + raise OSError(msg) + return backend.load_library(path, flags) + +def _make_ffi_library(ffi, libname, flags): + backend = ffi._backend + backendlib = _load_backend_lib(backend, libname, flags) + # + def accessor_function(name): + key = 'function ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + value = backendlib.load_function(BType, name) + library.__dict__[name] = value + # + def accessor_variable(name): + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + read_variable = backendlib.read_variable + write_variable = backendlib.write_variable + setattr(FFILibrary, name, property( + lambda self: read_variable(BType, name), + lambda self, value: write_variable(BType, name, value))) + # + def addressof_var(name): + try: + return addr_variables[name] + except KeyError: + with ffi._lock: + if name not in addr_variables: + key = 'variable ' + name + tp, _ = ffi._parser._declarations[key] + BType = ffi._get_cached_btype(tp) + if BType.kind != 'array': + BType = model.pointer_cache(ffi, BType) + p = backendlib.load_function(BType, name) + addr_variables[name] = p + return addr_variables[name] + # + def accessor_constant(name): + raise NotImplementedError("non-integer constant '%s' cannot be " + "accessed from a dlopen() library" % (name,)) + # + def accessor_int_constant(name): + library.__dict__[name] = ffi._parser._int_constants[name] + # + accessors = {} + accessors_version = [False] + addr_variables = {} + # + def update_accessors(): + if accessors_version[0] is ffi._cdef_version: + return + # + for key, (tp, _) in ffi._parser._declarations.items(): + if not isinstance(tp, model.EnumType): + tag, name = key.split(' ', 1) + if tag == 'function': + accessors[name] = accessor_function + elif tag == 'variable': + accessors[name] = accessor_variable + elif tag == 'constant': + accessors[name] = accessor_constant + else: + for i, enumname in enumerate(tp.enumerators): + def accessor_enum(name, tp=tp, i=i): + tp.check_not_partial() + library.__dict__[name] = tp.enumvalues[i] + accessors[enumname] = accessor_enum + for name in ffi._parser._int_constants: + accessors.setdefault(name, accessor_int_constant) + accessors_version[0] = ffi._cdef_version + # + def make_accessor(name): + with ffi._lock: + if name in library.__dict__ or name in FFILibrary.__dict__: + return # added by another thread while waiting for the lock + if name not in accessors: + update_accessors() + if name not in accessors: + raise AttributeError(name) + accessors[name](name) + # + class FFILibrary(object): + def __getattr__(self, name): + make_accessor(name) + return getattr(self, name) + def __setattr__(self, name, value): + try: + property = getattr(self.__class__, name) + except AttributeError: + make_accessor(name) + setattr(self, name, value) + else: + property.__set__(self, value) + def __dir__(self): + with ffi._lock: + update_accessors() + return accessors.keys() + def __addressof__(self, name): + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + make_accessor(name) + if name in library.__dict__: + return library.__dict__[name] + if name in FFILibrary.__dict__: + return addressof_var(name) + raise AttributeError("cffi library has no function or " + "global variable named '%s'" % (name,)) + def __cffi_close__(self): + backendlib.close_lib() + self.__dict__.clear() + # + if isinstance(libname, basestring): + try: + if not isinstance(libname, str): # unicode, on Python 2 + libname = libname.encode('utf-8') + FFILibrary.__name__ = 'FFILibrary_%s' % libname + except UnicodeError: + pass + library = FFILibrary() + return library, library.__dict__ + +def _builtin_function_type(func): + # a hack to make at least ffi.typeof(builtin_function) work, + # if the builtin function was obtained by 'vengine_cpy'. + import sys + try: + module = sys.modules[func.__module__] + ffi = module._cffi_original_ffi + types_of_builtin_funcs = module._cffi_types_of_builtin_funcs + tp = types_of_builtin_funcs[func] + except (KeyError, AttributeError, TypeError): + return None + else: + with ffi._lock: + return ffi._get_cached_btype(tp) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py new file mode 100644 index 000000000..3368a2ac6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py @@ -0,0 +1,1121 @@ +import ctypes, ctypes.util, operator, sys +from . import model + +if sys.version_info < (3,): + bytechr = chr +else: + unicode = str + long = int + xrange = range + bytechr = lambda num: bytes([num]) + +class CTypesType(type): + pass + +class CTypesData(object): + __metaclass__ = CTypesType + __slots__ = ['__weakref__'] + __name__ = '' + + def __init__(self, *args): + raise TypeError("cannot instantiate %r" % (self.__class__,)) + + @classmethod + def _newp(cls, init): + raise TypeError("expected a pointer or array ctype, got '%s'" + % (cls._get_c_name(),)) + + @staticmethod + def _to_ctypes(value): + raise TypeError + + @classmethod + def _arg_to_ctypes(cls, *value): + try: + ctype = cls._ctype + except AttributeError: + raise TypeError("cannot create an instance of %r" % (cls,)) + if value: + res = cls._to_ctypes(*value) + if not isinstance(res, ctype): + res = cls._ctype(res) + else: + res = cls._ctype() + return res + + @classmethod + def _create_ctype_obj(cls, init): + if init is None: + return cls._arg_to_ctypes() + else: + return cls._arg_to_ctypes(init) + + @staticmethod + def _from_ctypes(ctypes_value): + raise TypeError + + @classmethod + def _get_c_name(cls, replace_with=''): + return cls._reftypename.replace(' &', replace_with) + + @classmethod + def _fix_class(cls): + cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) + cls.__module__ = 'ffi' + + def _get_own_repr(self): + raise NotImplementedError + + def _addr_repr(self, address): + if address == 0: + return 'NULL' + else: + if address < 0: + address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) + return '0x%x' % address + + def __repr__(self, c_name=None): + own = self._get_own_repr() + return '' % (c_name or self._get_c_name(), own) + + def _convert_to_address(self, BClass): + if BClass is None: + raise TypeError("cannot convert %r to an address" % ( + self._get_c_name(),)) + else: + raise TypeError("cannot convert %r to %r" % ( + self._get_c_name(), BClass._get_c_name())) + + @classmethod + def _get_size(cls): + return ctypes.sizeof(cls._ctype) + + def _get_size_of_instance(self): + return ctypes.sizeof(self._ctype) + + @classmethod + def _cast_from(cls, source): + raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) + + def _cast_to_integer(self): + return self._convert_to_address(None) + + @classmethod + def _alignment(cls): + return ctypes.alignment(cls._ctype) + + def __iter__(self): + raise TypeError("cdata %r does not support iteration" % ( + self._get_c_name()),) + + def _make_cmp(name): + cmpfunc = getattr(operator, name) + def cmp(self, other): + v_is_ptr = not isinstance(self, CTypesGenericPrimitive) + w_is_ptr = (isinstance(other, CTypesData) and + not isinstance(other, CTypesGenericPrimitive)) + if v_is_ptr and w_is_ptr: + return cmpfunc(self._convert_to_address(None), + other._convert_to_address(None)) + elif v_is_ptr or w_is_ptr: + return NotImplemented + else: + if isinstance(self, CTypesGenericPrimitive): + self = self._value + if isinstance(other, CTypesGenericPrimitive): + other = other._value + return cmpfunc(self, other) + cmp.func_name = name + return cmp + + __eq__ = _make_cmp('__eq__') + __ne__ = _make_cmp('__ne__') + __lt__ = _make_cmp('__lt__') + __le__ = _make_cmp('__le__') + __gt__ = _make_cmp('__gt__') + __ge__ = _make_cmp('__ge__') + + def __hash__(self): + return hash(self._convert_to_address(None)) + + def _to_string(self, maxlen): + raise TypeError("string(): %r" % (self,)) + + +class CTypesGenericPrimitive(CTypesData): + __slots__ = [] + + def __hash__(self): + return hash(self._value) + + def _get_own_repr(self): + return repr(self._from_ctypes(self._value)) + + +class CTypesGenericArray(CTypesData): + __slots__ = [] + + @classmethod + def _newp(cls, init): + return cls(init) + + def __iter__(self): + for i in xrange(len(self)): + yield self[i] + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + +class CTypesGenericPtr(CTypesData): + __slots__ = ['_address', '_as_ctype_ptr'] + _automatic_casts = False + kind = "pointer" + + @classmethod + def _newp(cls, init): + return cls(init) + + @classmethod + def _cast_from(cls, source): + if source is None: + address = 0 + elif isinstance(source, CTypesData): + address = source._cast_to_integer() + elif isinstance(source, (int, long)): + address = source + else: + raise TypeError("bad type for cast to %r: %r" % + (cls, type(source).__name__)) + return cls._new_pointer_at(address) + + @classmethod + def _new_pointer_at(cls, address): + self = cls.__new__(cls) + self._address = address + self._as_ctype_ptr = ctypes.cast(address, cls._ctype) + return self + + def _get_own_repr(self): + try: + return self._addr_repr(self._address) + except AttributeError: + return '???' + + def _cast_to_integer(self): + return self._address + + def __nonzero__(self): + return bool(self._address) + __bool__ = __nonzero__ + + @classmethod + def _to_ctypes(cls, value): + if not isinstance(value, CTypesData): + raise TypeError("unexpected %s object" % type(value).__name__) + address = value._convert_to_address(cls) + return ctypes.cast(address, cls._ctype) + + @classmethod + def _from_ctypes(cls, ctypes_ptr): + address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 + return cls._new_pointer_at(address) + + @classmethod + def _initialize(cls, ctypes_ptr, value): + if value: + ctypes_ptr.contents = cls._to_ctypes(value).contents + + def _convert_to_address(self, BClass): + if (BClass in (self.__class__, None) or BClass._automatic_casts + or self._automatic_casts): + return self._address + else: + return CTypesData._convert_to_address(self, BClass) + + +class CTypesBaseStructOrUnion(CTypesData): + __slots__ = ['_blob'] + + @classmethod + def _create_ctype_obj(cls, init): + # may be overridden + raise TypeError("cannot instantiate opaque type %s" % (cls,)) + + def _get_own_repr(self): + return self._addr_repr(ctypes.addressof(self._blob)) + + @classmethod + def _offsetof(cls, fieldname): + return getattr(cls._ctype, fieldname).offset + + def _convert_to_address(self, BClass): + if getattr(BClass, '_BItem', None) is self.__class__: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @classmethod + def _from_ctypes(cls, ctypes_struct_or_union): + self = cls.__new__(cls) + self._blob = ctypes_struct_or_union + return self + + @classmethod + def _to_ctypes(cls, value): + return value._blob + + def __repr__(self, c_name=None): + return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) + + +class CTypesBackend(object): + + PRIMITIVE_TYPES = { + 'char': ctypes.c_char, + 'short': ctypes.c_short, + 'int': ctypes.c_int, + 'long': ctypes.c_long, + 'long long': ctypes.c_longlong, + 'signed char': ctypes.c_byte, + 'unsigned char': ctypes.c_ubyte, + 'unsigned short': ctypes.c_ushort, + 'unsigned int': ctypes.c_uint, + 'unsigned long': ctypes.c_ulong, + 'unsigned long long': ctypes.c_ulonglong, + 'float': ctypes.c_float, + 'double': ctypes.c_double, + '_Bool': ctypes.c_bool, + } + + for _name in ['unsigned long long', 'unsigned long', + 'unsigned int', 'unsigned short', 'unsigned char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] + + for _name in ['long long', 'long', 'int', 'short', 'signed char']: + _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) + PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_void_p): + PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] + PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] + if _size == ctypes.sizeof(ctypes.c_size_t): + PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] + + + def __init__(self): + self.RTLD_LAZY = 0 # not supported anyway by ctypes + self.RTLD_NOW = 0 + self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL + self.RTLD_LOCAL = ctypes.RTLD_LOCAL + + def set_ffi(self, ffi): + self.ffi = ffi + + def _get_types(self): + return CTypesData, CTypesType + + def load_library(self, path, flags=0): + cdll = ctypes.CDLL(path, flags) + return CTypesLibrary(self, cdll) + + def new_void_type(self): + class CTypesVoid(CTypesData): + __slots__ = [] + _reftypename = 'void &' + @staticmethod + def _from_ctypes(novalue): + return None + @staticmethod + def _to_ctypes(novalue): + if novalue is not None: + raise TypeError("None expected, got %s object" % + (type(novalue).__name__,)) + return None + CTypesVoid._fix_class() + return CTypesVoid + + def new_primitive_type(self, name): + if name == 'wchar_t': + raise NotImplementedError(name) + ctype = self.PRIMITIVE_TYPES[name] + if name == 'char': + kind = 'char' + elif name in ('float', 'double'): + kind = 'float' + else: + if name in ('signed char', 'unsigned char'): + kind = 'byte' + elif name == '_Bool': + kind = 'bool' + else: + kind = 'int' + is_signed = (ctype(-1).value == -1) + # + def _cast_source_to_int(source): + if isinstance(source, (int, long, float)): + source = int(source) + elif isinstance(source, CTypesData): + source = source._cast_to_integer() + elif isinstance(source, bytes): + source = ord(source) + elif source is None: + source = 0 + else: + raise TypeError("bad type for cast to %r: %r" % + (CTypesPrimitive, type(source).__name__)) + return source + # + kind1 = kind + class CTypesPrimitive(CTypesGenericPrimitive): + __slots__ = ['_value'] + _ctype = ctype + _reftypename = '%s &' % name + kind = kind1 + + def __init__(self, value): + self._value = value + + @staticmethod + def _create_ctype_obj(init): + if init is None: + return ctype() + return ctype(CTypesPrimitive._to_ctypes(init)) + + if kind == 'int' or kind == 'byte': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = ctype(source).value # cast within range + return cls(source) + def __int__(self): + return self._value + + if kind == 'bool': + @classmethod + def _cast_from(cls, source): + if not isinstance(source, (int, long, float)): + source = _cast_source_to_int(source) + return cls(bool(source)) + def __int__(self): + return int(self._value) + + if kind == 'char': + @classmethod + def _cast_from(cls, source): + source = _cast_source_to_int(source) + source = bytechr(source & 0xFF) + return cls(source) + def __int__(self): + return ord(self._value) + + if kind == 'float': + @classmethod + def _cast_from(cls, source): + if isinstance(source, float): + pass + elif isinstance(source, CTypesGenericPrimitive): + if hasattr(source, '__float__'): + source = float(source) + else: + source = int(source) + else: + source = _cast_source_to_int(source) + source = ctype(source).value # fix precision + return cls(source) + def __int__(self): + return int(self._value) + def __float__(self): + return self._value + + _cast_to_integer = __int__ + + if kind == 'int' or kind == 'byte' or kind == 'bool': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long)): + if isinstance(x, CTypesData): + x = int(x) + else: + raise TypeError("integer expected, got %s" % + type(x).__name__) + if ctype(x).value != x: + if not is_signed and x < 0: + raise OverflowError("%s: negative integer" % name) + else: + raise OverflowError("%s: integer out of bounds" + % name) + return x + + if kind == 'char': + @staticmethod + def _to_ctypes(x): + if isinstance(x, bytes) and len(x) == 1: + return x + if isinstance(x, CTypesPrimitive): # > + return x._value + raise TypeError("character expected, got %s" % + type(x).__name__) + def __nonzero__(self): + return ord(self._value) != 0 + else: + def __nonzero__(self): + return self._value != 0 + __bool__ = __nonzero__ + + if kind == 'float': + @staticmethod + def _to_ctypes(x): + if not isinstance(x, (int, long, float, CTypesData)): + raise TypeError("float expected, got %s" % + type(x).__name__) + return ctype(x).value + + @staticmethod + def _from_ctypes(value): + return getattr(value, 'value', value) + + @staticmethod + def _initialize(blob, init): + blob.value = CTypesPrimitive._to_ctypes(init) + + if kind == 'char': + def _to_string(self, maxlen): + return self._value + if kind == 'byte': + def _to_string(self, maxlen): + return chr(self._value & 0xff) + # + CTypesPrimitive._fix_class() + return CTypesPrimitive + + def new_pointer_type(self, BItem): + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'charp' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'bytep' + elif BItem is getbtype(model.void_type): + kind = 'voidp' + else: + kind = 'generic' + # + class CTypesPtr(CTypesGenericPtr): + __slots__ = ['_own'] + if kind == 'charp': + __slots__ += ['__as_strbuf'] + _BItem = BItem + if hasattr(BItem, '_ctype'): + _ctype = ctypes.POINTER(BItem._ctype) + _bitem_size = ctypes.sizeof(BItem._ctype) + else: + _ctype = ctypes.c_void_p + if issubclass(BItem, CTypesGenericArray): + _reftypename = BItem._get_c_name('(* &)') + else: + _reftypename = BItem._get_c_name(' * &') + + def __init__(self, init): + ctypeobj = BItem._create_ctype_obj(init) + if kind == 'charp': + self.__as_strbuf = ctypes.create_string_buffer( + ctypeobj.value + b'\x00') + self._as_ctype_ptr = ctypes.cast( + self.__as_strbuf, self._ctype) + else: + self._as_ctype_ptr = ctypes.pointer(ctypeobj) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own = True + + def __add__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address + + other * self._bitem_size) + else: + return NotImplemented + + def __sub__(self, other): + if isinstance(other, (int, long)): + return self._new_pointer_at(self._address - + other * self._bitem_size) + elif type(self) is type(other): + return (self._address - other._address) // self._bitem_size + else: + return NotImplemented + + def __getitem__(self, index): + if getattr(self, '_own', False) and index != 0: + raise IndexError + return BItem._from_ctypes(self._as_ctype_ptr[index]) + + def __setitem__(self, index, value): + self._as_ctype_ptr[index] = BItem._to_ctypes(value) + + if kind == 'charp' or kind == 'voidp': + @classmethod + def _arg_to_ctypes(cls, *value): + if value and isinstance(value[0], bytes): + return ctypes.c_char_p(value[0]) + else: + return super(CTypesPtr, cls)._arg_to_ctypes(*value) + + if kind == 'charp' or kind == 'bytep': + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = sys.maxsize + p = ctypes.cast(self._as_ctype_ptr, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % ( + ctypes.sizeof(self._as_ctype_ptr.contents),) + return super(CTypesPtr, self)._get_own_repr() + # + if (BItem is self.ffi._get_cached_btype(model.void_type) or + BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): + CTypesPtr._automatic_casts = True + # + CTypesPtr._fix_class() + return CTypesPtr + + def new_array_type(self, CTypesPtr, length): + if length is None: + brackets = ' &[]' + else: + brackets = ' &[%d]' % length + BItem = CTypesPtr._BItem + getbtype = self.ffi._get_cached_btype + if BItem is getbtype(model.PrimitiveType('char')): + kind = 'char' + elif BItem in (getbtype(model.PrimitiveType('signed char')), + getbtype(model.PrimitiveType('unsigned char'))): + kind = 'byte' + else: + kind = 'generic' + # + class CTypesArray(CTypesGenericArray): + __slots__ = ['_blob', '_own'] + if length is not None: + _ctype = BItem._ctype * length + else: + __slots__.append('_ctype') + _reftypename = BItem._get_c_name(brackets) + _declared_length = length + _CTPtr = CTypesPtr + + def __init__(self, init): + if length is None: + if isinstance(init, (int, long)): + len1 = init + init = None + elif kind == 'char' and isinstance(init, bytes): + len1 = len(init) + 1 # extra null + else: + init = tuple(init) + len1 = len(init) + self._ctype = BItem._ctype * len1 + self._blob = self._ctype() + self._own = True + if init is not None: + self._initialize(self._blob, init) + + @staticmethod + def _initialize(blob, init): + if isinstance(init, bytes): + init = [init[i:i+1] for i in range(len(init))] + else: + if isinstance(init, CTypesGenericArray): + if (len(init) != len(blob) or + not isinstance(init, CTypesArray)): + raise TypeError("length/type mismatch: %s" % (init,)) + init = tuple(init) + if len(init) > len(blob): + raise IndexError("too many initializers") + addr = ctypes.cast(blob, ctypes.c_void_p).value + PTR = ctypes.POINTER(BItem._ctype) + itemsize = ctypes.sizeof(BItem._ctype) + for i, value in enumerate(init): + p = ctypes.cast(addr + i * itemsize, PTR) + BItem._initialize(p.contents, value) + + def __len__(self): + return len(self._blob) + + def __getitem__(self, index): + if not (0 <= index < len(self._blob)): + raise IndexError + return BItem._from_ctypes(self._blob[index]) + + def __setitem__(self, index, value): + if not (0 <= index < len(self._blob)): + raise IndexError + self._blob[index] = BItem._to_ctypes(value) + + if kind == 'char' or kind == 'byte': + def _to_string(self, maxlen): + if maxlen < 0: + maxlen = len(self._blob) + p = ctypes.cast(self._blob, + ctypes.POINTER(ctypes.c_char)) + n = 0 + while n < maxlen and p[n] != b'\x00': + n += 1 + return b''.join([p[i] for i in range(n)]) + + def _get_own_repr(self): + if getattr(self, '_own', False): + return 'owning %d bytes' % (ctypes.sizeof(self._blob),) + return super(CTypesArray, self)._get_own_repr() + + def _convert_to_address(self, BClass): + if BClass in (CTypesPtr, None) or BClass._automatic_casts: + return ctypes.addressof(self._blob) + else: + return CTypesData._convert_to_address(self, BClass) + + @staticmethod + def _from_ctypes(ctypes_array): + self = CTypesArray.__new__(CTypesArray) + self._blob = ctypes_array + return self + + @staticmethod + def _arg_to_ctypes(value): + return CTypesPtr._arg_to_ctypes(value) + + def __add__(self, other): + if isinstance(other, (int, long)): + return CTypesPtr._new_pointer_at( + ctypes.addressof(self._blob) + + other * ctypes.sizeof(BItem._ctype)) + else: + return NotImplemented + + @classmethod + def _cast_from(cls, source): + raise NotImplementedError("casting to %r" % ( + cls._get_c_name(),)) + # + CTypesArray._fix_class() + return CTypesArray + + def _new_struct_or_union(self, kind, name, base_ctypes_class): + # + class struct_or_union(base_ctypes_class): + pass + struct_or_union.__name__ = '%s_%s' % (kind, name) + kind1 = kind + # + class CTypesStructOrUnion(CTypesBaseStructOrUnion): + __slots__ = ['_blob'] + _ctype = struct_or_union + _reftypename = '%s &' % (name,) + _kind = kind = kind1 + # + CTypesStructOrUnion._fix_class() + return CTypesStructOrUnion + + def new_struct_type(self, name): + return self._new_struct_or_union('struct', name, ctypes.Structure) + + def new_union_type(self, name): + return self._new_struct_or_union('union', name, ctypes.Union) + + def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, + totalsize=-1, totalalignment=-1, sflags=0, + pack=0): + if totalsize >= 0 or totalalignment >= 0: + raise NotImplementedError("the ctypes backend of CFFI does not support " + "structures completed by verify(); please " + "compile and install the _cffi_backend module.") + struct_or_union = CTypesStructOrUnion._ctype + fnames = [fname for (fname, BField, bitsize) in fields] + btypes = [BField for (fname, BField, bitsize) in fields] + bitfields = [bitsize for (fname, BField, bitsize) in fields] + # + bfield_types = {} + cfields = [] + for (fname, BField, bitsize) in fields: + if bitsize < 0: + cfields.append((fname, BField._ctype)) + bfield_types[fname] = BField + else: + cfields.append((fname, BField._ctype, bitsize)) + bfield_types[fname] = Ellipsis + if sflags & 8: + struct_or_union._pack_ = 1 + elif pack: + struct_or_union._pack_ = pack + struct_or_union._fields_ = cfields + CTypesStructOrUnion._bfield_types = bfield_types + # + @staticmethod + def _create_ctype_obj(init): + result = struct_or_union() + if init is not None: + initialize(result, init) + return result + CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj + # + def initialize(blob, init): + if is_union: + if len(init) > 1: + raise ValueError("union initializer: %d items given, but " + "only one supported (use a dict if needed)" + % (len(init),)) + if not isinstance(init, dict): + if isinstance(init, (bytes, unicode)): + raise TypeError("union initializer: got a str") + init = tuple(init) + if len(init) > len(fnames): + raise ValueError("too many values for %s initializer" % + CTypesStructOrUnion._get_c_name()) + init = dict(zip(fnames, init)) + addr = ctypes.addressof(blob) + for fname, value in init.items(): + BField, bitsize = name2fieldtype[fname] + assert bitsize < 0, \ + "not implemented: initializer with bit fields" + offset = CTypesStructOrUnion._offsetof(fname) + PTR = ctypes.POINTER(BField._ctype) + p = ctypes.cast(addr + offset, PTR) + BField._initialize(p.contents, value) + is_union = CTypesStructOrUnion._kind == 'union' + name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) + # + for fname, BField, bitsize in fields: + if fname == '': + raise NotImplementedError("nested anonymous structs/unions") + if hasattr(CTypesStructOrUnion, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + if bitsize < 0: + def getter(self, fname=fname, BField=BField, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BField._from_ctypes(p.contents) + def setter(self, value, fname=fname, BField=BField): + setattr(self._blob, fname, BField._to_ctypes(value)) + # + if issubclass(BField, CTypesGenericArray): + setter = None + if BField._declared_length == 0: + def getter(self, fname=fname, BFieldPtr=BField._CTPtr, + offset=CTypesStructOrUnion._offsetof(fname), + PTR=ctypes.POINTER(BField._ctype)): + addr = ctypes.addressof(self._blob) + p = ctypes.cast(addr + offset, PTR) + return BFieldPtr._from_ctypes(p) + # + else: + def getter(self, fname=fname, BField=BField): + return BField._from_ctypes(getattr(self._blob, fname)) + def setter(self, value, fname=fname, BField=BField): + # xxx obscure workaround + value = BField._to_ctypes(value) + oldvalue = getattr(self._blob, fname) + setattr(self._blob, fname, value) + if value != getattr(self._blob, fname): + setattr(self._blob, fname, oldvalue) + raise OverflowError("value too large for bitfield") + setattr(CTypesStructOrUnion, fname, property(getter, setter)) + # + CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) + for fname in fnames: + if hasattr(CTypesPtr, fname): + raise ValueError("the field name %r conflicts in " + "the ctypes backend" % fname) + def getter(self, fname=fname): + return getattr(self[0], fname) + def setter(self, value, fname=fname): + setattr(self[0], fname, value) + setattr(CTypesPtr, fname, property(getter, setter)) + + def new_function_type(self, BArgs, BResult, has_varargs): + nameargs = [BArg._get_c_name() for BArg in BArgs] + if has_varargs: + nameargs.append('...') + nameargs = ', '.join(nameargs) + # + class CTypesFunctionPtr(CTypesGenericPtr): + __slots__ = ['_own_callback', '_name'] + _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), + *[BArg._ctype for BArg in BArgs], + use_errno=True) + _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) + + def __init__(self, init, error=None): + # create a callback to the Python callable init() + import traceback + assert not has_varargs, "varargs not supported for callbacks" + if getattr(BResult, '_ctype', None) is not None: + error = BResult._from_ctypes( + BResult._create_ctype_obj(error)) + else: + error = None + def callback(*args): + args2 = [] + for arg, BArg in zip(args, BArgs): + args2.append(BArg._from_ctypes(arg)) + try: + res2 = init(*args2) + res2 = BResult._to_ctypes(res2) + except: + traceback.print_exc() + res2 = error + if issubclass(BResult, CTypesGenericPtr): + if res2: + res2 = ctypes.cast(res2, ctypes.c_void_p).value + # .value: http://bugs.python.org/issue1574593 + else: + res2 = None + #print repr(res2) + return res2 + if issubclass(BResult, CTypesGenericPtr): + # The only pointers callbacks can return are void*s: + # http://bugs.python.org/issue5710 + callback_ctype = ctypes.CFUNCTYPE( + ctypes.c_void_p, + *[BArg._ctype for BArg in BArgs], + use_errno=True) + else: + callback_ctype = CTypesFunctionPtr._ctype + self._as_ctype_ptr = callback_ctype(callback) + self._address = ctypes.cast(self._as_ctype_ptr, + ctypes.c_void_p).value + self._own_callback = init + + @staticmethod + def _initialize(ctypes_ptr, value): + if value: + raise NotImplementedError("ctypes backend: not supported: " + "initializers for function pointers") + + def __repr__(self): + c_name = getattr(self, '_name', None) + if c_name: + i = self._reftypename.index('(* &)') + if self._reftypename[i-1] not in ' )*': + c_name = ' ' + c_name + c_name = self._reftypename.replace('(* &)', c_name) + return CTypesData.__repr__(self, c_name) + + def _get_own_repr(self): + if getattr(self, '_own_callback', None) is not None: + return 'calling %r' % (self._own_callback,) + return super(CTypesFunctionPtr, self)._get_own_repr() + + def __call__(self, *args): + if has_varargs: + assert len(args) >= len(BArgs) + extraargs = args[len(BArgs):] + args = args[:len(BArgs)] + else: + assert len(args) == len(BArgs) + ctypes_args = [] + for arg, BArg in zip(args, BArgs): + ctypes_args.append(BArg._arg_to_ctypes(arg)) + if has_varargs: + for i, arg in enumerate(extraargs): + if arg is None: + ctypes_args.append(ctypes.c_void_p(0)) # NULL + continue + if not isinstance(arg, CTypesData): + raise TypeError( + "argument %d passed in the variadic part " + "needs to be a cdata object (got %s)" % + (1 + len(BArgs) + i, type(arg).__name__)) + ctypes_args.append(arg._arg_to_ctypes(arg)) + result = self._as_ctype_ptr(*ctypes_args) + return BResult._from_ctypes(result) + # + CTypesFunctionPtr._fix_class() + return CTypesFunctionPtr + + def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): + assert isinstance(name, str) + reverse_mapping = dict(zip(reversed(enumvalues), + reversed(enumerators))) + # + class CTypesEnum(CTypesInt): + __slots__ = [] + _reftypename = '%s &' % name + + def _get_own_repr(self): + value = self._value + try: + return '%d: %s' % (value, reverse_mapping[value]) + except KeyError: + return str(value) + + def _to_string(self, maxlen): + value = self._value + try: + return reverse_mapping[value] + except KeyError: + return str(value) + # + CTypesEnum._fix_class() + return CTypesEnum + + def get_errno(self): + return ctypes.get_errno() + + def set_errno(self, value): + ctypes.set_errno(value) + + def string(self, b, maxlen=-1): + return b._to_string(maxlen) + + def buffer(self, bptr, size=-1): + raise NotImplementedError("buffer() with ctypes backend") + + def sizeof(self, cdata_or_BType): + if isinstance(cdata_or_BType, CTypesData): + return cdata_or_BType._get_size_of_instance() + else: + assert issubclass(cdata_or_BType, CTypesData) + return cdata_or_BType._get_size() + + def alignof(self, BType): + assert issubclass(BType, CTypesData) + return BType._alignment() + + def newp(self, BType, source): + if not issubclass(BType, CTypesData): + raise TypeError + return BType._newp(source) + + def cast(self, BType, source): + return BType._cast_from(source) + + def callback(self, BType, source, error, onerror): + assert onerror is None # XXX not implemented + return BType(source, error) + + _weakref_cache_ref = None + + def gcp(self, cdata, destructor, size=0): + if self._weakref_cache_ref is None: + import weakref + class MyRef(weakref.ref): + def __eq__(self, other): + myref = self() + return self is other or ( + myref is not None and myref is other()) + def __ne__(self, other): + return not (self == other) + def __hash__(self): + try: + return self._hash + except AttributeError: + self._hash = hash(self()) + return self._hash + self._weakref_cache_ref = {}, MyRef + weak_cache, MyRef = self._weakref_cache_ref + + if destructor is None: + try: + del weak_cache[MyRef(cdata)] + except KeyError: + raise TypeError("Can remove destructor only on a object " + "previously returned by ffi.gc()") + return None + + def remove(k): + cdata, destructor = weak_cache.pop(k, (None, None)) + if destructor is not None: + destructor(cdata) + + new_cdata = self.cast(self.typeof(cdata), cdata) + assert new_cdata is not cdata + weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) + return new_cdata + + typeof = type + + def getcname(self, BType, replace_with): + return BType._get_c_name(replace_with) + + def typeoffsetof(self, BType, fieldname, num=0): + if isinstance(fieldname, str): + if num == 0 and issubclass(BType, CTypesGenericPtr): + BType = BType._BItem + if not issubclass(BType, CTypesBaseStructOrUnion): + raise TypeError("expected a struct or union ctype") + BField = BType._bfield_types[fieldname] + if BField is Ellipsis: + raise TypeError("not supported for bitfields") + return (BField, BType._offsetof(fieldname)) + elif isinstance(fieldname, (int, long)): + if issubclass(BType, CTypesGenericArray): + BType = BType._CTPtr + if not issubclass(BType, CTypesGenericPtr): + raise TypeError("expected an array or ptr ctype") + BItem = BType._BItem + offset = BItem._get_size() * fieldname + if offset > sys.maxsize: + raise OverflowError + return (BItem, offset) + else: + raise TypeError(type(fieldname)) + + def rawaddressof(self, BTypePtr, cdata, offset=None): + if isinstance(cdata, CTypesBaseStructOrUnion): + ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) + elif isinstance(cdata, CTypesGenericPtr): + if offset is None or not issubclass(type(cdata)._BItem, + CTypesBaseStructOrUnion): + raise TypeError("unexpected cdata type") + ptr = type(cdata)._to_ctypes(cdata) + elif isinstance(cdata, CTypesGenericArray): + ptr = type(cdata)._to_ctypes(cdata) + else: + raise TypeError("expected a ") + if offset: + ptr = ctypes.cast( + ctypes.c_void_p( + ctypes.cast(ptr, ctypes.c_void_p).value + offset), + type(ptr)) + return BTypePtr._from_ctypes(ptr) + + +class CTypesLibrary(object): + + def __init__(self, backend, cdll): + self.backend = backend + self.cdll = cdll + + def load_function(self, BType, name): + c_func = getattr(self.cdll, name) + funcobj = BType._from_ctypes(c_func) + funcobj._name = name + return funcobj + + def read_variable(self, BType, name): + try: + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + except AttributeError as e: + raise NotImplementedError(e) + return BType._from_ctypes(ctypes_obj) + + def write_variable(self, BType, name, value): + new_ctypes_obj = BType._to_ctypes(value) + ctypes_obj = BType._ctype.in_dll(self.cdll, name) + ctypes.memmove(ctypes.addressof(ctypes_obj), + ctypes.addressof(new_ctypes_obj), + ctypes.sizeof(BType._ctype)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py new file mode 100644 index 000000000..b26ccc965 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py @@ -0,0 +1,187 @@ +from .error import VerificationError + +class CffiOp(object): + def __init__(self, op, arg): + self.op = op + self.arg = arg + + def as_c_expr(self): + if self.op is None: + assert isinstance(self.arg, str) + return '(_cffi_opcode_t)(%s)' % (self.arg,) + classname = CLASS_NAME[self.op] + return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) + + def as_python_bytes(self): + if self.op is None and self.arg.isdigit(): + value = int(self.arg) # non-negative: '-' not in self.arg + if value >= 2**31: + raise OverflowError("cannot emit %r: limited to 2**31-1" + % (self.arg,)) + return format_four_bytes(value) + if isinstance(self.arg, str): + raise VerificationError("cannot emit to Python: %r" % (self.arg,)) + return format_four_bytes((self.arg << 8) | self.op) + + def __str__(self): + classname = CLASS_NAME.get(self.op, self.op) + return '(%s %s)' % (classname, self.arg) + +def format_four_bytes(num): + return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( + (num >> 24) & 0xFF, + (num >> 16) & 0xFF, + (num >> 8) & 0xFF, + (num ) & 0xFF) + +OP_PRIMITIVE = 1 +OP_POINTER = 3 +OP_ARRAY = 5 +OP_OPEN_ARRAY = 7 +OP_STRUCT_UNION = 9 +OP_ENUM = 11 +OP_FUNCTION = 13 +OP_FUNCTION_END = 15 +OP_NOOP = 17 +OP_BITFIELD = 19 +OP_TYPENAME = 21 +OP_CPYTHON_BLTN_V = 23 # varargs +OP_CPYTHON_BLTN_N = 25 # noargs +OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) +OP_CONSTANT = 29 +OP_CONSTANT_INT = 31 +OP_GLOBAL_VAR = 33 +OP_DLOPEN_FUNC = 35 +OP_DLOPEN_CONST = 37 +OP_GLOBAL_VAR_F = 39 +OP_EXTERN_PYTHON = 41 + +PRIM_VOID = 0 +PRIM_BOOL = 1 +PRIM_CHAR = 2 +PRIM_SCHAR = 3 +PRIM_UCHAR = 4 +PRIM_SHORT = 5 +PRIM_USHORT = 6 +PRIM_INT = 7 +PRIM_UINT = 8 +PRIM_LONG = 9 +PRIM_ULONG = 10 +PRIM_LONGLONG = 11 +PRIM_ULONGLONG = 12 +PRIM_FLOAT = 13 +PRIM_DOUBLE = 14 +PRIM_LONGDOUBLE = 15 + +PRIM_WCHAR = 16 +PRIM_INT8 = 17 +PRIM_UINT8 = 18 +PRIM_INT16 = 19 +PRIM_UINT16 = 20 +PRIM_INT32 = 21 +PRIM_UINT32 = 22 +PRIM_INT64 = 23 +PRIM_UINT64 = 24 +PRIM_INTPTR = 25 +PRIM_UINTPTR = 26 +PRIM_PTRDIFF = 27 +PRIM_SIZE = 28 +PRIM_SSIZE = 29 +PRIM_INT_LEAST8 = 30 +PRIM_UINT_LEAST8 = 31 +PRIM_INT_LEAST16 = 32 +PRIM_UINT_LEAST16 = 33 +PRIM_INT_LEAST32 = 34 +PRIM_UINT_LEAST32 = 35 +PRIM_INT_LEAST64 = 36 +PRIM_UINT_LEAST64 = 37 +PRIM_INT_FAST8 = 38 +PRIM_UINT_FAST8 = 39 +PRIM_INT_FAST16 = 40 +PRIM_UINT_FAST16 = 41 +PRIM_INT_FAST32 = 42 +PRIM_UINT_FAST32 = 43 +PRIM_INT_FAST64 = 44 +PRIM_UINT_FAST64 = 45 +PRIM_INTMAX = 46 +PRIM_UINTMAX = 47 +PRIM_FLOATCOMPLEX = 48 +PRIM_DOUBLECOMPLEX = 49 +PRIM_CHAR16 = 50 +PRIM_CHAR32 = 51 + +_NUM_PRIM = 52 +_UNKNOWN_PRIM = -1 +_UNKNOWN_FLOAT_PRIM = -2 +_UNKNOWN_LONG_DOUBLE = -3 + +_IO_FILE_STRUCT = -1 + +PRIMITIVE_TO_INDEX = { + 'char': PRIM_CHAR, + 'short': PRIM_SHORT, + 'int': PRIM_INT, + 'long': PRIM_LONG, + 'long long': PRIM_LONGLONG, + 'signed char': PRIM_SCHAR, + 'unsigned char': PRIM_UCHAR, + 'unsigned short': PRIM_USHORT, + 'unsigned int': PRIM_UINT, + 'unsigned long': PRIM_ULONG, + 'unsigned long long': PRIM_ULONGLONG, + 'float': PRIM_FLOAT, + 'double': PRIM_DOUBLE, + 'long double': PRIM_LONGDOUBLE, + 'float _Complex': PRIM_FLOATCOMPLEX, + 'double _Complex': PRIM_DOUBLECOMPLEX, + '_Bool': PRIM_BOOL, + 'wchar_t': PRIM_WCHAR, + 'char16_t': PRIM_CHAR16, + 'char32_t': PRIM_CHAR32, + 'int8_t': PRIM_INT8, + 'uint8_t': PRIM_UINT8, + 'int16_t': PRIM_INT16, + 'uint16_t': PRIM_UINT16, + 'int32_t': PRIM_INT32, + 'uint32_t': PRIM_UINT32, + 'int64_t': PRIM_INT64, + 'uint64_t': PRIM_UINT64, + 'intptr_t': PRIM_INTPTR, + 'uintptr_t': PRIM_UINTPTR, + 'ptrdiff_t': PRIM_PTRDIFF, + 'size_t': PRIM_SIZE, + 'ssize_t': PRIM_SSIZE, + 'int_least8_t': PRIM_INT_LEAST8, + 'uint_least8_t': PRIM_UINT_LEAST8, + 'int_least16_t': PRIM_INT_LEAST16, + 'uint_least16_t': PRIM_UINT_LEAST16, + 'int_least32_t': PRIM_INT_LEAST32, + 'uint_least32_t': PRIM_UINT_LEAST32, + 'int_least64_t': PRIM_INT_LEAST64, + 'uint_least64_t': PRIM_UINT_LEAST64, + 'int_fast8_t': PRIM_INT_FAST8, + 'uint_fast8_t': PRIM_UINT_FAST8, + 'int_fast16_t': PRIM_INT_FAST16, + 'uint_fast16_t': PRIM_UINT_FAST16, + 'int_fast32_t': PRIM_INT_FAST32, + 'uint_fast32_t': PRIM_UINT_FAST32, + 'int_fast64_t': PRIM_INT_FAST64, + 'uint_fast64_t': PRIM_UINT_FAST64, + 'intmax_t': PRIM_INTMAX, + 'uintmax_t': PRIM_UINTMAX, + } + +F_UNION = 0x01 +F_CHECK_FIELDS = 0x02 +F_PACKED = 0x04 +F_EXTERNAL = 0x08 +F_OPAQUE = 0x10 + +G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) + for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', + 'F_EXTERNAL', 'F_OPAQUE']]) + +CLASS_NAME = {} +for _name, _value in list(globals().items()): + if _name.startswith('OP_') and isinstance(_value, int): + CLASS_NAME[_value] = _name[3:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py new file mode 100644 index 000000000..e5045ee2c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py @@ -0,0 +1,80 @@ +import sys +from . import model +from .error import FFIError + + +COMMON_TYPES = {} + +try: + # fetch "bool" and all simple Windows types + from _cffi_backend import _get_common_types + _get_common_types(COMMON_TYPES) +except ImportError: + pass + +COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') +COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above + +for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: + if _type.endswith('_t'): + COMMON_TYPES[_type] = _type +del _type + +_CACHE = {} + +def resolve_common_type(parser, commontype): + try: + return _CACHE[commontype] + except KeyError: + cdecl = COMMON_TYPES.get(commontype, commontype) + if not isinstance(cdecl, str): + result, quals = cdecl, 0 # cdecl is already a BaseType + elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: + result, quals = model.PrimitiveType(cdecl), 0 + elif cdecl == 'set-unicode-needed': + raise FFIError("The Windows type %r is only available after " + "you call ffi.set_unicode()" % (commontype,)) + else: + if commontype == cdecl: + raise FFIError( + "Unsupported type: %r. Please look at " + "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " + "and file an issue if you think this type should really " + "be supported." % (commontype,)) + result, quals = parser.parse_type_and_quals(cdecl) # recursive + + assert isinstance(result, model.BaseTypeByIdentity) + _CACHE[commontype] = result, quals + return result, quals + + +# ____________________________________________________________ +# extra types for Windows (most of them are in commontypes.c) + + +def win_common_types(): + return { + "UNICODE_STRING": model.StructType( + "_UNICODE_STRING", + ["Length", + "MaximumLength", + "Buffer"], + [model.PrimitiveType("unsigned short"), + model.PrimitiveType("unsigned short"), + model.PointerType(model.PrimitiveType("wchar_t"))], + [-1, -1, -1]), + "PUNICODE_STRING": "UNICODE_STRING *", + "PCUNICODE_STRING": "const UNICODE_STRING *", + + "TBYTE": "set-unicode-needed", + "TCHAR": "set-unicode-needed", + "LPCTSTR": "set-unicode-needed", + "PCTSTR": "set-unicode-needed", + "LPTSTR": "set-unicode-needed", + "PTSTR": "set-unicode-needed", + "PTBYTE": "set-unicode-needed", + "PTCHAR": "set-unicode-needed", + } + +if sys.platform == 'win32': + COMMON_TYPES.update(win_common_types()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py new file mode 100644 index 000000000..c4acd23aa --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py @@ -0,0 +1,1006 @@ +from . import model +from .commontypes import COMMON_TYPES, resolve_common_type +from .error import FFIError, CDefError +try: + from . import _pycparser as pycparser +except ImportError: + import pycparser +import weakref, re, sys + +try: + if sys.version_info < (3,): + import thread as _thread + else: + import _thread + lock = _thread.allocate_lock() +except ImportError: + lock = None + +def _workaround_for_static_import_finders(): + # Issue #392: packaging tools like cx_Freeze can not find these + # because pycparser uses exec dynamic import. This is an obscure + # workaround. This function is never called. + import pycparser.yacctab + import pycparser.lextab + +CDEF_SOURCE_STRING = "" +_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", + re.DOTALL | re.MULTILINE) +_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" + r"\b((?:[^\n\\]|\\.)*?)$", + re.DOTALL | re.MULTILINE) +_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) +_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") +_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") +_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") +_r_words = re.compile(r"\w+|\S") +_parser_cache = None +_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) +_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") +_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") +_r_cdecl = re.compile(r"\b__cdecl\b") +_r_extern_python = re.compile(r'\bextern\s*"' + r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') +_r_star_const_space = re.compile( # matches "* const " + r"[*]\s*((const|volatile|restrict)\b\s*)+") +_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" + r"\.\.\.") +_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") + +def _get_parser(): + global _parser_cache + if _parser_cache is None: + _parser_cache = pycparser.CParser() + return _parser_cache + +def _workaround_for_old_pycparser(csource): + # Workaround for a pycparser issue (fixed between pycparser 2.10 and + # 2.14): "char*const***" gives us a wrong syntax tree, the same as + # for "char***(*const)". This means we can't tell the difference + # afterwards. But "char(*const(***))" gives us the right syntax + # tree. The issue only occurs if there are several stars in + # sequence with no parenthesis inbetween, just possibly qualifiers. + # Attempt to fix it by adding some parentheses in the source: each + # time we see "* const" or "* const *", we add an opening + # parenthesis before each star---the hard part is figuring out where + # to close them. + parts = [] + while True: + match = _r_star_const_space.search(csource) + if not match: + break + #print repr(''.join(parts)+csource), '=>', + parts.append(csource[:match.start()]) + parts.append('('); closing = ')' + parts.append(match.group()) # e.g. "* const " + endpos = match.end() + if csource.startswith('*', endpos): + parts.append('('); closing += ')' + level = 0 + i = endpos + while i < len(csource): + c = csource[i] + if c == '(': + level += 1 + elif c == ')': + if level == 0: + break + level -= 1 + elif c in ',;=': + if level == 0: + break + i += 1 + csource = csource[endpos:i] + closing + csource[i:] + #print repr(''.join(parts)+csource) + parts.append(csource) + return ''.join(parts) + +def _preprocess_extern_python(csource): + # input: `extern "Python" int foo(int);` or + # `extern "Python" { int foo(int); }` + # output: + # void __cffi_extern_python_start; + # int foo(int); + # void __cffi_extern_python_stop; + # + # input: `extern "Python+C" int foo(int);` + # output: + # void __cffi_extern_python_plus_c_start; + # int foo(int); + # void __cffi_extern_python_stop; + parts = [] + while True: + match = _r_extern_python.search(csource) + if not match: + break + endpos = match.end() - 1 + #print + #print ''.join(parts)+csource + #print '=>' + parts.append(csource[:match.start()]) + if 'C' in match.group(1): + parts.append('void __cffi_extern_python_plus_c_start; ') + else: + parts.append('void __cffi_extern_python_start; ') + if csource[endpos] == '{': + # grouping variant + closing = csource.find('}', endpos) + if closing < 0: + raise CDefError("'extern \"Python\" {': no '}' found") + if csource.find('{', endpos + 1, closing) >= 0: + raise NotImplementedError("cannot use { } inside a block " + "'extern \"Python\" { ... }'") + parts.append(csource[endpos+1:closing]) + csource = csource[closing+1:] + else: + # non-grouping variant + semicolon = csource.find(';', endpos) + if semicolon < 0: + raise CDefError("'extern \"Python\": no ';' found") + parts.append(csource[endpos:semicolon+1]) + csource = csource[semicolon+1:] + parts.append(' void __cffi_extern_python_stop;') + #print ''.join(parts)+csource + #print + parts.append(csource) + return ''.join(parts) + +def _warn_for_string_literal(csource): + if '"' not in csource: + return + for line in csource.splitlines(): + if '"' in line and not line.lstrip().startswith('#'): + import warnings + warnings.warn("String literal found in cdef() or type source. " + "String literals are ignored here, but you should " + "remove them anyway because some character sequences " + "confuse pre-parsing.") + break + +def _warn_for_non_extern_non_static_global_variable(decl): + if not decl.storage: + import warnings + warnings.warn("Global variable '%s' in cdef(): for consistency " + "with C it should have a storage class specifier " + "(usually 'extern')" % (decl.name,)) + +def _remove_line_directives(csource): + # _r_line_directive matches whole lines, without the final \n, if they + # start with '#line' with some spacing allowed, or '#NUMBER'. This + # function stores them away and replaces them with exactly the string + # '#line@N', where N is the index in the list 'line_directives'. + line_directives = [] + def replace(m): + i = len(line_directives) + line_directives.append(m.group()) + return '#line@%d' % i + csource = _r_line_directive.sub(replace, csource) + return csource, line_directives + +def _put_back_line_directives(csource, line_directives): + def replace(m): + s = m.group() + if not s.startswith('#line@'): + raise AssertionError("unexpected #line directive " + "(should have been processed and removed") + return line_directives[int(s[6:])] + return _r_line_directive.sub(replace, csource) + +def _preprocess(csource): + # First, remove the lines of the form '#line N "filename"' because + # the "filename" part could confuse the rest + csource, line_directives = _remove_line_directives(csource) + # Remove comments. NOTE: this only work because the cdef() section + # should not contain any string literals (except in line directives)! + def replace_keeping_newlines(m): + return ' ' + m.group().count('\n') * '\n' + csource = _r_comment.sub(replace_keeping_newlines, csource) + # Remove the "#define FOO x" lines + macros = {} + for match in _r_define.finditer(csource): + macroname, macrovalue = match.groups() + macrovalue = macrovalue.replace('\\\n', '').strip() + macros[macroname] = macrovalue + csource = _r_define.sub('', csource) + # + if pycparser.__version__ < '2.14': + csource = _workaround_for_old_pycparser(csource) + # + # BIG HACK: replace WINAPI or __stdcall with "volatile const". + # It doesn't make sense for the return type of a function to be + # "volatile volatile const", so we abuse it to detect __stdcall... + # Hack number 2 is that "int(volatile *fptr)();" is not valid C + # syntax, so we place the "volatile" before the opening parenthesis. + csource = _r_stdcall2.sub(' volatile volatile const(', csource) + csource = _r_stdcall1.sub(' volatile volatile const ', csource) + csource = _r_cdecl.sub(' ', csource) + # + # Replace `extern "Python"` with start/end markers + csource = _preprocess_extern_python(csource) + # + # Now there should not be any string literal left; warn if we get one + _warn_for_string_literal(csource) + # + # Replace "[...]" with "[__dotdotdotarray__]" + csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) + # + # Replace "...}" with "__dotdotdotNUM__}". This construction should + # occur only at the end of enums; at the end of structs we have "...;}" + # and at the end of vararg functions "...);". Also replace "=...[,}]" + # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when + # giving an unknown value. + matches = list(_r_partial_enum.finditer(csource)) + for number, match in enumerate(reversed(matches)): + p = match.start() + if csource[p] == '=': + p2 = csource.find('...', p, match.end()) + assert p2 > p + csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, + csource[p2+3:]) + else: + assert csource[p:p+3] == '...' + csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, + csource[p+3:]) + # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" + csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) + # Replace "float ..." or "double..." with "__dotdotdotfloat__" + csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) + # Replace all remaining "..." with the same name, "__dotdotdot__", + # which is declared with a typedef for the purpose of C parsing. + csource = csource.replace('...', ' __dotdotdot__ ') + # Finally, put back the line directives + csource = _put_back_line_directives(csource, line_directives) + return csource, macros + +def _common_type_names(csource): + # Look in the source for what looks like usages of types from the + # list of common types. A "usage" is approximated here as the + # appearance of the word, minus a "definition" of the type, which + # is the last word in a "typedef" statement. Approximative only + # but should be fine for all the common types. + look_for_words = set(COMMON_TYPES) + look_for_words.add(';') + look_for_words.add(',') + look_for_words.add('(') + look_for_words.add(')') + look_for_words.add('typedef') + words_used = set() + is_typedef = False + paren = 0 + previous_word = '' + for word in _r_words.findall(csource): + if word in look_for_words: + if word == ';': + if is_typedef: + words_used.discard(previous_word) + look_for_words.discard(previous_word) + is_typedef = False + elif word == 'typedef': + is_typedef = True + paren = 0 + elif word == '(': + paren += 1 + elif word == ')': + paren -= 1 + elif word == ',': + if is_typedef and paren == 0: + words_used.discard(previous_word) + look_for_words.discard(previous_word) + else: # word in COMMON_TYPES + words_used.add(word) + previous_word = word + return words_used + + +class Parser(object): + + def __init__(self): + self._declarations = {} + self._included_declarations = set() + self._anonymous_counter = 0 + self._structnode2type = weakref.WeakKeyDictionary() + self._options = {} + self._int_constants = {} + self._recomplete = [] + self._uses_new_feature = None + + def _parse(self, csource): + csource, macros = _preprocess(csource) + # XXX: for more efficiency we would need to poke into the + # internals of CParser... the following registers the + # typedefs, because their presence or absence influences the + # parsing itself (but what they are typedef'ed to plays no role) + ctn = _common_type_names(csource) + typenames = [] + for name in sorted(self._declarations): + if name.startswith('typedef '): + name = name[8:] + typenames.append(name) + ctn.discard(name) + typenames += sorted(ctn) + # + csourcelines = [] + csourcelines.append('# 1 ""') + for typename in typenames: + csourcelines.append('typedef int %s;' % typename) + csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' + ' __dotdotdot__;') + # this forces pycparser to consider the following in the file + # called from line 1 + csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) + csourcelines.append(csource) + fullcsource = '\n'.join(csourcelines) + if lock is not None: + lock.acquire() # pycparser is not thread-safe... + try: + ast = _get_parser().parse(fullcsource) + except pycparser.c_parser.ParseError as e: + self.convert_pycparser_error(e, csource) + finally: + if lock is not None: + lock.release() + # csource will be used to find buggy source text + return ast, macros, csource + + def _convert_pycparser_error(self, e, csource): + # xxx look for ":NUM:" at the start of str(e) + # and interpret that as a line number. This will not work if + # the user gives explicit ``# NUM "FILE"`` directives. + line = None + msg = str(e) + match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) + if match: + linenum = int(match.group(1), 10) + csourcelines = csource.splitlines() + if 1 <= linenum <= len(csourcelines): + line = csourcelines[linenum-1] + return line + + def convert_pycparser_error(self, e, csource): + line = self._convert_pycparser_error(e, csource) + + msg = str(e) + if line: + msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) + else: + msg = 'parse error\n%s' % (msg,) + raise CDefError(msg) + + def parse(self, csource, override=False, packed=False, pack=None, + dllexport=False): + if packed: + if packed != True: + raise ValueError("'packed' should be False or True; use " + "'pack' to give another value") + if pack: + raise ValueError("cannot give both 'pack' and 'packed'") + pack = 1 + elif pack: + if pack & (pack - 1): + raise ValueError("'pack' must be a power of two, not %r" % + (pack,)) + else: + pack = 0 + prev_options = self._options + try: + self._options = {'override': override, + 'packed': pack, + 'dllexport': dllexport} + self._internal_parse(csource) + finally: + self._options = prev_options + + def _internal_parse(self, csource): + ast, macros, csource = self._parse(csource) + # add the macros + self._process_macros(macros) + # find the first "__dotdotdot__" and use that as a separator + # between the repeated typedefs and the real csource + iterator = iter(ast.ext) + for decl in iterator: + if decl.name == '__dotdotdot__': + break + else: + assert 0 + current_decl = None + # + try: + self._inside_extern_python = '__cffi_extern_python_stop' + for decl in iterator: + current_decl = decl + if isinstance(decl, pycparser.c_ast.Decl): + self._parse_decl(decl) + elif isinstance(decl, pycparser.c_ast.Typedef): + if not decl.name: + raise CDefError("typedef does not declare any name", + decl) + quals = 0 + if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and + decl.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_type(decl) + elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and + isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and + isinstance(decl.type.type.type, + pycparser.c_ast.IdentifierType) and + decl.type.type.type.names[-1].startswith('__dotdotdot')): + realtype = self._get_unknown_ptr_type(decl) + else: + realtype, quals = self._get_type_and_quals( + decl.type, name=decl.name, partial_length_ok=True, + typedef_example="*(%s *)0" % (decl.name,)) + self._declare('typedef ' + decl.name, realtype, quals=quals) + elif decl.__class__.__name__ == 'Pragma': + pass # skip pragma, only in pycparser 2.15 + else: + raise CDefError("unexpected <%s>: this construct is valid " + "C but not valid in cdef()" % + decl.__class__.__name__, decl) + except CDefError as e: + if len(e.args) == 1: + e.args = e.args + (current_decl,) + raise + except FFIError as e: + msg = self._convert_pycparser_error(e, csource) + if msg: + e.args = (e.args[0] + "\n *** Err: %s" % msg,) + raise + + def _add_constants(self, key, val): + if key in self._int_constants: + if self._int_constants[key] == val: + return # ignore identical double declarations + raise FFIError( + "multiple declarations of constant: %s" % (key,)) + self._int_constants[key] = val + + def _add_integer_constant(self, name, int_str): + int_str = int_str.lower().rstrip("ul") + neg = int_str.startswith('-') + if neg: + int_str = int_str[1:] + # "010" is not valid oct in py3 + if (int_str.startswith("0") and int_str != '0' + and not int_str.startswith("0x")): + int_str = "0o" + int_str[1:] + pyvalue = int(int_str, 0) + if neg: + pyvalue = -pyvalue + self._add_constants(name, pyvalue) + self._declare('macro ' + name, pyvalue) + + def _process_macros(self, macros): + for key, value in macros.items(): + value = value.strip() + if _r_int_literal.match(value): + self._add_integer_constant(key, value) + elif value == '...': + self._declare('macro ' + key, value) + else: + raise CDefError( + 'only supports one of the following syntax:\n' + ' #define %s ... (literally dot-dot-dot)\n' + ' #define %s NUMBER (with NUMBER an integer' + ' constant, decimal/hex/octal)\n' + 'got:\n' + ' #define %s %s' + % (key, key, key, value)) + + def _declare_function(self, tp, quals, decl): + tp = self._get_type_pointer(tp, quals) + if self._options.get('dllexport'): + tag = 'dllexport_python ' + elif self._inside_extern_python == '__cffi_extern_python_start': + tag = 'extern_python ' + elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': + tag = 'extern_python_plus_c ' + else: + tag = 'function ' + self._declare(tag + decl.name, tp) + + def _parse_decl(self, decl): + node = decl.type + if isinstance(node, pycparser.c_ast.FuncDecl): + tp, quals = self._get_type_and_quals(node, name=decl.name) + assert isinstance(tp, model.RawFunctionType) + self._declare_function(tp, quals, decl) + else: + if isinstance(node, pycparser.c_ast.Struct): + self._get_struct_union_enum_type('struct', node) + elif isinstance(node, pycparser.c_ast.Union): + self._get_struct_union_enum_type('union', node) + elif isinstance(node, pycparser.c_ast.Enum): + self._get_struct_union_enum_type('enum', node) + elif not decl.name: + raise CDefError("construct does not declare any variable", + decl) + # + if decl.name: + tp, quals = self._get_type_and_quals(node, + partial_length_ok=True) + if tp.is_raw_function: + self._declare_function(tp, quals, decl) + elif (tp.is_integer_type() and + hasattr(decl, 'init') and + hasattr(decl.init, 'value') and + _r_int_literal.match(decl.init.value)): + self._add_integer_constant(decl.name, decl.init.value) + elif (tp.is_integer_type() and + isinstance(decl.init, pycparser.c_ast.UnaryOp) and + decl.init.op == '-' and + hasattr(decl.init.expr, 'value') and + _r_int_literal.match(decl.init.expr.value)): + self._add_integer_constant(decl.name, + '-' + decl.init.expr.value) + elif (tp is model.void_type and + decl.name.startswith('__cffi_extern_python_')): + # hack: `extern "Python"` in the C source is replaced + # with "void __cffi_extern_python_start;" and + # "void __cffi_extern_python_stop;" + self._inside_extern_python = decl.name + else: + if self._inside_extern_python !='__cffi_extern_python_stop': + raise CDefError( + "cannot declare constants or " + "variables with 'extern \"Python\"'") + if (quals & model.Q_CONST) and not tp.is_array_type: + self._declare('constant ' + decl.name, tp, quals=quals) + else: + _warn_for_non_extern_non_static_global_variable(decl) + self._declare('variable ' + decl.name, tp, quals=quals) + + def parse_type(self, cdecl): + return self.parse_type_and_quals(cdecl)[0] + + def parse_type_and_quals(self, cdecl): + ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] + assert not macros + exprnode = ast.ext[-1].type.args.params[0] + if isinstance(exprnode, pycparser.c_ast.ID): + raise CDefError("unknown identifier '%s'" % (exprnode.name,)) + return self._get_type_and_quals(exprnode.type) + + def _declare(self, name, obj, included=False, quals=0): + if name in self._declarations: + prevobj, prevquals = self._declarations[name] + if prevobj is obj and prevquals == quals: + return + if not self._options.get('override'): + raise FFIError( + "multiple declarations of %s (for interactive usage, " + "try cdef(xx, override=True))" % (name,)) + assert '__dotdotdot__' not in name.split() + self._declarations[name] = (obj, quals) + if included: + self._included_declarations.add(obj) + + def _extract_quals(self, type): + quals = 0 + if isinstance(type, (pycparser.c_ast.TypeDecl, + pycparser.c_ast.PtrDecl)): + if 'const' in type.quals: + quals |= model.Q_CONST + if 'volatile' in type.quals: + quals |= model.Q_VOLATILE + if 'restrict' in type.quals: + quals |= model.Q_RESTRICT + return quals + + def _get_type_pointer(self, type, quals, declname=None): + if isinstance(type, model.RawFunctionType): + return type.as_function_pointer() + if (isinstance(type, model.StructOrUnionOrEnum) and + type.name.startswith('$') and type.name[1:].isdigit() and + type.forcename is None and declname is not None): + return model.NamedPointerType(type, declname, quals) + return model.PointerType(type, quals) + + def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, + typedef_example=None): + # first, dereference typedefs, if we have it already parsed, we're good + if (isinstance(typenode, pycparser.c_ast.TypeDecl) and + isinstance(typenode.type, pycparser.c_ast.IdentifierType) and + len(typenode.type.names) == 1 and + ('typedef ' + typenode.type.names[0]) in self._declarations): + tp, quals = self._declarations['typedef ' + typenode.type.names[0]] + quals |= self._extract_quals(typenode) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.ArrayDecl): + # array type + if typenode.dim is None: + length = None + else: + length = self._parse_constant( + typenode.dim, partial_length_ok=partial_length_ok) + # a hack: in 'typedef int foo_t[...][...];', don't use '...' as + # the length but use directly the C expression that would be + # generated by recompiler.py. This lets the typedef be used in + # many more places within recompiler.py + if typedef_example is not None: + if length == '...': + length = '_cffi_array_len(%s)' % (typedef_example,) + typedef_example = "*" + typedef_example + # + tp, quals = self._get_type_and_quals(typenode.type, + partial_length_ok=partial_length_ok, + typedef_example=typedef_example) + return model.ArrayType(tp, length), quals + # + if isinstance(typenode, pycparser.c_ast.PtrDecl): + # pointer type + itemtype, itemquals = self._get_type_and_quals(typenode.type) + tp = self._get_type_pointer(itemtype, itemquals, declname=name) + quals = self._extract_quals(typenode) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.TypeDecl): + quals = self._extract_quals(typenode) + type = typenode.type + if isinstance(type, pycparser.c_ast.IdentifierType): + # assume a primitive type. get it from .names, but reduce + # synonyms to a single chosen combination + names = list(type.names) + if names != ['signed', 'char']: # keep this unmodified + prefixes = {} + while names: + name = names[0] + if name in ('short', 'long', 'signed', 'unsigned'): + prefixes[name] = prefixes.get(name, 0) + 1 + del names[0] + else: + break + # ignore the 'signed' prefix below, and reorder the others + newnames = [] + for prefix in ('unsigned', 'short', 'long'): + for i in range(prefixes.get(prefix, 0)): + newnames.append(prefix) + if not names: + names = ['int'] # implicitly + if names == ['int']: # but kill it if 'short' or 'long' + if 'short' in prefixes or 'long' in prefixes: + names = [] + names = newnames + names + ident = ' '.join(names) + if ident == 'void': + return model.void_type, quals + if ident == '__dotdotdot__': + raise FFIError(':%d: bad usage of "..."' % + typenode.coord.line) + tp0, quals0 = resolve_common_type(self, ident) + return tp0, (quals | quals0) + # + if isinstance(type, pycparser.c_ast.Struct): + # 'struct foobar' + tp = self._get_struct_union_enum_type('struct', type, name) + return tp, quals + # + if isinstance(type, pycparser.c_ast.Union): + # 'union foobar' + tp = self._get_struct_union_enum_type('union', type, name) + return tp, quals + # + if isinstance(type, pycparser.c_ast.Enum): + # 'enum foobar' + tp = self._get_struct_union_enum_type('enum', type, name) + return tp, quals + # + if isinstance(typenode, pycparser.c_ast.FuncDecl): + # a function type + return self._parse_function_type(typenode, name), 0 + # + # nested anonymous structs or unions end up here + if isinstance(typenode, pycparser.c_ast.Struct): + return self._get_struct_union_enum_type('struct', typenode, name, + nested=True), 0 + if isinstance(typenode, pycparser.c_ast.Union): + return self._get_struct_union_enum_type('union', typenode, name, + nested=True), 0 + # + raise FFIError(":%d: bad or unsupported type declaration" % + typenode.coord.line) + + def _parse_function_type(self, typenode, funcname=None): + params = list(getattr(typenode.args, 'params', [])) + for i, arg in enumerate(params): + if not hasattr(arg, 'type'): + raise CDefError("%s arg %d: unknown type '%s'" + " (if you meant to use the old C syntax of giving" + " untyped arguments, it is not supported)" + % (funcname or 'in expression', i + 1, + getattr(arg, 'name', '?'))) + ellipsis = ( + len(params) > 0 and + isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and + isinstance(params[-1].type.type, + pycparser.c_ast.IdentifierType) and + params[-1].type.type.names == ['__dotdotdot__']) + if ellipsis: + params.pop() + if not params: + raise CDefError( + "%s: a function with only '(...)' as argument" + " is not correct C" % (funcname or 'in expression')) + args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) + for argdeclnode in params] + if not ellipsis and args == [model.void_type]: + args = [] + result, quals = self._get_type_and_quals(typenode.type) + # the 'quals' on the result type are ignored. HACK: we absure them + # to detect __stdcall functions: we textually replace "__stdcall" + # with "volatile volatile const" above. + abi = None + if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway + if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: + abi = '__stdcall' + return model.RawFunctionType(tuple(args), result, ellipsis, abi) + + def _as_func_arg(self, type, quals): + if isinstance(type, model.ArrayType): + return model.PointerType(type.item, quals) + elif isinstance(type, model.RawFunctionType): + return type.as_function_pointer() + else: + return type + + def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): + # First, a level of caching on the exact 'type' node of the AST. + # This is obscure, but needed because pycparser "unrolls" declarations + # such as "typedef struct { } foo_t, *foo_p" and we end up with + # an AST that is not a tree, but a DAG, with the "type" node of the + # two branches foo_t and foo_p of the trees being the same node. + # It's a bit silly but detecting "DAG-ness" in the AST tree seems + # to be the only way to distinguish this case from two independent + # structs. See test_struct_with_two_usages. + try: + return self._structnode2type[type] + except KeyError: + pass + # + # Note that this must handle parsing "struct foo" any number of + # times and always return the same StructType object. Additionally, + # one of these times (not necessarily the first), the fields of + # the struct can be specified with "struct foo { ...fields... }". + # If no name is given, then we have to create a new anonymous struct + # with no caching; in this case, the fields are either specified + # right now or never. + # + force_name = name + name = type.name + # + # get the type or create it if needed + if name is None: + # 'force_name' is used to guess a more readable name for + # anonymous structs, for the common case "typedef struct { } foo". + if force_name is not None: + explicit_name = '$%s' % force_name + else: + self._anonymous_counter += 1 + explicit_name = '$%d' % self._anonymous_counter + tp = None + else: + explicit_name = name + key = '%s %s' % (kind, name) + tp, _ = self._declarations.get(key, (None, None)) + # + if tp is None: + if kind == 'struct': + tp = model.StructType(explicit_name, None, None, None) + elif kind == 'union': + tp = model.UnionType(explicit_name, None, None, None) + elif kind == 'enum': + if explicit_name == '__dotdotdot__': + raise CDefError("Enums cannot be declared with ...") + tp = self._build_enum_type(explicit_name, type.values) + else: + raise AssertionError("kind = %r" % (kind,)) + if name is not None: + self._declare(key, tp) + else: + if kind == 'enum' and type.values is not None: + raise NotImplementedError( + "enum %s: the '{}' declaration should appear on the first " + "time the enum is mentioned, not later" % explicit_name) + if not tp.forcename: + tp.force_the_name(force_name) + if tp.forcename and '$' in tp.name: + self._declare('anonymous %s' % tp.forcename, tp) + # + self._structnode2type[type] = tp + # + # enums: done here + if kind == 'enum': + return tp + # + # is there a 'type.decls'? If yes, then this is the place in the + # C sources that declare the fields. If no, then just return the + # existing type, possibly still incomplete. + if type.decls is None: + return tp + # + if tp.fldnames is not None: + raise CDefError("duplicate declaration of struct %s" % name) + fldnames = [] + fldtypes = [] + fldbitsize = [] + fldquals = [] + for decl in type.decls: + if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and + ''.join(decl.type.names) == '__dotdotdot__'): + # XXX pycparser is inconsistent: 'names' should be a list + # of strings, but is sometimes just one string. Use + # str.join() as a way to cope with both. + self._make_partial(tp, nested) + continue + if decl.bitsize is None: + bitsize = -1 + else: + bitsize = self._parse_constant(decl.bitsize) + self._partial_length = False + type, fqual = self._get_type_and_quals(decl.type, + partial_length_ok=True) + if self._partial_length: + self._make_partial(tp, nested) + if isinstance(type, model.StructType) and type.partial: + self._make_partial(tp, nested) + fldnames.append(decl.name or '') + fldtypes.append(type) + fldbitsize.append(bitsize) + fldquals.append(fqual) + tp.fldnames = tuple(fldnames) + tp.fldtypes = tuple(fldtypes) + tp.fldbitsize = tuple(fldbitsize) + tp.fldquals = tuple(fldquals) + if fldbitsize != [-1] * len(fldbitsize): + if isinstance(tp, model.StructType) and tp.partial: + raise NotImplementedError("%s: using both bitfields and '...;'" + % (tp,)) + tp.packed = self._options.get('packed') + if tp.completed: # must be re-completed: it is not opaque any more + tp.completed = 0 + self._recomplete.append(tp) + return tp + + def _make_partial(self, tp, nested): + if not isinstance(tp, model.StructOrUnion): + raise CDefError("%s cannot be partial" % (tp,)) + if not tp.has_c_name() and not nested: + raise NotImplementedError("%s is partial but has no C name" %(tp,)) + tp.partial = True + + def _parse_constant(self, exprnode, partial_length_ok=False): + # for now, limited to expressions that are an immediate number + # or positive/negative number + if isinstance(exprnode, pycparser.c_ast.Constant): + s = exprnode.value + if '0' <= s[0] <= '9': + s = s.rstrip('uUlL') + try: + if s.startswith('0'): + return int(s, 8) + else: + return int(s, 10) + except ValueError: + if len(s) > 1: + if s.lower()[0:2] == '0x': + return int(s, 16) + elif s.lower()[0:2] == '0b': + return int(s, 2) + raise CDefError("invalid constant %r" % (s,)) + elif s[0] == "'" and s[-1] == "'" and ( + len(s) == 3 or (len(s) == 4 and s[1] == "\\")): + return ord(s[-2]) + else: + raise CDefError("invalid constant %r" % (s,)) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '+'): + return self._parse_constant(exprnode.expr) + # + if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and + exprnode.op == '-'): + return -self._parse_constant(exprnode.expr) + # load previously defined int constant + if (isinstance(exprnode, pycparser.c_ast.ID) and + exprnode.name in self._int_constants): + return self._int_constants[exprnode.name] + # + if (isinstance(exprnode, pycparser.c_ast.ID) and + exprnode.name == '__dotdotdotarray__'): + if partial_length_ok: + self._partial_length = True + return '...' + raise FFIError(":%d: unsupported '[...]' here, cannot derive " + "the actual array length in this context" + % exprnode.coord.line) + # + if isinstance(exprnode, pycparser.c_ast.BinaryOp): + left = self._parse_constant(exprnode.left) + right = self._parse_constant(exprnode.right) + if exprnode.op == '+': + return left + right + elif exprnode.op == '-': + return left - right + elif exprnode.op == '*': + return left * right + elif exprnode.op == '/': + return self._c_div(left, right) + elif exprnode.op == '%': + return left - self._c_div(left, right) * right + elif exprnode.op == '<<': + return left << right + elif exprnode.op == '>>': + return left >> right + elif exprnode.op == '&': + return left & right + elif exprnode.op == '|': + return left | right + elif exprnode.op == '^': + return left ^ right + # + raise FFIError(":%d: unsupported expression: expected a " + "simple numeric constant" % exprnode.coord.line) + + def _c_div(self, a, b): + result = a // b + if ((a < 0) ^ (b < 0)) and (a % b) != 0: + result += 1 + return result + + def _build_enum_type(self, explicit_name, decls): + if decls is not None: + partial = False + enumerators = [] + enumvalues = [] + nextenumvalue = 0 + for enum in decls.enumerators: + if _r_enum_dotdotdot.match(enum.name): + partial = True + continue + if enum.value is not None: + nextenumvalue = self._parse_constant(enum.value) + enumerators.append(enum.name) + enumvalues.append(nextenumvalue) + self._add_constants(enum.name, nextenumvalue) + nextenumvalue += 1 + enumerators = tuple(enumerators) + enumvalues = tuple(enumvalues) + tp = model.EnumType(explicit_name, enumerators, enumvalues) + tp.partial = partial + else: # opaque enum + tp = model.EnumType(explicit_name, (), ()) + return tp + + def include(self, other): + for name, (tp, quals) in other._declarations.items(): + if name.startswith('anonymous $enum_$'): + continue # fix for test_anonymous_enum_include + kind = name.split(' ', 1)[0] + if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): + self._declare(name, tp, included=True, quals=quals) + for k, v in other._int_constants.items(): + self._add_constants(k, v) + + def _get_unknown_type(self, decl): + typenames = decl.type.type.names + if typenames == ['__dotdotdot__']: + return model.unknown_type(decl.name) + + if typenames == ['__dotdotdotint__']: + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef int... %s'" % decl.name + return model.UnknownIntegerType(decl.name) + + if typenames == ['__dotdotdotfloat__']: + # note: not for 'long double' so far + if self._uses_new_feature is None: + self._uses_new_feature = "'typedef float... %s'" % decl.name + return model.UnknownFloatType(decl.name) + + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) + + def _get_unknown_ptr_type(self, decl): + if decl.type.type.type.names == ['__dotdotdot__']: + return model.unknown_ptr_type(decl.name) + raise FFIError(':%d: unsupported usage of "..." in typedef' + % decl.coord.line) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py new file mode 100644 index 000000000..0f8f406f5 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py @@ -0,0 +1,31 @@ + +class FFIError(Exception): + __module__ = 'cffi' + +class CDefError(Exception): + __module__ = 'cffi' + def __str__(self): + try: + current_decl = self.args[1] + filename = current_decl.coord.file + linenum = current_decl.coord.line + prefix = '%s:%d: ' % (filename, linenum) + except (AttributeError, TypeError, IndexError): + prefix = '' + return '%s%s' % (prefix, self.args[0]) + +class VerificationError(Exception): + """ An error raised when verification fails + """ + __module__ = 'cffi' + +class VerificationMissing(Exception): + """ An error raised when incomplete structures are passed into + cdef, but no verification has been done + """ + __module__ = 'cffi' + +class PkgConfigError(Exception): + """ An error raised for missing modules in pkg-config + """ + __module__ = 'cffi' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py new file mode 100644 index 000000000..0feab6ee9 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py @@ -0,0 +1,127 @@ +import sys, os +from .error import VerificationError + + +LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', + 'extra_objects', 'depends'] + +def get_extension(srcfilename, modname, sources=(), **kwds): + _hack_at_distutils() + from distutils.core import Extension + allsources = [srcfilename] + for src in sources: + allsources.append(os.path.normpath(src)) + return Extension(name=modname, sources=allsources, **kwds) + +def compile(tmpdir, ext, compiler_verbose=0, debug=None): + """Compile a C extension module using distutils.""" + + _hack_at_distutils() + saved_environ = os.environ.copy() + try: + outputfilename = _build(tmpdir, ext, compiler_verbose, debug) + outputfilename = os.path.abspath(outputfilename) + finally: + # workaround for a distutils bugs where some env vars can + # become longer and longer every time it is used + for key, value in saved_environ.items(): + if os.environ.get(key) != value: + os.environ[key] = value + return outputfilename + +def _build(tmpdir, ext, compiler_verbose=0, debug=None): + # XXX compact but horrible :-( + from distutils.core import Distribution + import distutils.errors, distutils.log + # + dist = Distribution({'ext_modules': [ext]}) + dist.parse_config_files() + options = dist.get_option_dict('build_ext') + if debug is None: + debug = sys.flags.debug + options['debug'] = ('ffiplatform', debug) + options['force'] = ('ffiplatform', True) + options['build_lib'] = ('ffiplatform', tmpdir) + options['build_temp'] = ('ffiplatform', tmpdir) + # + try: + old_level = distutils.log.set_threshold(0) or 0 + try: + distutils.log.set_verbosity(compiler_verbose) + dist.run_command('build_ext') + cmd_obj = dist.get_command_obj('build_ext') + [soname] = cmd_obj.get_outputs() + finally: + distutils.log.set_threshold(old_level) + except (distutils.errors.CompileError, + distutils.errors.LinkError) as e: + raise VerificationError('%s: %s' % (e.__class__.__name__, e)) + # + return soname + +try: + from os.path import samefile +except ImportError: + def samefile(f1, f2): + return os.path.abspath(f1) == os.path.abspath(f2) + +def maybe_relative_path(path): + if not os.path.isabs(path): + return path # already relative + dir = path + names = [] + while True: + prevdir = dir + dir, name = os.path.split(prevdir) + if dir == prevdir or not dir: + return path # failed to make it relative + names.append(name) + try: + if samefile(dir, os.curdir): + names.reverse() + return os.path.join(*names) + except OSError: + pass + +# ____________________________________________________________ + +try: + int_or_long = (int, long) + import cStringIO +except NameError: + int_or_long = int # Python 3 + import io as cStringIO + +def _flatten(x, f): + if isinstance(x, str): + f.write('%ds%s' % (len(x), x)) + elif isinstance(x, dict): + keys = sorted(x.keys()) + f.write('%dd' % len(keys)) + for key in keys: + _flatten(key, f) + _flatten(x[key], f) + elif isinstance(x, (list, tuple)): + f.write('%dl' % len(x)) + for value in x: + _flatten(value, f) + elif isinstance(x, int_or_long): + f.write('%di' % (x,)) + else: + raise TypeError( + "the keywords to verify() contains unsupported object %r" % (x,)) + +def flatten(x): + f = cStringIO.StringIO() + _flatten(x, f) + return f.getvalue() + +def _hack_at_distutils(): + # Windows-only workaround for some configurations: see + # https://bugs.python.org/issue23246 (Python 2.7 with + # a specific MS compiler suite download) + if sys.platform == "win32": + try: + import setuptools # for side-effects, patches distutils + except ImportError: + pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py new file mode 100644 index 000000000..2e40ed857 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py @@ -0,0 +1,30 @@ +import sys + +if sys.version_info < (3,): + try: + from thread import allocate_lock + except ImportError: + from dummy_thread import allocate_lock +else: + try: + from _thread import allocate_lock + except ImportError: + from _dummy_thread import allocate_lock + + +##import sys +##l1 = allocate_lock + +##class allocate_lock(object): +## def __init__(self): +## self._real = l1() +## def __enter__(self): +## for i in range(4, 0, -1): +## print sys._getframe(i).f_code +## print +## return self._real.__enter__() +## def __exit__(self, *args): +## return self._real.__exit__(*args) +## def acquire(self, f): +## assert f is False +## return self._real.acquire(f) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py new file mode 100644 index 000000000..065e8c0a7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py @@ -0,0 +1,617 @@ +import types +import weakref + +from .lock import allocate_lock +from .error import CDefError, VerificationError, VerificationMissing + +# type qualifiers +Q_CONST = 0x01 +Q_RESTRICT = 0x02 +Q_VOLATILE = 0x04 + +def qualify(quals, replace_with): + if quals & Q_CONST: + replace_with = ' const ' + replace_with.lstrip() + if quals & Q_VOLATILE: + replace_with = ' volatile ' + replace_with.lstrip() + if quals & Q_RESTRICT: + # It seems that __restrict is supported by gcc and msvc. + # If you hit some different compiler, add a #define in + # _cffi_include.h for it (and in its copies, documented there) + replace_with = ' __restrict ' + replace_with.lstrip() + return replace_with + + +class BaseTypeByIdentity(object): + is_array_type = False + is_raw_function = False + + def get_c_name(self, replace_with='', context='a C file', quals=0): + result = self.c_name_with_marker + assert result.count('&') == 1 + # some logic duplication with ffi.getctype()... :-( + replace_with = replace_with.strip() + if replace_with: + if replace_with.startswith('*') and '&[' in result: + replace_with = '(%s)' % replace_with + elif not replace_with[0] in '[(': + replace_with = ' ' + replace_with + replace_with = qualify(quals, replace_with) + result = result.replace('&', replace_with) + if '$' in result: + raise VerificationError( + "cannot generate '%s' in %s: unknown type name" + % (self._get_c_name(), context)) + return result + + def _get_c_name(self): + return self.c_name_with_marker.replace('&', '') + + def has_c_name(self): + return '$' not in self._get_c_name() + + def is_integer_type(self): + return False + + def get_cached_btype(self, ffi, finishlist, can_delay=False): + try: + BType = ffi._cached_btypes[self] + except KeyError: + BType = self.build_backend_type(ffi, finishlist) + BType2 = ffi._cached_btypes.setdefault(self, BType) + assert BType2 is BType + return BType + + def __repr__(self): + return '<%s>' % (self._get_c_name(),) + + def _get_items(self): + return [(name, getattr(self, name)) for name in self._attrs_] + + +class BaseType(BaseTypeByIdentity): + + def __eq__(self, other): + return (self.__class__ == other.__class__ and + self._get_items() == other._get_items()) + + def __ne__(self, other): + return not self == other + + def __hash__(self): + return hash((self.__class__, tuple(self._get_items()))) + + +class VoidType(BaseType): + _attrs_ = () + + def __init__(self): + self.c_name_with_marker = 'void&' + + def build_backend_type(self, ffi, finishlist): + return global_cache(self, ffi, 'new_void_type') + +void_type = VoidType() + + +class BasePrimitiveType(BaseType): + def is_complex_type(self): + return False + + +class PrimitiveType(BasePrimitiveType): + _attrs_ = ('name',) + + ALL_PRIMITIVE_TYPES = { + 'char': 'c', + 'short': 'i', + 'int': 'i', + 'long': 'i', + 'long long': 'i', + 'signed char': 'i', + 'unsigned char': 'i', + 'unsigned short': 'i', + 'unsigned int': 'i', + 'unsigned long': 'i', + 'unsigned long long': 'i', + 'float': 'f', + 'double': 'f', + 'long double': 'f', + 'float _Complex': 'j', + 'double _Complex': 'j', + '_Bool': 'i', + # the following types are not primitive in the C sense + 'wchar_t': 'c', + 'char16_t': 'c', + 'char32_t': 'c', + 'int8_t': 'i', + 'uint8_t': 'i', + 'int16_t': 'i', + 'uint16_t': 'i', + 'int32_t': 'i', + 'uint32_t': 'i', + 'int64_t': 'i', + 'uint64_t': 'i', + 'int_least8_t': 'i', + 'uint_least8_t': 'i', + 'int_least16_t': 'i', + 'uint_least16_t': 'i', + 'int_least32_t': 'i', + 'uint_least32_t': 'i', + 'int_least64_t': 'i', + 'uint_least64_t': 'i', + 'int_fast8_t': 'i', + 'uint_fast8_t': 'i', + 'int_fast16_t': 'i', + 'uint_fast16_t': 'i', + 'int_fast32_t': 'i', + 'uint_fast32_t': 'i', + 'int_fast64_t': 'i', + 'uint_fast64_t': 'i', + 'intptr_t': 'i', + 'uintptr_t': 'i', + 'intmax_t': 'i', + 'uintmax_t': 'i', + 'ptrdiff_t': 'i', + 'size_t': 'i', + 'ssize_t': 'i', + } + + def __init__(self, name): + assert name in self.ALL_PRIMITIVE_TYPES + self.name = name + self.c_name_with_marker = name + '&' + + def is_char_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' + def is_integer_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' + def is_float_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' + def is_complex_type(self): + return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' + + def build_backend_type(self, ffi, finishlist): + return global_cache(self, ffi, 'new_primitive_type', self.name) + + +class UnknownIntegerType(BasePrimitiveType): + _attrs_ = ('name',) + + def __init__(self, name): + self.name = name + self.c_name_with_marker = name + '&' + + def is_integer_type(self): + return True + + def build_backend_type(self, ffi, finishlist): + raise NotImplementedError("integer type '%s' can only be used after " + "compilation" % self.name) + +class UnknownFloatType(BasePrimitiveType): + _attrs_ = ('name', ) + + def __init__(self, name): + self.name = name + self.c_name_with_marker = name + '&' + + def build_backend_type(self, ffi, finishlist): + raise NotImplementedError("float type '%s' can only be used after " + "compilation" % self.name) + + +class BaseFunctionType(BaseType): + _attrs_ = ('args', 'result', 'ellipsis', 'abi') + + def __init__(self, args, result, ellipsis, abi=None): + self.args = args + self.result = result + self.ellipsis = ellipsis + self.abi = abi + # + reprargs = [arg._get_c_name() for arg in self.args] + if self.ellipsis: + reprargs.append('...') + reprargs = reprargs or ['void'] + replace_with = self._base_pattern % (', '.join(reprargs),) + if abi is not None: + replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] + self.c_name_with_marker = ( + self.result.c_name_with_marker.replace('&', replace_with)) + + +class RawFunctionType(BaseFunctionType): + # Corresponds to a C type like 'int(int)', which is the C type of + # a function, but not a pointer-to-function. The backend has no + # notion of such a type; it's used temporarily by parsing. + _base_pattern = '(&)(%s)' + is_raw_function = True + + def build_backend_type(self, ffi, finishlist): + raise CDefError("cannot render the type %r: it is a function " + "type, not a pointer-to-function type" % (self,)) + + def as_function_pointer(self): + return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) + + +class FunctionPtrType(BaseFunctionType): + _base_pattern = '(*&)(%s)' + + def build_backend_type(self, ffi, finishlist): + result = self.result.get_cached_btype(ffi, finishlist) + args = [] + for tp in self.args: + args.append(tp.get_cached_btype(ffi, finishlist)) + abi_args = () + if self.abi == "__stdcall": + if not self.ellipsis: # __stdcall ignored for variadic funcs + try: + abi_args = (ffi._backend.FFI_STDCALL,) + except AttributeError: + pass + return global_cache(self, ffi, 'new_function_type', + tuple(args), result, self.ellipsis, *abi_args) + + def as_raw_function(self): + return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) + + +class PointerType(BaseType): + _attrs_ = ('totype', 'quals') + + def __init__(self, totype, quals=0): + self.totype = totype + self.quals = quals + extra = qualify(quals, " *&") + if totype.is_array_type: + extra = "(%s)" % (extra.lstrip(),) + self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) + + def build_backend_type(self, ffi, finishlist): + BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) + return global_cache(self, ffi, 'new_pointer_type', BItem) + +voidp_type = PointerType(void_type) + +def ConstPointerType(totype): + return PointerType(totype, Q_CONST) + +const_voidp_type = ConstPointerType(void_type) + + +class NamedPointerType(PointerType): + _attrs_ = ('totype', 'name') + + def __init__(self, totype, name, quals=0): + PointerType.__init__(self, totype, quals) + self.name = name + self.c_name_with_marker = name + '&' + + +class ArrayType(BaseType): + _attrs_ = ('item', 'length') + is_array_type = True + + def __init__(self, item, length): + self.item = item + self.length = length + # + if length is None: + brackets = '&[]' + elif length == '...': + brackets = '&[/*...*/]' + else: + brackets = '&[%s]' % length + self.c_name_with_marker = ( + self.item.c_name_with_marker.replace('&', brackets)) + + def length_is_unknown(self): + return isinstance(self.length, str) + + def resolve_length(self, newlength): + return ArrayType(self.item, newlength) + + def build_backend_type(self, ffi, finishlist): + if self.length_is_unknown(): + raise CDefError("cannot render the type %r: unknown length" % + (self,)) + self.item.get_cached_btype(ffi, finishlist) # force the item BType + BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) + return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) + +char_array_type = ArrayType(PrimitiveType('char'), None) + + +class StructOrUnionOrEnum(BaseTypeByIdentity): + _attrs_ = ('name',) + forcename = None + + def build_c_name_with_marker(self): + name = self.forcename or '%s %s' % (self.kind, self.name) + self.c_name_with_marker = name + '&' + + def force_the_name(self, forcename): + self.forcename = forcename + self.build_c_name_with_marker() + + def get_official_name(self): + assert self.c_name_with_marker.endswith('&') + return self.c_name_with_marker[:-1] + + +class StructOrUnion(StructOrUnionOrEnum): + fixedlayout = None + completed = 0 + partial = False + packed = 0 + + def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): + self.name = name + self.fldnames = fldnames + self.fldtypes = fldtypes + self.fldbitsize = fldbitsize + self.fldquals = fldquals + self.build_c_name_with_marker() + + def anonymous_struct_fields(self): + if self.fldtypes is not None: + for name, type in zip(self.fldnames, self.fldtypes): + if name == '' and isinstance(type, StructOrUnion): + yield type + + def enumfields(self, expand_anonymous_struct_union=True): + fldquals = self.fldquals + if fldquals is None: + fldquals = (0,) * len(self.fldnames) + for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, + self.fldbitsize, fldquals): + if (name == '' and isinstance(type, StructOrUnion) + and expand_anonymous_struct_union): + # nested anonymous struct/union + for result in type.enumfields(): + yield result + else: + yield (name, type, bitsize, quals) + + def force_flatten(self): + # force the struct or union to have a declaration that lists + # directly all fields returned by enumfields(), flattening + # nested anonymous structs/unions. + names = [] + types = [] + bitsizes = [] + fldquals = [] + for name, type, bitsize, quals in self.enumfields(): + names.append(name) + types.append(type) + bitsizes.append(bitsize) + fldquals.append(quals) + self.fldnames = tuple(names) + self.fldtypes = tuple(types) + self.fldbitsize = tuple(bitsizes) + self.fldquals = tuple(fldquals) + + def get_cached_btype(self, ffi, finishlist, can_delay=False): + BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, + can_delay) + if not can_delay: + self.finish_backend_type(ffi, finishlist) + return BType + + def finish_backend_type(self, ffi, finishlist): + if self.completed: + if self.completed != 2: + raise NotImplementedError("recursive structure declaration " + "for '%s'" % (self.name,)) + return + BType = ffi._cached_btypes[self] + # + self.completed = 1 + # + if self.fldtypes is None: + pass # not completing it: it's an opaque struct + # + elif self.fixedlayout is None: + fldtypes = [tp.get_cached_btype(ffi, finishlist) + for tp in self.fldtypes] + lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) + extra_flags = () + if self.packed: + if self.packed == 1: + extra_flags = (8,) # SF_PACKED + else: + extra_flags = (0, self.packed) + ffi._backend.complete_struct_or_union(BType, lst, self, + -1, -1, *extra_flags) + # + else: + fldtypes = [] + fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout + for i in range(len(self.fldnames)): + fsize = fieldsize[i] + ftype = self.fldtypes[i] + # + if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): + # fix the length to match the total size + BItemType = ftype.item.get_cached_btype(ffi, finishlist) + nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) + if nrest != 0: + self._verification_error( + "field '%s.%s' has a bogus size?" % ( + self.name, self.fldnames[i] or '{}')) + ftype = ftype.resolve_length(nlen) + self.fldtypes = (self.fldtypes[:i] + (ftype,) + + self.fldtypes[i+1:]) + # + BFieldType = ftype.get_cached_btype(ffi, finishlist) + if isinstance(ftype, ArrayType) and ftype.length is None: + assert fsize == 0 + else: + bitemsize = ffi.sizeof(BFieldType) + if bitemsize != fsize: + self._verification_error( + "field '%s.%s' is declared as %d bytes, but is " + "really %d bytes" % (self.name, + self.fldnames[i] or '{}', + bitemsize, fsize)) + fldtypes.append(BFieldType) + # + lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) + ffi._backend.complete_struct_or_union(BType, lst, self, + totalsize, totalalignment) + self.completed = 2 + + def _verification_error(self, msg): + raise VerificationError(msg) + + def check_not_partial(self): + if self.partial and self.fixedlayout is None: + raise VerificationMissing(self._get_c_name()) + + def build_backend_type(self, ffi, finishlist): + self.check_not_partial() + finishlist.append(self) + # + return global_cache(self, ffi, 'new_%s_type' % self.kind, + self.get_official_name(), key=self) + + +class StructType(StructOrUnion): + kind = 'struct' + + +class UnionType(StructOrUnion): + kind = 'union' + + +class EnumType(StructOrUnionOrEnum): + kind = 'enum' + partial = False + partial_resolved = False + + def __init__(self, name, enumerators, enumvalues, baseinttype=None): + self.name = name + self.enumerators = enumerators + self.enumvalues = enumvalues + self.baseinttype = baseinttype + self.build_c_name_with_marker() + + def force_the_name(self, forcename): + StructOrUnionOrEnum.force_the_name(self, forcename) + if self.forcename is None: + name = self.get_official_name() + self.forcename = '$' + name.replace(' ', '_') + + def check_not_partial(self): + if self.partial and not self.partial_resolved: + raise VerificationMissing(self._get_c_name()) + + def build_backend_type(self, ffi, finishlist): + self.check_not_partial() + base_btype = self.build_baseinttype(ffi, finishlist) + return global_cache(self, ffi, 'new_enum_type', + self.get_official_name(), + self.enumerators, self.enumvalues, + base_btype, key=self) + + def build_baseinttype(self, ffi, finishlist): + if self.baseinttype is not None: + return self.baseinttype.get_cached_btype(ffi, finishlist) + # + if self.enumvalues: + smallest_value = min(self.enumvalues) + largest_value = max(self.enumvalues) + else: + import warnings + try: + # XXX! The goal is to ensure that the warnings.warn() + # will not suppress the warning. We want to get it + # several times if we reach this point several times. + __warningregistry__.clear() + except NameError: + pass + warnings.warn("%r has no values explicitly defined; " + "guessing that it is equivalent to 'unsigned int'" + % self._get_c_name()) + smallest_value = largest_value = 0 + if smallest_value < 0: # needs a signed type + sign = 1 + candidate1 = PrimitiveType("int") + candidate2 = PrimitiveType("long") + else: + sign = 0 + candidate1 = PrimitiveType("unsigned int") + candidate2 = PrimitiveType("unsigned long") + btype1 = candidate1.get_cached_btype(ffi, finishlist) + btype2 = candidate2.get_cached_btype(ffi, finishlist) + size1 = ffi.sizeof(btype1) + size2 = ffi.sizeof(btype2) + if (smallest_value >= ((-1) << (8*size1-1)) and + largest_value < (1 << (8*size1-sign))): + return btype1 + if (smallest_value >= ((-1) << (8*size2-1)) and + largest_value < (1 << (8*size2-sign))): + return btype2 + raise CDefError("%s values don't all fit into either 'long' " + "or 'unsigned long'" % self._get_c_name()) + +def unknown_type(name, structname=None): + if structname is None: + structname = '$%s' % name + tp = StructType(structname, None, None, None) + tp.force_the_name(name) + tp.origin = "unknown_type" + return tp + +def unknown_ptr_type(name, structname=None): + if structname is None: + structname = '$$%s' % name + tp = StructType(structname, None, None, None) + return NamedPointerType(tp, name) + + +global_lock = allocate_lock() +_typecache_cffi_backend = weakref.WeakValueDictionary() + +def get_typecache(backend): + # returns _typecache_cffi_backend if backend is the _cffi_backend + # module, or type(backend).__typecache if backend is an instance of + # CTypesBackend (or some FakeBackend class during tests) + if isinstance(backend, types.ModuleType): + return _typecache_cffi_backend + with global_lock: + if not hasattr(type(backend), '__typecache'): + type(backend).__typecache = weakref.WeakValueDictionary() + return type(backend).__typecache + +def global_cache(srctype, ffi, funcname, *args, **kwds): + key = kwds.pop('key', (funcname, args)) + assert not kwds + try: + return ffi._typecache[key] + except KeyError: + pass + try: + res = getattr(ffi._backend, funcname)(*args) + except NotImplementedError as e: + raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) + # note that setdefault() on WeakValueDictionary is not atomic + # and contains a rare bug (http://bugs.python.org/issue19542); + # we have to use a lock and do it ourselves + cache = ffi._typecache + with global_lock: + res1 = cache.get(key) + if res1 is None: + cache[key] = res + return res + else: + return res1 + +def pointer_cache(ffi, BType): + return global_cache('?', ffi, 'new_pointer_type', BType) + +def attach_exception_info(e, name): + if e.args and type(e.args[0]) is str: + e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h new file mode 100644 index 000000000..ea1aa24ee --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h @@ -0,0 +1,181 @@ + +/* This part is from file 'cffi/parse_c_type.h'. It is copied at the + beginning of C sources generated by CFFI's ffi.set_source(). */ + +typedef void *_cffi_opcode_t; + +#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) +#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) +#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) + +#define _CFFI_OP_PRIMITIVE 1 +#define _CFFI_OP_POINTER 3 +#define _CFFI_OP_ARRAY 5 +#define _CFFI_OP_OPEN_ARRAY 7 +#define _CFFI_OP_STRUCT_UNION 9 +#define _CFFI_OP_ENUM 11 +#define _CFFI_OP_FUNCTION 13 +#define _CFFI_OP_FUNCTION_END 15 +#define _CFFI_OP_NOOP 17 +#define _CFFI_OP_BITFIELD 19 +#define _CFFI_OP_TYPENAME 21 +#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs +#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs +#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) +#define _CFFI_OP_CONSTANT 29 +#define _CFFI_OP_CONSTANT_INT 31 +#define _CFFI_OP_GLOBAL_VAR 33 +#define _CFFI_OP_DLOPEN_FUNC 35 +#define _CFFI_OP_DLOPEN_CONST 37 +#define _CFFI_OP_GLOBAL_VAR_F 39 +#define _CFFI_OP_EXTERN_PYTHON 41 + +#define _CFFI_PRIM_VOID 0 +#define _CFFI_PRIM_BOOL 1 +#define _CFFI_PRIM_CHAR 2 +#define _CFFI_PRIM_SCHAR 3 +#define _CFFI_PRIM_UCHAR 4 +#define _CFFI_PRIM_SHORT 5 +#define _CFFI_PRIM_USHORT 6 +#define _CFFI_PRIM_INT 7 +#define _CFFI_PRIM_UINT 8 +#define _CFFI_PRIM_LONG 9 +#define _CFFI_PRIM_ULONG 10 +#define _CFFI_PRIM_LONGLONG 11 +#define _CFFI_PRIM_ULONGLONG 12 +#define _CFFI_PRIM_FLOAT 13 +#define _CFFI_PRIM_DOUBLE 14 +#define _CFFI_PRIM_LONGDOUBLE 15 + +#define _CFFI_PRIM_WCHAR 16 +#define _CFFI_PRIM_INT8 17 +#define _CFFI_PRIM_UINT8 18 +#define _CFFI_PRIM_INT16 19 +#define _CFFI_PRIM_UINT16 20 +#define _CFFI_PRIM_INT32 21 +#define _CFFI_PRIM_UINT32 22 +#define _CFFI_PRIM_INT64 23 +#define _CFFI_PRIM_UINT64 24 +#define _CFFI_PRIM_INTPTR 25 +#define _CFFI_PRIM_UINTPTR 26 +#define _CFFI_PRIM_PTRDIFF 27 +#define _CFFI_PRIM_SIZE 28 +#define _CFFI_PRIM_SSIZE 29 +#define _CFFI_PRIM_INT_LEAST8 30 +#define _CFFI_PRIM_UINT_LEAST8 31 +#define _CFFI_PRIM_INT_LEAST16 32 +#define _CFFI_PRIM_UINT_LEAST16 33 +#define _CFFI_PRIM_INT_LEAST32 34 +#define _CFFI_PRIM_UINT_LEAST32 35 +#define _CFFI_PRIM_INT_LEAST64 36 +#define _CFFI_PRIM_UINT_LEAST64 37 +#define _CFFI_PRIM_INT_FAST8 38 +#define _CFFI_PRIM_UINT_FAST8 39 +#define _CFFI_PRIM_INT_FAST16 40 +#define _CFFI_PRIM_UINT_FAST16 41 +#define _CFFI_PRIM_INT_FAST32 42 +#define _CFFI_PRIM_UINT_FAST32 43 +#define _CFFI_PRIM_INT_FAST64 44 +#define _CFFI_PRIM_UINT_FAST64 45 +#define _CFFI_PRIM_INTMAX 46 +#define _CFFI_PRIM_UINTMAX 47 +#define _CFFI_PRIM_FLOATCOMPLEX 48 +#define _CFFI_PRIM_DOUBLECOMPLEX 49 +#define _CFFI_PRIM_CHAR16 50 +#define _CFFI_PRIM_CHAR32 51 + +#define _CFFI__NUM_PRIM 52 +#define _CFFI__UNKNOWN_PRIM (-1) +#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) +#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) + +#define _CFFI__IO_FILE_STRUCT (-1) + + +struct _cffi_global_s { + const char *name; + void *address; + _cffi_opcode_t type_op; + void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown + // OP_CPYTHON_BLTN_*: addr of direct function +}; + +struct _cffi_getconst_s { + unsigned long long value; + const struct _cffi_type_context_s *ctx; + int gindex; +}; + +struct _cffi_struct_union_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_STRUCT_UNION + int flags; // _CFFI_F_* flags below + size_t size; + int alignment; + int first_field_index; // -> _cffi_fields array + int num_fields; +}; +#define _CFFI_F_UNION 0x01 // is a union, not a struct +#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the + // "standard layout" or if some are missing +#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct +#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() +#define _CFFI_F_OPAQUE 0x10 // opaque + +struct _cffi_field_s { + const char *name; + size_t field_offset; + size_t field_size; + _cffi_opcode_t field_type_op; +}; + +struct _cffi_enum_s { + const char *name; + int type_index; // -> _cffi_types, on a OP_ENUM + int type_prim; // _CFFI_PRIM_xxx + const char *enumerators; // comma-delimited string +}; + +struct _cffi_typename_s { + const char *name; + int type_index; /* if opaque, points to a possibly artificial + OP_STRUCT which is itself opaque */ +}; + +struct _cffi_type_context_s { + _cffi_opcode_t *types; + const struct _cffi_global_s *globals; + const struct _cffi_field_s *fields; + const struct _cffi_struct_union_s *struct_unions; + const struct _cffi_enum_s *enums; + const struct _cffi_typename_s *typenames; + int num_globals; + int num_struct_unions; + int num_enums; + int num_typenames; + const char *const *includes; + int num_types; + int flags; /* future extension */ +}; + +struct _cffi_parse_info_s { + const struct _cffi_type_context_s *ctx; + _cffi_opcode_t *output; + unsigned int output_size; + size_t error_location; + const char *error_message; +}; + +struct _cffi_externpy_s { + const char *name; + size_t size_of_result; + void *reserved1, *reserved2; +}; + +#ifdef _CFFI_INTERNAL +static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); +static int search_in_globals(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, + const char *search, size_t search_len); +#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py new file mode 100644 index 000000000..89708a504 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py @@ -0,0 +1,121 @@ +# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi +import sys, os, subprocess + +from .error import PkgConfigError + + +def merge_flags(cfg1, cfg2): + """Merge values from cffi config flags cfg2 to cf1 + + Example: + merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) + {"libraries": ["one", "two"]} + """ + for key, value in cfg2.items(): + if key not in cfg1: + cfg1[key] = value + else: + if not isinstance(cfg1[key], list): + raise TypeError("cfg1[%r] should be a list of strings" % (key,)) + if not isinstance(value, list): + raise TypeError("cfg2[%r] should be a list of strings" % (key,)) + cfg1[key].extend(value) + return cfg1 + + +def call(libname, flag, encoding=sys.getfilesystemencoding()): + """Calls pkg-config and returns the output if found + """ + a = ["pkg-config", "--print-errors"] + a.append(flag) + a.append(libname) + try: + pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except EnvironmentError as e: + raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) + + bout, berr = pc.communicate() + if pc.returncode != 0: + try: + berr = berr.decode(encoding) + except Exception: + pass + raise PkgConfigError(berr.strip()) + + if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x + try: + bout = bout.decode(encoding) + except UnicodeDecodeError: + raise PkgConfigError("pkg-config %s %s returned bytes that cannot " + "be decoded with encoding %r:\n%r" % + (flag, libname, encoding, bout)) + + if os.altsep != '\\' and '\\' in bout: + raise PkgConfigError("pkg-config %s %s returned an unsupported " + "backslash-escaped output:\n%r" % + (flag, libname, bout)) + return bout + + +def flags_from_pkgconfig(libs): + r"""Return compiler line flags for FFI.set_source based on pkg-config output + + Usage + ... + ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) + + If pkg-config is installed on build machine, then arguments include_dirs, + library_dirs, libraries, define_macros, extra_compile_args and + extra_link_args are extended with an output of pkg-config for libfoo and + libbar. + + Raises PkgConfigError in case the pkg-config call fails. + """ + + def get_include_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-I")] + + def get_library_dirs(string): + return [x[2:] for x in string.split() if x.startswith("-L")] + + def get_libraries(string): + return [x[2:] for x in string.split() if x.startswith("-l")] + + # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils + def get_macros(string): + def _macro(x): + x = x[2:] # drop "-D" + if '=' in x: + return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") + else: + return (x, None) # "-Dfoo" => ("foo", None) + return [_macro(x) for x in string.split() if x.startswith("-D")] + + def get_other_cflags(string): + return [x for x in string.split() if not x.startswith("-I") and + not x.startswith("-D")] + + def get_other_libs(string): + return [x for x in string.split() if not x.startswith("-L") and + not x.startswith("-l")] + + # return kwargs for given libname + def kwargs(libname): + fse = sys.getfilesystemencoding() + all_cflags = call(libname, "--cflags") + all_libs = call(libname, "--libs") + return { + "include_dirs": get_include_dirs(all_cflags), + "library_dirs": get_library_dirs(all_libs), + "libraries": get_libraries(all_libs), + "define_macros": get_macros(all_cflags), + "extra_compile_args": get_other_cflags(all_cflags), + "extra_link_args": get_other_libs(all_libs), + } + + # merge all arguments together + ret = {} + for libname in libs: + lib_flags = kwargs(libname) + merge_flags(ret, lib_flags) + return ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py new file mode 100644 index 000000000..4e33dde21 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py @@ -0,0 +1,1581 @@ +import os, sys, io +from . import ffiplatform, model +from .error import VerificationError +from .cffi_opcode import * + +VERSION_BASE = 0x2601 +VERSION_EMBEDDED = 0x2701 +VERSION_CHAR16CHAR32 = 0x2801 + +USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or + sys.version_info >= (3, 5)) + + +class GlobalExpr: + def __init__(self, name, address, type_op, size=0, check_value=0): + self.name = name + self.address = address + self.type_op = type_op + self.size = size + self.check_value = check_value + + def as_c_expr(self): + return ' { "%s", (void *)%s, %s, (void *)%s },' % ( + self.name, self.address, self.type_op.as_c_expr(), self.size) + + def as_python_expr(self): + return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, + self.check_value) + +class FieldExpr: + def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): + self.name = name + self.field_offset = field_offset + self.field_size = field_size + self.fbitsize = fbitsize + self.field_type_op = field_type_op + + def as_c_expr(self): + spaces = " " * len(self.name) + return (' { "%s", %s,\n' % (self.name, self.field_offset) + + ' %s %s,\n' % (spaces, self.field_size) + + ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) + + def as_python_expr(self): + raise NotImplementedError + + def as_field_python_expr(self): + if self.field_type_op.op == OP_NOOP: + size_expr = '' + elif self.field_type_op.op == OP_BITFIELD: + size_expr = format_four_bytes(self.fbitsize) + else: + raise NotImplementedError + return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), + size_expr, + self.name) + +class StructUnionExpr: + def __init__(self, name, type_index, flags, size, alignment, comment, + first_field_index, c_fields): + self.name = name + self.type_index = type_index + self.flags = flags + self.size = size + self.alignment = alignment + self.comment = comment + self.first_field_index = first_field_index + self.c_fields = c_fields + + def as_c_expr(self): + return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) + + '\n %s, %s, ' % (self.size, self.alignment) + + '%d, %d ' % (self.first_field_index, len(self.c_fields)) + + ('/* %s */ ' % self.comment if self.comment else '') + + '},') + + def as_python_expr(self): + flags = eval(self.flags, G_FLAGS) + fields_expr = [c_field.as_field_python_expr() + for c_field in self.c_fields] + return "(b'%s%s%s',%s)" % ( + format_four_bytes(self.type_index), + format_four_bytes(flags), + self.name, + ','.join(fields_expr)) + +class EnumExpr: + def __init__(self, name, type_index, size, signed, allenums): + self.name = name + self.type_index = type_index + self.size = size + self.signed = signed + self.allenums = allenums + + def as_c_expr(self): + return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' + ' "%s" },' % (self.name, self.type_index, + self.size, self.signed, self.allenums)) + + def as_python_expr(self): + prim_index = { + (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, + (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, + (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, + (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, + }[self.size, self.signed] + return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), + format_four_bytes(prim_index), + self.name, self.allenums) + +class TypenameExpr: + def __init__(self, name, type_index): + self.name = name + self.type_index = type_index + + def as_c_expr(self): + return ' { "%s", %d },' % (self.name, self.type_index) + + def as_python_expr(self): + return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) + + +# ____________________________________________________________ + + +class Recompiler: + _num_externpy = 0 + + def __init__(self, ffi, module_name, target_is_python=False): + self.ffi = ffi + self.module_name = module_name + self.target_is_python = target_is_python + self._version = VERSION_BASE + + def needs_version(self, ver): + self._version = max(self._version, ver) + + def collect_type_table(self): + self._typesdict = {} + self._generate("collecttype") + # + all_decls = sorted(self._typesdict, key=str) + # + # prepare all FUNCTION bytecode sequences first + self.cffi_types = [] + for tp in all_decls: + if tp.is_raw_function: + assert self._typesdict[tp] is None + self._typesdict[tp] = len(self.cffi_types) + self.cffi_types.append(tp) # placeholder + for tp1 in tp.args: + assert isinstance(tp1, (model.VoidType, + model.BasePrimitiveType, + model.PointerType, + model.StructOrUnionOrEnum, + model.FunctionPtrType)) + if self._typesdict[tp1] is None: + self._typesdict[tp1] = len(self.cffi_types) + self.cffi_types.append(tp1) # placeholder + self.cffi_types.append('END') # placeholder + # + # prepare all OTHER bytecode sequences + for tp in all_decls: + if not tp.is_raw_function and self._typesdict[tp] is None: + self._typesdict[tp] = len(self.cffi_types) + self.cffi_types.append(tp) # placeholder + if tp.is_array_type and tp.length is not None: + self.cffi_types.append('LEN') # placeholder + assert None not in self._typesdict.values() + # + # collect all structs and unions and enums + self._struct_unions = {} + self._enums = {} + for tp in all_decls: + if isinstance(tp, model.StructOrUnion): + self._struct_unions[tp] = None + elif isinstance(tp, model.EnumType): + self._enums[tp] = None + for i, tp in enumerate(sorted(self._struct_unions, + key=lambda tp: tp.name)): + self._struct_unions[tp] = i + for i, tp in enumerate(sorted(self._enums, + key=lambda tp: tp.name)): + self._enums[tp] = i + # + # emit all bytecode sequences now + for tp in all_decls: + method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) + method(tp, self._typesdict[tp]) + # + # consistency check + for op in self.cffi_types: + assert isinstance(op, CffiOp) + self.cffi_types = tuple(self.cffi_types) # don't change any more + + def _enum_fields(self, tp): + # When producing C, expand all anonymous struct/union fields. + # That's necessary to have C code checking the offsets of the + # individual fields contained in them. When producing Python, + # don't do it and instead write it like it is, with the + # corresponding fields having an empty name. Empty names are + # recognized at runtime when we import the generated Python + # file. + expand_anonymous_struct_union = not self.target_is_python + return tp.enumfields(expand_anonymous_struct_union) + + def _do_collect_type(self, tp): + if not isinstance(tp, model.BaseTypeByIdentity): + if isinstance(tp, tuple): + for x in tp: + self._do_collect_type(x) + return + if tp not in self._typesdict: + self._typesdict[tp] = None + if isinstance(tp, model.FunctionPtrType): + self._do_collect_type(tp.as_raw_function()) + elif isinstance(tp, model.StructOrUnion): + if tp.fldtypes is not None and ( + tp not in self.ffi._parser._included_declarations): + for name1, tp1, _, _ in self._enum_fields(tp): + self._do_collect_type(self._field_type(tp, name1, tp1)) + else: + for _, x in tp._get_items(): + self._do_collect_type(x) + + def _generate(self, step_name): + lst = self.ffi._parser._declarations.items() + for name, (tp, quals) in sorted(lst): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_cpy_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in recompile(): %r" % name) + try: + self._current_quals = quals + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + # ---------- + + ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] + + def collect_step_tables(self): + # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. + self._lsts = {} + for step_name in self.ALL_STEPS: + self._lsts[step_name] = [] + self._seen_struct_unions = set() + self._generate("ctx") + self._add_missing_struct_unions() + # + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + if step_name != "field": + lst.sort(key=lambda entry: entry.name) + self._lsts[step_name] = tuple(lst) # don't change any more + # + # check for a possible internal inconsistency: _cffi_struct_unions + # should have been generated with exactly self._struct_unions + lst = self._lsts["struct_union"] + for tp, i in self._struct_unions.items(): + assert i < len(lst) + assert lst[i].name == tp.name + assert len(lst) == len(self._struct_unions) + # same with enums + lst = self._lsts["enum"] + for tp, i in self._enums.items(): + assert i < len(lst) + assert lst[i].name == tp.name + assert len(lst) == len(self._enums) + + # ---------- + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def write_source_to_f(self, f, preamble): + if self.target_is_python: + assert preamble is None + self.write_py_source_to_f(f) + else: + assert preamble is not None + self.write_c_source_to_f(f, preamble) + + def _rel_readlines(self, filename): + g = open(os.path.join(os.path.dirname(__file__), filename), 'r') + lines = g.readlines() + g.close() + return lines + + def write_c_source_to_f(self, f, preamble): + self._f = f + prnt = self._prnt + if self.ffi._embedding is not None: + prnt('#define _CFFI_USE_EMBEDDING') + if not USE_LIMITED_API: + prnt('#define _CFFI_NO_LIMITED_API') + # + # first the '#include' (actually done by inlining the file's content) + lines = self._rel_readlines('_cffi_include.h') + i = lines.index('#include "parse_c_type.h"\n') + lines[i:i+1] = self._rel_readlines('parse_c_type.h') + prnt(''.join(lines)) + # + # if we have ffi._embedding != None, we give it here as a macro + # and include an extra file + base_module_name = self.module_name.split('.')[-1] + if self.ffi._embedding is not None: + prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) + prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') + self._print_string_literal_in_array(self.ffi._embedding) + prnt('0 };') + prnt('#ifdef PYPY_VERSION') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( + base_module_name,)) + prnt('#elif PY_MAJOR_VERSION >= 3') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( + base_module_name,)) + prnt('#else') + prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( + base_module_name,)) + prnt('#endif') + lines = self._rel_readlines('_embedding.h') + i = lines.index('#include "_cffi_errors.h"\n') + lines[i:i+1] = self._rel_readlines('_cffi_errors.h') + prnt(''.join(lines)) + self.needs_version(VERSION_EMBEDDED) + # + # then paste the C source given by the user, verbatim. + prnt('/************************************************************/') + prnt() + prnt(preamble) + prnt() + prnt('/************************************************************/') + prnt() + # + # the declaration of '_cffi_types' + prnt('static void *_cffi_types[] = {') + typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) + for i, op in enumerate(self.cffi_types): + comment = '' + if i in typeindex2type: + comment = ' // ' + typeindex2type[i]._get_c_name() + prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) + if not self.cffi_types: + prnt(' 0') + prnt('};') + prnt() + # + # call generate_cpy_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._seen_constants = set() + self._generate("decl") + # + # the declaration of '_cffi_globals' and '_cffi_typenames' + nums = {} + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + nums[step_name] = len(lst) + if nums[step_name] > 0: + prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( + step_name, step_name)) + for entry in lst: + prnt(entry.as_c_expr()) + prnt('};') + prnt() + # + # the declaration of '_cffi_includes' + if self.ffi._included_ffis: + prnt('static const char * const _cffi_includes[] = {') + for ffi_to_include in self.ffi._included_ffis: + try: + included_module_name, included_source = ( + ffi_to_include._assigned_source[:2]) + except AttributeError: + raise VerificationError( + "ffi object %r includes %r, but the latter has not " + "been prepared with set_source()" % ( + self.ffi, ffi_to_include,)) + if included_source is None: + raise VerificationError( + "not implemented yet: ffi.include() of a Python-based " + "ffi inside a C-based ffi") + prnt(' "%s",' % (included_module_name,)) + prnt(' NULL') + prnt('};') + prnt() + # + # the declaration of '_cffi_type_context' + prnt('static const struct _cffi_type_context_s _cffi_type_context = {') + prnt(' _cffi_types,') + for step_name in self.ALL_STEPS: + if nums[step_name] > 0: + prnt(' _cffi_%ss,' % step_name) + else: + prnt(' NULL, /* no %ss */' % step_name) + for step_name in self.ALL_STEPS: + if step_name != "field": + prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) + if self.ffi._included_ffis: + prnt(' _cffi_includes,') + else: + prnt(' NULL, /* no includes */') + prnt(' %d, /* num_types */' % (len(self.cffi_types),)) + flags = 0 + if self._num_externpy > 0 or self.ffi._embedding is not None: + flags |= 1 # set to mean that we use extern "Python" + prnt(' %d, /* flags */' % flags) + prnt('};') + prnt() + # + # the init function + prnt('#ifdef __GNUC__') + prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') + prnt('#endif') + prnt() + prnt('#ifdef PYPY_VERSION') + prnt('PyMODINIT_FUNC') + prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) + prnt('{') + if flags & 1: + prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') + prnt(' _cffi_call_python_org = ' + '(void(*)(struct _cffi_externpy_s *, char *))p[1];') + prnt(' }') + prnt(' p[0] = (const void *)0x%x;' % self._version) + prnt(' p[1] = &_cffi_type_context;') + prnt('#if PY_MAJOR_VERSION >= 3') + prnt(' return NULL;') + prnt('#endif') + prnt('}') + # on Windows, distutils insists on putting init_cffi_xyz in + # 'export_symbols', so instead of fighting it, just give up and + # give it one + prnt('# ifdef _MSC_VER') + prnt(' PyMODINIT_FUNC') + prnt('# if PY_MAJOR_VERSION >= 3') + prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) + prnt('# else') + prnt(' init%s(void) { }' % (base_module_name,)) + prnt('# endif') + prnt('# endif') + prnt('#elif PY_MAJOR_VERSION >= 3') + prnt('PyMODINIT_FUNC') + prnt('PyInit_%s(void)' % (base_module_name,)) + prnt('{') + prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( + self.module_name, self._version)) + prnt('}') + prnt('#else') + prnt('PyMODINIT_FUNC') + prnt('init%s(void)' % (base_module_name,)) + prnt('{') + prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( + self.module_name, self._version)) + prnt('}') + prnt('#endif') + prnt() + prnt('#ifdef __GNUC__') + prnt('# pragma GCC visibility pop') + prnt('#endif') + self._version = None + + def _to_py(self, x): + if isinstance(x, str): + return "b'%s'" % (x,) + if isinstance(x, (list, tuple)): + rep = [self._to_py(item) for item in x] + if len(rep) == 1: + rep.append('') + return "(%s)" % (','.join(rep),) + return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. + + def write_py_source_to_f(self, f): + self._f = f + prnt = self._prnt + # + # header + prnt("# auto-generated file") + prnt("import _cffi_backend") + # + # the 'import' of the included ffis + num_includes = len(self.ffi._included_ffis or ()) + for i in range(num_includes): + ffi_to_include = self.ffi._included_ffis[i] + try: + included_module_name, included_source = ( + ffi_to_include._assigned_source[:2]) + except AttributeError: + raise VerificationError( + "ffi object %r includes %r, but the latter has not " + "been prepared with set_source()" % ( + self.ffi, ffi_to_include,)) + if included_source is not None: + raise VerificationError( + "not implemented yet: ffi.include() of a C-based " + "ffi inside a Python-based ffi") + prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) + prnt() + prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) + prnt(" _version = 0x%x," % (self._version,)) + self._version = None + # + # the '_types' keyword argument + self.cffi_types = tuple(self.cffi_types) # don't change any more + types_lst = [op.as_python_bytes() for op in self.cffi_types] + prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) + typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) + # + # the keyword arguments from ALL_STEPS + for step_name in self.ALL_STEPS: + lst = self._lsts[step_name] + if len(lst) > 0 and step_name != "field": + prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) + # + # the '_includes' keyword argument + if num_includes > 0: + prnt(' _includes = (%s,),' % ( + ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) + # + # the footer + prnt(')') + + # ---------- + + def _gettypenum(self, type): + # a KeyError here is a bug. please report it! :-) + return self._typesdict[type] + + def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): + extraarg = '' + if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): + if tp.is_integer_type() and tp.name != '_Bool': + converter = '_cffi_to_c_int' + extraarg = ', %s' % tp.name + elif isinstance(tp, model.UnknownFloatType): + # don't check with is_float_type(): it may be a 'long + # double' here, and _cffi_to_c_double would loose precision + converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) + else: + cname = tp.get_c_name('') + converter = '(%s)_cffi_to_c_%s' % (cname, + tp.name.replace(' ', '_')) + if cname in ('char16_t', 'char32_t'): + self.needs_version(VERSION_CHAR16CHAR32) + errvalue = '-1' + # + elif isinstance(tp, model.PointerType): + self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, + tovar, errcode) + return + # + elif (isinstance(tp, model.StructOrUnionOrEnum) or + isinstance(tp, model.BasePrimitiveType)): + # a struct (not a struct pointer) as a function argument; + # or, a complex (the same code works) + self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' + % (tovar, self._gettypenum(tp), fromvar)) + self._prnt(' %s;' % errcode) + return + # + elif isinstance(tp, model.FunctionPtrType): + converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') + extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) + errvalue = 'NULL' + # + else: + raise NotImplementedError(tp) + # + self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) + self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( + tovar, tp.get_c_name(''), errvalue)) + self._prnt(' %s;' % errcode) + + def _extra_local_variables(self, tp, localvars, freelines): + if isinstance(tp, model.PointerType): + localvars.add('Py_ssize_t datasize') + localvars.add('struct _cffi_freeme_s *large_args_free = NULL') + freelines.add('if (large_args_free != NULL)' + ' _cffi_free_array_arguments(large_args_free);') + + def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): + self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') + self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( + self._gettypenum(tp), fromvar, tovar)) + self._prnt(' if (datasize != 0) {') + self._prnt(' %s = ((size_t)datasize) <= 640 ? ' + '(%s)alloca((size_t)datasize) : NULL;' % ( + tovar, tp.get_c_name(''))) + self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' + '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) + self._prnt(' datasize, &large_args_free) < 0)') + self._prnt(' %s;' % errcode) + self._prnt(' }') + + def _convert_expr_from_c(self, tp, var, context): + if isinstance(tp, model.BasePrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) + elif isinstance(tp, model.UnknownFloatType): + return '_cffi_from_c_double(%s)' % (var,) + elif tp.name != 'long double' and not tp.is_complex_type(): + cname = tp.name.replace(' ', '_') + if cname in ('char16_t', 'char32_t'): + self.needs_version(VERSION_CHAR16CHAR32) + return '_cffi_from_c_%s(%s)' % (cname, var) + else: + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.ArrayType): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(model.PointerType(tp.item))) + elif isinstance(tp, model.StructOrUnion): + if tp.fldnames is None: + raise TypeError("'%s' is used as %s, but is opaque" % ( + tp._get_c_name(), context)) + return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.EnumType): + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + else: + raise NotImplementedError(tp) + + # ---------- + # typedefs + + def _typedef_type(self, tp, name): + return self._global_type(tp, "(*(%s *)0)" % (name,)) + + def _generate_cpy_typedef_collecttype(self, tp, name): + self._do_collect_type(self._typedef_type(tp, name)) + + def _generate_cpy_typedef_decl(self, tp, name): + pass + + def _typedef_ctx(self, tp, name): + type_index = self._typesdict[tp] + self._lsts["typename"].append(TypenameExpr(name, type_index)) + + def _generate_cpy_typedef_ctx(self, tp, name): + tp = self._typedef_type(tp, name) + self._typedef_ctx(tp, name) + if getattr(tp, "origin", None) == "unknown_type": + self._struct_ctx(tp, tp.name, approxname=None) + elif isinstance(tp, model.NamedPointerType): + self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, + named_ptr=tp) + + # ---------- + # function declarations + + def _generate_cpy_function_collecttype(self, tp, name): + self._do_collect_type(tp.as_raw_function()) + if tp.ellipsis and not self.target_is_python: + self._do_collect_type(tp) + + def _generate_cpy_function_decl(self, tp, name): + assert not self.target_is_python + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no CPython wrapper) + self._generate_cpy_constant_decl(tp, name) + return + prnt = self._prnt + numargs = len(tp.args) + if numargs == 0: + argname = 'noarg' + elif numargs == 1: + argname = 'arg0' + else: + argname = 'args' + # + # ------------------------------ + # the 'd' version of the function, only for addressof(lib, 'func') + arguments = [] + call_arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arguments.append(type.get_c_name(' x%d' % i, context)) + call_arguments.append('x%d' % i) + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) + prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) + prnt('{') + call_arguments = ', '.join(call_arguments) + result_code = 'return ' + if isinstance(tp.result, model.VoidType): + result_code = '' + prnt(' %s%s(%s);' % (result_code, name, call_arguments)) + prnt('}') + # + prnt('#ifndef PYPY_VERSION') # ------------------------------ + # + prnt('static PyObject *') + prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) + prnt('{') + # + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arg = type.get_c_name(' x%d' % i, context) + prnt(' %s;' % arg) + # + localvars = set() + freelines = set() + for type in tp.args: + self._extra_local_variables(type, localvars, freelines) + for decl in sorted(localvars): + prnt(' %s;' % (decl,)) + # + if not isinstance(tp.result, model.VoidType): + result_code = 'result = ' + context = 'result of %s' % name + result_decl = ' %s;' % tp.result.get_c_name(' result', context) + prnt(result_decl) + prnt(' PyObject *pyresult;') + else: + result_decl = None + result_code = '' + # + if len(tp.args) > 1: + rng = range(len(tp.args)) + for i in rng: + prnt(' PyObject *arg%d;' % i) + prnt() + prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( + name, len(rng), len(rng), + ', '.join(['&arg%d' % i for i in rng]))) + prnt(' return NULL;') + prnt() + # + for i, type in enumerate(tp.args): + self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, + 'return NULL') + prnt() + # + prnt(' Py_BEGIN_ALLOW_THREADS') + prnt(' _cffi_restore_errno();') + call_arguments = ['x%d' % i for i in range(len(tp.args))] + call_arguments = ', '.join(call_arguments) + prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) + prnt(' _cffi_save_errno();') + prnt(' Py_END_ALLOW_THREADS') + prnt() + # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') + if result_code: + prnt(' pyresult = %s;' % + self._convert_expr_from_c(tp.result, 'result', 'result type')) + for freeline in freelines: + prnt(' ' + freeline) + prnt(' return pyresult;') + else: + for freeline in freelines: + prnt(' ' + freeline) + prnt(' Py_INCREF(Py_None);') + prnt(' return Py_None;') + prnt('}') + # + prnt('#else') # ------------------------------ + # + # the PyPy version: need to replace struct/union arguments with + # pointers, and if the result is a struct/union, insert a first + # arg that is a pointer to the result. We also do that for + # complex args and return type. + def need_indirection(type): + return (isinstance(type, model.StructOrUnion) or + (isinstance(type, model.PrimitiveType) and + type.is_complex_type())) + difference = False + arguments = [] + call_arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + indirection = '' + if need_indirection(type): + indirection = '*' + difference = True + arg = type.get_c_name(' %sx%d' % (indirection, i), context) + arguments.append(arg) + call_arguments.append('%sx%d' % (indirection, i)) + tp_result = tp.result + if need_indirection(tp_result): + context = 'result of %s' % name + arg = tp_result.get_c_name(' *result', context) + arguments.insert(0, arg) + tp_result = model.void_type + result_decl = None + result_code = '*result = ' + difference = True + if difference: + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, + repr_arguments) + prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) + prnt('{') + if result_decl: + prnt(result_decl) + call_arguments = ', '.join(call_arguments) + prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) + if result_decl: + prnt(' return result;') + prnt('}') + else: + prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) + # + prnt('#endif') # ------------------------------ + prnt() + + def _generate_cpy_function_ctx(self, tp, name): + if tp.ellipsis and not self.target_is_python: + self._generate_cpy_constant_ctx(tp, name) + return + type_index = self._typesdict[tp.as_raw_function()] + numargs = len(tp.args) + if self.target_is_python: + meth_kind = OP_DLOPEN_FUNC + elif numargs == 0: + meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' + elif numargs == 1: + meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' + else: + meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' + self._lsts["global"].append( + GlobalExpr(name, '_cffi_f_%s' % name, + CffiOp(meth_kind, type_index), + size='_cffi_d_%s' % name)) + + # ---------- + # named structs or unions + + def _field_type(self, tp_struct, field_name, tp_field): + if isinstance(tp_field, model.ArrayType): + actual_length = tp_field.length + if actual_length == '...': + ptr_struct_name = tp_struct.get_c_name('*') + actual_length = '_cffi_array_len(((%s)0)->%s)' % ( + ptr_struct_name, field_name) + tp_item = self._field_type(tp_struct, '%s[0]' % field_name, + tp_field.item) + tp_field = model.ArrayType(tp_item, actual_length) + return tp_field + + def _struct_collecttype(self, tp): + self._do_collect_type(tp) + if self.target_is_python: + # also requires nested anon struct/unions in ABI mode, recursively + for fldtype in tp.anonymous_struct_fields(): + self._struct_collecttype(fldtype) + + def _struct_decl(self, tp, cname, approxname): + if tp.fldtypes is None: + return + prnt = self._prnt + checkfuncname = '_cffi_checkfld_%s' % (approxname,) + prnt('_CFFI_UNUSED_FN') + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in self._enum_fields(tp): + try: + if ftype.is_integer_type() or fbitsize >= 0: + # accept all integers, but complain on float or double + if fname != '': + prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " + "an integer */" % (fname, cname, fname)) + continue + # only accept exactly the type declared, except that '[]' + # is interpreted as a '*' and so will match any array length. + # (It would also match '*', but that's harder to detect...) + while (isinstance(ftype, model.ArrayType) + and (ftype.length is None or ftype.length == '...')): + ftype = ftype.item + fname = fname + '[0]' + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) + prnt() + + def _struct_ctx(self, tp, cname, approxname, named_ptr=None): + type_index = self._typesdict[tp] + reason_for_not_expanding = None + flags = [] + if isinstance(tp, model.UnionType): + flags.append("_CFFI_F_UNION") + if tp.fldtypes is None: + flags.append("_CFFI_F_OPAQUE") + reason_for_not_expanding = "opaque" + if (tp not in self.ffi._parser._included_declarations and + (named_ptr is None or + named_ptr not in self.ffi._parser._included_declarations)): + if tp.fldtypes is None: + pass # opaque + elif tp.partial or any(tp.anonymous_struct_fields()): + pass # field layout obtained silently from the C compiler + else: + flags.append("_CFFI_F_CHECK_FIELDS") + if tp.packed: + if tp.packed > 1: + raise NotImplementedError( + "%r is declared with 'pack=%r'; only 0 or 1 are " + "supported in API mode (try to use \"...;\", which " + "does not require a 'pack' declaration)" % + (tp, tp.packed)) + flags.append("_CFFI_F_PACKED") + else: + flags.append("_CFFI_F_EXTERNAL") + reason_for_not_expanding = "external" + flags = '|'.join(flags) or '0' + c_fields = [] + if reason_for_not_expanding is None: + enumfields = list(self._enum_fields(tp)) + for fldname, fldtype, fbitsize, fqual in enumfields: + fldtype = self._field_type(tp, fldname, fldtype) + self._check_not_opaque(fldtype, + "field '%s.%s'" % (tp.name, fldname)) + # cname is None for _add_missing_struct_unions() only + op = OP_NOOP + if fbitsize >= 0: + op = OP_BITFIELD + size = '%d /* bits */' % fbitsize + elif cname is None or ( + isinstance(fldtype, model.ArrayType) and + fldtype.length is None): + size = '(size_t)-1' + else: + size = 'sizeof(((%s)0)->%s)' % ( + tp.get_c_name('*') if named_ptr is None + else named_ptr.name, + fldname) + if cname is None or fbitsize >= 0: + offset = '(size_t)-1' + elif named_ptr is not None: + offset = '((char *)&((%s)0)->%s) - (char *)0' % ( + named_ptr.name, fldname) + else: + offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) + c_fields.append( + FieldExpr(fldname, offset, size, fbitsize, + CffiOp(op, self._typesdict[fldtype]))) + first_field_index = len(self._lsts["field"]) + self._lsts["field"].extend(c_fields) + # + if cname is None: # unknown name, for _add_missing_struct_unions + size = '(size_t)-2' + align = -2 + comment = "unnamed" + else: + if named_ptr is not None: + size = 'sizeof(*(%s)0)' % (named_ptr.name,) + align = '-1 /* unknown alignment */' + else: + size = 'sizeof(%s)' % (cname,) + align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) + comment = None + else: + size = '(size_t)-1' + align = -1 + first_field_index = -1 + comment = reason_for_not_expanding + self._lsts["struct_union"].append( + StructUnionExpr(tp.name, type_index, flags, size, align, comment, + first_field_index, c_fields)) + self._seen_struct_unions.add(tp) + + def _check_not_opaque(self, tp, location): + while isinstance(tp, model.ArrayType): + tp = tp.item + if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: + raise TypeError( + "%s is of an opaque type (not declared in cdef())" % location) + + def _add_missing_struct_unions(self): + # not very nice, but some struct declarations might be missing + # because they don't have any known C name. Check that they are + # not partial (we can't complete or verify them!) and emit them + # anonymously. + lst = list(self._struct_unions.items()) + lst.sort(key=lambda tp_order: tp_order[1]) + for tp, order in lst: + if tp not in self._seen_struct_unions: + if tp.partial: + raise NotImplementedError("internal inconsistency: %r is " + "partial but was not seen at " + "this point" % (tp,)) + if tp.name.startswith('$') and tp.name[1:].isdigit(): + approxname = tp.name[1:] + elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': + approxname = 'FILE' + self._typedef_ctx(tp, 'FILE') + else: + raise NotImplementedError("internal inconsistency: %r" % + (tp,)) + self._struct_ctx(tp, None, approxname) + + def _generate_cpy_struct_collecttype(self, tp, name): + self._struct_collecttype(tp) + _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype + + def _struct_names(self, tp): + cname = tp.get_c_name('') + if ' ' in cname: + return cname, cname.replace(' ', '_') + else: + return cname, '_' + cname + + def _generate_cpy_struct_decl(self, tp, name): + self._struct_decl(tp, *self._struct_names(tp)) + _generate_cpy_union_decl = _generate_cpy_struct_decl + + def _generate_cpy_struct_ctx(self, tp, name): + self._struct_ctx(tp, *self._struct_names(tp)) + _generate_cpy_union_ctx = _generate_cpy_struct_ctx + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + def _generate_cpy_anonymous_collecttype(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_collecttype(tp, name) + else: + self._struct_collecttype(tp) + + def _generate_cpy_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_decl(tp) + else: + self._struct_decl(tp, name, 'typedef_' + name) + + def _generate_cpy_anonymous_ctx(self, tp, name): + if isinstance(tp, model.EnumType): + self._enum_ctx(tp, name) + else: + self._struct_ctx(tp, name, 'typedef_' + name) + + # ---------- + # constants, declared with "static const ..." + + def _generate_cpy_const(self, is_int, name, tp=None, category='const', + check_value=None): + if (category, name) in self._seen_constants: + raise VerificationError( + "duplicate declaration of %s '%s'" % (category, name)) + self._seen_constants.add((category, name)) + # + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + if is_int: + prnt('static int %s(unsigned long long *o)' % funcname) + prnt('{') + prnt(' int n = (%s) <= 0;' % (name,)) + prnt(' *o = (unsigned long long)((%s) | 0);' + ' /* check that %s is an integer */' % (name, name)) + if check_value is not None: + if check_value > 0: + check_value = '%dU' % (check_value,) + prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) + prnt(' n |= 2;') + prnt(' return n;') + prnt('}') + else: + assert check_value is None + prnt('static void %s(char *o)' % funcname) + prnt('{') + prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) + prnt('}') + prnt() + + def _generate_cpy_constant_collecttype(self, tp, name): + is_int = tp.is_integer_type() + if not is_int or self.target_is_python: + self._do_collect_type(tp) + + def _generate_cpy_constant_decl(self, tp, name): + is_int = tp.is_integer_type() + self._generate_cpy_const(is_int, name, tp) + + def _generate_cpy_constant_ctx(self, tp, name): + if not self.target_is_python and tp.is_integer_type(): + type_op = CffiOp(OP_CONSTANT_INT, -1) + else: + if self.target_is_python: + const_kind = OP_DLOPEN_CONST + else: + const_kind = OP_CONSTANT + type_index = self._typesdict[tp] + type_op = CffiOp(const_kind, type_index) + self._lsts["global"].append( + GlobalExpr(name, '_cffi_const_%s' % name, type_op)) + + # ---------- + # enums + + def _generate_cpy_enum_collecttype(self, tp, name): + self._do_collect_type(tp) + + def _generate_cpy_enum_decl(self, tp, name=None): + for enumerator in tp.enumerators: + self._generate_cpy_const(True, enumerator) + + def _enum_ctx(self, tp, cname): + type_index = self._typesdict[tp] + type_op = CffiOp(OP_ENUM, -1) + if self.target_is_python: + tp.check_not_partial() + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._lsts["global"].append( + GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, + check_value=enumvalue)) + # + if cname is not None and '$' not in cname and not self.target_is_python: + size = "sizeof(%s)" % cname + signed = "((%s)-1) <= 0" % cname + else: + basetp = tp.build_baseinttype(self.ffi, []) + size = self.ffi.sizeof(basetp) + signed = int(int(self.ffi.cast(basetp, -1)) < 0) + allenums = ",".join(tp.enumerators) + self._lsts["enum"].append( + EnumExpr(tp.name, type_index, size, signed, allenums)) + + def _generate_cpy_enum_ctx(self, tp, name): + self._enum_ctx(tp, tp._get_c_name()) + + # ---------- + # macros: for now only for integers + + def _generate_cpy_macro_collecttype(self, tp, name): + pass + + def _generate_cpy_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) + + def _generate_cpy_macro_ctx(self, tp, name): + if tp == '...': + if self.target_is_python: + raise VerificationError( + "cannot use the syntax '...' in '#define %s ...' when " + "using the ABI mode" % (name,)) + check_value = None + else: + check_value = tp # an integer + type_op = CffiOp(OP_CONSTANT_INT, -1) + self._lsts["global"].append( + GlobalExpr(name, '_cffi_const_%s' % name, type_op, + check_value=check_value)) + + # ---------- + # global variables + + def _global_type(self, tp, global_name): + if isinstance(tp, model.ArrayType): + actual_length = tp.length + if actual_length == '...': + actual_length = '_cffi_array_len(%s)' % (global_name,) + tp_item = self._global_type(tp.item, '%s[0]' % global_name) + tp = model.ArrayType(tp_item, actual_length) + return tp + + def _generate_cpy_variable_collecttype(self, tp, name): + self._do_collect_type(self._global_type(tp, name)) + + def _generate_cpy_variable_decl(self, tp, name): + prnt = self._prnt + tp = self._global_type(tp, name) + if isinstance(tp, model.ArrayType) and tp.length is None: + tp = tp.item + ampersand = '' + else: + ampersand = '&' + # This code assumes that casts from "tp *" to "void *" is a + # no-op, i.e. a function that returns a "tp *" can be called + # as if it returned a "void *". This should be generally true + # on any modern machine. The only exception to that rule (on + # uncommon architectures, and as far as I can tell) might be + # if 'tp' were a function type, but that is not possible here. + # (If 'tp' is a function _pointer_ type, then casts from "fn_t + # **" to "void *" are again no-ops, as far as I can tell.) + decl = '*_cffi_var_%s(void)' % (name,) + prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) + prnt('{') + prnt(' return %s(%s);' % (ampersand, name)) + prnt('}') + prnt() + + def _generate_cpy_variable_ctx(self, tp, name): + tp = self._global_type(tp, name) + type_index = self._typesdict[tp] + if self.target_is_python: + op = OP_GLOBAL_VAR + else: + op = OP_GLOBAL_VAR_F + self._lsts["global"].append( + GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) + + # ---------- + # extern "Python" + + def _generate_cpy_extern_python_collecttype(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + self._do_collect_type(tp) + _generate_cpy_dllexport_python_collecttype = \ + _generate_cpy_extern_python_plus_c_collecttype = \ + _generate_cpy_extern_python_collecttype + + def _extern_python_decl(self, tp, name, tag_and_space): + prnt = self._prnt + if isinstance(tp.result, model.VoidType): + size_of_result = '0' + else: + context = 'result of %s' % name + size_of_result = '(int)sizeof(%s)' % ( + tp.result.get_c_name('', context),) + prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) + prnt(' { "%s.%s", %s, 0, 0 };' % ( + self.module_name, name, size_of_result)) + prnt() + # + arguments = [] + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + arg = type.get_c_name(' a%d' % i, context) + arguments.append(arg) + # + repr_arguments = ', '.join(arguments) + repr_arguments = repr_arguments or 'void' + name_and_arguments = '%s(%s)' % (name, repr_arguments) + if tp.abi == "__stdcall": + name_and_arguments = '_cffi_stdcall ' + name_and_arguments + # + def may_need_128_bits(tp): + return (isinstance(tp, model.PrimitiveType) and + tp.name == 'long double') + # + size_of_a = max(len(tp.args)*8, 8) + if may_need_128_bits(tp.result): + size_of_a = max(size_of_a, 16) + if isinstance(tp.result, model.StructOrUnion): + size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( + tp.result.get_c_name(''), size_of_a, + tp.result.get_c_name(''), size_of_a) + prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) + prnt('{') + prnt(' char a[%s];' % size_of_a) + prnt(' char *p = a;') + for i, type in enumerate(tp.args): + arg = 'a%d' % i + if (isinstance(type, model.StructOrUnion) or + may_need_128_bits(type)): + arg = '&' + arg + type = model.PointerType(type) + prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) + prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) + if not isinstance(tp.result, model.VoidType): + prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) + prnt('}') + prnt() + self._num_externpy += 1 + + def _generate_cpy_extern_python_decl(self, tp, name): + self._extern_python_decl(tp, name, 'static ') + + def _generate_cpy_dllexport_python_decl(self, tp, name): + self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') + + def _generate_cpy_extern_python_plus_c_decl(self, tp, name): + self._extern_python_decl(tp, name, '') + + def _generate_cpy_extern_python_ctx(self, tp, name): + if self.target_is_python: + raise VerificationError( + "cannot use 'extern \"Python\"' in the ABI mode") + if tp.ellipsis: + raise NotImplementedError("a vararg function is extern \"Python\"") + type_index = self._typesdict[tp] + type_op = CffiOp(OP_EXTERN_PYTHON, type_index) + self._lsts["global"].append( + GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) + + _generate_cpy_dllexport_python_ctx = \ + _generate_cpy_extern_python_plus_c_ctx = \ + _generate_cpy_extern_python_ctx + + def _print_string_literal_in_array(self, s): + prnt = self._prnt + prnt('// # NB. this is not a string because of a size limit in MSVC') + if not isinstance(s, bytes): # unicode + s = s.encode('utf-8') # -> bytes + else: + s.decode('utf-8') # got bytes, check for valid utf-8 + try: + s.decode('ascii') + except UnicodeDecodeError: + s = b'# -*- encoding: utf8 -*-\n' + s + for line in s.splitlines(True): + comment = line + if type('//') is bytes: # python2 + line = map(ord, line) # make a list of integers + else: # python3 + # type(line) is bytes, which enumerates like a list of integers + comment = ascii(comment)[1:-1] + prnt(('// ' + comment).rstrip()) + printed_line = '' + for c in line: + if len(printed_line) >= 76: + prnt(printed_line) + printed_line = '' + printed_line += '%d,' % (c,) + prnt(printed_line) + + # ---------- + # emitting the opcodes for individual types + + def _emit_bytecode_VoidType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) + + def _emit_bytecode_PrimitiveType(self, tp, index): + prim_index = PRIMITIVE_TO_INDEX[tp.name] + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) + + def _emit_bytecode_UnknownIntegerType(self, tp, index): + s = ('_cffi_prim_int(sizeof(%s), (\n' + ' ((%s)-1) | 0 /* check that %s is an integer type */\n' + ' ) <= 0)' % (tp.name, tp.name, tp.name)) + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) + + def _emit_bytecode_UnknownFloatType(self, tp, index): + s = ('_cffi_prim_float(sizeof(%s) *\n' + ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' + ' )' % (tp.name, tp.name)) + self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) + + def _emit_bytecode_RawFunctionType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) + index += 1 + for tp1 in tp.args: + realindex = self._typesdict[tp1] + if index != realindex: + if isinstance(tp1, model.PrimitiveType): + self._emit_bytecode_PrimitiveType(tp1, index) + else: + self.cffi_types[index] = CffiOp(OP_NOOP, realindex) + index += 1 + flags = int(tp.ellipsis) + if tp.abi is not None: + if tp.abi == '__stdcall': + flags |= 2 + else: + raise NotImplementedError("abi=%r" % (tp.abi,)) + self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) + + def _emit_bytecode_PointerType(self, tp, index): + self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) + + _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType + _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType + + def _emit_bytecode_FunctionPtrType(self, tp, index): + raw = tp.as_raw_function() + self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) + + def _emit_bytecode_ArrayType(self, tp, index): + item_index = self._typesdict[tp.item] + if tp.length is None: + self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) + elif tp.length == '...': + raise VerificationError( + "type %s badly placed: the '...' array length can only be " + "used on global arrays or on fields of structures" % ( + str(tp).replace('/*...*/', '...'),)) + else: + assert self.cffi_types[index + 1] == 'LEN' + self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) + self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) + + def _emit_bytecode_StructType(self, tp, index): + struct_index = self._struct_unions[tp] + self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) + _emit_bytecode_UnionType = _emit_bytecode_StructType + + def _emit_bytecode_EnumType(self, tp, index): + enum_index = self._enums[tp] + self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super(NativeIO, self).write(s) + +def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): + if verbose: + print("generating %s" % (target_file,)) + recompiler = Recompiler(ffi, module_name, + target_is_python=(preamble is None)) + recompiler.collect_type_table() + recompiler.collect_step_tables() + f = NativeIO() + recompiler.write_source_to_f(f, preamble) + output = f.getvalue() + try: + with open(target_file, 'r') as f1: + if f1.read(len(output) + 1) != output: + raise IOError + if verbose: + print("(already up-to-date)") + return False # already up-to-date + except IOError: + tmp_file = '%s.~%d' % (target_file, os.getpid()) + with open(tmp_file, 'w') as f1: + f1.write(output) + try: + os.rename(tmp_file, target_file) + except OSError: + os.unlink(target_file) + os.rename(tmp_file, target_file) + return True + +def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): + assert preamble is not None + return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, + verbose) + +def make_py_source(ffi, module_name, target_py_file, verbose=False): + return _make_c_or_py_source(ffi, module_name, None, target_py_file, + verbose) + +def _modname_to_file(outputdir, modname, extension): + parts = modname.split('.') + try: + os.makedirs(os.path.join(outputdir, *parts[:-1])) + except OSError: + pass + parts[-1] += extension + return os.path.join(outputdir, *parts), parts + + +# Aaargh. Distutils is not tested at all for the purpose of compiling +# DLLs that are not extension modules. Here are some hacks to work +# around that, in the _patch_for_*() functions... + +def _patch_meth(patchlist, cls, name, new_meth): + old = getattr(cls, name) + patchlist.append((cls, name, old)) + setattr(cls, name, new_meth) + return old + +def _unpatch_meths(patchlist): + for cls, name, old_meth in reversed(patchlist): + setattr(cls, name, old_meth) + +def _patch_for_embedding(patchlist): + if sys.platform == 'win32': + # we must not remove the manifest when building for embedding! + from distutils.msvc9compiler import MSVCCompiler + _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', + lambda self, manifest_file: manifest_file) + + if sys.platform == 'darwin': + # we must not make a '-bundle', but a '-dynamiclib' instead + from distutils.ccompiler import CCompiler + def my_link_shared_object(self, *args, **kwds): + if '-bundle' in self.linker_so: + self.linker_so = list(self.linker_so) + i = self.linker_so.index('-bundle') + self.linker_so[i] = '-dynamiclib' + return old_link_shared_object(self, *args, **kwds) + old_link_shared_object = _patch_meth(patchlist, CCompiler, + 'link_shared_object', + my_link_shared_object) + +def _patch_for_target(patchlist, target): + from distutils.command.build_ext import build_ext + # if 'target' is different from '*', we need to patch some internal + # method to just return this 'target' value, instead of having it + # built from module_name + if target.endswith('.*'): + target = target[:-2] + if sys.platform == 'win32': + target += '.dll' + elif sys.platform == 'darwin': + target += '.dylib' + else: + target += '.so' + _patch_meth(patchlist, build_ext, 'get_ext_filename', + lambda self, ext_name: target) + + +def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, + c_file=None, source_extension='.c', extradir=None, + compiler_verbose=1, target=None, debug=None, **kwds): + if not isinstance(module_name, str): + module_name = module_name.encode('ascii') + if ffi._windows_unicode: + ffi._apply_windows_unicode(kwds) + if preamble is not None: + embedding = (ffi._embedding is not None) + if embedding: + ffi._apply_embedding_fix(kwds) + if c_file is None: + c_file, parts = _modname_to_file(tmpdir, module_name, + source_extension) + if extradir: + parts = [extradir] + parts + ext_c_file = os.path.join(*parts) + else: + ext_c_file = c_file + # + if target is None: + if embedding: + target = '%s.*' % module_name + else: + target = '*' + # + ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) + updated = make_c_source(ffi, module_name, preamble, c_file, + verbose=compiler_verbose) + if call_c_compiler: + patchlist = [] + cwd = os.getcwd() + try: + if embedding: + _patch_for_embedding(patchlist) + if target != '*': + _patch_for_target(patchlist, target) + if compiler_verbose: + if tmpdir == '.': + msg = 'the current directory is' + else: + msg = 'setting the current directory to' + print('%s %r' % (msg, os.path.abspath(tmpdir))) + os.chdir(tmpdir) + outputfilename = ffiplatform.compile('.', ext, + compiler_verbose, debug) + finally: + os.chdir(cwd) + _unpatch_meths(patchlist) + return outputfilename + else: + return ext, updated + else: + if c_file is None: + c_file, _ = _modname_to_file(tmpdir, module_name, '.py') + updated = make_py_source(ffi, module_name, c_file, + verbose=compiler_verbose) + if call_c_compiler: + return c_file + else: + return None, updated + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py new file mode 100644 index 000000000..5df6494fe --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py @@ -0,0 +1,219 @@ +import os +import sys + +try: + basestring +except NameError: + # Python 3.x + basestring = str + +def error(msg): + from distutils.errors import DistutilsSetupError + raise DistutilsSetupError(msg) + + +def execfile(filename, glob): + # We use execfile() (here rewritten for Python 3) instead of + # __import__() to load the build script. The problem with + # a normal import is that in some packages, the intermediate + # __init__.py files may already try to import the file that + # we are generating. + with open(filename) as f: + src = f.read() + src += '\n' # Python 2.6 compatibility + code = compile(src, filename, 'exec') + exec(code, glob, glob) + + +def add_cffi_module(dist, mod_spec): + from cffi.api import FFI + + if not isinstance(mod_spec, basestring): + error("argument to 'cffi_modules=...' must be a str or a list of str," + " not %r" % (type(mod_spec).__name__,)) + mod_spec = str(mod_spec) + try: + build_file_name, ffi_var_name = mod_spec.split(':') + except ValueError: + error("%r must be of the form 'path/build.py:ffi_variable'" % + (mod_spec,)) + if not os.path.exists(build_file_name): + ext = '' + rewritten = build_file_name.replace('.', '/') + '.py' + if os.path.exists(rewritten): + ext = ' (rewrite cffi_modules to [%r])' % ( + rewritten + ':' + ffi_var_name,) + error("%r does not name an existing file%s" % (build_file_name, ext)) + + mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} + execfile(build_file_name, mod_vars) + + try: + ffi = mod_vars[ffi_var_name] + except KeyError: + error("%r: object %r not found in module" % (mod_spec, + ffi_var_name)) + if not isinstance(ffi, FFI): + ffi = ffi() # maybe it's a function instead of directly an ffi + if not isinstance(ffi, FFI): + error("%r is not an FFI instance (got %r)" % (mod_spec, + type(ffi).__name__)) + if not hasattr(ffi, '_assigned_source'): + error("%r: the set_source() method was not called" % (mod_spec,)) + module_name, source, source_extension, kwds = ffi._assigned_source + if ffi._windows_unicode: + kwds = kwds.copy() + ffi._apply_windows_unicode(kwds) + + if source is None: + _add_py_module(dist, ffi, module_name) + else: + _add_c_module(dist, ffi, module_name, source, source_extension, kwds) + +def _set_py_limited_api(Extension, kwds): + """ + Add py_limited_api to kwds if setuptools >= 26 is in use. + Do not alter the setting if it already exists. + Setuptools takes care of ignoring the flag on Python 2 and PyPy. + + CPython itself should ignore the flag in a debugging version + (by not listing .abi3.so in the extensions it supports), but + it doesn't so far, creating troubles. That's why we check + for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent + of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) + + On Windows, with CPython <= 3.4, it's better not to use py_limited_api + because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. + Recently (2020) we started shipping only >= 3.5 wheels, though. So + we'll give it another try and set py_limited_api on Windows >= 3.5. + """ + from cffi import recompiler + + if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') + and recompiler.USE_LIMITED_API): + import setuptools + try: + setuptools_major_version = int(setuptools.__version__.partition('.')[0]) + if setuptools_major_version >= 26: + kwds['py_limited_api'] = True + except ValueError: # certain development versions of setuptools + # If we don't know the version number of setuptools, we + # try to set 'py_limited_api' anyway. At worst, we get a + # warning. + kwds['py_limited_api'] = True + return kwds + +def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): + from distutils.core import Extension + # We are a setuptools extension. Need this build_ext for py_limited_api. + from setuptools.command.build_ext import build_ext + from distutils.dir_util import mkpath + from distutils import log + from cffi import recompiler + + allsources = ['$PLACEHOLDER'] + allsources.extend(kwds.pop('sources', [])) + kwds = _set_py_limited_api(Extension, kwds) + ext = Extension(name=module_name, sources=allsources, **kwds) + + def make_mod(tmpdir, pre_run=None): + c_file = os.path.join(tmpdir, module_name + source_extension) + log.info("generating cffi module %r" % c_file) + mkpath(tmpdir) + # a setuptools-only, API-only hook: called with the "ext" and "ffi" + # arguments just before we turn the ffi into C code. To use it, + # subclass the 'distutils.command.build_ext.build_ext' class and + # add a method 'def pre_run(self, ext, ffi)'. + if pre_run is not None: + pre_run(ext, ffi) + updated = recompiler.make_c_source(ffi, module_name, source, c_file) + if not updated: + log.info("already up-to-date") + return c_file + + if dist.ext_modules is None: + dist.ext_modules = [] + dist.ext_modules.append(ext) + + base_class = dist.cmdclass.get('build_ext', build_ext) + class build_ext_make_mod(base_class): + def run(self): + if ext.sources[0] == '$PLACEHOLDER': + pre_run = getattr(self, 'pre_run', None) + ext.sources[0] = make_mod(self.build_temp, pre_run) + base_class.run(self) + dist.cmdclass['build_ext'] = build_ext_make_mod + # NB. multiple runs here will create multiple 'build_ext_make_mod' + # classes. Even in this case the 'build_ext' command should be + # run once; but just in case, the logic above does nothing if + # called again. + + +def _add_py_module(dist, ffi, module_name): + from distutils.dir_util import mkpath + from setuptools.command.build_py import build_py + from setuptools.command.build_ext import build_ext + from distutils import log + from cffi import recompiler + + def generate_mod(py_file): + log.info("generating cffi module %r" % py_file) + mkpath(os.path.dirname(py_file)) + updated = recompiler.make_py_source(ffi, module_name, py_file) + if not updated: + log.info("already up-to-date") + + base_class = dist.cmdclass.get('build_py', build_py) + class build_py_make_mod(base_class): + def run(self): + base_class.run(self) + module_path = module_name.split('.') + module_path[-1] += '.py' + generate_mod(os.path.join(self.build_lib, *module_path)) + def get_source_files(self): + # This is called from 'setup.py sdist' only. Exclude + # the generate .py module in this case. + saved_py_modules = self.py_modules + try: + if saved_py_modules: + self.py_modules = [m for m in saved_py_modules + if m != module_name] + return base_class.get_source_files(self) + finally: + self.py_modules = saved_py_modules + dist.cmdclass['build_py'] = build_py_make_mod + + # distutils and setuptools have no notion I could find of a + # generated python module. If we don't add module_name to + # dist.py_modules, then things mostly work but there are some + # combination of options (--root and --record) that will miss + # the module. So we add it here, which gives a few apparently + # harmless warnings about not finding the file outside the + # build directory. + # Then we need to hack more in get_source_files(); see above. + if dist.py_modules is None: + dist.py_modules = [] + dist.py_modules.append(module_name) + + # the following is only for "build_ext -i" + base_class_2 = dist.cmdclass.get('build_ext', build_ext) + class build_ext_make_mod(base_class_2): + def run(self): + base_class_2.run(self) + if self.inplace: + # from get_ext_fullpath() in distutils/command/build_ext.py + module_path = module_name.split('.') + package = '.'.join(module_path[:-1]) + build_py = self.get_finalized_command('build_py') + package_dir = build_py.get_package_dir(package) + file_name = module_path[-1] + '.py' + generate_mod(os.path.join(package_dir, file_name)) + dist.cmdclass['build_ext'] = build_ext_make_mod + +def cffi_modules(dist, attr, value): + assert attr == 'cffi_modules' + if isinstance(value, basestring): + value = [value] + + for cffi_module in value: + add_cffi_module(dist, cffi_module) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py new file mode 100644 index 000000000..8c26db092 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py @@ -0,0 +1,1076 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, imp +from . import model +from .error import VerificationError + + +class VCPythonEngine(object): + _class_key = 'x' + _gen_python_module = True + + def __init__(self, verifier): + self.verifier = verifier + self.ffi = verifier.ffi + self._struct_pending_verification = {} + self._types_of_builtin_functions = {} + + def patch_extension_kwds(self, kwds): + pass + + def find_module(self, module_name, path, so_suffixes): + try: + f, filename, descr = imp.find_module(module_name, path) + except ImportError: + return None + if f is not None: + f.close() + # Note that after a setuptools installation, there are both .py + # and .so files with the same basename. The code here relies on + # imp.find_module() locating the .so in priority. + if descr[0] not in so_suffixes: + return None + return filename + + def collect_types(self): + self._typesdict = {} + self._generate("collecttype") + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def _gettypenum(self, type): + # a KeyError here is a bug. please report it! :-) + return self._typesdict[type] + + def _do_collect_type(self, tp): + if ((not isinstance(tp, model.PrimitiveType) + or tp.name == 'long double') + and tp not in self._typesdict): + num = len(self._typesdict) + self._typesdict[tp] = num + + def write_source_to_f(self): + self.collect_types() + # + # The new module will have a _cffi_setup() function that receives + # objects from the ffi world, and that calls some setup code in + # the module. This setup code is split in several independent + # functions, e.g. one per constant. The functions are "chained" + # by ending in a tail call to each other. + # + # This is further split in two chained lists, depending on if we + # can do it at import-time or if we must wait for _cffi_setup() to + # provide us with the objects. This is needed because we + # need the values of the enum constants in order to build the + # that we may have to pass to _cffi_setup(). + # + # The following two 'chained_list_constants' items contains + # the head of these two chained lists, as a string that gives the + # call to do, if any. + self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] + # + prnt = self._prnt + # first paste some standard set of lines that are mostly '#define' + prnt(cffimod_header) + prnt() + # then paste the C source given by the user, verbatim. + prnt(self.verifier.preamble) + prnt() + # + # call generate_cpy_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._generate("decl") + # + # implement the function _cffi_setup_custom() as calling the + # head of the chained list. + self._generate_setup_custom() + prnt() + # + # produce the method table, including the entries for the + # generated Python->C function wrappers, which are done + # by generate_cpy_function_method(). + prnt('static PyMethodDef _cffi_methods[] = {') + self._generate("method") + prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') + prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') + prnt('};') + prnt() + # + # standard init. + modname = self.verifier.get_module_name() + constants = self._chained_list_constants[False] + prnt('#if PY_MAJOR_VERSION >= 3') + prnt() + prnt('static struct PyModuleDef _cffi_module_def = {') + prnt(' PyModuleDef_HEAD_INIT,') + prnt(' "%s",' % modname) + prnt(' NULL,') + prnt(' -1,') + prnt(' _cffi_methods,') + prnt(' NULL, NULL, NULL, NULL') + prnt('};') + prnt() + prnt('PyMODINIT_FUNC') + prnt('PyInit_%s(void)' % modname) + prnt('{') + prnt(' PyObject *lib;') + prnt(' lib = PyModule_Create(&_cffi_module_def);') + prnt(' if (lib == NULL)') + prnt(' return NULL;') + prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) + prnt(' Py_DECREF(lib);') + prnt(' return NULL;') + prnt(' }') + prnt(' return lib;') + prnt('}') + prnt() + prnt('#else') + prnt() + prnt('PyMODINIT_FUNC') + prnt('init%s(void)' % modname) + prnt('{') + prnt(' PyObject *lib;') + prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) + prnt(' if (lib == NULL)') + prnt(' return;') + prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) + prnt(' return;') + prnt(' return;') + prnt('}') + prnt() + prnt('#endif') + + def load_library(self, flags=None): + # XXX review all usages of 'self' here! + # import it as a new extension module + imp.acquire_lock() + try: + if hasattr(sys, "getdlopenflags"): + previous_flags = sys.getdlopenflags() + try: + if hasattr(sys, "setdlopenflags") and flags is not None: + sys.setdlopenflags(flags) + module = imp.load_dynamic(self.verifier.get_module_name(), + self.verifier.modulefilename) + except ImportError as e: + error = "importing %r: %s" % (self.verifier.modulefilename, e) + raise VerificationError(error) + finally: + if hasattr(sys, "setdlopenflags"): + sys.setdlopenflags(previous_flags) + finally: + imp.release_lock() + # + # call loading_cpy_struct() to get the struct layout inferred by + # the C compiler + self._load(module, 'loading') + # + # the C code will need the objects. Collect them in + # order in a list. + revmapping = dict([(value, key) + for (key, value) in self._typesdict.items()]) + lst = [revmapping[i] for i in range(len(revmapping))] + lst = list(map(self.ffi._get_cached_btype, lst)) + # + # build the FFILibrary class and instance and call _cffi_setup(). + # this will set up some fields like '_cffi_types', and only then + # it will invoke the chained list of functions that will really + # build (notably) the constant objects, as if they are + # pointers, and store them as attributes on the 'library' object. + class FFILibrary(object): + _cffi_python_module = module + _cffi_ffi = self.ffi + _cffi_dir = [] + def __dir__(self): + return FFILibrary._cffi_dir + list(self.__dict__) + library = FFILibrary() + if module._cffi_setup(lst, VerificationError, library): + import warnings + warnings.warn("reimporting %r might overwrite older definitions" + % (self.verifier.get_module_name())) + # + # finally, call the loaded_cpy_xxx() functions. This will perform + # the final adjustments, like copying the Python->C wrapper + # functions from the module to the 'library' object, and setting + # up the FFILibrary class with properties for the global C variables. + self._load(module, 'loaded', library=library) + module._cffi_original_ffi = self.ffi + module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions + return library + + def _get_declarations(self): + lst = [(key, tp) for (key, (tp, qual)) in + self.ffi._parser._declarations.items()] + lst.sort() + return lst + + def _generate(self, step_name): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_cpy_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in verify(): %r" % name) + try: + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _load(self, module, step_name, **kwds): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) + try: + method(tp, realname, module, **kwds) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _generate_nothing(self, tp, name): + pass + + def _loaded_noop(self, tp, name, module, **kwds): + pass + + # ---------- + + def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): + extraarg = '' + if isinstance(tp, model.PrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + converter = '_cffi_to_c_int' + extraarg = ', %s' % tp.name + else: + converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), + tp.name.replace(' ', '_')) + errvalue = '-1' + # + elif isinstance(tp, model.PointerType): + self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, + tovar, errcode) + return + # + elif isinstance(tp, (model.StructOrUnion, model.EnumType)): + # a struct (not a struct pointer) as a function argument + self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' + % (tovar, self._gettypenum(tp), fromvar)) + self._prnt(' %s;' % errcode) + return + # + elif isinstance(tp, model.FunctionPtrType): + converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') + extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) + errvalue = 'NULL' + # + else: + raise NotImplementedError(tp) + # + self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) + self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( + tovar, tp.get_c_name(''), errvalue)) + self._prnt(' %s;' % errcode) + + def _extra_local_variables(self, tp, localvars, freelines): + if isinstance(tp, model.PointerType): + localvars.add('Py_ssize_t datasize') + localvars.add('struct _cffi_freeme_s *large_args_free = NULL') + freelines.add('if (large_args_free != NULL)' + ' _cffi_free_array_arguments(large_args_free);') + + def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): + self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') + self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( + self._gettypenum(tp), fromvar, tovar)) + self._prnt(' if (datasize != 0) {') + self._prnt(' %s = ((size_t)datasize) <= 640 ? ' + 'alloca((size_t)datasize) : NULL;' % (tovar,)) + self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' + '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) + self._prnt(' datasize, &large_args_free) < 0)') + self._prnt(' %s;' % errcode) + self._prnt(' }') + + def _convert_expr_from_c(self, tp, var, context): + if isinstance(tp, model.PrimitiveType): + if tp.is_integer_type() and tp.name != '_Bool': + return '_cffi_from_c_int(%s, %s)' % (var, tp.name) + elif tp.name != 'long double': + return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) + else: + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.ArrayType): + return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( + var, self._gettypenum(model.PointerType(tp.item))) + elif isinstance(tp, model.StructOrUnion): + if tp.fldnames is None: + raise TypeError("'%s' is used as %s, but is opaque" % ( + tp._get_c_name(), context)) + return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + elif isinstance(tp, model.EnumType): + return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( + var, self._gettypenum(tp)) + else: + raise NotImplementedError(tp) + + # ---------- + # typedefs: generates no code so far + + _generate_cpy_typedef_collecttype = _generate_nothing + _generate_cpy_typedef_decl = _generate_nothing + _generate_cpy_typedef_method = _generate_nothing + _loading_cpy_typedef = _loaded_noop + _loaded_cpy_typedef = _loaded_noop + + # ---------- + # function declarations + + def _generate_cpy_function_collecttype(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + self._do_collect_type(tp) + else: + # don't call _do_collect_type(tp) in this common case, + # otherwise test_autofilled_struct_as_argument fails + for type in tp.args: + self._do_collect_type(type) + self._do_collect_type(tp.result) + + def _generate_cpy_function_decl(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no CPython wrapper) + self._generate_cpy_const(False, name, tp) + return + prnt = self._prnt + numargs = len(tp.args) + if numargs == 0: + argname = 'noarg' + elif numargs == 1: + argname = 'arg0' + else: + argname = 'args' + prnt('static PyObject *') + prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) + prnt('{') + # + context = 'argument of %s' % name + for i, type in enumerate(tp.args): + prnt(' %s;' % type.get_c_name(' x%d' % i, context)) + # + localvars = set() + freelines = set() + for type in tp.args: + self._extra_local_variables(type, localvars, freelines) + for decl in sorted(localvars): + prnt(' %s;' % (decl,)) + # + if not isinstance(tp.result, model.VoidType): + result_code = 'result = ' + context = 'result of %s' % name + prnt(' %s;' % tp.result.get_c_name(' result', context)) + prnt(' PyObject *pyresult;') + else: + result_code = '' + # + if len(tp.args) > 1: + rng = range(len(tp.args)) + for i in rng: + prnt(' PyObject *arg%d;' % i) + prnt() + prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( + 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) + prnt(' return NULL;') + prnt() + # + for i, type in enumerate(tp.args): + self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, + 'return NULL') + prnt() + # + prnt(' Py_BEGIN_ALLOW_THREADS') + prnt(' _cffi_restore_errno();') + prnt(' { %s%s(%s); }' % ( + result_code, name, + ', '.join(['x%d' % i for i in range(len(tp.args))]))) + prnt(' _cffi_save_errno();') + prnt(' Py_END_ALLOW_THREADS') + prnt() + # + prnt(' (void)self; /* unused */') + if numargs == 0: + prnt(' (void)noarg; /* unused */') + if result_code: + prnt(' pyresult = %s;' % + self._convert_expr_from_c(tp.result, 'result', 'result type')) + for freeline in freelines: + prnt(' ' + freeline) + prnt(' return pyresult;') + else: + for freeline in freelines: + prnt(' ' + freeline) + prnt(' Py_INCREF(Py_None);') + prnt(' return Py_None;') + prnt('}') + prnt() + + def _generate_cpy_function_method(self, tp, name): + if tp.ellipsis: + return + numargs = len(tp.args) + if numargs == 0: + meth = 'METH_NOARGS' + elif numargs == 1: + meth = 'METH_O' + else: + meth = 'METH_VARARGS' + self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) + + _loading_cpy_function = _loaded_noop + + def _loaded_cpy_function(self, tp, name, module, library): + if tp.ellipsis: + return + func = getattr(module, name) + setattr(library, name, func) + self._types_of_builtin_functions[func] = tp + + # ---------- + # named structs + + _generate_cpy_struct_collecttype = _generate_nothing + def _generate_cpy_struct_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'struct', name) + def _generate_cpy_struct_method(self, tp, name): + self._generate_struct_or_union_method(tp, 'struct', name) + def _loading_cpy_struct(self, tp, name, module): + self._loading_struct_or_union(tp, 'struct', name, module) + def _loaded_cpy_struct(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + _generate_cpy_union_collecttype = _generate_nothing + def _generate_cpy_union_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'union', name) + def _generate_cpy_union_method(self, tp, name): + self._generate_struct_or_union_method(tp, 'union', name) + def _loading_cpy_union(self, tp, name, module): + self._loading_struct_or_union(tp, 'union', name, module) + def _loaded_cpy_union(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_struct_or_union_decl(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + checkfuncname = '_cffi_check_%s_%s' % (prefix, name) + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + cname = ('%s %s' % (prefix, name)).strip() + # + prnt = self._prnt + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if (isinstance(ftype, model.PrimitiveType) + and ftype.is_integer_type()) or fbitsize >= 0: + # accept all integers, but complain on float or double + prnt(' (void)((p->%s) << 1);' % fname) + else: + # only accept exactly the type declared. + try: + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + prnt('static PyObject *') + prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) + prnt('{') + prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) + prnt(' static Py_ssize_t nums[] = {') + prnt(' sizeof(%s),' % cname) + prnt(' offsetof(struct _cffi_aligncheck, y),') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + prnt(' offsetof(%s, %s),' % (cname, fname)) + if isinstance(ftype, model.ArrayType) and ftype.length is None: + prnt(' 0, /* %s */' % ftype._get_c_name()) + else: + prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) + prnt(' -1') + prnt(' };') + prnt(' (void)self; /* unused */') + prnt(' (void)noarg; /* unused */') + prnt(' return _cffi_get_struct_layout(nums);') + prnt(' /* the next line is not executed, but compiled */') + prnt(' %s(0);' % (checkfuncname,)) + prnt('}') + prnt() + + def _generate_struct_or_union_method(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, + layoutfuncname)) + + def _loading_struct_or_union(self, tp, prefix, name, module): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + # + function = getattr(module, layoutfuncname) + layout = function() + if isinstance(tp, model.StructOrUnion) and tp.partial: + # use the function()'s sizes and offsets to guide the + # layout of the struct + totalsize = layout[0] + totalalignment = layout[1] + fieldofs = layout[2::2] + fieldsize = layout[3::2] + tp.force_flatten() + assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) + tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment + else: + cname = ('%s %s' % (prefix, name)).strip() + self._struct_pending_verification[tp] = layout, cname + + def _loaded_struct_or_union(self, tp): + if tp.fldnames is None: + return # nothing to do with opaque structs + self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered + + if tp in self._struct_pending_verification: + # check that the layout sizes and offsets match the real ones + def check(realvalue, expectedvalue, msg): + if realvalue != expectedvalue: + raise VerificationError( + "%s (we have %d, but C compiler says %d)" + % (msg, expectedvalue, realvalue)) + ffi = self.ffi + BStruct = ffi._get_cached_btype(tp) + layout, cname = self._struct_pending_verification.pop(tp) + check(layout[0], ffi.sizeof(BStruct), "wrong total size") + check(layout[1], ffi.alignof(BStruct), "wrong total alignment") + i = 2 + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + check(layout[i], ffi.offsetof(BStruct, fname), + "wrong offset for field %r" % (fname,)) + if layout[i+1] != 0: + BField = ffi._get_cached_btype(ftype) + check(layout[i+1], ffi.sizeof(BField), + "wrong size for field %r" % (fname,)) + i += 2 + assert i == len(layout) + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + _generate_cpy_anonymous_collecttype = _generate_nothing + + def _generate_cpy_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_cpy_enum_decl(tp, name, '') + else: + self._generate_struct_or_union_decl(tp, '', name) + + def _generate_cpy_anonymous_method(self, tp, name): + if not isinstance(tp, model.EnumType): + self._generate_struct_or_union_method(tp, '', name) + + def _loading_cpy_anonymous(self, tp, name, module): + if isinstance(tp, model.EnumType): + self._loading_cpy_enum(tp, name, module) + else: + self._loading_struct_or_union(tp, '', name, module) + + def _loaded_cpy_anonymous(self, tp, name, module, **kwds): + if isinstance(tp, model.EnumType): + self._loaded_cpy_enum(tp, name, module, **kwds) + else: + self._loaded_struct_or_union(tp) + + # ---------- + # constants, likely declared with '#define' + + def _generate_cpy_const(self, is_int, name, tp=None, category='const', + vartp=None, delayed=True, size_too=False, + check_value=None): + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + prnt('static int %s(PyObject *lib)' % funcname) + prnt('{') + prnt(' PyObject *o;') + prnt(' int res;') + if not is_int: + prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) + else: + assert category == 'const' + # + if check_value is not None: + self._check_int_constant_value(name, check_value) + # + if not is_int: + if category == 'var': + realexpr = '&' + name + else: + realexpr = name + prnt(' i = (%s);' % (realexpr,)) + prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', + 'variable type'),)) + assert delayed + else: + prnt(' o = _cffi_from_c_int_const(%s);' % name) + prnt(' if (o == NULL)') + prnt(' return -1;') + if size_too: + prnt(' {') + prnt(' PyObject *o1 = o;') + prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' + % (name,)) + prnt(' Py_DECREF(o1);') + prnt(' if (o == NULL)') + prnt(' return -1;') + prnt(' }') + prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) + prnt(' Py_DECREF(o);') + prnt(' if (res < 0)') + prnt(' return -1;') + prnt(' return %s;' % self._chained_list_constants[delayed]) + self._chained_list_constants[delayed] = funcname + '(lib)' + prnt('}') + prnt() + + def _generate_cpy_constant_collecttype(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + if not is_int: + self._do_collect_type(tp) + + def _generate_cpy_constant_decl(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + self._generate_cpy_const(is_int, name, tp) + + _generate_cpy_constant_method = _generate_nothing + _loading_cpy_constant = _loaded_noop + _loaded_cpy_constant = _loaded_noop + + # ---------- + # enums + + def _check_int_constant_value(self, name, value, err_prefix=''): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % + name) + prnt(' PyErr_Format(_cffi_VerificationError,') + prnt(' "%s%s has the real value %s, not %s",') + prnt(' "%s", "%s", buf, "%d");' % ( + err_prefix, name, value)) + prnt(' return -1;') + prnt(' }') + + def _enum_funcname(self, prefix, name): + # "$enum_$1" => "___D_enum____D_1" + name = name.replace('$', '___D_') + return '_cffi_e_%s_%s' % (prefix, name) + + def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): + if tp.partial: + for enumerator in tp.enumerators: + self._generate_cpy_const(True, enumerator, delayed=False) + return + # + funcname = self._enum_funcname(prefix, name) + prnt = self._prnt + prnt('static int %s(PyObject *lib)' % funcname) + prnt('{') + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._check_int_constant_value(enumerator, enumvalue, + "enum %s: " % name) + prnt(' return %s;' % self._chained_list_constants[True]) + self._chained_list_constants[True] = funcname + '(lib)' + prnt('}') + prnt() + + _generate_cpy_enum_collecttype = _generate_nothing + _generate_cpy_enum_method = _generate_nothing + + def _loading_cpy_enum(self, tp, name, module): + if tp.partial: + enumvalues = [getattr(module, enumerator) + for enumerator in tp.enumerators] + tp.enumvalues = tuple(enumvalues) + tp.partial_resolved = True + + def _loaded_cpy_enum(self, tp, name, module, library): + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + setattr(library, enumerator, enumvalue) + + # ---------- + # macros: for now only for integers + + def _generate_cpy_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_cpy_const(True, name, check_value=check_value) + + _generate_cpy_macro_collecttype = _generate_nothing + _generate_cpy_macro_method = _generate_nothing + _loading_cpy_macro = _loaded_noop + _loaded_cpy_macro = _loaded_noop + + # ---------- + # global variables + + def _generate_cpy_variable_collecttype(self, tp, name): + if isinstance(tp, model.ArrayType): + tp_ptr = model.PointerType(tp.item) + else: + tp_ptr = model.PointerType(tp) + self._do_collect_type(tp_ptr) + + def _generate_cpy_variable_decl(self, tp, name): + if isinstance(tp, model.ArrayType): + tp_ptr = model.PointerType(tp.item) + self._generate_cpy_const(False, name, tp, vartp=tp_ptr, + size_too = tp.length_is_unknown()) + else: + tp_ptr = model.PointerType(tp) + self._generate_cpy_const(False, name, tp_ptr, category='var') + + _generate_cpy_variable_method = _generate_nothing + _loading_cpy_variable = _loaded_noop + + def _loaded_cpy_variable(self, tp, name, module, library): + value = getattr(library, name) + if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the + # sense that "a=..." is forbidden + if tp.length_is_unknown(): + assert isinstance(value, tuple) + (value, size) = value + BItemType = self.ffi._get_cached_btype(tp.item) + length, rest = divmod(size, self.ffi.sizeof(BItemType)) + if rest != 0: + raise VerificationError( + "bad size: %r does not seem to be an array of %s" % + (name, tp.item)) + tp = tp.resolve_length(length) + # 'value' is a which we have to replace with + # a if the N is actually known + if tp.length is not None: + BArray = self.ffi._get_cached_btype(tp) + value = self.ffi.cast(BArray, value) + setattr(library, name, value) + return + # remove ptr= from the library instance, and replace + # it by a property on the class, which reads/writes into ptr[0]. + ptr = value + delattr(library, name) + def getter(library): + return ptr[0] + def setter(library, value): + ptr[0] = value + setattr(type(library), name, property(getter, setter)) + type(library)._cffi_dir.append(name) + + # ---------- + + def _generate_setup_custom(self): + prnt = self._prnt + prnt('static int _cffi_setup_custom(PyObject *lib)') + prnt('{') + prnt(' return %s;' % self._chained_list_constants[True]) + prnt('}') + +cffimod_header = r''' +#include +#include + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +#endif + +#if PY_MAJOR_VERSION < 3 +# undef PyCapsule_CheckExact +# undef PyCapsule_GetPointer +# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) +# define PyCapsule_GetPointer(capsule, name) \ + (PyCObject_AsVoidPtr(capsule)) +#endif + +#if PY_MAJOR_VERSION >= 3 +# define PyInt_FromLong PyLong_FromLong +#endif + +#define _cffi_from_c_double PyFloat_FromDouble +#define _cffi_from_c_float PyFloat_FromDouble +#define _cffi_from_c_long PyInt_FromLong +#define _cffi_from_c_ulong PyLong_FromUnsignedLong +#define _cffi_from_c_longlong PyLong_FromLongLong +#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong +#define _cffi_from_c__Bool PyBool_FromLong + +#define _cffi_to_c_double PyFloat_AsDouble +#define _cffi_to_c_float PyFloat_AsDouble + +#define _cffi_from_c_int_const(x) \ + (((x) > 0) ? \ + ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ + PyInt_FromLong((long)(x)) : \ + PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ + ((long long)(x) >= (long long)LONG_MIN) ? \ + PyInt_FromLong((long)(x)) : \ + PyLong_FromLongLong((long long)(x))) + +#define _cffi_from_c_int(x, type) \ + (((type)-1) > 0 ? /* unsigned */ \ + (sizeof(type) < sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + sizeof(type) == sizeof(long) ? \ + PyLong_FromUnsignedLong((unsigned long)x) : \ + PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ + (sizeof(type) <= sizeof(long) ? \ + PyInt_FromLong((long)x) : \ + PyLong_FromLongLong((long long)x))) + +#define _cffi_to_c_int(o, type) \ + ((type)( \ + sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ + : (type)_cffi_to_c_i8(o)) : \ + sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ + : (type)_cffi_to_c_i16(o)) : \ + sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ + : (type)_cffi_to_c_i32(o)) : \ + sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ + : (type)_cffi_to_c_i64(o)) : \ + (Py_FatalError("unsupported size for type " #type), (type)0))) + +#define _cffi_to_c_i8 \ + ((int(*)(PyObject *))_cffi_exports[1]) +#define _cffi_to_c_u8 \ + ((int(*)(PyObject *))_cffi_exports[2]) +#define _cffi_to_c_i16 \ + ((int(*)(PyObject *))_cffi_exports[3]) +#define _cffi_to_c_u16 \ + ((int(*)(PyObject *))_cffi_exports[4]) +#define _cffi_to_c_i32 \ + ((int(*)(PyObject *))_cffi_exports[5]) +#define _cffi_to_c_u32 \ + ((unsigned int(*)(PyObject *))_cffi_exports[6]) +#define _cffi_to_c_i64 \ + ((long long(*)(PyObject *))_cffi_exports[7]) +#define _cffi_to_c_u64 \ + ((unsigned long long(*)(PyObject *))_cffi_exports[8]) +#define _cffi_to_c_char \ + ((int(*)(PyObject *))_cffi_exports[9]) +#define _cffi_from_c_pointer \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) +#define _cffi_to_c_pointer \ + ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) +#define _cffi_get_struct_layout \ + ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) +#define _cffi_restore_errno \ + ((void(*)(void))_cffi_exports[13]) +#define _cffi_save_errno \ + ((void(*)(void))_cffi_exports[14]) +#define _cffi_from_c_char \ + ((PyObject *(*)(char))_cffi_exports[15]) +#define _cffi_from_c_deref \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) +#define _cffi_to_c \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) +#define _cffi_from_c_struct \ + ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) +#define _cffi_to_c_wchar_t \ + ((wchar_t(*)(PyObject *))_cffi_exports[19]) +#define _cffi_from_c_wchar_t \ + ((PyObject *(*)(wchar_t))_cffi_exports[20]) +#define _cffi_to_c_long_double \ + ((long double(*)(PyObject *))_cffi_exports[21]) +#define _cffi_to_c__Bool \ + ((_Bool(*)(PyObject *))_cffi_exports[22]) +#define _cffi_prepare_pointer_call_argument \ + ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) +#define _cffi_convert_array_from_object \ + ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) +#define _CFFI_NUM_EXPORTS 25 + +typedef struct _ctypedescr CTypeDescrObject; + +static void *_cffi_exports[_CFFI_NUM_EXPORTS]; +static PyObject *_cffi_types, *_cffi_VerificationError; + +static int _cffi_setup_custom(PyObject *lib); /* forward */ + +static PyObject *_cffi_setup(PyObject *self, PyObject *args) +{ + PyObject *library; + int was_alive = (_cffi_types != NULL); + (void)self; /* unused */ + if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, + &library)) + return NULL; + Py_INCREF(_cffi_types); + Py_INCREF(_cffi_VerificationError); + if (_cffi_setup_custom(library) < 0) + return NULL; + return PyBool_FromLong(was_alive); +} + +union _cffi_union_alignment_u { + unsigned char m_char; + unsigned short m_short; + unsigned int m_int; + unsigned long m_long; + unsigned long long m_longlong; + float m_float; + double m_double; + long double m_longdouble; +}; + +struct _cffi_freeme_s { + struct _cffi_freeme_s *next; + union _cffi_union_alignment_u alignment; +}; + +#ifdef __GNUC__ + __attribute__((unused)) +#endif +static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, + char **output_data, Py_ssize_t datasize, + struct _cffi_freeme_s **freeme) +{ + char *p; + if (datasize < 0) + return -1; + + p = *output_data; + if (p == NULL) { + struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( + offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); + if (fp == NULL) + return -1; + fp->next = *freeme; + *freeme = fp; + p = *output_data = (char *)&fp->alignment; + } + memset((void *)p, 0, (size_t)datasize); + return _cffi_convert_array_from_object(p, ctptr, arg); +} + +#ifdef __GNUC__ + __attribute__((unused)) +#endif +static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) +{ + do { + void *p = (void *)freeme; + freeme = freeme->next; + PyObject_Free(p); + } while (freeme != NULL); +} + +static int _cffi_init(void) +{ + PyObject *module, *c_api_object = NULL; + + module = PyImport_ImportModule("_cffi_backend"); + if (module == NULL) + goto failure; + + c_api_object = PyObject_GetAttrString(module, "_C_API"); + if (c_api_object == NULL) + goto failure; + if (!PyCapsule_CheckExact(c_api_object)) { + PyErr_SetNone(PyExc_ImportError); + goto failure; + } + memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), + _CFFI_NUM_EXPORTS * sizeof(void *)); + + Py_DECREF(module); + Py_DECREF(c_api_object); + return 0; + + failure: + Py_XDECREF(module); + Py_XDECREF(c_api_object); + return -1; +} + +#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) + +/**********/ +''' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py new file mode 100644 index 000000000..72e144de0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py @@ -0,0 +1,675 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, os +import types + +from . import model +from .error import VerificationError + + +class VGenericEngine(object): + _class_key = 'g' + _gen_python_module = False + + def __init__(self, verifier): + self.verifier = verifier + self.ffi = verifier.ffi + self.export_symbols = [] + self._struct_pending_verification = {} + + def patch_extension_kwds(self, kwds): + # add 'export_symbols' to the dictionary. Note that we add the + # list before filling it. When we fill it, it will thus also show + # up in kwds['export_symbols']. + kwds.setdefault('export_symbols', self.export_symbols) + + def find_module(self, module_name, path, so_suffixes): + for so_suffix in so_suffixes: + basename = module_name + so_suffix + if path is None: + path = sys.path + for dirname in path: + filename = os.path.join(dirname, basename) + if os.path.isfile(filename): + return filename + + def collect_types(self): + pass # not needed in the generic engine + + def _prnt(self, what=''): + self._f.write(what + '\n') + + def write_source_to_f(self): + prnt = self._prnt + # first paste some standard set of lines that are mostly '#include' + prnt(cffimod_header) + # then paste the C source given by the user, verbatim. + prnt(self.verifier.preamble) + # + # call generate_gen_xxx_decl(), for every xxx found from + # ffi._parser._declarations. This generates all the functions. + self._generate('decl') + # + # on Windows, distutils insists on putting init_cffi_xyz in + # 'export_symbols', so instead of fighting it, just give up and + # give it one + if sys.platform == 'win32': + if sys.version_info >= (3,): + prefix = 'PyInit_' + else: + prefix = 'init' + modname = self.verifier.get_module_name() + prnt("void %s%s(void) { }\n" % (prefix, modname)) + + def load_library(self, flags=0): + # import it with the CFFI backend + backend = self.ffi._backend + # needs to make a path that contains '/', on Posix + filename = os.path.join(os.curdir, self.verifier.modulefilename) + module = backend.load_library(filename, flags) + # + # call loading_gen_struct() to get the struct layout inferred by + # the C compiler + self._load(module, 'loading') + + # build the FFILibrary class and instance, this is a module subclass + # because modules are expected to have usually-constant-attributes and + # in PyPy this means the JIT is able to treat attributes as constant, + # which we want. + class FFILibrary(types.ModuleType): + _cffi_generic_module = module + _cffi_ffi = self.ffi + _cffi_dir = [] + def __dir__(self): + return FFILibrary._cffi_dir + library = FFILibrary("") + # + # finally, call the loaded_gen_xxx() functions. This will set + # up the 'library' object. + self._load(module, 'loaded', library=library) + return library + + def _get_declarations(self): + lst = [(key, tp) for (key, (tp, qual)) in + self.ffi._parser._declarations.items()] + lst.sort() + return lst + + def _generate(self, step_name): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + try: + method = getattr(self, '_generate_gen_%s_%s' % (kind, + step_name)) + except AttributeError: + raise VerificationError( + "not implemented in verify(): %r" % name) + try: + method(tp, realname) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _load(self, module, step_name, **kwds): + for name, tp in self._get_declarations(): + kind, realname = name.split(' ', 1) + method = getattr(self, '_%s_gen_%s' % (step_name, kind)) + try: + method(tp, realname, module, **kwds) + except Exception as e: + model.attach_exception_info(e, name) + raise + + def _generate_nothing(self, tp, name): + pass + + def _loaded_noop(self, tp, name, module, **kwds): + pass + + # ---------- + # typedefs: generates no code so far + + _generate_gen_typedef_decl = _generate_nothing + _loading_gen_typedef = _loaded_noop + _loaded_gen_typedef = _loaded_noop + + # ---------- + # function declarations + + def _generate_gen_function_decl(self, tp, name): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + # cannot support vararg functions better than this: check for its + # exact type (including the fixed arguments), and build it as a + # constant function pointer (no _cffi_f_%s wrapper) + self._generate_gen_const(False, name, tp) + return + prnt = self._prnt + numargs = len(tp.args) + argnames = [] + for i, type in enumerate(tp.args): + indirection = '' + if isinstance(type, model.StructOrUnion): + indirection = '*' + argnames.append('%sx%d' % (indirection, i)) + context = 'argument of %s' % name + arglist = [type.get_c_name(' %s' % arg, context) + for type, arg in zip(tp.args, argnames)] + tpresult = tp.result + if isinstance(tpresult, model.StructOrUnion): + arglist.insert(0, tpresult.get_c_name(' *r', context)) + tpresult = model.void_type + arglist = ', '.join(arglist) or 'void' + wrappername = '_cffi_f_%s' % name + self.export_symbols.append(wrappername) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) + context = 'result of %s' % name + prnt(tpresult.get_c_name(funcdecl, context)) + prnt('{') + # + if isinstance(tp.result, model.StructOrUnion): + result_code = '*r = ' + elif not isinstance(tp.result, model.VoidType): + result_code = 'return ' + else: + result_code = '' + prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) + prnt('}') + prnt() + + _loading_gen_function = _loaded_noop + + def _loaded_gen_function(self, tp, name, module, library): + assert isinstance(tp, model.FunctionPtrType) + if tp.ellipsis: + newfunction = self._load_constant(False, tp, name, module) + else: + indirections = [] + base_tp = tp + if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) + or isinstance(tp.result, model.StructOrUnion)): + indirect_args = [] + for i, typ in enumerate(tp.args): + if isinstance(typ, model.StructOrUnion): + typ = model.PointerType(typ) + indirections.append((i, typ)) + indirect_args.append(typ) + indirect_result = tp.result + if isinstance(indirect_result, model.StructOrUnion): + if indirect_result.fldtypes is None: + raise TypeError("'%s' is used as result type, " + "but is opaque" % ( + indirect_result._get_c_name(),)) + indirect_result = model.PointerType(indirect_result) + indirect_args.insert(0, indirect_result) + indirections.insert(0, ("result", indirect_result)) + indirect_result = model.void_type + tp = model.FunctionPtrType(tuple(indirect_args), + indirect_result, tp.ellipsis) + BFunc = self.ffi._get_cached_btype(tp) + wrappername = '_cffi_f_%s' % name + newfunction = module.load_function(BFunc, wrappername) + for i, typ in indirections: + newfunction = self._make_struct_wrapper(newfunction, i, typ, + base_tp) + setattr(library, name, newfunction) + type(library)._cffi_dir.append(name) + + def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): + backend = self.ffi._backend + BType = self.ffi._get_cached_btype(tp) + if i == "result": + ffi = self.ffi + def newfunc(*args): + res = ffi.new(BType) + oldfunc(res, *args) + return res[0] + else: + def newfunc(*args): + args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] + return oldfunc(*args) + newfunc._cffi_base_type = base_tp + return newfunc + + # ---------- + # named structs + + def _generate_gen_struct_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'struct', name) + + def _loading_gen_struct(self, tp, name, module): + self._loading_struct_or_union(tp, 'struct', name, module) + + def _loaded_gen_struct(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_gen_union_decl(self, tp, name): + assert name == tp.name + self._generate_struct_or_union_decl(tp, 'union', name) + + def _loading_gen_union(self, tp, name, module): + self._loading_struct_or_union(tp, 'union', name, module) + + def _loaded_gen_union(self, tp, name, module, **kwds): + self._loaded_struct_or_union(tp) + + def _generate_struct_or_union_decl(self, tp, prefix, name): + if tp.fldnames is None: + return # nothing to do with opaque structs + checkfuncname = '_cffi_check_%s_%s' % (prefix, name) + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + cname = ('%s %s' % (prefix, name)).strip() + # + prnt = self._prnt + prnt('static void %s(%s *p)' % (checkfuncname, cname)) + prnt('{') + prnt(' /* only to generate compile-time warnings or errors */') + prnt(' (void)p;') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if (isinstance(ftype, model.PrimitiveType) + and ftype.is_integer_type()) or fbitsize >= 0: + # accept all integers, but complain on float or double + prnt(' (void)((p->%s) << 1);' % fname) + else: + # only accept exactly the type declared. + try: + prnt(' { %s = &p->%s; (void)tmp; }' % ( + ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), + fname)) + except VerificationError as e: + prnt(' /* %s */' % str(e)) # cannot verify it, ignore + prnt('}') + self.export_symbols.append(layoutfuncname) + prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) + prnt('{') + prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) + prnt(' static intptr_t nums[] = {') + prnt(' sizeof(%s),' % cname) + prnt(' offsetof(struct _cffi_aligncheck, y),') + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + prnt(' offsetof(%s, %s),' % (cname, fname)) + if isinstance(ftype, model.ArrayType) and ftype.length is None: + prnt(' 0, /* %s */' % ftype._get_c_name()) + else: + prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) + prnt(' -1') + prnt(' };') + prnt(' return nums[i];') + prnt(' /* the next line is not executed, but compiled */') + prnt(' %s(0);' % (checkfuncname,)) + prnt('}') + prnt() + + def _loading_struct_or_union(self, tp, prefix, name, module): + if tp.fldnames is None: + return # nothing to do with opaque structs + layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) + # + BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] + function = module.load_function(BFunc, layoutfuncname) + layout = [] + num = 0 + while True: + x = function(num) + if x < 0: break + layout.append(x) + num += 1 + if isinstance(tp, model.StructOrUnion) and tp.partial: + # use the function()'s sizes and offsets to guide the + # layout of the struct + totalsize = layout[0] + totalalignment = layout[1] + fieldofs = layout[2::2] + fieldsize = layout[3::2] + tp.force_flatten() + assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) + tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment + else: + cname = ('%s %s' % (prefix, name)).strip() + self._struct_pending_verification[tp] = layout, cname + + def _loaded_struct_or_union(self, tp): + if tp.fldnames is None: + return # nothing to do with opaque structs + self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered + + if tp in self._struct_pending_verification: + # check that the layout sizes and offsets match the real ones + def check(realvalue, expectedvalue, msg): + if realvalue != expectedvalue: + raise VerificationError( + "%s (we have %d, but C compiler says %d)" + % (msg, expectedvalue, realvalue)) + ffi = self.ffi + BStruct = ffi._get_cached_btype(tp) + layout, cname = self._struct_pending_verification.pop(tp) + check(layout[0], ffi.sizeof(BStruct), "wrong total size") + check(layout[1], ffi.alignof(BStruct), "wrong total alignment") + i = 2 + for fname, ftype, fbitsize, fqual in tp.enumfields(): + if fbitsize >= 0: + continue # xxx ignore fbitsize for now + check(layout[i], ffi.offsetof(BStruct, fname), + "wrong offset for field %r" % (fname,)) + if layout[i+1] != 0: + BField = ffi._get_cached_btype(ftype) + check(layout[i+1], ffi.sizeof(BField), + "wrong size for field %r" % (fname,)) + i += 2 + assert i == len(layout) + + # ---------- + # 'anonymous' declarations. These are produced for anonymous structs + # or unions; the 'name' is obtained by a typedef. + + def _generate_gen_anonymous_decl(self, tp, name): + if isinstance(tp, model.EnumType): + self._generate_gen_enum_decl(tp, name, '') + else: + self._generate_struct_or_union_decl(tp, '', name) + + def _loading_gen_anonymous(self, tp, name, module): + if isinstance(tp, model.EnumType): + self._loading_gen_enum(tp, name, module, '') + else: + self._loading_struct_or_union(tp, '', name, module) + + def _loaded_gen_anonymous(self, tp, name, module, **kwds): + if isinstance(tp, model.EnumType): + self._loaded_gen_enum(tp, name, module, **kwds) + else: + self._loaded_struct_or_union(tp) + + # ---------- + # constants, likely declared with '#define' + + def _generate_gen_const(self, is_int, name, tp=None, category='const', + check_value=None): + prnt = self._prnt + funcname = '_cffi_%s_%s' % (category, name) + self.export_symbols.append(funcname) + if check_value is not None: + assert is_int + assert category == 'const' + prnt('int %s(char *out_error)' % funcname) + prnt('{') + self._check_int_constant_value(name, check_value) + prnt(' return 0;') + prnt('}') + elif is_int: + assert category == 'const' + prnt('int %s(long long *out_value)' % funcname) + prnt('{') + prnt(' *out_value = (long long)(%s);' % (name,)) + prnt(' return (%s) <= 0;' % (name,)) + prnt('}') + else: + assert tp is not None + assert check_value is None + if category == 'var': + ampersand = '&' + else: + ampersand = '' + extra = '' + if category == 'const' and isinstance(tp, model.StructOrUnion): + extra = 'const *' + ampersand = '&' + prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) + prnt('{') + prnt(' return (%s%s);' % (ampersand, name)) + prnt('}') + prnt() + + def _generate_gen_constant_decl(self, tp, name): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + self._generate_gen_const(is_int, name, tp) + + _loading_gen_constant = _loaded_noop + + def _load_constant(self, is_int, tp, name, module, check_value=None): + funcname = '_cffi_const_%s' % name + if check_value is not None: + assert is_int + self._load_known_int_constant(module, funcname) + value = check_value + elif is_int: + BType = self.ffi._typeof_locked("long long*")[0] + BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType) + negative = function(p) + value = int(p[0]) + if value < 0 and not negative: + BLongLong = self.ffi._typeof_locked("long long")[0] + value += (1 << (8*self.ffi.sizeof(BLongLong))) + else: + assert check_value is None + fntypeextra = '(*)(void)' + if isinstance(tp, model.StructOrUnion): + fntypeextra = '*' + fntypeextra + BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] + function = module.load_function(BFunc, funcname) + value = function() + if isinstance(tp, model.StructOrUnion): + value = value[0] + return value + + def _loaded_gen_constant(self, tp, name, module, library): + is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() + value = self._load_constant(is_int, tp, name, module) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + + # ---------- + # enums + + def _check_int_constant_value(self, name, value): + prnt = self._prnt + if value <= 0: + prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( + name, name, value)) + else: + prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( + name, name, value)) + prnt(' char buf[64];') + prnt(' if ((%s) <= 0)' % name) + prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) + prnt(' else') + prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % + name) + prnt(' sprintf(out_error, "%s has the real value %s, not %s",') + prnt(' "%s", buf, "%d");' % (name[:100], value)) + prnt(' return -1;') + prnt(' }') + + def _load_known_int_constant(self, module, funcname): + BType = self.ffi._typeof_locked("char[]")[0] + BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] + function = module.load_function(BFunc, funcname) + p = self.ffi.new(BType, 256) + if function(p) < 0: + error = self.ffi.string(p) + if sys.version_info >= (3,): + error = str(error, 'utf-8') + raise VerificationError(error) + + def _enum_funcname(self, prefix, name): + # "$enum_$1" => "___D_enum____D_1" + name = name.replace('$', '___D_') + return '_cffi_e_%s_%s' % (prefix, name) + + def _generate_gen_enum_decl(self, tp, name, prefix='enum'): + if tp.partial: + for enumerator in tp.enumerators: + self._generate_gen_const(True, enumerator) + return + # + funcname = self._enum_funcname(prefix, name) + self.export_symbols.append(funcname) + prnt = self._prnt + prnt('int %s(char *out_error)' % funcname) + prnt('{') + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + self._check_int_constant_value(enumerator, enumvalue) + prnt(' return 0;') + prnt('}') + prnt() + + def _loading_gen_enum(self, tp, name, module, prefix='enum'): + if tp.partial: + enumvalues = [self._load_constant(True, tp, enumerator, module) + for enumerator in tp.enumerators] + tp.enumvalues = tuple(enumvalues) + tp.partial_resolved = True + else: + funcname = self._enum_funcname(prefix, name) + self._load_known_int_constant(module, funcname) + + def _loaded_gen_enum(self, tp, name, module, library): + for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): + setattr(library, enumerator, enumvalue) + type(library)._cffi_dir.append(enumerator) + + # ---------- + # macros: for now only for integers + + def _generate_gen_macro_decl(self, tp, name): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + self._generate_gen_const(True, name, check_value=check_value) + + _loading_gen_macro = _loaded_noop + + def _loaded_gen_macro(self, tp, name, module, library): + if tp == '...': + check_value = None + else: + check_value = tp # an integer + value = self._load_constant(True, tp, name, module, + check_value=check_value) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + + # ---------- + # global variables + + def _generate_gen_variable_decl(self, tp, name): + if isinstance(tp, model.ArrayType): + if tp.length_is_unknown(): + prnt = self._prnt + funcname = '_cffi_sizeof_%s' % (name,) + self.export_symbols.append(funcname) + prnt("size_t %s(void)" % funcname) + prnt("{") + prnt(" return sizeof(%s);" % (name,)) + prnt("}") + tp_ptr = model.PointerType(tp.item) + self._generate_gen_const(False, name, tp_ptr) + else: + tp_ptr = model.PointerType(tp) + self._generate_gen_const(False, name, tp_ptr, category='var') + + _loading_gen_variable = _loaded_noop + + def _loaded_gen_variable(self, tp, name, module, library): + if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the + # sense that "a=..." is forbidden + if tp.length_is_unknown(): + funcname = '_cffi_sizeof_%s' % (name,) + BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] + function = module.load_function(BFunc, funcname) + size = function() + BItemType = self.ffi._get_cached_btype(tp.item) + length, rest = divmod(size, self.ffi.sizeof(BItemType)) + if rest != 0: + raise VerificationError( + "bad size: %r does not seem to be an array of %s" % + (name, tp.item)) + tp = tp.resolve_length(length) + tp_ptr = model.PointerType(tp.item) + value = self._load_constant(False, tp_ptr, name, module) + # 'value' is a which we have to replace with + # a if the N is actually known + if tp.length is not None: + BArray = self.ffi._get_cached_btype(tp) + value = self.ffi.cast(BArray, value) + setattr(library, name, value) + type(library)._cffi_dir.append(name) + return + # remove ptr= from the library instance, and replace + # it by a property on the class, which reads/writes into ptr[0]. + funcname = '_cffi_var_%s' % name + BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] + function = module.load_function(BFunc, funcname) + ptr = function() + def getter(library): + return ptr[0] + def setter(library, value): + ptr[0] = value + setattr(type(library), name, property(getter, setter)) + type(library)._cffi_dir.append(name) + +cffimod_header = r''' +#include +#include +#include +#include +#include /* XXX for ssize_t on some platforms */ + +/* this block of #ifs should be kept exactly identical between + c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py + and cffi/_cffi_include.h */ +#if defined(_MSC_VER) +# include /* for alloca() */ +# if _MSC_VER < 1600 /* MSVC < 2010 */ + typedef __int8 int8_t; + typedef __int16 int16_t; + typedef __int32 int32_t; + typedef __int64 int64_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int64 uint64_t; + typedef __int8 int_least8_t; + typedef __int16 int_least16_t; + typedef __int32 int_least32_t; + typedef __int64 int_least64_t; + typedef unsigned __int8 uint_least8_t; + typedef unsigned __int16 uint_least16_t; + typedef unsigned __int32 uint_least32_t; + typedef unsigned __int64 uint_least64_t; + typedef __int8 int_fast8_t; + typedef __int16 int_fast16_t; + typedef __int32 int_fast32_t; + typedef __int64 int_fast64_t; + typedef unsigned __int8 uint_fast8_t; + typedef unsigned __int16 uint_fast16_t; + typedef unsigned __int32 uint_fast32_t; + typedef unsigned __int64 uint_fast64_t; + typedef __int64 intmax_t; + typedef unsigned __int64 uintmax_t; +# else +# include +# endif +# if _MSC_VER < 1800 /* MSVC < 2013 */ +# ifndef __cplusplus + typedef unsigned char _Bool; +# endif +# endif +#else +# include +# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) +# include +# endif +#endif +''' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py new file mode 100644 index 000000000..9fbfef27a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py @@ -0,0 +1,307 @@ +# +# DEPRECATED: implementation for ffi.verify() +# +import sys, os, binascii, shutil, io +from . import __version_verifier_modules__ +from . import ffiplatform +from .error import VerificationError + +if sys.version_info >= (3, 3): + import importlib.machinery + def _extension_suffixes(): + return importlib.machinery.EXTENSION_SUFFIXES[:] +else: + import imp + def _extension_suffixes(): + return [suffix for suffix, _, type in imp.get_suffixes() + if type == imp.C_EXTENSION] + + +if sys.version_info >= (3,): + NativeIO = io.StringIO +else: + class NativeIO(io.BytesIO): + def write(self, s): + if isinstance(s, unicode): + s = s.encode('ascii') + super(NativeIO, self).write(s) + + +class Verifier(object): + + def __init__(self, ffi, preamble, tmpdir=None, modulename=None, + ext_package=None, tag='', force_generic_engine=False, + source_extension='.c', flags=None, relative_to=None, **kwds): + if ffi._parser._uses_new_feature: + raise VerificationError( + "feature not supported with ffi.verify(), but only " + "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) + self.ffi = ffi + self.preamble = preamble + if not modulename: + flattened_kwds = ffiplatform.flatten(kwds) + vengine_class = _locate_engine_class(ffi, force_generic_engine) + self._vengine = vengine_class(self) + self._vengine.patch_extension_kwds(kwds) + self.flags = flags + self.kwds = self.make_relative_to(kwds, relative_to) + # + if modulename: + if tag: + raise TypeError("can't specify both 'modulename' and 'tag'") + else: + key = '\x00'.join(['%d.%d' % sys.version_info[:2], + __version_verifier_modules__, + preamble, flattened_kwds] + + ffi._cdefsources) + if sys.version_info >= (3,): + key = key.encode('utf-8') + k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) + k1 = k1.lstrip('0x').rstrip('L') + k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) + k2 = k2.lstrip('0').rstrip('L') + modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, + k1, k2) + suffix = _get_so_suffixes()[0] + self.tmpdir = tmpdir or _caller_dir_pycache() + self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) + self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) + self.ext_package = ext_package + self._has_source = False + self._has_module = False + + def write_source(self, file=None): + """Write the C source code. It is produced in 'self.sourcefilename', + which can be tweaked beforehand.""" + with self.ffi._lock: + if self._has_source and file is None: + raise VerificationError( + "source code already written") + self._write_source(file) + + def compile_module(self): + """Write the C source code (if not done already) and compile it. + This produces a dynamic link library in 'self.modulefilename'.""" + with self.ffi._lock: + if self._has_module: + raise VerificationError("module already compiled") + if not self._has_source: + self._write_source() + self._compile_module() + + def load_library(self): + """Get a C module from this Verifier instance. + Returns an instance of a FFILibrary class that behaves like the + objects returned by ffi.dlopen(), but that delegates all + operations to the C module. If necessary, the C code is written + and compiled first. + """ + with self.ffi._lock: + if not self._has_module: + self._locate_module() + if not self._has_module: + if not self._has_source: + self._write_source() + self._compile_module() + return self._load_library() + + def get_module_name(self): + basename = os.path.basename(self.modulefilename) + # kill both the .so extension and the other .'s, as introduced + # by Python 3: 'basename.cpython-33m.so' + basename = basename.split('.', 1)[0] + # and the _d added in Python 2 debug builds --- but try to be + # conservative and not kill a legitimate _d + if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): + basename = basename[:-2] + return basename + + def get_extension(self): + ffiplatform._hack_at_distutils() # backward compatibility hack + if not self._has_source: + with self.ffi._lock: + if not self._has_source: + self._write_source() + sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) + modname = self.get_module_name() + return ffiplatform.get_extension(sourcename, modname, **self.kwds) + + def generates_python_module(self): + return self._vengine._gen_python_module + + def make_relative_to(self, kwds, relative_to): + if relative_to and os.path.dirname(relative_to): + dirname = os.path.dirname(relative_to) + kwds = kwds.copy() + for key in ffiplatform.LIST_OF_FILE_NAMES: + if key in kwds: + lst = kwds[key] + if not isinstance(lst, (list, tuple)): + raise TypeError("keyword '%s' should be a list or tuple" + % (key,)) + lst = [os.path.join(dirname, fn) for fn in lst] + kwds[key] = lst + return kwds + + # ---------- + + def _locate_module(self): + if not os.path.isfile(self.modulefilename): + if self.ext_package: + try: + pkg = __import__(self.ext_package, None, None, ['__doc__']) + except ImportError: + return # cannot import the package itself, give up + # (e.g. it might be called differently before installation) + path = pkg.__path__ + else: + path = None + filename = self._vengine.find_module(self.get_module_name(), path, + _get_so_suffixes()) + if filename is None: + return + self.modulefilename = filename + self._vengine.collect_types() + self._has_module = True + + def _write_source_to(self, file): + self._vengine._f = file + try: + self._vengine.write_source_to_f() + finally: + del self._vengine._f + + def _write_source(self, file=None): + if file is not None: + self._write_source_to(file) + else: + # Write our source file to an in memory file. + f = NativeIO() + self._write_source_to(f) + source_data = f.getvalue() + + # Determine if this matches the current file + if os.path.exists(self.sourcefilename): + with open(self.sourcefilename, "r") as fp: + needs_written = not (fp.read() == source_data) + else: + needs_written = True + + # Actually write the file out if it doesn't match + if needs_written: + _ensure_dir(self.sourcefilename) + with open(self.sourcefilename, "w") as fp: + fp.write(source_data) + + # Set this flag + self._has_source = True + + def _compile_module(self): + # compile this C source + tmpdir = os.path.dirname(self.sourcefilename) + outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) + try: + same = ffiplatform.samefile(outputfilename, self.modulefilename) + except OSError: + same = False + if not same: + _ensure_dir(self.modulefilename) + shutil.move(outputfilename, self.modulefilename) + self._has_module = True + + def _load_library(self): + assert self._has_module + if self.flags is not None: + return self._vengine.load_library(self.flags) + else: + return self._vengine.load_library() + +# ____________________________________________________________ + +_FORCE_GENERIC_ENGINE = False # for tests + +def _locate_engine_class(ffi, force_generic_engine): + if _FORCE_GENERIC_ENGINE: + force_generic_engine = True + if not force_generic_engine: + if '__pypy__' in sys.builtin_module_names: + force_generic_engine = True + else: + try: + import _cffi_backend + except ImportError: + _cffi_backend = '?' + if ffi._backend is not _cffi_backend: + force_generic_engine = True + if force_generic_engine: + from . import vengine_gen + return vengine_gen.VGenericEngine + else: + from . import vengine_cpy + return vengine_cpy.VCPythonEngine + +# ____________________________________________________________ + +_TMPDIR = None + +def _caller_dir_pycache(): + if _TMPDIR: + return _TMPDIR + result = os.environ.get('CFFI_TMPDIR') + if result: + return result + filename = sys._getframe(2).f_code.co_filename + return os.path.abspath(os.path.join(os.path.dirname(filename), + '__pycache__')) + +def set_tmpdir(dirname): + """Set the temporary directory to use instead of __pycache__.""" + global _TMPDIR + _TMPDIR = dirname + +def cleanup_tmpdir(tmpdir=None, keep_so=False): + """Clean up the temporary directory by removing all files in it + called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" + tmpdir = tmpdir or _caller_dir_pycache() + try: + filelist = os.listdir(tmpdir) + except OSError: + return + if keep_so: + suffix = '.c' # only remove .c files + else: + suffix = _get_so_suffixes()[0].lower() + for fn in filelist: + if fn.lower().startswith('_cffi_') and ( + fn.lower().endswith(suffix) or fn.lower().endswith('.c')): + try: + os.unlink(os.path.join(tmpdir, fn)) + except OSError: + pass + clean_dir = [os.path.join(tmpdir, 'build')] + for dir in clean_dir: + try: + for fn in os.listdir(dir): + fn = os.path.join(dir, fn) + if os.path.isdir(fn): + clean_dir.append(fn) + else: + os.unlink(fn) + except OSError: + pass + +def _get_so_suffixes(): + suffixes = _extension_suffixes() + if not suffixes: + # bah, no C_EXTENSION available. Occurs on pypy without cpyext + if sys.platform == 'win32': + suffixes = [".pyd"] + else: + suffixes = [".so"] + + return suffixes + +def _ensure_dir(filename): + dirname = os.path.dirname(filename) + if dirname and not os.path.isdir(dirname): + os.makedirs(dirname) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE new file mode 100644 index 000000000..07074259b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE @@ -0,0 +1,6 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made +under the terms of *both* these licenses. + +The code used in the OS random engine is derived from CPython, and is licensed +under the terms of the PSF License Agreement. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE new file mode 100644 index 000000000..62589edd1 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD new file mode 100644 index 000000000..ec1a29d34 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD @@ -0,0 +1,27 @@ +Copyright (c) Individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of PyCA Cryptography nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF new file mode 100644 index 000000000..4d3a4f57d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF @@ -0,0 +1,41 @@ +1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and + the Individual or Organization ("Licensee") accessing and otherwise using Python + 2.7.12 software in source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby + grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, + analyze, test, perform and/or display publicly, prepare derivative works, + distribute, and otherwise use Python 2.7.12 alone or in any derivative + version, provided, however, that PSF's License Agreement and PSF's notice of + copyright, i.e., "Copyright © 2001-2016 Python Software Foundation; All Rights + Reserved" are retained in Python 2.7.12 alone or in any derivative version + prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on or + incorporates Python 2.7.12 or any part thereof, and wants to make the + derivative work available to others as provided herein, then Licensee hereby + agrees to include in any such work a brief summary of the changes made to Python + 2.7.12. + +4. PSF is making Python 2.7.12 available to Licensee on an "AS IS" basis. + PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF + EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR + WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE + USE OF PYTHON 2.7.12 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.12 + FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF + MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.12, OR ANY DERIVATIVE + THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material breach of + its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any relationship + of agency, partnership, or joint venture between PSF and Licensee. This License + Agreement does not grant permission to use PSF trademarks or trade name in a + trademark sense to endorse or promote products or services of Licensee, or any + third party. + +8. By copying, installing or otherwise using Python 2.7.12, Licensee agrees + to be bound by the terms and conditions of this License Agreement. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA new file mode 100644 index 000000000..a6b90cb1b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA @@ -0,0 +1,134 @@ +Metadata-Version: 2.1 +Name: cryptography +Version: 39.0.0 +Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers. +Home-page: https://github.com/pyca/cryptography +Author: The Python Cryptographic Authority and individual contributors +Author-email: cryptography-dev@python.org +License: (Apache-2.0 OR BSD-3-Clause) AND PSF-2.0 +Project-URL: Documentation, https://cryptography.io/ +Project-URL: Source, https://github.com/pyca/cryptography/ +Project-URL: Issues, https://github.com/pyca/cryptography/issues +Project-URL: Changelog, https://cryptography.io/en/latest/changelog/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: License :: OSI Approved :: BSD License +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Security :: Cryptography +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +License-File: LICENSE.PSF +Requires-Dist: cffi (>=1.12) +Provides-Extra: docs +Requires-Dist: sphinx (!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0,>=1.6.5) ; extra == 'docs' +Requires-Dist: sphinx-rtd-theme ; extra == 'docs' +Provides-Extra: docstest +Requires-Dist: pyenchant (>=1.6.11) ; extra == 'docstest' +Requires-Dist: twine (>=1.12.0) ; extra == 'docstest' +Requires-Dist: sphinxcontrib-spelling (>=4.0.1) ; extra == 'docstest' +Provides-Extra: pep8test +Requires-Dist: black ; extra == 'pep8test' +Requires-Dist: ruff ; extra == 'pep8test' +Provides-Extra: sdist +Requires-Dist: setuptools-rust (>=0.11.4) ; extra == 'sdist' +Provides-Extra: ssh +Requires-Dist: bcrypt (>=3.1.5) ; extra == 'ssh' +Provides-Extra: test +Requires-Dist: pytest (>=6.2.0) ; extra == 'test' +Requires-Dist: pytest-benchmark ; extra == 'test' +Requires-Dist: pytest-cov ; extra == 'test' +Requires-Dist: pytest-subtests ; extra == 'test' +Requires-Dist: pytest-xdist ; extra == 'test' +Requires-Dist: pretend ; extra == 'test' +Requires-Dist: iso8601 ; extra == 'test' +Requires-Dist: pytz ; extra == 'test' +Requires-Dist: hypothesis (!=3.79.2,>=1.11.4) ; extra == 'test' + +pyca/cryptography +================= + +.. image:: https://img.shields.io/pypi/v/cryptography.svg + :target: https://pypi.org/project/cryptography/ + :alt: Latest Version + +.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest + :target: https://cryptography.io + :alt: Latest Docs + +.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main + :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain + + +``cryptography`` is a package which provides cryptographic recipes and +primitives to Python developers. Our goal is for it to be your "cryptographic +standard library". It supports Python 3.6+ and PyPy3 7.2+. + +``cryptography`` includes both high level recipes and low level interfaces to +common cryptographic algorithms such as symmetric ciphers, message digests, and +key derivation functions. For example, to encrypt something with +``cryptography``'s high level symmetric encryption recipe: + +.. code-block:: pycon + + >>> from cryptography.fernet import Fernet + >>> # Put this somewhere safe! + >>> key = Fernet.generate_key() + >>> f = Fernet(key) + >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") + >>> token + b'...' + >>> f.decrypt(token) + b'A really secret message. Not for prying eyes.' + +You can find more information in the `documentation`_. + +You can install ``cryptography`` with: + +.. code-block:: console + + $ pip install cryptography + +For full details see `the installation documentation`_. + +Discussion +~~~~~~~~~~ + +If you run into bugs, you can file them in our `issue tracker`_. + +We maintain a `cryptography-dev`_ mailing list for development discussion. + +You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get +involved. + +Security +~~~~~~~~ + +Need to report a security issue? Please consult our `security reporting`_ +documentation. + + +.. _`documentation`: https://cryptography.io/ +.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ +.. _`issue tracker`: https://github.com/pyca/cryptography/issues +.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev +.. _`security reporting`: https://cryptography.io/en/latest/security/ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD new file mode 100644 index 000000000..59e30294f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD @@ -0,0 +1,180 @@ +cryptography-39.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +cryptography-39.0.0.dist-info/LICENSE,sha256=Q9rSzHUqtyHNmp827OcPtTq3cTVR8tPYaU2OjFoG1uI,323 +cryptography-39.0.0.dist-info/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 +cryptography-39.0.0.dist-info/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 +cryptography-39.0.0.dist-info/LICENSE.PSF,sha256=aT7ApmKzn5laTyUrA6YiKUVHDBtvEsoCkY5O_g32S58,2415 +cryptography-39.0.0.dist-info/METADATA,sha256=9_Lv-r92FYmcasTY96FUL4KvsyXjXD16mimgcXZDS-I,5398 +cryptography-39.0.0.dist-info/RECORD,, +cryptography-39.0.0.dist-info/WHEEL,sha256=CFPxCuvaTIZS0h5c8o2xFStPFn1i6Rraxc2uR61QpoA,100 +cryptography-39.0.0.dist-info/top_level.txt,sha256=KNaT-Sn2K4uxNaEbe6mYdDn3qWDMlp4y-MtWfB73nJc,13 +cryptography/__about__.py,sha256=NhtFVw-0uEYjXuKl7COCAUliy4IsXpQnSVM0TbWb7pk,417 +cryptography/__init__.py,sha256=iQ8I-Ks1V8lhzOac6IRPpiZZak2MvW-Gvduvhl0lXg8,781 +cryptography/__pycache__/__about__.cpython-310.pyc,, +cryptography/__pycache__/__init__.cpython-310.pyc,, +cryptography/__pycache__/exceptions.cpython-310.pyc,, +cryptography/__pycache__/fernet.cpython-310.pyc,, +cryptography/__pycache__/utils.cpython-310.pyc,, +cryptography/exceptions.py,sha256=sN_VVTF_LuKMM6R-lIASFFuzAmz1uZ2Qbcdko9WyS64,1471 +cryptography/fernet.py,sha256=qO4sQurx79k-5yOh4UnUZGm51zod0wRXJchz0l063To,6851 +cryptography/hazmat/__init__.py,sha256=OYlvgprzULzZlsf3yYTsd6VUVyQmpsbHjgJdNnsyRwE,418 +cryptography/hazmat/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/__pycache__/_oid.cpython-310.pyc,, +cryptography/hazmat/_oid.py,sha256=rCvnwb0z0VCKn7Y92IEQAoPErrANWREydYflZSNRrao,14155 +cryptography/hazmat/backends/__init__.py,sha256=bgrjB1SX2vXX-rmfG7A4PqGkq-isqQVXGaZtjWHAgj0,324 +cryptography/hazmat/backends/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__init__.py,sha256=oCa7eZbqvHsQ1pBeD_OOfnGxVaZbCfWnAKnHqOyPf1c,270 +cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/aead.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/ciphers.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/cmac.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/decode_asn1.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/dh.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/dsa.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/ec.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/ed25519.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/ed448.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/hashes.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/hmac.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/poly1305.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/rsa.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/utils.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/x25519.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/__pycache__/x448.cpython-310.pyc,, +cryptography/hazmat/backends/openssl/aead.py,sha256=uS6dkj7hKQzrQXyktJUXnBK2212sW18RSlXzGp4X5W8,9830 +cryptography/hazmat/backends/openssl/backend.py,sha256=8XELtLZx3twWXGbS41StTpYU9BkWTUA4ms2d966qT70,95375 +cryptography/hazmat/backends/openssl/ciphers.py,sha256=wGMLOZ2wuULqERxWajmb9QpmUewQU4XkI9Un0JtLazU,10341 +cryptography/hazmat/backends/openssl/cmac.py,sha256=cFZtDpqN5PNzo1X9tm8N8WDV5X81GRFXuXRUsjyFtF4,3005 +cryptography/hazmat/backends/openssl/decode_asn1.py,sha256=nSqtgO5MJVf_UUkvw9tez10zhGnsGHq24OP1X2GKOe4,1113 +cryptography/hazmat/backends/openssl/dh.py,sha256=dmWD1JK5mJenrVRJqd_u4hkOI4Y5Zalhu8HtwEDYUxI,12216 +cryptography/hazmat/backends/openssl/dsa.py,sha256=SQwoCTiNHrWjDQOFag3GznWG5K9CWM1AizqJ4usTRbY,8927 +cryptography/hazmat/backends/openssl/ec.py,sha256=kgxwW508FTXDwGG-7pSywBLlICZKKfo4bcTHnNpsvJY,11103 +cryptography/hazmat/backends/openssl/ed25519.py,sha256=adWaawleloe9T0BctejcclybE51dwb-CmL_b0f6zBiU,5921 +cryptography/hazmat/backends/openssl/ed448.py,sha256=Ja_GMzDBcs_8N2PpmU2dd6sszbJh3xP-TrN88MkQLBI,5875 +cryptography/hazmat/backends/openssl/hashes.py,sha256=yFuHeO8qDPRbH2B9JJtW51wEVfhu11SFs3lhHBHGyPA,3240 +cryptography/hazmat/backends/openssl/hmac.py,sha256=mN7irlzO6Rbc3UIDqlySwaW5KoCn28N8gKS3lh9WEUg,3094 +cryptography/hazmat/backends/openssl/poly1305.py,sha256=Oivx5k9DcAU_BSySxEQiw5tE1pcz-ljmFpmXAPZqJrI,2513 +cryptography/hazmat/backends/openssl/rsa.py,sha256=bTVNj5ODSDvgCl_OdTolCMZC25okI_AU2g7qAr5qlfk,21626 +cryptography/hazmat/backends/openssl/utils.py,sha256=7Ara81KkY0QCLPqW6kUG9dEsp52cZ3kOUJczwEpecJ0,1977 +cryptography/hazmat/backends/openssl/x25519.py,sha256=Fu8e-z3iV69oVIXz962vu0VRKRTsuFAUpuquEfSm9tY,4753 +cryptography/hazmat/backends/openssl/x448.py,sha256=6tZgh44ipS_UWJ6amueXxc8xIXdIfFtdpvnhri-oxXs,4339 +cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/bindings/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/bindings/_openssl.pyd,sha256=WTSmmo8FNePv2ZJo2JSuUtmqcBGKX_66Pi_k-vzktGQ,3964416 +cryptography/hazmat/bindings/_openssl.pyi,sha256=mpNJLuYLbCVrd5i33FBTmWwL_55Dw7JPkSLlSX9Q7oI,230 +cryptography/hazmat/bindings/_rust.pyd,sha256=6bMZ866dhCecXoJ1uXlcaWhdNEjWM96UyCSoEgM24BE,1686528 +cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=CHojGtYxYjj16E8tJiVJy950XuGMATdGq6PPkztHQxs,983 +cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=9CyI-grOsLQB_hfnhJPoG9dNOdJ7Zg6B0iUpzCowh44,592 +cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=Y6ZY8P6xlz5WFedNj1U4nxcaFFXFTHUVnGFB6LA9b9M,909 +cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=VkTC78wjJgb_qrboOYIFPuFZ3W46zsr6zsxnlrOMwao,460 +cryptography/hazmat/bindings/_rust/x509.pyi,sha256=jwTin2QHfdX7XfhZPwMp0JVw_UbyB-YG2GGwFG15a74,1751 +cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-310.pyc,, +cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-310.pyc,, +cryptography/hazmat/bindings/openssl/_conditional.py,sha256=M7T58AwF0LqrUWzMofCB8t8WKITd2-UJqMdZAc_bL9A,9893 +cryptography/hazmat/bindings/openssl/binding.py,sha256=ItRz2bAUPbtHaQFtH7dGmazO5kspBh6gjBdRVzcfo8k,8738 +cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/primitives/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/_serialization.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/cmac.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/constant_time.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/hashes.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/hmac.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/keywrap.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/padding.cpython-310.pyc,, +cryptography/hazmat/primitives/__pycache__/poly1305.cpython-310.pyc,, +cryptography/hazmat/primitives/_asymmetric.py,sha256=QacvnyA1fcXWbSAASCiodHVcTYwkaMdzq6KUIlaO7H0,496 +cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=3VSLRa30MqRs9qeNwopLG3_7bIQAp7Q77EJk6i9yJEs,1063 +cryptography/hazmat/primitives/_serialization.py,sha256=HssBsIm3rNVPct1nZTACJzbymZc2WaZAWdkg1l5slD0,5196 +cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 +cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-310.pyc,, +cryptography/hazmat/primitives/asymmetric/dh.py,sha256=swBaY7eQWFb6EEc8mxygeryA7pEXkbjjU9-fx-0G2gE,6627 +cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=JufsxrrxeJQlsiWMmx_44l90FNRw19o9kcKtk4rO8TU,7885 +cryptography/hazmat/primitives/asymmetric/ec.py,sha256=CdxppDV1lV2QlrQ0EhniqvFi8wp8PDYsvFWdpzyyVIY,12725 +cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=7Btmrsamd1joaFbjNOMekA4VtWfKJD-paJcubJzyRPc,2727 +cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=oR-j4jGcWUnGxWi1GygHxVZbgkSOKHsR6y1E3Lf6wYM,2647 +cryptography/hazmat/primitives/asymmetric/padding.py,sha256=EkKuY9e6UFqSuQ0LvyKYKl_L19tOfNCTlHWEiKgHeUc,2690 +cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=njFky5AkSrsBh47PeVLjj81SOLOiZaxAUSzGWD2Znxw,11479 +cryptography/hazmat/primitives/asymmetric/types.py,sha256=A-jXO0if3rZbKONGkYvisMiLntNx6P4g_xCNpxi50W8,1813 +cryptography/hazmat/primitives/asymmetric/utils.py,sha256=p6nF7EzF0sp5GYFTw1HEhPYYjuTik53WTUkvuPIfDRk,755 +cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=-nbaGlgT1sufO9Ic-urwKDql8Da0U3GL6hZJIMqHgVc,2588 +cryptography/hazmat/primitives/asymmetric/x448.py,sha256=V3lxb1VOiRTa3bzVUC3uZat2ogfExUOdktCIGUUMZ2Y,2556 +cryptography/hazmat/primitives/ciphers/__init__.py,sha256=2K5I_haxK0BLNqSZcQUqcjf8FmHY8xV1U-XjfgUmkM8,645 +cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-310.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-310.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-310.pyc,, +cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-310.pyc,, +cryptography/hazmat/primitives/ciphers/aead.py,sha256=vteCHw01e57SKCpfkbiPZw4CLswb9-40Ozhbqh_YmHI,11940 +cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=J1qeJK97fpSaX1E0ENsWvJG_qKGoOZvm57baBGOQofQ,5135 +cryptography/hazmat/primitives/ciphers/base.py,sha256=HFC7opE-LpjxBI3g7v2Q1EGb3VhKj-JBKNFsL_-AlnY,8270 +cryptography/hazmat/primitives/ciphers/modes.py,sha256=OnoG6UsqsLzV_zfIjJySoE-cOakfiloclOa99t-nNWM,8358 +cryptography/hazmat/primitives/cmac.py,sha256=ZbpwI87EhO3maiwqzttN1z0ObsAO1ufnl2Px5b9uJ1c,2036 +cryptography/hazmat/primitives/constant_time.py,sha256=6bkW00QjhKusdgsQbexXhMlGX0XRN59XNmxWS2W38NA,387 +cryptography/hazmat/primitives/hashes.py,sha256=RuDy0vgDOZh8BAH-2RTiETWvlJR2giBHgAYYFCVgxQo,6043 +cryptography/hazmat/primitives/hmac.py,sha256=pKiyxmJVcixW7Xk7w4ofde6Z7F8UohqGZa01PoxRotc,2122 +cryptography/hazmat/primitives/kdf/__init__.py,sha256=DcZhzfLG8d8IYBH771lGTVU5S87OQDpu3nrfOwZnsmA,715 +cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-310.pyc,, +cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=nz7Paa4oBXXPpCJO-6-tm_zxvOwxpwtjaXUzF5Ylf9A,3759 +cryptography/hazmat/primitives/kdf/hkdf.py,sha256=eQrZVEuv_GcSXpxSYR2GH3H8UJJXKsk4EZ2euJA4srw,3018 +cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=Ys2ITSbEw49V1v_DagQBd17owQr2A2iyPue4mot4Z_g,9196 +cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=wEMH4CJfPccCg9apQLXyWUWBrZLTpYLLnoZEnzvaHQo,2032 +cryptography/hazmat/primitives/kdf/scrypt.py,sha256=Wt7jj51vsedNtQX-LZI41geqUZnBFYnrhOXpoheLsOM,2227 +cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=H401RIRI2cIu52v8IM6ZCqDdCO2zuJPQogS2w_yIGpc,2005 +cryptography/hazmat/primitives/keywrap.py,sha256=TWqyG9K7k-Ymq4kcIw7u3NIKUPVDtv6bimwxIJYTe20,5643 +cryptography/hazmat/primitives/padding.py,sha256=xruasOE5Cd8KEQ-yp9W6v9WKPvKH-GudHCPKQ7A8HfI,6207 +cryptography/hazmat/primitives/poly1305.py,sha256=QvxPMrqjgKJt0mOZSeZKk4NcxsNCd2kgfI-X1CmyUW4,1837 +cryptography/hazmat/primitives/serialization/__init__.py,sha256=pKGAzJ0YIIOutw1sC1paGCiqKSXhUwu7dBkAxwjacQU,1196 +cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-310.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-310.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-310.pyc,, +cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-310.pyc,, +cryptography/hazmat/primitives/serialization/base.py,sha256=yYluZmJzaOsClGO0aUk5QZ3tcFDDFcNaoK6EevJcrAw,1967 +cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mEd4_K-5YF4BVqmAMFFEMir6wCFA0j_rKlu_Ef4QLRQ,6716 +cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=97USeLUaVfGsJp6mKpoDC4SCLNYmJTcyW2vOzNG0b4w,7055 +cryptography/hazmat/primitives/serialization/ssh.py,sha256=EZ9a1FiAaxznp6b8L99lt-BspSnleHGlU-3XRwLVvfc,23966 +cryptography/hazmat/primitives/twofactor/__init__.py,sha256=ZHo4zwWidFP2RWFl8luiNuYkVMZPghzx54izPNSCtD4,222 +cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-310.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-310.pyc,, +cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-310.pyc,, +cryptography/hazmat/primitives/twofactor/hotp.py,sha256=DT4RoUzQtiFwBw30GnDpe9L0c2W9w6jirsR74QtS_rA,2989 +cryptography/hazmat/primitives/twofactor/totp.py,sha256=Pr2Zh9KHqYGyix2C9KCAEXAjqOgkqBrF_BdGLv9INcI,1449 +cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +cryptography/utils.py,sha256=lBhYLBbA8MXX_yyZmhY3lOQ5Cyp3iZWBD18l49WAXCE,4095 +cryptography/x509/__init__.py,sha256=4WL6dxMLEWM9Wa9f_SpNbwcQNg76dut5zigRcHpRoTA,7719 +cryptography/x509/__pycache__/__init__.cpython-310.pyc,, +cryptography/x509/__pycache__/base.cpython-310.pyc,, +cryptography/x509/__pycache__/certificate_transparency.cpython-310.pyc,, +cryptography/x509/__pycache__/extensions.cpython-310.pyc,, +cryptography/x509/__pycache__/general_name.cpython-310.pyc,, +cryptography/x509/__pycache__/name.cpython-310.pyc,, +cryptography/x509/__pycache__/ocsp.cpython-310.pyc,, +cryptography/x509/__pycache__/oid.cpython-310.pyc,, +cryptography/x509/base.py,sha256=6oZ_nqLqO-eya_KTTCcTRP7Pr8d2YUvZTN3xxyLnZzw,34442 +cryptography/x509/certificate_transparency.py,sha256=jkjOvVu8bS5ljHov2AWdWScENQxylmDgESk01koC0Rs,2226 +cryptography/x509/extensions.py,sha256=ZWp47YmQ30KcLB25j7tWLcAeyEHw4DeQleeCmfcZqE0,65289 +cryptography/x509/general_name.py,sha256=976U2AwKWDXJEwas2JgByeSpfG2IKyWREL9oB5m6Azo,7907 +cryptography/x509/name.py,sha256=lIYhyGvkcKiFxBSqgahtdc_zy6MeshgQ9O0YdXxXIRM,14837 +cryptography/x509/ocsp.py,sha256=Lq23RjbLj0tUrsQbvMF8Mt1vD2s9-f4PhxrBXSECJ4s,18507 +cryptography/x509/oid.py,sha256=dAllMplMi_Kc_lEiQKnSM-rTN5w--a1UZucV-HvQOb0,793 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL new file mode 100644 index 000000000..cd6a22a16 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: false +Tag: cp36-abi3-win_amd64 + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt new file mode 100644 index 000000000..0d38bc5ea --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt @@ -0,0 +1 @@ +cryptography diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py new file mode 100644 index 000000000..83439a962 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py @@ -0,0 +1,15 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +__all__ = [ + "__version__", + "__author__", + "__copyright__", +] + +__version__ = "39.0.0" + +__author__ = "The Python Cryptographic Authority and individual contributors" +__copyright__ = "Copyright 2013-2022 {}".format(__author__) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py new file mode 100644 index 000000000..07c894ea3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py @@ -0,0 +1,25 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import sys +import warnings + +from cryptography.__about__ import __author__, __copyright__, __version__ +from cryptography.utils import CryptographyDeprecationWarning + +__all__ = [ + "__version__", + "__author__", + "__copyright__", +] + +if sys.version_info[:2] == (3, 6): + warnings.warn( + "Python 3.6 is no longer supported by the Python core team. " + "Therefore, support for it is deprecated in cryptography. The next " + "release of cryptography (40.0) will be the last to support Python " + "3.6.", + CryptographyDeprecationWarning, + stacklevel=2, + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py new file mode 100644 index 000000000..a315703c3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils + +if typing.TYPE_CHECKING: + from cryptography.hazmat.bindings.openssl.binding import ( + _OpenSSLErrorWithText, + ) + + +class _Reasons(utils.Enum): + BACKEND_MISSING_INTERFACE = 0 + UNSUPPORTED_HASH = 1 + UNSUPPORTED_CIPHER = 2 + UNSUPPORTED_PADDING = 3 + UNSUPPORTED_MGF = 4 + UNSUPPORTED_PUBLIC_KEY_ALGORITHM = 5 + UNSUPPORTED_ELLIPTIC_CURVE = 6 + UNSUPPORTED_SERIALIZATION = 7 + UNSUPPORTED_X509 = 8 + UNSUPPORTED_EXCHANGE_ALGORITHM = 9 + UNSUPPORTED_DIFFIE_HELLMAN = 10 + UNSUPPORTED_MAC = 11 + + +class UnsupportedAlgorithm(Exception): + def __init__( + self, message: str, reason: typing.Optional[_Reasons] = None + ) -> None: + super(UnsupportedAlgorithm, self).__init__(message) + self._reason = reason + + +class AlreadyFinalized(Exception): + pass + + +class AlreadyUpdated(Exception): + pass + + +class NotYetFinalized(Exception): + pass + + +class InvalidTag(Exception): + pass + + +class InvalidSignature(Exception): + pass + + +class InternalError(Exception): + def __init__( + self, msg: str, err_code: typing.List["_OpenSSLErrorWithText"] + ) -> None: + super(InternalError, self).__init__(msg) + self.err_code = err_code + + +class InvalidKey(Exception): + pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py new file mode 100644 index 000000000..a2601f80f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py @@ -0,0 +1,220 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import base64 +import binascii +import os +import time +import typing + +from cryptography import utils +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import hashes, padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.hmac import HMAC + + +class InvalidToken(Exception): + pass + + +_MAX_CLOCK_SKEW = 60 + + +class Fernet: + def __init__( + self, + key: typing.Union[bytes, str], + backend: typing.Any = None, + ) -> None: + try: + key = base64.urlsafe_b64decode(key) + except binascii.Error as exc: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) from exc + if len(key) != 32: + raise ValueError( + "Fernet key must be 32 url-safe base64-encoded bytes." + ) + + self._signing_key = key[:16] + self._encryption_key = key[16:] + + @classmethod + def generate_key(cls) -> bytes: + return base64.urlsafe_b64encode(os.urandom(32)) + + def encrypt(self, data: bytes) -> bytes: + return self.encrypt_at_time(data, int(time.time())) + + def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: + iv = os.urandom(16) + return self._encrypt_from_parts(data, current_time, iv) + + def _encrypt_from_parts( + self, data: bytes, current_time: int, iv: bytes + ) -> bytes: + utils._check_bytes("data", data) + + padder = padding.PKCS7(algorithms.AES.block_size).padder() + padded_data = padder.update(data) + padder.finalize() + encryptor = Cipher( + algorithms.AES(self._encryption_key), + modes.CBC(iv), + ).encryptor() + ciphertext = encryptor.update(padded_data) + encryptor.finalize() + + basic_parts = ( + b"\x80" + + current_time.to_bytes(length=8, byteorder="big") + + iv + + ciphertext + ) + + h = HMAC(self._signing_key, hashes.SHA256()) + h.update(basic_parts) + hmac = h.finalize() + return base64.urlsafe_b64encode(basic_parts + hmac) + + def decrypt( + self, token: typing.Union[bytes, str], ttl: typing.Optional[int] = None + ) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(token) + if ttl is None: + time_info = None + else: + time_info = (ttl, int(time.time())) + return self._decrypt_data(data, timestamp, time_info) + + def decrypt_at_time( + self, token: typing.Union[bytes, str], ttl: int, current_time: int + ) -> bytes: + if ttl is None: + raise ValueError( + "decrypt_at_time() can only be used with a non-None ttl" + ) + timestamp, data = Fernet._get_unverified_token_data(token) + return self._decrypt_data(data, timestamp, (ttl, current_time)) + + def extract_timestamp(self, token: typing.Union[bytes, str]) -> int: + timestamp, data = Fernet._get_unverified_token_data(token) + # Verify the token was not tampered with. + self._verify_signature(data) + return timestamp + + @staticmethod + def _get_unverified_token_data( + token: typing.Union[bytes, str] + ) -> typing.Tuple[int, bytes]: + if not isinstance(token, (str, bytes)): + raise TypeError("token must be bytes or str") + + try: + data = base64.urlsafe_b64decode(token) + except (TypeError, binascii.Error): + raise InvalidToken + + if not data or data[0] != 0x80: + raise InvalidToken + + if len(data) < 9: + raise InvalidToken + + timestamp = int.from_bytes(data[1:9], byteorder="big") + return timestamp, data + + def _verify_signature(self, data: bytes) -> None: + h = HMAC(self._signing_key, hashes.SHA256()) + h.update(data[:-32]) + try: + h.verify(data[-32:]) + except InvalidSignature: + raise InvalidToken + + def _decrypt_data( + self, + data: bytes, + timestamp: int, + time_info: typing.Optional[typing.Tuple[int, int]], + ) -> bytes: + if time_info is not None: + ttl, current_time = time_info + if timestamp + ttl < current_time: + raise InvalidToken + + if current_time + _MAX_CLOCK_SKEW < timestamp: + raise InvalidToken + + self._verify_signature(data) + + iv = data[9:25] + ciphertext = data[25:-32] + decryptor = Cipher( + algorithms.AES(self._encryption_key), modes.CBC(iv) + ).decryptor() + plaintext_padded = decryptor.update(ciphertext) + try: + plaintext_padded += decryptor.finalize() + except ValueError: + raise InvalidToken + unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() + + unpadded = unpadder.update(plaintext_padded) + try: + unpadded += unpadder.finalize() + except ValueError: + raise InvalidToken + return unpadded + + +class MultiFernet: + def __init__(self, fernets: typing.Iterable[Fernet]): + fernets = list(fernets) + if not fernets: + raise ValueError( + "MultiFernet requires at least one Fernet instance" + ) + self._fernets = fernets + + def encrypt(self, msg: bytes) -> bytes: + return self.encrypt_at_time(msg, int(time.time())) + + def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes: + return self._fernets[0].encrypt_at_time(msg, current_time) + + def rotate(self, msg: typing.Union[bytes, str]) -> bytes: + timestamp, data = Fernet._get_unverified_token_data(msg) + for f in self._fernets: + try: + p = f._decrypt_data(data, timestamp, None) + break + except InvalidToken: + pass + else: + raise InvalidToken + + iv = os.urandom(16) + return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) + + def decrypt( + self, msg: typing.Union[bytes, str], ttl: typing.Optional[int] = None + ) -> bytes: + for f in self._fernets: + try: + return f.decrypt(msg, ttl) + except InvalidToken: + pass + raise InvalidToken + + def decrypt_at_time( + self, msg: typing.Union[bytes, str], ttl: int, current_time: int + ) -> bytes: + for f in self._fernets: + try: + return f.decrypt_at_time(msg, ttl, current_time) + except InvalidToken: + pass + raise InvalidToken diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py new file mode 100644 index 000000000..007694bc5 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py @@ -0,0 +1,10 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +Hazardous Materials + +This is a "Hazardous Materials" module. You should ONLY use it if you're +100% absolutely sure that you know what you're doing because this module +is full of land mines, dragons, and dinosaurs with laser guns. +""" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py new file mode 100644 index 000000000..927ffc4c5 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py @@ -0,0 +1,293 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.bindings._rust import ( + ObjectIdentifier as ObjectIdentifier, +) +from cryptography.hazmat.primitives import hashes + + +class ExtensionOID: + SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") + SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") + KEY_USAGE = ObjectIdentifier("2.5.29.15") + SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") + ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") + BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") + NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") + CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") + CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") + POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") + AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") + POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") + EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") + FRESHEST_CRL = ObjectIdentifier("2.5.29.46") + INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") + ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") + AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") + SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") + OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") + TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") + CRL_NUMBER = ObjectIdentifier("2.5.29.20") + DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") + PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier( + "1.3.6.1.4.1.11129.2.4.2" + ) + PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") + SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") + + +class OCSPExtensionOID: + NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") + + +class CRLEntryExtensionOID: + CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") + CRL_REASON = ObjectIdentifier("2.5.29.21") + INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") + + +class NameOID: + COMMON_NAME = ObjectIdentifier("2.5.4.3") + COUNTRY_NAME = ObjectIdentifier("2.5.4.6") + LOCALITY_NAME = ObjectIdentifier("2.5.4.7") + STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") + STREET_ADDRESS = ObjectIdentifier("2.5.4.9") + ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") + ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") + SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") + SURNAME = ObjectIdentifier("2.5.4.4") + GIVEN_NAME = ObjectIdentifier("2.5.4.42") + TITLE = ObjectIdentifier("2.5.4.12") + GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") + X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") + DN_QUALIFIER = ObjectIdentifier("2.5.4.46") + PSEUDONYM = ObjectIdentifier("2.5.4.65") + USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") + DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") + EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") + JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") + JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") + JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( + "1.3.6.1.4.1.311.60.2.1.2" + ) + BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") + POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") + POSTAL_CODE = ObjectIdentifier("2.5.4.17") + INN = ObjectIdentifier("1.2.643.3.131.1.1") + OGRN = ObjectIdentifier("1.2.643.100.1") + SNILS = ObjectIdentifier("1.2.643.100.3") + UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") + + +class SignatureAlgorithmOID: + RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") + RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") + # This is an alternate OID for RSA with SHA1 that is occasionally seen + _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") + RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") + RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") + RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") + RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") + RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13") + RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14") + RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15") + RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16") + RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") + ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") + ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") + ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") + ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") + ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") + ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9") + ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10") + ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11") + ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12") + DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") + DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") + DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") + DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3") + DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4") + ED25519 = ObjectIdentifier("1.3.101.112") + ED448 = ObjectIdentifier("1.3.101.113") + GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") + GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") + GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") + + +_SIG_OIDS_TO_HASH: typing.Dict[ + ObjectIdentifier, typing.Optional[hashes.HashAlgorithm] +] = { + SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), + SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(), + SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(), + SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(), + SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(), + SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(), + SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), + SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), + SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), + SignatureAlgorithmOID.ED25519: None, + SignatureAlgorithmOID.ED448: None, + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, +} + + +class ExtendedKeyUsageOID: + SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") + CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") + CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") + EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") + TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") + OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") + ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") + SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2") + KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5") + IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17") + CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") + + +class AuthorityInformationAccessOID: + CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") + OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") + + +class SubjectInformationAccessOID: + CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5") + + +class CertificatePoliciesOID: + CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") + CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") + ANY_POLICY = ObjectIdentifier("2.5.29.32.0") + + +class AttributeOID: + CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7") + UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") + + +_OID_NAMES = { + NameOID.COMMON_NAME: "commonName", + NameOID.COUNTRY_NAME: "countryName", + NameOID.LOCALITY_NAME: "localityName", + NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", + NameOID.STREET_ADDRESS: "streetAddress", + NameOID.ORGANIZATION_NAME: "organizationName", + NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", + NameOID.SERIAL_NUMBER: "serialNumber", + NameOID.SURNAME: "surname", + NameOID.GIVEN_NAME: "givenName", + NameOID.TITLE: "title", + NameOID.GENERATION_QUALIFIER: "generationQualifier", + NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", + NameOID.DN_QUALIFIER: "dnQualifier", + NameOID.PSEUDONYM: "pseudonym", + NameOID.USER_ID: "userID", + NameOID.DOMAIN_COMPONENT: "domainComponent", + NameOID.EMAIL_ADDRESS: "emailAddress", + NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", + NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", + NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( + "jurisdictionStateOrProvinceName" + ), + NameOID.BUSINESS_CATEGORY: "businessCategory", + NameOID.POSTAL_ADDRESS: "postalAddress", + NameOID.POSTAL_CODE: "postalCode", + NameOID.INN: "INN", + NameOID.OGRN: "OGRN", + NameOID.SNILS: "SNILS", + NameOID.UNSTRUCTURED_NAME: "unstructuredName", + SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", + SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", + SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS", + SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", + SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", + SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", + SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", + SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", + SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", + SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", + SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", + SignatureAlgorithmOID.ED25519: "ed25519", + SignatureAlgorithmOID.ED448: "ed448", + SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( + "GOST R 34.11-94 with GOST R 34.10-2001" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" + ), + SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( + "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" + ), + ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", + ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", + ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", + ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", + ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", + ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", + ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin", + ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC", + ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", + ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", + ExtensionOID.KEY_USAGE: "keyUsage", + ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", + ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", + ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", + ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: ( + "signedCertificateTimestampList" + ), + ExtensionOID.PRECERT_POISON: "ctPoison", + CRLEntryExtensionOID.CRL_REASON: "cRLReason", + CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", + CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", + ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", + ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", + ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", + ExtensionOID.POLICY_MAPPINGS: "policyMappings", + ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", + ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", + ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", + ExtensionOID.FRESHEST_CRL: "freshestCRL", + ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", + ExtensionOID.ISSUING_DISTRIBUTION_POINT: ("issuingDistributionPoint"), + ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", + ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", + ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", + ExtensionOID.CRL_NUMBER: "cRLNumber", + ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", + ExtensionOID.TLS_FEATURE: "TLSFeature", + AuthorityInformationAccessOID.OCSP: "OCSP", + AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", + SubjectInformationAccessOID.CA_REPOSITORY: "caRepository", + CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", + CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", + OCSPExtensionOID.NONCE: "OCSPNonce", + AttributeOID.CHALLENGE_PASSWORD: "challengePassword", +} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py new file mode 100644 index 000000000..3926f85f1 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py @@ -0,0 +1,10 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from typing import Any + + +def default_backend() -> Any: + from cryptography.hazmat.backends.openssl.backend import backend + + return backend diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py new file mode 100644 index 000000000..42c4539df --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py @@ -0,0 +1,8 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography.hazmat.backends.openssl.backend import backend + +__all__ = ["backend"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py new file mode 100644 index 000000000..5b0fd2217 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py @@ -0,0 +1,306 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import InvalidTag + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + from cryptography.hazmat.primitives.ciphers.aead import ( + AESCCM, + AESGCM, + AESOCB3, + AESSIV, + ChaCha20Poly1305, + ) + + _AEAD_TYPES = typing.Union[ + AESCCM, AESGCM, AESOCB3, AESSIV, ChaCha20Poly1305 + ] + +_ENCRYPT = 1 +_DECRYPT = 0 + + +def _aead_cipher_name(cipher: "_AEAD_TYPES") -> bytes: + from cryptography.hazmat.primitives.ciphers.aead import ( + AESCCM, + AESGCM, + AESOCB3, + AESSIV, + ChaCha20Poly1305, + ) + + if isinstance(cipher, ChaCha20Poly1305): + return b"chacha20-poly1305" + elif isinstance(cipher, AESCCM): + return f"aes-{len(cipher._key) * 8}-ccm".encode("ascii") + elif isinstance(cipher, AESOCB3): + return f"aes-{len(cipher._key) * 8}-ocb".encode("ascii") + elif isinstance(cipher, AESSIV): + return f"aes-{len(cipher._key) * 8 // 2}-siv".encode("ascii") + else: + assert isinstance(cipher, AESGCM) + return f"aes-{len(cipher._key) * 8}-gcm".encode("ascii") + + +def _evp_cipher(cipher_name: bytes, backend: "Backend"): + if cipher_name.endswith(b"-siv"): + evp_cipher = backend._lib.EVP_CIPHER_fetch( + backend._ffi.NULL, + cipher_name, + backend._ffi.NULL, + ) + backend.openssl_assert(evp_cipher != backend._ffi.NULL) + evp_cipher = backend._ffi.gc(evp_cipher, backend._lib.EVP_CIPHER_free) + else: + evp_cipher = backend._lib.EVP_get_cipherbyname(cipher_name) + backend.openssl_assert(evp_cipher != backend._ffi.NULL) + + return evp_cipher + + +def _aead_create_ctx( + backend: "Backend", + cipher: "_AEAD_TYPES", + key: bytes, +): + ctx = backend._lib.EVP_CIPHER_CTX_new() + backend.openssl_assert(ctx != backend._ffi.NULL) + ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free) + cipher_name = _aead_cipher_name(cipher) + evp_cipher = _evp_cipher(cipher_name, backend) + key_ptr = backend._ffi.from_buffer(key) + res = backend._lib.EVP_CipherInit_ex( + ctx, + evp_cipher, + backend._ffi.NULL, + key_ptr, + backend._ffi.NULL, + 0, + ) + backend.openssl_assert(res != 0) + return ctx + + +def _aead_setup( + backend: "Backend", + cipher_name: bytes, + key: bytes, + nonce: bytes, + tag: typing.Optional[bytes], + tag_len: int, + operation: int, +): + evp_cipher = _evp_cipher(cipher_name, backend) + ctx = backend._lib.EVP_CIPHER_CTX_new() + ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free) + res = backend._lib.EVP_CipherInit_ex( + ctx, + evp_cipher, + backend._ffi.NULL, + backend._ffi.NULL, + backend._ffi.NULL, + int(operation == _ENCRYPT), + ) + backend.openssl_assert(res != 0) + # CCM requires the IVLEN to be set before calling SET_TAG on decrypt + res = backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, + backend._lib.EVP_CTRL_AEAD_SET_IVLEN, + len(nonce), + backend._ffi.NULL, + ) + backend.openssl_assert(res != 0) + if operation == _DECRYPT: + assert tag is not None + _set_tag(backend, ctx, tag) + elif cipher_name.endswith(b"-ccm"): + res = backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, tag_len, backend._ffi.NULL + ) + backend.openssl_assert(res != 0) + + nonce_ptr = backend._ffi.from_buffer(nonce) + key_ptr = backend._ffi.from_buffer(key) + res = backend._lib.EVP_CipherInit_ex( + ctx, + backend._ffi.NULL, + backend._ffi.NULL, + key_ptr, + nonce_ptr, + int(operation == _ENCRYPT), + ) + backend.openssl_assert(res != 0) + return ctx + + +def _set_tag(backend, ctx, tag: bytes) -> None: + res = backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag + ) + backend.openssl_assert(res != 0) + + +def _set_nonce_operation(backend, ctx, nonce: bytes, operation: int) -> None: + nonce_ptr = backend._ffi.from_buffer(nonce) + res = backend._lib.EVP_CipherInit_ex( + ctx, + backend._ffi.NULL, + backend._ffi.NULL, + backend._ffi.NULL, + nonce_ptr, + int(operation == _ENCRYPT), + ) + backend.openssl_assert(res != 0) + + +def _set_length(backend: "Backend", ctx, data_len: int) -> None: + intptr = backend._ffi.new("int *") + res = backend._lib.EVP_CipherUpdate( + ctx, backend._ffi.NULL, intptr, backend._ffi.NULL, data_len + ) + backend.openssl_assert(res != 0) + + +def _process_aad(backend: "Backend", ctx, associated_data: bytes) -> None: + outlen = backend._ffi.new("int *") + res = backend._lib.EVP_CipherUpdate( + ctx, backend._ffi.NULL, outlen, associated_data, len(associated_data) + ) + backend.openssl_assert(res != 0) + + +def _process_data(backend: "Backend", ctx, data: bytes) -> bytes: + outlen = backend._ffi.new("int *") + buf = backend._ffi.new("unsigned char[]", len(data)) + res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) + if res == 0: + # AES SIV can error here if the data is invalid on decrypt + backend._consume_errors() + raise InvalidTag + return backend._ffi.buffer(buf, outlen[0])[:] + + +def _encrypt( + backend: "Backend", + cipher: "_AEAD_TYPES", + nonce: bytes, + data: bytes, + associated_data: typing.List[bytes], + tag_length: int, + ctx: typing.Any = None, +) -> bytes: + from cryptography.hazmat.primitives.ciphers.aead import AESCCM, AESSIV + + if ctx is None: + cipher_name = _aead_cipher_name(cipher) + ctx = _aead_setup( + backend, + cipher_name, + cipher._key, + nonce, + None, + tag_length, + _ENCRYPT, + ) + else: + _set_nonce_operation(backend, ctx, nonce, _ENCRYPT) + + # CCM requires us to pass the length of the data before processing anything + # However calling this with any other AEAD results in an error + if isinstance(cipher, AESCCM): + _set_length(backend, ctx, len(data)) + + for ad in associated_data: + _process_aad(backend, ctx, ad) + processed_data = _process_data(backend, ctx, data) + outlen = backend._ffi.new("int *") + # All AEADs we support besides OCB are streaming so they return nothing + # in finalization. OCB can return up to (16 byte block - 1) bytes so + # we need a buffer here too. + buf = backend._ffi.new("unsigned char[]", 16) + res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen) + backend.openssl_assert(res != 0) + processed_data += backend._ffi.buffer(buf, outlen[0])[:] + tag_buf = backend._ffi.new("unsigned char[]", tag_length) + res = backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, backend._lib.EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf + ) + backend.openssl_assert(res != 0) + tag = backend._ffi.buffer(tag_buf)[:] + + if isinstance(cipher, AESSIV): + # RFC 5297 defines the output as IV || C, where the tag we generate is + # the "IV" and C is the ciphertext. This is the opposite of our + # other AEADs, which are Ciphertext || Tag + backend.openssl_assert(len(tag) == 16) + return tag + processed_data + else: + return processed_data + tag + + +def _decrypt( + backend: "Backend", + cipher: "_AEAD_TYPES", + nonce: bytes, + data: bytes, + associated_data: typing.List[bytes], + tag_length: int, + ctx: typing.Any = None, +) -> bytes: + from cryptography.hazmat.primitives.ciphers.aead import AESCCM, AESSIV + + if len(data) < tag_length: + raise InvalidTag + + if isinstance(cipher, AESSIV): + # RFC 5297 defines the output as IV || C, where the tag we generate is + # the "IV" and C is the ciphertext. This is the opposite of our + # other AEADs, which are Ciphertext || Tag + tag = data[:tag_length] + data = data[tag_length:] + else: + tag = data[-tag_length:] + data = data[:-tag_length] + if ctx is None: + cipher_name = _aead_cipher_name(cipher) + ctx = _aead_setup( + backend, cipher_name, cipher._key, nonce, tag, tag_length, _DECRYPT + ) + else: + _set_nonce_operation(backend, ctx, nonce, _DECRYPT) + _set_tag(backend, ctx, tag) + + # CCM requires us to pass the length of the data before processing anything + # However calling this with any other AEAD results in an error + if isinstance(cipher, AESCCM): + _set_length(backend, ctx, len(data)) + + for ad in associated_data: + _process_aad(backend, ctx, ad) + # CCM has a different error path if the tag doesn't match. Errors are + # raised in Update and Final is irrelevant. + if isinstance(cipher, AESCCM): + outlen = backend._ffi.new("int *") + buf = backend._ffi.new("unsigned char[]", len(data)) + res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) + if res != 1: + backend._consume_errors() + raise InvalidTag + + processed_data = backend._ffi.buffer(buf, outlen[0])[:] + else: + processed_data = _process_data(backend, ctx, data) + outlen = backend._ffi.new("int *") + # OCB can return up to 15 bytes (16 byte block - 1) in finalization + buf = backend._ffi.new("unsigned char[]", 16) + res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen) + processed_data += backend._ffi.buffer(buf, outlen[0])[:] + if res == 0: + backend._consume_errors() + raise InvalidTag + + return processed_data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py new file mode 100644 index 000000000..48f4265b0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py @@ -0,0 +1,2514 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import collections +import contextlib +import itertools +import typing +import warnings +from contextlib import contextmanager + +from cryptography import utils, x509 +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.backends.openssl import aead +from cryptography.hazmat.backends.openssl.ciphers import _CipherContext +from cryptography.hazmat.backends.openssl.cmac import _CMACContext +from cryptography.hazmat.backends.openssl.dh import ( + _dh_params_dup, + _DHParameters, + _DHPrivateKey, + _DHPublicKey, +) +from cryptography.hazmat.backends.openssl.dsa import ( + _DSAParameters, + _DSAPrivateKey, + _DSAPublicKey, +) +from cryptography.hazmat.backends.openssl.ec import ( + _EllipticCurvePrivateKey, + _EllipticCurvePublicKey, +) +from cryptography.hazmat.backends.openssl.ed448 import ( + _ED448_KEY_SIZE, + _Ed448PrivateKey, + _Ed448PublicKey, +) +from cryptography.hazmat.backends.openssl.ed25519 import ( + _Ed25519PrivateKey, + _Ed25519PublicKey, +) +from cryptography.hazmat.backends.openssl.hashes import _HashContext +from cryptography.hazmat.backends.openssl.hmac import _HMACContext +from cryptography.hazmat.backends.openssl.poly1305 import ( + _POLY1305_KEY_SIZE, + _Poly1305Context, +) +from cryptography.hazmat.backends.openssl.rsa import ( + _RSAPrivateKey, + _RSAPublicKey, +) +from cryptography.hazmat.backends.openssl.x448 import ( + _X448PrivateKey, + _X448PublicKey, +) +from cryptography.hazmat.backends.openssl.x25519 import ( + _X25519PrivateKey, + _X25519PublicKey, +) +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.bindings.openssl import binding +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding +from cryptography.hazmat.primitives.asymmetric import ( + dh, + dsa, + ec, + ed448, + ed25519, + rsa, + x448, + x25519, +) +from cryptography.hazmat.primitives.asymmetric.padding import ( + MGF1, + OAEP, + PSS, + PKCS1v15, +) +from cryptography.hazmat.primitives.asymmetric.types import ( + CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, + PRIVATE_KEY_TYPES, + PUBLIC_KEY_TYPES, +) +from cryptography.hazmat.primitives.ciphers import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers.algorithms import ( + AES, + AES128, + AES256, + ARC4, + SM4, + Camellia, + ChaCha20, + TripleDES, + _BlowfishInternal, + _CAST5Internal, + _IDEAInternal, + _SEEDInternal, +) +from cryptography.hazmat.primitives.ciphers.modes import ( + CBC, + CFB, + CFB8, + CTR, + ECB, + GCM, + OFB, + XTS, + Mode, +) +from cryptography.hazmat.primitives.kdf import scrypt +from cryptography.hazmat.primitives.serialization import ssh +from cryptography.hazmat.primitives.serialization.pkcs12 import ( + _ALLOWED_PKCS12_TYPES, + _PKCS12_CAS_TYPES, + PBES, + PKCS12Certificate, + PKCS12KeyAndCertificates, +) + +_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) + + +# Not actually supported, just used as a marker for some serialization tests. +class _RC2: + pass + + +class Backend: + """ + OpenSSL API binding interfaces. + """ + + name = "openssl" + + # FIPS has opinions about acceptable algorithms and key sizes, but the + # disallowed algorithms are still present in OpenSSL. They just error if + # you try to use them. To avoid that we allowlist the algorithms in + # FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are. + _fips_aead = { + b"aes-128-ccm", + b"aes-192-ccm", + b"aes-256-ccm", + b"aes-128-gcm", + b"aes-192-gcm", + b"aes-256-gcm", + } + # TripleDES encryption is disallowed/deprecated throughout 2023 in + # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA). + _fips_ciphers = (AES,) + # Sometimes SHA1 is still permissible. That logic is contained + # within the various *_supported methods. + _fips_hashes = ( + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + hashes.SHA512_224, + hashes.SHA512_256, + hashes.SHA3_224, + hashes.SHA3_256, + hashes.SHA3_384, + hashes.SHA3_512, + hashes.SHAKE128, + hashes.SHAKE256, + ) + _fips_ecdh_curves = ( + ec.SECP224R1, + ec.SECP256R1, + ec.SECP384R1, + ec.SECP521R1, + ) + _fips_rsa_min_key_size = 2048 + _fips_rsa_min_public_exponent = 65537 + _fips_dsa_min_modulus = 1 << 2048 + _fips_dh_min_key_size = 2048 + _fips_dh_min_modulus = 1 << _fips_dh_min_key_size + + def __init__(self): + self._binding = binding.Binding() + self._ffi = self._binding.ffi + self._lib = self._binding.lib + self._fips_enabled = self._is_fips_enabled() + + self._cipher_registry = {} + self._register_default_ciphers() + if self._fips_enabled and self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: + warnings.warn( + "OpenSSL FIPS mode is enabled. Can't enable DRBG fork safety.", + UserWarning, + ) + else: + self.activate_osrandom_engine() + self._dh_types = [self._lib.EVP_PKEY_DH] + if self._lib.Cryptography_HAS_EVP_PKEY_DHX: + self._dh_types.append(self._lib.EVP_PKEY_DHX) + + def __repr__(self) -> str: + return "".format( + self.openssl_version_text(), + self._fips_enabled, + self._binding._legacy_provider_loaded, + ) + + def openssl_assert( + self, + ok: bool, + errors: typing.Optional[typing.List[binding._OpenSSLError]] = None, + ) -> None: + return binding._openssl_assert(self._lib, ok, errors=errors) + + def _is_fips_enabled(self) -> bool: + if self._lib.Cryptography_HAS_300_FIPS: + mode = self._lib.EVP_default_properties_is_fips_enabled( + self._ffi.NULL + ) + else: + mode = self._lib.FIPS_mode() + + if mode == 0: + # OpenSSL without FIPS pushes an error on the error stack + self._lib.ERR_clear_error() + return bool(mode) + + def _enable_fips(self) -> None: + # This function enables FIPS mode for OpenSSL 3.0.0 on installs that + # have the FIPS provider installed properly. + self._binding._enable_fips() + assert self._is_fips_enabled() + self._fips_enabled = self._is_fips_enabled() + + def activate_builtin_random(self) -> None: + if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: + # Obtain a new structural reference. + e = self._lib.ENGINE_get_default_RAND() + if e != self._ffi.NULL: + self._lib.ENGINE_unregister_RAND(e) + # Reset the RNG to use the built-in. + res = self._lib.RAND_set_rand_method(self._ffi.NULL) + self.openssl_assert(res == 1) + # decrement the structural reference from get_default_RAND + res = self._lib.ENGINE_finish(e) + self.openssl_assert(res == 1) + + @contextlib.contextmanager + def _get_osurandom_engine(self): + # Fetches an engine by id and returns it. This creates a structural + # reference. + e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id) + self.openssl_assert(e != self._ffi.NULL) + # Initialize the engine for use. This adds a functional reference. + res = self._lib.ENGINE_init(e) + self.openssl_assert(res == 1) + + try: + yield e + finally: + # Decrement the structural ref incremented by ENGINE_by_id. + res = self._lib.ENGINE_free(e) + self.openssl_assert(res == 1) + # Decrement the functional ref incremented by ENGINE_init. + res = self._lib.ENGINE_finish(e) + self.openssl_assert(res == 1) + + def activate_osrandom_engine(self) -> None: + if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: + # Unregister and free the current engine. + self.activate_builtin_random() + with self._get_osurandom_engine() as e: + # Set the engine as the default RAND provider. + res = self._lib.ENGINE_set_default_RAND(e) + self.openssl_assert(res == 1) + # Reset the RNG to use the engine + res = self._lib.RAND_set_rand_method(self._ffi.NULL) + self.openssl_assert(res == 1) + + def osrandom_engine_implementation(self) -> str: + buf = self._ffi.new("char[]", 64) + with self._get_osurandom_engine() as e: + res = self._lib.ENGINE_ctrl_cmd( + e, b"get_implementation", len(buf), buf, self._ffi.NULL, 0 + ) + self.openssl_assert(res > 0) + return self._ffi.string(buf).decode("ascii") + + def openssl_version_text(self) -> str: + """ + Friendly string name of the loaded OpenSSL library. This is not + necessarily the same version as it was compiled against. + + Example: OpenSSL 1.1.1d 10 Sep 2019 + """ + return self._ffi.string( + self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION) + ).decode("ascii") + + def openssl_version_number(self) -> int: + return self._lib.OpenSSL_version_num() + + def create_hmac_ctx( + self, key: bytes, algorithm: hashes.HashAlgorithm + ) -> _HMACContext: + return _HMACContext(self, key, algorithm) + + def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm): + if algorithm.name == "blake2b" or algorithm.name == "blake2s": + alg = "{}{}".format( + algorithm.name, algorithm.digest_size * 8 + ).encode("ascii") + else: + alg = algorithm.name.encode("ascii") + + evp_md = self._lib.EVP_get_digestbyname(alg) + return evp_md + + def _evp_md_non_null_from_algorithm(self, algorithm: hashes.HashAlgorithm): + evp_md = self._evp_md_from_algorithm(algorithm) + self.openssl_assert(evp_md != self._ffi.NULL) + return evp_md + + def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if self._fips_enabled and not isinstance(algorithm, self._fips_hashes): + return False + + evp_md = self._evp_md_from_algorithm(algorithm) + return evp_md != self._ffi.NULL + + def signature_hash_supported( + self, algorithm: hashes.HashAlgorithm + ) -> bool: + # Dedicated check for hashing algorithm use in message digest for + # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption). + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return False + return self.hash_supported(algorithm) + + def scrypt_supported(self) -> bool: + if self._fips_enabled: + return False + else: + return self._lib.Cryptography_HAS_SCRYPT == 1 + + def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + # FIPS mode still allows SHA1 for HMAC + if self._fips_enabled and isinstance(algorithm, hashes.SHA1): + return True + + return self.hash_supported(algorithm) + + def create_hash_ctx( + self, algorithm: hashes.HashAlgorithm + ) -> hashes.HashContext: + return _HashContext(self, algorithm) + + def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: + if self._fips_enabled: + # FIPS mode requires AES. TripleDES is disallowed/deprecated in + # FIPS 140-3. + if not isinstance(cipher, self._fips_ciphers): + return False + + try: + adapter = self._cipher_registry[type(cipher), type(mode)] + except KeyError: + return False + evp_cipher = adapter(self, cipher, mode) + return self._ffi.NULL != evp_cipher + + def register_cipher_adapter(self, cipher_cls, mode_cls, adapter): + if (cipher_cls, mode_cls) in self._cipher_registry: + raise ValueError( + "Duplicate registration for: {} {}.".format( + cipher_cls, mode_cls + ) + ) + self._cipher_registry[cipher_cls, mode_cls] = adapter + + def _register_default_ciphers(self) -> None: + for cipher_cls in [AES, AES128, AES256]: + for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]: + self.register_cipher_adapter( + cipher_cls, + mode_cls, + GetCipherByName( + "{cipher.name}-{cipher.key_size}-{mode.name}" + ), + ) + for mode_cls in [CBC, CTR, ECB, OFB, CFB]: + self.register_cipher_adapter( + Camellia, + mode_cls, + GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"), + ) + for mode_cls in [CBC, CFB, CFB8, OFB]: + self.register_cipher_adapter( + TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}") + ) + self.register_cipher_adapter( + TripleDES, ECB, GetCipherByName("des-ede3") + ) + self.register_cipher_adapter( + ChaCha20, type(None), GetCipherByName("chacha20") + ) + self.register_cipher_adapter(AES, XTS, _get_xts_cipher) + for mode_cls in [ECB, CBC, OFB, CFB, CTR]: + self.register_cipher_adapter( + SM4, mode_cls, GetCipherByName("sm4-{mode.name}") + ) + # Don't register legacy ciphers if they're unavailable. Hypothetically + # this wouldn't be necessary because we test availability by seeing if + # we get an EVP_CIPHER * in the _CipherContext __init__, but OpenSSL 3 + # will return a valid pointer even though the cipher is unavailable. + if ( + self._binding._legacy_provider_loaded + or not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER + ): + for mode_cls in [CBC, CFB, OFB, ECB]: + self.register_cipher_adapter( + _BlowfishInternal, + mode_cls, + GetCipherByName("bf-{mode.name}"), + ) + for mode_cls in [CBC, CFB, OFB, ECB]: + self.register_cipher_adapter( + _SEEDInternal, + mode_cls, + GetCipherByName("seed-{mode.name}"), + ) + for cipher_cls, mode_cls in itertools.product( + [_CAST5Internal, _IDEAInternal], + [CBC, OFB, CFB, ECB], + ): + self.register_cipher_adapter( + cipher_cls, + mode_cls, + GetCipherByName("{cipher.name}-{mode.name}"), + ) + self.register_cipher_adapter( + ARC4, type(None), GetCipherByName("rc4") + ) + # We don't actually support RC2, this is just used by some tests. + self.register_cipher_adapter( + _RC2, type(None), GetCipherByName("rc2") + ) + + def create_symmetric_encryption_ctx( + self, cipher: CipherAlgorithm, mode: Mode + ) -> _CipherContext: + return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) + + def create_symmetric_decryption_ctx( + self, cipher: CipherAlgorithm, mode: Mode + ) -> _CipherContext: + return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) + + def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + return self.hmac_supported(algorithm) + + def derive_pbkdf2_hmac( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: bytes, + iterations: int, + key_material: bytes, + ) -> bytes: + buf = self._ffi.new("unsigned char[]", length) + evp_md = self._evp_md_non_null_from_algorithm(algorithm) + key_material_ptr = self._ffi.from_buffer(key_material) + res = self._lib.PKCS5_PBKDF2_HMAC( + key_material_ptr, + len(key_material), + salt, + len(salt), + iterations, + evp_md, + length, + buf, + ) + self.openssl_assert(res == 1) + return self._ffi.buffer(buf)[:] + + def _consume_errors(self) -> typing.List[binding._OpenSSLError]: + return binding._consume_errors(self._lib) + + def _consume_errors_with_text( + self, + ) -> typing.List[binding._OpenSSLErrorWithText]: + return binding._consume_errors_with_text(self._lib) + + def _bn_to_int(self, bn) -> int: + assert bn != self._ffi.NULL + self.openssl_assert(not self._lib.BN_is_negative(bn)) + + bn_num_bytes = self._lib.BN_num_bytes(bn) + bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes) + bin_len = self._lib.BN_bn2bin(bn, bin_ptr) + # A zero length means the BN has value 0 + self.openssl_assert(bin_len >= 0) + val = int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big") + return val + + def _int_to_bn(self, num: int, bn=None): + """ + Converts a python integer to a BIGNUM. The returned BIGNUM will not + be garbage collected (to support adding them to structs that take + ownership of the object). Be sure to register it for GC if it will + be discarded after use. + """ + assert bn is None or bn != self._ffi.NULL + + if bn is None: + bn = self._ffi.NULL + + binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big") + bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn) + self.openssl_assert(bn_ptr != self._ffi.NULL) + return bn_ptr + + def generate_rsa_private_key( + self, public_exponent: int, key_size: int + ) -> rsa.RSAPrivateKey: + rsa._verify_rsa_parameters(public_exponent, key_size) + + rsa_cdata = self._lib.RSA_new() + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + + bn = self._int_to_bn(public_exponent) + bn = self._ffi.gc(bn, self._lib.BN_free) + + res = self._lib.RSA_generate_key_ex( + rsa_cdata, key_size, bn, self._ffi.NULL + ) + self.openssl_assert(res == 1) + evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) + + # We can skip RSA key validation here since we just generated the key + return _RSAPrivateKey( + self, rsa_cdata, evp_pkey, unsafe_skip_rsa_key_validation=True + ) + + def generate_rsa_parameters_supported( + self, public_exponent: int, key_size: int + ) -> bool: + return ( + public_exponent >= 3 + and public_exponent & 1 != 0 + and key_size >= 512 + ) + + def load_rsa_private_numbers( + self, + numbers: rsa.RSAPrivateNumbers, + unsafe_skip_rsa_key_validation: bool, + ) -> rsa.RSAPrivateKey: + rsa._check_private_key_components( + numbers.p, + numbers.q, + numbers.d, + numbers.dmp1, + numbers.dmq1, + numbers.iqmp, + numbers.public_numbers.e, + numbers.public_numbers.n, + ) + rsa_cdata = self._lib.RSA_new() + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + p = self._int_to_bn(numbers.p) + q = self._int_to_bn(numbers.q) + d = self._int_to_bn(numbers.d) + dmp1 = self._int_to_bn(numbers.dmp1) + dmq1 = self._int_to_bn(numbers.dmq1) + iqmp = self._int_to_bn(numbers.iqmp) + e = self._int_to_bn(numbers.public_numbers.e) + n = self._int_to_bn(numbers.public_numbers.n) + res = self._lib.RSA_set0_factors(rsa_cdata, p, q) + self.openssl_assert(res == 1) + res = self._lib.RSA_set0_key(rsa_cdata, n, e, d) + self.openssl_assert(res == 1) + res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp) + self.openssl_assert(res == 1) + evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) + + return _RSAPrivateKey( + self, + rsa_cdata, + evp_pkey, + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, + ) + + def load_rsa_public_numbers( + self, numbers: rsa.RSAPublicNumbers + ) -> rsa.RSAPublicKey: + rsa._check_public_key_components(numbers.e, numbers.n) + rsa_cdata = self._lib.RSA_new() + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + e = self._int_to_bn(numbers.e) + n = self._int_to_bn(numbers.n) + res = self._lib.RSA_set0_key(rsa_cdata, n, e, self._ffi.NULL) + self.openssl_assert(res == 1) + evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) + + return _RSAPublicKey(self, rsa_cdata, evp_pkey) + + def _create_evp_pkey_gc(self): + evp_pkey = self._lib.EVP_PKEY_new() + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return evp_pkey + + def _rsa_cdata_to_evp_pkey(self, rsa_cdata): + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) + self.openssl_assert(res == 1) + return evp_pkey + + def _bytes_to_bio(self, data: bytes): + """ + Return a _MemoryBIO namedtuple of (BIO, char*). + + The char* is the storage for the BIO and it must stay alive until the + BIO is finished with. + """ + data_ptr = self._ffi.from_buffer(data) + bio = self._lib.BIO_new_mem_buf(data_ptr, len(data)) + self.openssl_assert(bio != self._ffi.NULL) + + return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr) + + def _create_mem_bio_gc(self): + """ + Creates an empty memory BIO. + """ + bio_method = self._lib.BIO_s_mem() + self.openssl_assert(bio_method != self._ffi.NULL) + bio = self._lib.BIO_new(bio_method) + self.openssl_assert(bio != self._ffi.NULL) + bio = self._ffi.gc(bio, self._lib.BIO_free) + return bio + + def _read_mem_bio(self, bio) -> bytes: + """ + Reads a memory BIO. This only works on memory BIOs. + """ + buf = self._ffi.new("char **") + buf_len = self._lib.BIO_get_mem_data(bio, buf) + self.openssl_assert(buf_len > 0) + self.openssl_assert(buf[0] != self._ffi.NULL) + bio_data = self._ffi.buffer(buf[0], buf_len)[:] + return bio_data + + def _evp_pkey_to_private_key( + self, evp_pkey, unsafe_skip_rsa_key_validation: bool + ) -> PRIVATE_KEY_TYPES: + """ + Return the appropriate type of PrivateKey given an evp_pkey cdata + pointer. + """ + + key_type = self._lib.EVP_PKEY_id(evp_pkey) + + if key_type == self._lib.EVP_PKEY_RSA: + rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + return _RSAPrivateKey( + self, + rsa_cdata, + evp_pkey, + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, + ) + elif ( + key_type == self._lib.EVP_PKEY_RSA_PSS + and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL + and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL + and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E + ): + # At the moment the way we handle RSA PSS keys is to strip the + # PSS constraints from them and treat them as normal RSA keys + # Unfortunately the RSA * itself tracks this data so we need to + # extract, serialize, and reload it without the constraints. + rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + bio = self._create_mem_bio_gc() + res = self._lib.i2d_RSAPrivateKey_bio(bio, rsa_cdata) + self.openssl_assert(res == 1) + return self.load_der_private_key( + self._read_mem_bio(bio), + password=None, + unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, + ) + elif key_type == self._lib.EVP_PKEY_DSA: + dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) + self.openssl_assert(dsa_cdata != self._ffi.NULL) + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + return _DSAPrivateKey(self, dsa_cdata, evp_pkey) + elif key_type == self._lib.EVP_PKEY_EC: + ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) + self.openssl_assert(ec_cdata != self._ffi.NULL) + ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) + elif key_type in self._dh_types: + dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHPrivateKey(self, dh_cdata, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None): + # EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL + return _Ed25519PrivateKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_X448", None): + # EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL + return _X448PrivateKey(self, evp_pkey) + elif key_type == self._lib.EVP_PKEY_X25519: + return _X25519PrivateKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None): + # EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL + return _Ed448PrivateKey(self, evp_pkey) + else: + raise UnsupportedAlgorithm("Unsupported key type.") + + def _evp_pkey_to_public_key(self, evp_pkey) -> PUBLIC_KEY_TYPES: + """ + Return the appropriate type of PublicKey given an evp_pkey cdata + pointer. + """ + + key_type = self._lib.EVP_PKEY_id(evp_pkey) + + if key_type == self._lib.EVP_PKEY_RSA: + rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + return _RSAPublicKey(self, rsa_cdata, evp_pkey) + elif ( + key_type == self._lib.EVP_PKEY_RSA_PSS + and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL + and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL + and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E + ): + rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) + self.openssl_assert(rsa_cdata != self._ffi.NULL) + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + bio = self._create_mem_bio_gc() + res = self._lib.i2d_RSAPublicKey_bio(bio, rsa_cdata) + self.openssl_assert(res == 1) + return self.load_der_public_key(self._read_mem_bio(bio)) + elif key_type == self._lib.EVP_PKEY_DSA: + dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) + self.openssl_assert(dsa_cdata != self._ffi.NULL) + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + return _DSAPublicKey(self, dsa_cdata, evp_pkey) + elif key_type == self._lib.EVP_PKEY_EC: + ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) + if ec_cdata == self._ffi.NULL: + errors = self._consume_errors_with_text() + raise ValueError("Unable to load EC key", errors) + ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + elif key_type in self._dh_types: + dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHPublicKey(self, dh_cdata, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None): + # EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL + return _Ed25519PublicKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_X448", None): + # EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL + return _X448PublicKey(self, evp_pkey) + elif key_type == self._lib.EVP_PKEY_X25519: + return _X25519PublicKey(self, evp_pkey) + elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None): + # EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL + return _Ed448PublicKey(self, evp_pkey) + else: + raise UnsupportedAlgorithm("Unsupported key type.") + + def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + return isinstance( + algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ) + + def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool: + if isinstance(padding, PKCS1v15): + return True + elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): + # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked + # as signature algorithm. + if self._fips_enabled and isinstance( + padding._mgf._algorithm, hashes.SHA1 + ): + return True + else: + return self.hash_supported(padding._mgf._algorithm) + elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): + return self._oaep_hash_supported( + padding._mgf._algorithm + ) and self._oaep_hash_supported(padding._algorithm) + else: + return False + + def generate_dsa_parameters(self, key_size: int) -> dsa.DSAParameters: + if key_size not in (1024, 2048, 3072, 4096): + raise ValueError( + "Key size must be 1024, 2048, 3072, or 4096 bits." + ) + + ctx = self._lib.DSA_new() + self.openssl_assert(ctx != self._ffi.NULL) + ctx = self._ffi.gc(ctx, self._lib.DSA_free) + + res = self._lib.DSA_generate_parameters_ex( + ctx, + key_size, + self._ffi.NULL, + 0, + self._ffi.NULL, + self._ffi.NULL, + self._ffi.NULL, + ) + + self.openssl_assert(res == 1) + + return _DSAParameters(self, ctx) + + def generate_dsa_private_key( + self, parameters: dsa.DSAParameters + ) -> dsa.DSAPrivateKey: + ctx = self._lib.DSAparams_dup( + parameters._dsa_cdata # type: ignore[attr-defined] + ) + self.openssl_assert(ctx != self._ffi.NULL) + ctx = self._ffi.gc(ctx, self._lib.DSA_free) + self._lib.DSA_generate_key(ctx) + evp_pkey = self._dsa_cdata_to_evp_pkey(ctx) + + return _DSAPrivateKey(self, ctx, evp_pkey) + + def generate_dsa_private_key_and_parameters( + self, key_size: int + ) -> dsa.DSAPrivateKey: + parameters = self.generate_dsa_parameters(key_size) + return self.generate_dsa_private_key(parameters) + + def _dsa_cdata_set_values(self, dsa_cdata, p, q, g, pub_key, priv_key): + res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) + self.openssl_assert(res == 1) + res = self._lib.DSA_set0_key(dsa_cdata, pub_key, priv_key) + self.openssl_assert(res == 1) + + def load_dsa_private_numbers( + self, numbers: dsa.DSAPrivateNumbers + ) -> dsa.DSAPrivateKey: + dsa._check_dsa_private_numbers(numbers) + parameter_numbers = numbers.public_numbers.parameter_numbers + + dsa_cdata = self._lib.DSA_new() + self.openssl_assert(dsa_cdata != self._ffi.NULL) + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + + p = self._int_to_bn(parameter_numbers.p) + q = self._int_to_bn(parameter_numbers.q) + g = self._int_to_bn(parameter_numbers.g) + pub_key = self._int_to_bn(numbers.public_numbers.y) + priv_key = self._int_to_bn(numbers.x) + self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) + + evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) + + return _DSAPrivateKey(self, dsa_cdata, evp_pkey) + + def load_dsa_public_numbers( + self, numbers: dsa.DSAPublicNumbers + ) -> dsa.DSAPublicKey: + dsa._check_dsa_parameters(numbers.parameter_numbers) + dsa_cdata = self._lib.DSA_new() + self.openssl_assert(dsa_cdata != self._ffi.NULL) + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + + p = self._int_to_bn(numbers.parameter_numbers.p) + q = self._int_to_bn(numbers.parameter_numbers.q) + g = self._int_to_bn(numbers.parameter_numbers.g) + pub_key = self._int_to_bn(numbers.y) + priv_key = self._ffi.NULL + self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) + + evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) + + return _DSAPublicKey(self, dsa_cdata, evp_pkey) + + def load_dsa_parameter_numbers( + self, numbers: dsa.DSAParameterNumbers + ) -> dsa.DSAParameters: + dsa._check_dsa_parameters(numbers) + dsa_cdata = self._lib.DSA_new() + self.openssl_assert(dsa_cdata != self._ffi.NULL) + dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) + + p = self._int_to_bn(numbers.p) + q = self._int_to_bn(numbers.q) + g = self._int_to_bn(numbers.g) + res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) + self.openssl_assert(res == 1) + + return _DSAParameters(self, dsa_cdata) + + def _dsa_cdata_to_evp_pkey(self, dsa_cdata): + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set1_DSA(evp_pkey, dsa_cdata) + self.openssl_assert(res == 1) + return evp_pkey + + def dsa_supported(self) -> bool: + return not self._fips_enabled + + def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: + if not self.dsa_supported(): + return False + return self.signature_hash_supported(algorithm) + + def cmac_algorithm_supported(self, algorithm) -> bool: + return self.cipher_supported( + algorithm, CBC(b"\x00" * algorithm.block_size) + ) + + def create_cmac_ctx(self, algorithm: BlockCipherAlgorithm) -> _CMACContext: + return _CMACContext(self, algorithm) + + def load_pem_private_key( + self, + data: bytes, + password: typing.Optional[bytes], + unsafe_skip_rsa_key_validation: bool, + ) -> PRIVATE_KEY_TYPES: + return self._load_key( + self._lib.PEM_read_bio_PrivateKey, + data, + password, + unsafe_skip_rsa_key_validation, + ) + + def load_pem_public_key(self, data: bytes) -> PUBLIC_KEY_TYPES: + mem_bio = self._bytes_to_bio(data) + # In OpenSSL 3.0.x the PEM_read_bio_PUBKEY function will invoke + # the default password callback if you pass an encrypted private + # key. This is very, very, very bad as the default callback can + # trigger an interactive console prompt, which will hang the + # Python process. We therefore provide our own callback to + # catch this and error out properly. + userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *") + evp_pkey = self._lib.PEM_read_bio_PUBKEY( + mem_bio.bio, + self._ffi.NULL, + self._ffi.addressof( + self._lib._original_lib, "Cryptography_pem_password_cb" + ), + userdata, + ) + if evp_pkey != self._ffi.NULL: + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return self._evp_pkey_to_public_key(evp_pkey) + else: + # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still + # need to check to see if it is a pure PKCS1 RSA public key (not + # embedded in a subjectPublicKeyInfo) + self._consume_errors() + res = self._lib.BIO_reset(mem_bio.bio) + self.openssl_assert(res == 1) + rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey( + mem_bio.bio, + self._ffi.NULL, + self._ffi.addressof( + self._lib._original_lib, "Cryptography_pem_password_cb" + ), + userdata, + ) + if rsa_cdata != self._ffi.NULL: + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) + return _RSAPublicKey(self, rsa_cdata, evp_pkey) + else: + self._handle_key_loading_error() + + def load_pem_parameters(self, data: bytes) -> dh.DHParameters: + mem_bio = self._bytes_to_bio(data) + # only DH is supported currently + dh_cdata = self._lib.PEM_read_bio_DHparams( + mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if dh_cdata != self._ffi.NULL: + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHParameters(self, dh_cdata) + else: + self._handle_key_loading_error() + + def load_der_private_key( + self, + data: bytes, + password: typing.Optional[bytes], + unsafe_skip_rsa_key_validation: bool, + ) -> PRIVATE_KEY_TYPES: + # OpenSSL has a function called d2i_AutoPrivateKey that in theory + # handles this automatically, however it doesn't handle encrypted + # private keys. Instead we try to load the key two different ways. + # First we'll try to load it as a traditional key. + bio_data = self._bytes_to_bio(data) + key = self._evp_pkey_from_der_traditional_key(bio_data, password) + if key: + return self._evp_pkey_to_private_key( + key, unsafe_skip_rsa_key_validation + ) + else: + # Finally we try to load it with the method that handles encrypted + # PKCS8 properly. + return self._load_key( + self._lib.d2i_PKCS8PrivateKey_bio, + data, + password, + unsafe_skip_rsa_key_validation, + ) + + def _evp_pkey_from_der_traditional_key(self, bio_data, password): + key = self._lib.d2i_PrivateKey_bio(bio_data.bio, self._ffi.NULL) + if key != self._ffi.NULL: + key = self._ffi.gc(key, self._lib.EVP_PKEY_free) + if password is not None: + raise TypeError( + "Password was given but private key is not encrypted." + ) + + return key + else: + self._consume_errors() + return None + + def load_der_public_key(self, data: bytes) -> PUBLIC_KEY_TYPES: + mem_bio = self._bytes_to_bio(data) + evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL) + if evp_pkey != self._ffi.NULL: + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return self._evp_pkey_to_public_key(evp_pkey) + else: + # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still + # need to check to see if it is a pure PKCS1 RSA public key (not + # embedded in a subjectPublicKeyInfo) + self._consume_errors() + res = self._lib.BIO_reset(mem_bio.bio) + self.openssl_assert(res == 1) + rsa_cdata = self._lib.d2i_RSAPublicKey_bio( + mem_bio.bio, self._ffi.NULL + ) + if rsa_cdata != self._ffi.NULL: + rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) + evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) + return _RSAPublicKey(self, rsa_cdata, evp_pkey) + else: + self._handle_key_loading_error() + + def load_der_parameters(self, data: bytes) -> dh.DHParameters: + mem_bio = self._bytes_to_bio(data) + dh_cdata = self._lib.d2i_DHparams_bio(mem_bio.bio, self._ffi.NULL) + if dh_cdata != self._ffi.NULL: + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHParameters(self, dh_cdata) + elif self._lib.Cryptography_HAS_EVP_PKEY_DHX: + # We check to see if the is dhx. + self._consume_errors() + res = self._lib.BIO_reset(mem_bio.bio) + self.openssl_assert(res == 1) + dh_cdata = self._lib.d2i_DHxparams_bio(mem_bio.bio, self._ffi.NULL) + if dh_cdata != self._ffi.NULL: + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + return _DHParameters(self, dh_cdata) + + self._handle_key_loading_error() + + def _cert2ossl(self, cert: x509.Certificate) -> typing.Any: + data = cert.public_bytes(serialization.Encoding.DER) + mem_bio = self._bytes_to_bio(data) + x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL) + self.openssl_assert(x509 != self._ffi.NULL) + x509 = self._ffi.gc(x509, self._lib.X509_free) + return x509 + + def _ossl2cert(self, x509: typing.Any) -> x509.Certificate: + bio = self._create_mem_bio_gc() + res = self._lib.i2d_X509_bio(bio, x509) + self.openssl_assert(res == 1) + return rust_x509.load_der_x509_certificate(self._read_mem_bio(bio)) + + def _csr2ossl(self, csr: x509.CertificateSigningRequest) -> typing.Any: + data = csr.public_bytes(serialization.Encoding.DER) + mem_bio = self._bytes_to_bio(data) + x509_req = self._lib.d2i_X509_REQ_bio(mem_bio.bio, self._ffi.NULL) + self.openssl_assert(x509_req != self._ffi.NULL) + x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) + return x509_req + + def _crl2ossl(self, crl: x509.CertificateRevocationList) -> typing.Any: + data = crl.public_bytes(serialization.Encoding.DER) + mem_bio = self._bytes_to_bio(data) + x509_crl = self._lib.d2i_X509_CRL_bio(mem_bio.bio, self._ffi.NULL) + self.openssl_assert(x509_crl != self._ffi.NULL) + x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) + return x509_crl + + def _crl_is_signature_valid( + self, + crl: x509.CertificateRevocationList, + public_key: CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, + ) -> bool: + if not isinstance( + public_key, + ( + _DSAPublicKey, + _RSAPublicKey, + _EllipticCurvePublicKey, + ), + ): + raise TypeError( + "Expecting one of DSAPublicKey, RSAPublicKey," + " or EllipticCurvePublicKey." + ) + x509_crl = self._crl2ossl(crl) + res = self._lib.X509_CRL_verify(x509_crl, public_key._evp_pkey) + + if res != 1: + self._consume_errors() + return False + + return True + + def _csr_is_signature_valid( + self, csr: x509.CertificateSigningRequest + ) -> bool: + x509_req = self._csr2ossl(csr) + pkey = self._lib.X509_REQ_get_pubkey(x509_req) + self.openssl_assert(pkey != self._ffi.NULL) + pkey = self._ffi.gc(pkey, self._lib.EVP_PKEY_free) + res = self._lib.X509_REQ_verify(x509_req, pkey) + + if res != 1: + self._consume_errors() + return False + + return True + + def _check_keys_correspond(self, key1, key2): + if self._lib.EVP_PKEY_cmp(key1._evp_pkey, key2._evp_pkey) != 1: + raise ValueError("Keys do not correspond") + + def _load_key( + self, openssl_read_func, data, password, unsafe_skip_rsa_key_validation + ): + mem_bio = self._bytes_to_bio(data) + + userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *") + if password is not None: + utils._check_byteslike("password", password) + password_ptr = self._ffi.from_buffer(password) + userdata.password = password_ptr + userdata.length = len(password) + + evp_pkey = openssl_read_func( + mem_bio.bio, + self._ffi.NULL, + self._ffi.addressof( + self._lib._original_lib, "Cryptography_pem_password_cb" + ), + userdata, + ) + + if evp_pkey == self._ffi.NULL: + if userdata.error != 0: + self._consume_errors() + if userdata.error == -1: + raise TypeError( + "Password was not given but private key is encrypted" + ) + else: + assert userdata.error == -2 + raise ValueError( + "Passwords longer than {} bytes are not supported " + "by this backend.".format(userdata.maxsize - 1) + ) + else: + self._handle_key_loading_error() + + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + if password is not None and userdata.called == 0: + raise TypeError( + "Password was given but private key is not encrypted." + ) + + assert ( + password is not None and userdata.called == 1 + ) or password is None + + return self._evp_pkey_to_private_key( + evp_pkey, unsafe_skip_rsa_key_validation + ) + + def _handle_key_loading_error(self) -> typing.NoReturn: + errors = self._consume_errors() + + if not errors: + raise ValueError( + "Could not deserialize key data. The data may be in an " + "incorrect format or it may be encrypted with an unsupported " + "algorithm." + ) + + elif ( + errors[0]._lib_reason_match( + self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT + ) + or errors[0]._lib_reason_match( + self._lib.ERR_LIB_PKCS12, + self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR, + ) + or ( + self._lib.Cryptography_HAS_PROVIDERS + and errors[0]._lib_reason_match( + self._lib.ERR_LIB_PROV, + self._lib.PROV_R_BAD_DECRYPT, + ) + ) + ): + raise ValueError("Bad decrypt. Incorrect password?") + + elif any( + error._lib_reason_match( + self._lib.ERR_LIB_EVP, + self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM, + ) + for error in errors + ): + raise ValueError("Unsupported public key algorithm.") + + else: + errors_with_text = binding._errors_with_text(errors) + raise ValueError( + "Could not deserialize key data. The data may be in an " + "incorrect format, it may be encrypted with an unsupported " + "algorithm, or it may be an unsupported key type (e.g. EC " + "curves with explicit parameters).", + errors_with_text, + ) + + def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool: + try: + curve_nid = self._elliptic_curve_to_nid(curve) + except UnsupportedAlgorithm: + curve_nid = self._lib.NID_undef + + group = self._lib.EC_GROUP_new_by_curve_name(curve_nid) + + if group == self._ffi.NULL: + self._consume_errors() + return False + else: + self.openssl_assert(curve_nid != self._lib.NID_undef) + self._lib.EC_GROUP_free(group) + return True + + def elliptic_curve_signature_algorithm_supported( + self, + signature_algorithm: ec.EllipticCurveSignatureAlgorithm, + curve: ec.EllipticCurve, + ) -> bool: + # We only support ECDSA right now. + if not isinstance(signature_algorithm, ec.ECDSA): + return False + + return self.elliptic_curve_supported(curve) + + def generate_elliptic_curve_private_key( + self, curve: ec.EllipticCurve + ) -> ec.EllipticCurvePrivateKey: + """ + Generate a new private key on the named curve. + """ + + if self.elliptic_curve_supported(curve): + ec_cdata = self._ec_key_new_by_curve(curve) + + res = self._lib.EC_KEY_generate_key(ec_cdata) + self.openssl_assert(res == 1) + + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + + return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) + else: + raise UnsupportedAlgorithm( + "Backend object does not support {}.".format(curve.name), + _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, + ) + + def load_elliptic_curve_private_numbers( + self, numbers: ec.EllipticCurvePrivateNumbers + ) -> ec.EllipticCurvePrivateKey: + public = numbers.public_numbers + + ec_cdata = self._ec_key_new_by_curve(public.curve) + + private_value = self._ffi.gc( + self._int_to_bn(numbers.private_value), self._lib.BN_clear_free + ) + res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value) + if res != 1: + self._consume_errors() + raise ValueError("Invalid EC key.") + + self._ec_key_set_public_key_affine_coordinates( + ec_cdata, public.x, public.y + ) + + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + + return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) + + def load_elliptic_curve_public_numbers( + self, numbers: ec.EllipticCurvePublicNumbers + ) -> ec.EllipticCurvePublicKey: + ec_cdata = self._ec_key_new_by_curve(numbers.curve) + self._ec_key_set_public_key_affine_coordinates( + ec_cdata, numbers.x, numbers.y + ) + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + + return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + + def load_elliptic_curve_public_bytes( + self, curve: ec.EllipticCurve, point_bytes: bytes + ) -> ec.EllipticCurvePublicKey: + ec_cdata = self._ec_key_new_by_curve(curve) + group = self._lib.EC_KEY_get0_group(ec_cdata) + self.openssl_assert(group != self._ffi.NULL) + point = self._lib.EC_POINT_new(group) + self.openssl_assert(point != self._ffi.NULL) + point = self._ffi.gc(point, self._lib.EC_POINT_free) + with self._tmp_bn_ctx() as bn_ctx: + res = self._lib.EC_POINT_oct2point( + group, point, point_bytes, len(point_bytes), bn_ctx + ) + if res != 1: + self._consume_errors() + raise ValueError("Invalid public bytes for the given curve") + + res = self._lib.EC_KEY_set_public_key(ec_cdata, point) + self.openssl_assert(res == 1) + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) + + def derive_elliptic_curve_private_key( + self, private_value: int, curve: ec.EllipticCurve + ) -> ec.EllipticCurvePrivateKey: + ec_cdata = self._ec_key_new_by_curve(curve) + + get_func, group = self._ec_key_determine_group_get_func(ec_cdata) + + point = self._lib.EC_POINT_new(group) + self.openssl_assert(point != self._ffi.NULL) + point = self._ffi.gc(point, self._lib.EC_POINT_free) + + value = self._int_to_bn(private_value) + value = self._ffi.gc(value, self._lib.BN_clear_free) + + with self._tmp_bn_ctx() as bn_ctx: + res = self._lib.EC_POINT_mul( + group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx + ) + self.openssl_assert(res == 1) + + bn_x = self._lib.BN_CTX_get(bn_ctx) + bn_y = self._lib.BN_CTX_get(bn_ctx) + + res = get_func(group, point, bn_x, bn_y, bn_ctx) + if res != 1: + self._consume_errors() + raise ValueError("Unable to derive key from private_value") + + res = self._lib.EC_KEY_set_public_key(ec_cdata, point) + self.openssl_assert(res == 1) + private = self._int_to_bn(private_value) + private = self._ffi.gc(private, self._lib.BN_clear_free) + res = self._lib.EC_KEY_set_private_key(ec_cdata, private) + self.openssl_assert(res == 1) + + evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) + + return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) + + def _ec_key_new_by_curve(self, curve: ec.EllipticCurve): + curve_nid = self._elliptic_curve_to_nid(curve) + return self._ec_key_new_by_curve_nid(curve_nid) + + def _ec_key_new_by_curve_nid(self, curve_nid: int): + ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) + self.openssl_assert(ec_cdata != self._ffi.NULL) + return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) + + def elliptic_curve_exchange_algorithm_supported( + self, algorithm: ec.ECDH, curve: ec.EllipticCurve + ) -> bool: + if self._fips_enabled and not isinstance( + curve, self._fips_ecdh_curves + ): + return False + + return self.elliptic_curve_supported(curve) and isinstance( + algorithm, ec.ECDH + ) + + def _ec_cdata_to_evp_pkey(self, ec_cdata): + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata) + self.openssl_assert(res == 1) + return evp_pkey + + def _elliptic_curve_to_nid(self, curve: ec.EllipticCurve) -> int: + """ + Get the NID for a curve name. + """ + + curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"} + + curve_name = curve_aliases.get(curve.name, curve.name) + + curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) + if curve_nid == self._lib.NID_undef: + raise UnsupportedAlgorithm( + "{} is not a supported elliptic curve".format(curve.name), + _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, + ) + return curve_nid + + @contextmanager + def _tmp_bn_ctx(self): + bn_ctx = self._lib.BN_CTX_new() + self.openssl_assert(bn_ctx != self._ffi.NULL) + bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free) + self._lib.BN_CTX_start(bn_ctx) + try: + yield bn_ctx + finally: + self._lib.BN_CTX_end(bn_ctx) + + def _ec_key_determine_group_get_func(self, ctx): + """ + Given an EC_KEY determine the group and what function is required to + get point coordinates. + """ + self.openssl_assert(ctx != self._ffi.NULL) + + nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field") + self.openssl_assert(nid_two_field != self._lib.NID_undef) + + group = self._lib.EC_KEY_get0_group(ctx) + self.openssl_assert(group != self._ffi.NULL) + + method = self._lib.EC_GROUP_method_of(group) + self.openssl_assert(method != self._ffi.NULL) + + nid = self._lib.EC_METHOD_get_field_type(method) + self.openssl_assert(nid != self._lib.NID_undef) + + if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M: + get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m + else: + get_func = self._lib.EC_POINT_get_affine_coordinates_GFp + + assert get_func + + return get_func, group + + def _ec_key_set_public_key_affine_coordinates(self, ctx, x: int, y: int): + """ + Sets the public key point in the EC_KEY context to the affine x and y + values. + """ + + if x < 0 or y < 0: + raise ValueError( + "Invalid EC key. Both x and y must be non-negative." + ) + + x = self._ffi.gc(self._int_to_bn(x), self._lib.BN_free) + y = self._ffi.gc(self._int_to_bn(y), self._lib.BN_free) + res = self._lib.EC_KEY_set_public_key_affine_coordinates(ctx, x, y) + if res != 1: + self._consume_errors() + raise ValueError("Invalid EC key.") + + def _private_key_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + key, + evp_pkey, + cdata, + ) -> bytes: + # validate argument types + if not isinstance(encoding, serialization.Encoding): + raise TypeError("encoding must be an item from the Encoding enum") + if not isinstance(format, serialization.PrivateFormat): + raise TypeError( + "format must be an item from the PrivateFormat enum" + ) + if not isinstance( + encryption_algorithm, serialization.KeySerializationEncryption + ): + raise TypeError( + "Encryption algorithm must be a KeySerializationEncryption " + "instance" + ) + + # validate password + if isinstance(encryption_algorithm, serialization.NoEncryption): + password = b"" + elif isinstance( + encryption_algorithm, serialization.BestAvailableEncryption + ): + password = encryption_algorithm.password + if len(password) > 1023: + raise ValueError( + "Passwords longer than 1023 bytes are not supported by " + "this backend" + ) + elif ( + isinstance( + encryption_algorithm, serialization._KeySerializationEncryption + ) + and encryption_algorithm._format + is format + is serialization.PrivateFormat.OpenSSH + ): + password = encryption_algorithm.password + else: + raise ValueError("Unsupported encryption type") + + # PKCS8 + PEM/DER + if format is serialization.PrivateFormat.PKCS8: + if encoding is serialization.Encoding.PEM: + write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey + elif encoding is serialization.Encoding.DER: + write_bio = self._lib.i2d_PKCS8PrivateKey_bio + else: + raise ValueError("Unsupported encoding for PKCS8") + return self._private_key_bytes_via_bio( + write_bio, evp_pkey, password + ) + + # TraditionalOpenSSL + PEM/DER + if format is serialization.PrivateFormat.TraditionalOpenSSL: + if self._fips_enabled and not isinstance( + encryption_algorithm, serialization.NoEncryption + ): + raise ValueError( + "Encrypted traditional OpenSSL format is not " + "supported in FIPS mode." + ) + key_type = self._lib.EVP_PKEY_id(evp_pkey) + + if encoding is serialization.Encoding.PEM: + if key_type == self._lib.EVP_PKEY_RSA: + write_bio = self._lib.PEM_write_bio_RSAPrivateKey + elif key_type == self._lib.EVP_PKEY_DSA: + write_bio = self._lib.PEM_write_bio_DSAPrivateKey + elif key_type == self._lib.EVP_PKEY_EC: + write_bio = self._lib.PEM_write_bio_ECPrivateKey + else: + raise ValueError( + "Unsupported key type for TraditionalOpenSSL" + ) + return self._private_key_bytes_via_bio( + write_bio, cdata, password + ) + + if encoding is serialization.Encoding.DER: + if password: + raise ValueError( + "Encryption is not supported for DER encoded " + "traditional OpenSSL keys" + ) + if key_type == self._lib.EVP_PKEY_RSA: + write_bio = self._lib.i2d_RSAPrivateKey_bio + elif key_type == self._lib.EVP_PKEY_EC: + write_bio = self._lib.i2d_ECPrivateKey_bio + elif key_type == self._lib.EVP_PKEY_DSA: + write_bio = self._lib.i2d_DSAPrivateKey_bio + else: + raise ValueError( + "Unsupported key type for TraditionalOpenSSL" + ) + return self._bio_func_output(write_bio, cdata) + + raise ValueError("Unsupported encoding for TraditionalOpenSSL") + + # OpenSSH + PEM + if format is serialization.PrivateFormat.OpenSSH: + if encoding is serialization.Encoding.PEM: + return ssh._serialize_ssh_private_key( + key, password, encryption_algorithm + ) + + raise ValueError( + "OpenSSH private key format can only be used" + " with PEM encoding" + ) + + # Anything that key-specific code was supposed to handle earlier, + # like Raw. + raise ValueError("format is invalid with this key") + + def _private_key_bytes_via_bio(self, write_bio, evp_pkey, password): + if not password: + evp_cipher = self._ffi.NULL + else: + # This is a curated value that we will update over time. + evp_cipher = self._lib.EVP_get_cipherbyname(b"aes-256-cbc") + + return self._bio_func_output( + write_bio, + evp_pkey, + evp_cipher, + password, + len(password), + self._ffi.NULL, + self._ffi.NULL, + ) + + def _bio_func_output(self, write_bio, *args): + bio = self._create_mem_bio_gc() + res = write_bio(bio, *args) + self.openssl_assert(res == 1) + return self._read_mem_bio(bio) + + def _public_key_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + key, + evp_pkey, + cdata, + ) -> bytes: + if not isinstance(encoding, serialization.Encoding): + raise TypeError("encoding must be an item from the Encoding enum") + if not isinstance(format, serialization.PublicFormat): + raise TypeError( + "format must be an item from the PublicFormat enum" + ) + + # SubjectPublicKeyInfo + PEM/DER + if format is serialization.PublicFormat.SubjectPublicKeyInfo: + if encoding is serialization.Encoding.PEM: + write_bio = self._lib.PEM_write_bio_PUBKEY + elif encoding is serialization.Encoding.DER: + write_bio = self._lib.i2d_PUBKEY_bio + else: + raise ValueError( + "SubjectPublicKeyInfo works only with PEM or DER encoding" + ) + return self._bio_func_output(write_bio, evp_pkey) + + # PKCS1 + PEM/DER + if format is serialization.PublicFormat.PKCS1: + # Only RSA is supported here. + key_type = self._lib.EVP_PKEY_id(evp_pkey) + if key_type != self._lib.EVP_PKEY_RSA: + raise ValueError("PKCS1 format is supported only for RSA keys") + + if encoding is serialization.Encoding.PEM: + write_bio = self._lib.PEM_write_bio_RSAPublicKey + elif encoding is serialization.Encoding.DER: + write_bio = self._lib.i2d_RSAPublicKey_bio + else: + raise ValueError("PKCS1 works only with PEM or DER encoding") + return self._bio_func_output(write_bio, cdata) + + # OpenSSH + OpenSSH + if format is serialization.PublicFormat.OpenSSH: + if encoding is serialization.Encoding.OpenSSH: + return ssh.serialize_ssh_public_key(key) + + raise ValueError( + "OpenSSH format must be used with OpenSSH encoding" + ) + + # Anything that key-specific code was supposed to handle earlier, + # like Raw, CompressedPoint, UncompressedPoint + raise ValueError("format is invalid with this key") + + def dh_supported(self) -> bool: + return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL + + def generate_dh_parameters( + self, generator: int, key_size: int + ) -> dh.DHParameters: + if key_size < dh._MIN_MODULUS_SIZE: + raise ValueError( + "DH key_size must be at least {} bits".format( + dh._MIN_MODULUS_SIZE + ) + ) + + if generator not in (2, 5): + raise ValueError("DH generator must be 2 or 5") + + dh_param_cdata = self._lib.DH_new() + self.openssl_assert(dh_param_cdata != self._ffi.NULL) + dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free) + + res = self._lib.DH_generate_parameters_ex( + dh_param_cdata, key_size, generator, self._ffi.NULL + ) + if res != 1: + errors = self._consume_errors_with_text() + raise ValueError("Unable to generate DH parameters", errors) + + return _DHParameters(self, dh_param_cdata) + + def _dh_cdata_to_evp_pkey(self, dh_cdata): + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set1_DH(evp_pkey, dh_cdata) + self.openssl_assert(res == 1) + return evp_pkey + + def generate_dh_private_key( + self, parameters: dh.DHParameters + ) -> dh.DHPrivateKey: + dh_key_cdata = _dh_params_dup( + parameters._dh_cdata, self # type: ignore[attr-defined] + ) + + res = self._lib.DH_generate_key(dh_key_cdata) + self.openssl_assert(res == 1) + + evp_pkey = self._dh_cdata_to_evp_pkey(dh_key_cdata) + + return _DHPrivateKey(self, dh_key_cdata, evp_pkey) + + def generate_dh_private_key_and_parameters( + self, generator: int, key_size: int + ) -> dh.DHPrivateKey: + return self.generate_dh_private_key( + self.generate_dh_parameters(generator, key_size) + ) + + def load_dh_private_numbers( + self, numbers: dh.DHPrivateNumbers + ) -> dh.DHPrivateKey: + parameter_numbers = numbers.public_numbers.parameter_numbers + + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(parameter_numbers.p) + g = self._int_to_bn(parameter_numbers.g) + + if parameter_numbers.q is not None: + q = self._int_to_bn(parameter_numbers.q) + else: + q = self._ffi.NULL + + pub_key = self._int_to_bn(numbers.public_numbers.y) + priv_key = self._int_to_bn(numbers.x) + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key) + self.openssl_assert(res == 1) + + codes = self._ffi.new("int[]", 1) + res = self._lib.DH_check(dh_cdata, codes) + self.openssl_assert(res == 1) + + # DH_check will return DH_NOT_SUITABLE_GENERATOR if p % 24 does not + # equal 11 when the generator is 2 (a quadratic nonresidue). + # We want to ignore that error because p % 24 == 23 is also fine. + # Specifically, g is then a quadratic residue. Within the context of + # Diffie-Hellman this means it can only generate half the possible + # values. That sounds bad, but quadratic nonresidues leak a bit of + # the key to the attacker in exchange for having the full key space + # available. See: https://crypto.stackexchange.com/questions/12961 + if codes[0] != 0 and not ( + parameter_numbers.g == 2 + and codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0 + ): + raise ValueError("DH private numbers did not pass safety checks.") + + evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) + + return _DHPrivateKey(self, dh_cdata, evp_pkey) + + def load_dh_public_numbers( + self, numbers: dh.DHPublicNumbers + ) -> dh.DHPublicKey: + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + parameter_numbers = numbers.parameter_numbers + + p = self._int_to_bn(parameter_numbers.p) + g = self._int_to_bn(parameter_numbers.g) + + if parameter_numbers.q is not None: + q = self._int_to_bn(parameter_numbers.q) + else: + q = self._ffi.NULL + + pub_key = self._int_to_bn(numbers.y) + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL) + self.openssl_assert(res == 1) + + evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) + + return _DHPublicKey(self, dh_cdata, evp_pkey) + + def load_dh_parameter_numbers( + self, numbers: dh.DHParameterNumbers + ) -> dh.DHParameters: + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(numbers.p) + g = self._int_to_bn(numbers.g) + + if numbers.q is not None: + q = self._int_to_bn(numbers.q) + else: + q = self._ffi.NULL + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + return _DHParameters(self, dh_cdata) + + def dh_parameters_supported( + self, p: int, g: int, q: typing.Optional[int] = None + ) -> bool: + dh_cdata = self._lib.DH_new() + self.openssl_assert(dh_cdata != self._ffi.NULL) + dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) + + p = self._int_to_bn(p) + g = self._int_to_bn(g) + + if q is not None: + q = self._int_to_bn(q) + else: + q = self._ffi.NULL + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) + self.openssl_assert(res == 1) + + codes = self._ffi.new("int[]", 1) + res = self._lib.DH_check(dh_cdata, codes) + self.openssl_assert(res == 1) + + return codes[0] == 0 + + def dh_x942_serialization_supported(self) -> bool: + return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 + + def x25519_load_public_bytes(self, data: bytes) -> x25519.X25519PublicKey: + # If/when LibreSSL adds support for EVP_PKEY_new_raw_public_key we + # can switch to it (Cryptography_HAS_RAW_KEY) + if len(data) != 32: + raise ValueError("An X25519 public key is 32 bytes long") + + evp_pkey = self._create_evp_pkey_gc() + res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519) + self.openssl_assert(res == 1) + res = self._lib.EVP_PKEY_set1_tls_encodedpoint( + evp_pkey, data, len(data) + ) + self.openssl_assert(res == 1) + return _X25519PublicKey(self, evp_pkey) + + def x25519_load_private_bytes( + self, data: bytes + ) -> x25519.X25519PrivateKey: + # If/when LibreSSL adds support for EVP_PKEY_new_raw_private_key we + # can switch to it (Cryptography_HAS_RAW_KEY) drop the + # zeroed_bytearray garbage. + # OpenSSL only has facilities for loading PKCS8 formatted private + # keys using the algorithm identifiers specified in + # https://tools.ietf.org/html/draft-ietf-curdle-pkix-09. + # This is the standard PKCS8 prefix for a 32 byte X25519 key. + # The form is: + # 0:d=0 hl=2 l= 46 cons: SEQUENCE + # 2:d=1 hl=2 l= 1 prim: INTEGER :00 + # 5:d=1 hl=2 l= 5 cons: SEQUENCE + # 7:d=2 hl=2 l= 3 prim: OBJECT :1.3.101.110 + # 12:d=1 hl=2 l= 34 prim: OCTET STRING (the key) + # Of course there's a bit more complexity. In reality OCTET STRING + # contains an OCTET STRING of length 32! So the last two bytes here + # are \x04\x20, which is an OCTET STRING of length 32. + if len(data) != 32: + raise ValueError("An X25519 private key is 32 bytes long") + + pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 ' + with self._zeroed_bytearray(48) as ba: + ba[0:16] = pkcs8_prefix + ba[16:] = data + bio = self._bytes_to_bio(ba) + evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL) + + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + self.openssl_assert( + self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_X25519 + ) + return _X25519PrivateKey(self, evp_pkey) + + def _evp_pkey_keygen_gc(self, nid): + evp_pkey_ctx = self._lib.EVP_PKEY_CTX_new_id(nid, self._ffi.NULL) + self.openssl_assert(evp_pkey_ctx != self._ffi.NULL) + evp_pkey_ctx = self._ffi.gc(evp_pkey_ctx, self._lib.EVP_PKEY_CTX_free) + res = self._lib.EVP_PKEY_keygen_init(evp_pkey_ctx) + self.openssl_assert(res == 1) + evp_ppkey = self._ffi.new("EVP_PKEY **") + res = self._lib.EVP_PKEY_keygen(evp_pkey_ctx, evp_ppkey) + self.openssl_assert(res == 1) + self.openssl_assert(evp_ppkey[0] != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_ppkey[0], self._lib.EVP_PKEY_free) + return evp_pkey + + def x25519_generate_key(self) -> x25519.X25519PrivateKey: + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X25519) + return _X25519PrivateKey(self, evp_pkey) + + def x25519_supported(self) -> bool: + if self._fips_enabled: + return False + return not self._lib.CRYPTOGRAPHY_IS_LIBRESSL + + def x448_load_public_bytes(self, data: bytes) -> x448.X448PublicKey: + if len(data) != 56: + raise ValueError("An X448 public key is 56 bytes long") + + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_X448, self._ffi.NULL, data, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return _X448PublicKey(self, evp_pkey) + + def x448_load_private_bytes(self, data: bytes) -> x448.X448PrivateKey: + if len(data) != 56: + raise ValueError("An X448 private key is 56 bytes long") + + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_X448, self._ffi.NULL, data_ptr, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + return _X448PrivateKey(self, evp_pkey) + + def x448_generate_key(self) -> x448.X448PrivateKey: + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X448) + return _X448PrivateKey(self, evp_pkey) + + def x448_supported(self) -> bool: + if self._fips_enabled: + return False + return ( + not self._lib.CRYPTOGRAPHY_IS_LIBRESSL + and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL + ) + + def ed25519_supported(self) -> bool: + if self._fips_enabled: + return False + return self._lib.CRYPTOGRAPHY_HAS_WORKING_ED25519 + + def ed25519_load_public_bytes( + self, data: bytes + ) -> ed25519.Ed25519PublicKey: + utils._check_bytes("data", data) + + if len(data) != ed25519._ED25519_KEY_SIZE: + raise ValueError("An Ed25519 public key is 32 bytes long") + + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_ED25519, self._ffi.NULL, data, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed25519PublicKey(self, evp_pkey) + + def ed25519_load_private_bytes( + self, data: bytes + ) -> ed25519.Ed25519PrivateKey: + if len(data) != ed25519._ED25519_KEY_SIZE: + raise ValueError("An Ed25519 private key is 32 bytes long") + + utils._check_byteslike("data", data) + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_ED25519, self._ffi.NULL, data_ptr, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed25519PrivateKey(self, evp_pkey) + + def ed25519_generate_key(self) -> ed25519.Ed25519PrivateKey: + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED25519) + return _Ed25519PrivateKey(self, evp_pkey) + + def ed448_supported(self) -> bool: + if self._fips_enabled: + return False + return ( + not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B + and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL + ) + + def ed448_load_public_bytes(self, data: bytes) -> ed448.Ed448PublicKey: + utils._check_bytes("data", data) + if len(data) != _ED448_KEY_SIZE: + raise ValueError("An Ed448 public key is 57 bytes long") + + evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( + self._lib.NID_ED448, self._ffi.NULL, data, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed448PublicKey(self, evp_pkey) + + def ed448_load_private_bytes(self, data: bytes) -> ed448.Ed448PrivateKey: + utils._check_byteslike("data", data) + if len(data) != _ED448_KEY_SIZE: + raise ValueError("An Ed448 private key is 57 bytes long") + + data_ptr = self._ffi.from_buffer(data) + evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( + self._lib.NID_ED448, self._ffi.NULL, data_ptr, len(data) + ) + self.openssl_assert(evp_pkey != self._ffi.NULL) + evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) + + return _Ed448PrivateKey(self, evp_pkey) + + def ed448_generate_key(self) -> ed448.Ed448PrivateKey: + evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED448) + return _Ed448PrivateKey(self, evp_pkey) + + def derive_scrypt( + self, + key_material: bytes, + salt: bytes, + length: int, + n: int, + r: int, + p: int, + ) -> bytes: + buf = self._ffi.new("unsigned char[]", length) + key_material_ptr = self._ffi.from_buffer(key_material) + res = self._lib.EVP_PBE_scrypt( + key_material_ptr, + len(key_material), + salt, + len(salt), + n, + r, + p, + scrypt._MEM_LIMIT, + buf, + length, + ) + if res != 1: + errors = self._consume_errors_with_text() + # memory required formula explained here: + # https://blog.filippo.io/the-scrypt-parameters/ + min_memory = 128 * n * r // (1024**2) + raise MemoryError( + "Not enough memory to derive key. These parameters require" + " {} MB of memory.".format(min_memory), + errors, + ) + return self._ffi.buffer(buf)[:] + + def aead_cipher_supported(self, cipher) -> bool: + cipher_name = aead._aead_cipher_name(cipher) + if self._fips_enabled and cipher_name not in self._fips_aead: + return False + # SIV isn't loaded through get_cipherbyname but instead a new fetch API + # only available in 3.0+. But if we know we're on 3.0+ then we know + # it's supported. + if cipher_name.endswith(b"-siv"): + return self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER == 1 + else: + return ( + self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL + ) + + @contextlib.contextmanager + def _zeroed_bytearray(self, length: int) -> typing.Iterator[bytearray]: + """ + This method creates a bytearray, which we copy data into (hopefully + also from a mutable buffer that can be dynamically erased!), and then + zero when we're done. + """ + ba = bytearray(length) + try: + yield ba + finally: + self._zero_data(ba, length) + + def _zero_data(self, data, length: int) -> None: + # We clear things this way because at the moment we're not + # sure of a better way that can guarantee it overwrites the + # memory of a bytearray and doesn't just replace the underlying char *. + for i in range(length): + data[i] = 0 + + @contextlib.contextmanager + def _zeroed_null_terminated_buf(self, data): + """ + This method takes bytes, which can be a bytestring or a mutable + buffer like a bytearray, and yields a null-terminated version of that + data. This is required because PKCS12_parse doesn't take a length with + its password char * and ffi.from_buffer doesn't provide null + termination. So, to support zeroing the data via bytearray we + need to build this ridiculous construct that copies the memory, but + zeroes it after use. + """ + if data is None: + yield self._ffi.NULL + else: + data_len = len(data) + buf = self._ffi.new("char[]", data_len + 1) + self._ffi.memmove(buf, data, data_len) + try: + yield buf + finally: + # Cast to a uint8_t * so we can assign by integer + self._zero_data(self._ffi.cast("uint8_t *", buf), data_len) + + def load_key_and_certificates_from_pkcs12( + self, data: bytes, password: typing.Optional[bytes] + ) -> typing.Tuple[ + typing.Optional[PRIVATE_KEY_TYPES], + typing.Optional[x509.Certificate], + typing.List[x509.Certificate], + ]: + pkcs12 = self.load_pkcs12(data, password) + return ( + pkcs12.key, + pkcs12.cert.certificate if pkcs12.cert else None, + [cert.certificate for cert in pkcs12.additional_certs], + ) + + def load_pkcs12( + self, data: bytes, password: typing.Optional[bytes] + ) -> PKCS12KeyAndCertificates: + if password is not None: + utils._check_byteslike("password", password) + + bio = self._bytes_to_bio(data) + p12 = self._lib.d2i_PKCS12_bio(bio.bio, self._ffi.NULL) + if p12 == self._ffi.NULL: + self._consume_errors() + raise ValueError("Could not deserialize PKCS12 data") + + p12 = self._ffi.gc(p12, self._lib.PKCS12_free) + evp_pkey_ptr = self._ffi.new("EVP_PKEY **") + x509_ptr = self._ffi.new("X509 **") + sk_x509_ptr = self._ffi.new("Cryptography_STACK_OF_X509 **") + with self._zeroed_null_terminated_buf(password) as password_buf: + res = self._lib.PKCS12_parse( + p12, password_buf, evp_pkey_ptr, x509_ptr, sk_x509_ptr + ) + if res == 0: + self._consume_errors() + raise ValueError("Invalid password or PKCS12 data") + + cert = None + key = None + additional_certificates = [] + + if evp_pkey_ptr[0] != self._ffi.NULL: + evp_pkey = self._ffi.gc(evp_pkey_ptr[0], self._lib.EVP_PKEY_free) + # We don't support turning off RSA key validation when loading + # PKCS12 keys + key = self._evp_pkey_to_private_key( + evp_pkey, unsafe_skip_rsa_key_validation=False + ) + + if x509_ptr[0] != self._ffi.NULL: + x509 = self._ffi.gc(x509_ptr[0], self._lib.X509_free) + cert_obj = self._ossl2cert(x509) + name = None + maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL) + if maybe_name != self._ffi.NULL: + name = self._ffi.string(maybe_name) + cert = PKCS12Certificate(cert_obj, name) + + if sk_x509_ptr[0] != self._ffi.NULL: + sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free) + num = self._lib.sk_X509_num(sk_x509_ptr[0]) + + # In OpenSSL < 3.0.0 PKCS12 parsing reverses the order of the + # certificates. + indices: typing.Iterable[int] + if ( + self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER + or self._lib.CRYPTOGRAPHY_IS_BORINGSSL + ): + indices = range(num) + else: + indices = reversed(range(num)) + + for i in indices: + x509 = self._lib.sk_X509_value(sk_x509, i) + self.openssl_assert(x509 != self._ffi.NULL) + x509 = self._ffi.gc(x509, self._lib.X509_free) + addl_cert = self._ossl2cert(x509) + addl_name = None + maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL) + if maybe_name != self._ffi.NULL: + addl_name = self._ffi.string(maybe_name) + additional_certificates.append( + PKCS12Certificate(addl_cert, addl_name) + ) + + return PKCS12KeyAndCertificates(key, cert, additional_certificates) + + def serialize_key_and_certificates_to_pkcs12( + self, + name: typing.Optional[bytes], + key: typing.Optional[_ALLOWED_PKCS12_TYPES], + cert: typing.Optional[x509.Certificate], + cas: typing.Optional[typing.List[_PKCS12_CAS_TYPES]], + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + password = None + if name is not None: + utils._check_bytes("name", name) + + if isinstance(encryption_algorithm, serialization.NoEncryption): + nid_cert = -1 + nid_key = -1 + pkcs12_iter = 0 + mac_iter = 0 + mac_alg = self._ffi.NULL + elif isinstance( + encryption_algorithm, serialization.BestAvailableEncryption + ): + # PKCS12 encryption is hopeless trash and can never be fixed. + # OpenSSL 3 supports PBESv2, but Libre and Boring do not, so + # we use PBESv1 with 3DES on the older paths. + if self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: + nid_cert = self._lib.NID_aes_256_cbc + nid_key = self._lib.NID_aes_256_cbc + else: + nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC + nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC + # At least we can set this higher than OpenSSL's default + pkcs12_iter = 20000 + # mac_iter chosen for compatibility reasons, see: + # https://www.openssl.org/docs/man1.1.1/man3/PKCS12_create.html + # Did we mention how lousy PKCS12 encryption is? + mac_iter = 1 + # MAC algorithm can only be set on OpenSSL 3.0.0+ + mac_alg = self._ffi.NULL + password = encryption_algorithm.password + elif ( + isinstance( + encryption_algorithm, serialization._KeySerializationEncryption + ) + and encryption_algorithm._format + is serialization.PrivateFormat.PKCS12 + ): + # Default to OpenSSL's defaults. Behavior will vary based on the + # version of OpenSSL cryptography is compiled against. + nid_cert = 0 + nid_key = 0 + # Use the default iters we use in best available + pkcs12_iter = 20000 + # See the Best Available comment for why this is 1 + mac_iter = 1 + password = encryption_algorithm.password + keycertalg = encryption_algorithm._key_cert_algorithm + if keycertalg is PBES.PBESv1SHA1And3KeyTripleDESCBC: + nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC + nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC + elif keycertalg is PBES.PBESv2SHA256AndAES256CBC: + if not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: + raise UnsupportedAlgorithm( + "PBESv2 is not supported by this version of OpenSSL" + ) + nid_cert = self._lib.NID_aes_256_cbc + nid_key = self._lib.NID_aes_256_cbc + else: + assert keycertalg is None + # We use OpenSSL's defaults + + if encryption_algorithm._hmac_hash is not None: + if not self._lib.Cryptography_HAS_PKCS12_SET_MAC: + raise UnsupportedAlgorithm( + "Setting MAC algorithm is not supported by this " + "version of OpenSSL." + ) + mac_alg = self._evp_md_non_null_from_algorithm( + encryption_algorithm._hmac_hash + ) + self.openssl_assert(mac_alg != self._ffi.NULL) + else: + mac_alg = self._ffi.NULL + + if encryption_algorithm._kdf_rounds is not None: + pkcs12_iter = encryption_algorithm._kdf_rounds + + else: + raise ValueError("Unsupported key encryption type") + + if cas is None or len(cas) == 0: + sk_x509 = self._ffi.NULL + else: + sk_x509 = self._lib.sk_X509_new_null() + sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free) + + # This list is to keep the x509 values alive until end of function + ossl_cas = [] + for ca in cas: + if isinstance(ca, PKCS12Certificate): + ca_alias = ca.friendly_name + ossl_ca = self._cert2ossl(ca.certificate) + with self._zeroed_null_terminated_buf( + ca_alias + ) as ca_name_buf: + res = self._lib.X509_alias_set1( + ossl_ca, ca_name_buf, -1 + ) + self.openssl_assert(res == 1) + else: + ossl_ca = self._cert2ossl(ca) + ossl_cas.append(ossl_ca) + res = self._lib.sk_X509_push(sk_x509, ossl_ca) + backend.openssl_assert(res >= 1) + + with self._zeroed_null_terminated_buf(password) as password_buf: + with self._zeroed_null_terminated_buf(name) as name_buf: + ossl_cert = self._cert2ossl(cert) if cert else self._ffi.NULL + if key is not None: + evp_pkey = key._evp_pkey # type: ignore[union-attr] + else: + evp_pkey = self._ffi.NULL + + p12 = self._lib.PKCS12_create( + password_buf, + name_buf, + evp_pkey, + ossl_cert, + sk_x509, + nid_key, + nid_cert, + pkcs12_iter, + mac_iter, + 0, + ) + + if ( + self._lib.Cryptography_HAS_PKCS12_SET_MAC + and mac_alg != self._ffi.NULL + ): + self._lib.PKCS12_set_mac( + p12, + password_buf, + -1, + self._ffi.NULL, + 0, + mac_iter, + mac_alg, + ) + + self.openssl_assert(p12 != self._ffi.NULL) + p12 = self._ffi.gc(p12, self._lib.PKCS12_free) + + bio = self._create_mem_bio_gc() + res = self._lib.i2d_PKCS12_bio(bio, p12) + self.openssl_assert(res > 0) + return self._read_mem_bio(bio) + + def poly1305_supported(self) -> bool: + if self._fips_enabled: + return False + return self._lib.Cryptography_HAS_POLY1305 == 1 + + def create_poly1305_ctx(self, key: bytes) -> _Poly1305Context: + utils._check_byteslike("key", key) + if len(key) != _POLY1305_KEY_SIZE: + raise ValueError("A poly1305 key is 32 bytes long") + + return _Poly1305Context(self, key) + + def pkcs7_supported(self) -> bool: + return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL + + def load_pem_pkcs7_certificates( + self, data: bytes + ) -> typing.List[x509.Certificate]: + utils._check_bytes("data", data) + bio = self._bytes_to_bio(data) + p7 = self._lib.PEM_read_bio_PKCS7( + bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + if p7 == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to parse PKCS7 data") + + p7 = self._ffi.gc(p7, self._lib.PKCS7_free) + return self._load_pkcs7_certificates(p7) + + def load_der_pkcs7_certificates( + self, data: bytes + ) -> typing.List[x509.Certificate]: + utils._check_bytes("data", data) + bio = self._bytes_to_bio(data) + p7 = self._lib.d2i_PKCS7_bio(bio.bio, self._ffi.NULL) + if p7 == self._ffi.NULL: + self._consume_errors() + raise ValueError("Unable to parse PKCS7 data") + + p7 = self._ffi.gc(p7, self._lib.PKCS7_free) + return self._load_pkcs7_certificates(p7) + + def _load_pkcs7_certificates(self, p7): + nid = self._lib.OBJ_obj2nid(p7.type) + self.openssl_assert(nid != self._lib.NID_undef) + if nid != self._lib.NID_pkcs7_signed: + raise UnsupportedAlgorithm( + "Only basic signed structures are currently supported. NID" + " for this data was {}".format(nid), + _Reasons.UNSUPPORTED_SERIALIZATION, + ) + + sk_x509 = p7.d.sign.cert + num = self._lib.sk_X509_num(sk_x509) + certs = [] + for i in range(num): + x509 = self._lib.sk_X509_value(sk_x509, i) + self.openssl_assert(x509 != self._ffi.NULL) + res = self._lib.X509_up_ref(x509) + self.openssl_assert(res == 1) + x509 = self._ffi.gc(x509, self._lib.X509_free) + cert = self._ossl2cert(x509) + certs.append(cert) + + return certs + + +class GetCipherByName: + def __init__(self, fmt: str): + self._fmt = fmt + + def __call__(self, backend: Backend, cipher: CipherAlgorithm, mode: Mode): + cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower() + evp_cipher = backend._lib.EVP_get_cipherbyname( + cipher_name.encode("ascii") + ) + + # try EVP_CIPHER_fetch if present + if ( + evp_cipher == backend._ffi.NULL + and backend._lib.Cryptography_HAS_300_EVP_CIPHER + ): + evp_cipher = backend._lib.EVP_CIPHER_fetch( + backend._ffi.NULL, + cipher_name.encode("ascii"), + backend._ffi.NULL, + ) + + backend._consume_errors() + return evp_cipher + + +def _get_xts_cipher(backend: Backend, cipher: AES, mode): + cipher_name = "aes-{}-xts".format(cipher.key_size // 2) + return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) + + +backend = Backend() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py new file mode 100644 index 000000000..fd2b6612f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py @@ -0,0 +1,281 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import ciphers +from cryptography.hazmat.primitives.ciphers import algorithms, modes + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +class _CipherContext: + _ENCRYPT = 1 + _DECRYPT = 0 + _MAX_CHUNK_SIZE = 2**30 - 1 + + def __init__( + self, backend: "Backend", cipher, mode, operation: int + ) -> None: + self._backend = backend + self._cipher = cipher + self._mode = mode + self._operation = operation + self._tag: typing.Optional[bytes] = None + + if isinstance(self._cipher, ciphers.BlockCipherAlgorithm): + self._block_size_bytes = self._cipher.block_size // 8 + else: + self._block_size_bytes = 1 + + ctx = self._backend._lib.EVP_CIPHER_CTX_new() + ctx = self._backend._ffi.gc( + ctx, self._backend._lib.EVP_CIPHER_CTX_free + ) + + registry = self._backend._cipher_registry + try: + adapter = registry[type(cipher), type(mode)] + except KeyError: + raise UnsupportedAlgorithm( + "cipher {} in {} mode is not supported " + "by this backend.".format( + cipher.name, mode.name if mode else mode + ), + _Reasons.UNSUPPORTED_CIPHER, + ) + + evp_cipher = adapter(self._backend, cipher, mode) + if evp_cipher == self._backend._ffi.NULL: + msg = "cipher {0.name} ".format(cipher) + if mode is not None: + msg += "in {0.name} mode ".format(mode) + msg += ( + "is not supported by this backend (Your version of OpenSSL " + "may be too old. Current version: {}.)" + ).format(self._backend.openssl_version_text()) + raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER) + + if isinstance(mode, modes.ModeWithInitializationVector): + iv_nonce = self._backend._ffi.from_buffer( + mode.initialization_vector + ) + elif isinstance(mode, modes.ModeWithTweak): + iv_nonce = self._backend._ffi.from_buffer(mode.tweak) + elif isinstance(mode, modes.ModeWithNonce): + iv_nonce = self._backend._ffi.from_buffer(mode.nonce) + elif isinstance(cipher, algorithms.ChaCha20): + iv_nonce = self._backend._ffi.from_buffer(cipher.nonce) + else: + iv_nonce = self._backend._ffi.NULL + # begin init with cipher and operation type + res = self._backend._lib.EVP_CipherInit_ex( + ctx, + evp_cipher, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + operation, + ) + self._backend.openssl_assert(res != 0) + # set the key length to handle variable key ciphers + res = self._backend._lib.EVP_CIPHER_CTX_set_key_length( + ctx, len(cipher.key) + ) + self._backend.openssl_assert(res != 0) + if isinstance(mode, modes.GCM): + res = self._backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, + self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN, + len(iv_nonce), + self._backend._ffi.NULL, + ) + self._backend.openssl_assert(res != 0) + if mode.tag is not None: + res = self._backend._lib.EVP_CIPHER_CTX_ctrl( + ctx, + self._backend._lib.EVP_CTRL_AEAD_SET_TAG, + len(mode.tag), + mode.tag, + ) + self._backend.openssl_assert(res != 0) + self._tag = mode.tag + + # pass key/iv + res = self._backend._lib.EVP_CipherInit_ex( + ctx, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.from_buffer(cipher.key), + iv_nonce, + operation, + ) + + # Check for XTS mode duplicate keys error + errors = self._backend._consume_errors() + lib = self._backend._lib + if res == 0 and ( + ( + lib.CRYPTOGRAPHY_OPENSSL_111D_OR_GREATER + and errors[0]._lib_reason_match( + lib.ERR_LIB_EVP, lib.EVP_R_XTS_DUPLICATED_KEYS + ) + ) + or ( + lib.Cryptography_HAS_PROVIDERS + and errors[0]._lib_reason_match( + lib.ERR_LIB_PROV, lib.PROV_R_XTS_DUPLICATED_KEYS + ) + ) + ): + raise ValueError("In XTS mode duplicated keys are not allowed") + + self._backend.openssl_assert(res != 0, errors=errors) + + # We purposely disable padding here as it's handled higher up in the + # API. + self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0) + self._ctx = ctx + + def update(self, data: bytes) -> bytes: + buf = bytearray(len(data) + self._block_size_bytes - 1) + n = self.update_into(data, buf) + return bytes(buf[:n]) + + def update_into(self, data: bytes, buf: bytes) -> int: + total_data_len = len(data) + if len(buf) < (total_data_len + self._block_size_bytes - 1): + raise ValueError( + "buffer must be at least {} bytes for this " + "payload".format(len(data) + self._block_size_bytes - 1) + ) + + data_processed = 0 + total_out = 0 + outlen = self._backend._ffi.new("int *") + baseoutbuf = self._backend._ffi.from_buffer(buf) + baseinbuf = self._backend._ffi.from_buffer(data) + + while data_processed != total_data_len: + outbuf = baseoutbuf + total_out + inbuf = baseinbuf + data_processed + inlen = min(self._MAX_CHUNK_SIZE, total_data_len - data_processed) + + res = self._backend._lib.EVP_CipherUpdate( + self._ctx, outbuf, outlen, inbuf, inlen + ) + if res == 0 and isinstance(self._mode, modes.XTS): + self._backend._consume_errors() + raise ValueError( + "In XTS mode you must supply at least a full block in the " + "first update call. For AES this is 16 bytes." + ) + else: + self._backend.openssl_assert(res != 0) + data_processed += inlen + total_out += outlen[0] + + return total_out + + def finalize(self) -> bytes: + if ( + self._operation == self._DECRYPT + and isinstance(self._mode, modes.ModeWithAuthenticationTag) + and self.tag is None + ): + raise ValueError( + "Authentication tag must be provided when decrypting." + ) + + buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes) + outlen = self._backend._ffi.new("int *") + res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) + if res == 0: + errors = self._backend._consume_errors() + + if not errors and isinstance(self._mode, modes.GCM): + raise InvalidTag + + lib = self._backend._lib + self._backend.openssl_assert( + errors[0]._lib_reason_match( + lib.ERR_LIB_EVP, + lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH, + ) + or ( + lib.Cryptography_HAS_PROVIDERS + and errors[0]._lib_reason_match( + lib.ERR_LIB_PROV, + lib.PROV_R_WRONG_FINAL_BLOCK_LENGTH, + ) + ) + or ( + lib.CRYPTOGRAPHY_IS_BORINGSSL + and errors[0].reason + == lib.CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH + ), + errors=errors, + ) + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length." + ) + + if ( + isinstance(self._mode, modes.GCM) + and self._operation == self._ENCRYPT + ): + tag_buf = self._backend._ffi.new( + "unsigned char[]", self._block_size_bytes + ) + res = self._backend._lib.EVP_CIPHER_CTX_ctrl( + self._ctx, + self._backend._lib.EVP_CTRL_AEAD_GET_TAG, + self._block_size_bytes, + tag_buf, + ) + self._backend.openssl_assert(res != 0) + self._tag = self._backend._ffi.buffer(tag_buf)[:] + + res = self._backend._lib.EVP_CIPHER_CTX_reset(self._ctx) + self._backend.openssl_assert(res == 1) + return self._backend._ffi.buffer(buf)[: outlen[0]] + + def finalize_with_tag(self, tag: bytes) -> bytes: + tag_len = len(tag) + if tag_len < self._mode._min_tag_length: + raise ValueError( + "Authentication tag must be {} bytes or longer.".format( + self._mode._min_tag_length + ) + ) + elif tag_len > self._block_size_bytes: + raise ValueError( + "Authentication tag cannot be more than {} bytes.".format( + self._block_size_bytes + ) + ) + res = self._backend._lib.EVP_CIPHER_CTX_ctrl( + self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag + ) + self._backend.openssl_assert(res != 0) + self._tag = tag + return self.finalize() + + def authenticate_additional_data(self, data: bytes) -> None: + outlen = self._backend._ffi.new("int *") + res = self._backend._lib.EVP_CipherUpdate( + self._ctx, + self._backend._ffi.NULL, + outlen, + self._backend._ffi.from_buffer(data), + len(data), + ) + self._backend.openssl_assert(res != 0) + + @property + def tag(self) -> typing.Optional[bytes]: + return self._tag diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py new file mode 100644 index 000000000..6f7363294 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py @@ -0,0 +1,87 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import ( + InvalidSignature, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.ciphers.modes import CBC + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + from cryptography.hazmat.primitives import ciphers + + +class _CMACContext: + def __init__( + self, + backend: "Backend", + algorithm: "ciphers.BlockCipherAlgorithm", + ctx=None, + ) -> None: + if not backend.cmac_algorithm_supported(algorithm): + raise UnsupportedAlgorithm( + "This backend does not support CMAC.", + _Reasons.UNSUPPORTED_CIPHER, + ) + + self._backend = backend + self._key = algorithm.key + self._algorithm = algorithm + self._output_length = algorithm.block_size // 8 + + if ctx is None: + registry = self._backend._cipher_registry + adapter = registry[type(algorithm), CBC] + + evp_cipher = adapter(self._backend, algorithm, CBC) + + ctx = self._backend._lib.CMAC_CTX_new() + + self._backend.openssl_assert(ctx != self._backend._ffi.NULL) + ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) + + key_ptr = self._backend._ffi.from_buffer(self._key) + res = self._backend._lib.CMAC_Init( + ctx, + key_ptr, + len(self._key), + evp_cipher, + self._backend._ffi.NULL, + ) + self._backend.openssl_assert(res == 1) + + self._ctx = ctx + + def update(self, data: bytes) -> None: + res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) + self._backend.openssl_assert(res == 1) + + def finalize(self) -> bytes: + buf = self._backend._ffi.new("unsigned char[]", self._output_length) + length = self._backend._ffi.new("size_t *", self._output_length) + res = self._backend._lib.CMAC_Final(self._ctx, buf, length) + self._backend.openssl_assert(res == 1) + + self._ctx = None + + return self._backend._ffi.buffer(buf)[:] + + def copy(self) -> "_CMACContext": + copied_ctx = self._backend._lib.CMAC_CTX_new() + copied_ctx = self._backend._ffi.gc( + copied_ctx, self._backend._lib.CMAC_CTX_free + ) + res = self._backend._lib.CMAC_CTX_copy(copied_ctx, self._ctx) + self._backend.openssl_assert(res == 1) + return _CMACContext(self._backend, self._algorithm, ctx=copied_ctx) + + def verify(self, signature: bytes) -> None: + digest = self.finalize() + if not constant_time.bytes_eq(digest, signature): + raise InvalidSignature("Signature did not match digest.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py new file mode 100644 index 000000000..df91d6d8a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py @@ -0,0 +1,31 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography import x509 + +# CRLReason ::= ENUMERATED { +# unspecified (0), +# keyCompromise (1), +# cACompromise (2), +# affiliationChanged (3), +# superseded (4), +# cessationOfOperation (5), +# certificateHold (6), +# -- value 7 is not used +# removeFromCRL (8), +# privilegeWithdrawn (9), +# aACompromise (10) } +_CRL_ENTRY_REASON_ENUM_TO_CODE = { + x509.ReasonFlags.unspecified: 0, + x509.ReasonFlags.key_compromise: 1, + x509.ReasonFlags.ca_compromise: 2, + x509.ReasonFlags.affiliation_changed: 3, + x509.ReasonFlags.superseded: 4, + x509.ReasonFlags.cessation_of_operation: 5, + x509.ReasonFlags.certificate_hold: 6, + x509.ReasonFlags.remove_from_crl: 8, + x509.ReasonFlags.privilege_withdrawn: 9, + x509.ReasonFlags.aa_compromise: 10, +} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py new file mode 100644 index 000000000..c429c0239 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py @@ -0,0 +1,317 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import dh + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +def _dh_params_dup(dh_cdata, backend: "Backend"): + lib = backend._lib + ffi = backend._ffi + + param_cdata = lib.DHparams_dup(dh_cdata) + backend.openssl_assert(param_cdata != ffi.NULL) + param_cdata = ffi.gc(param_cdata, lib.DH_free) + if lib.CRYPTOGRAPHY_IS_LIBRESSL: + # In libressl DHparams_dup don't copy q + q = ffi.new("BIGNUM **") + lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL) + q_dup = lib.BN_dup(q[0]) + res = lib.DH_set0_pqg(param_cdata, ffi.NULL, q_dup, ffi.NULL) + backend.openssl_assert(res == 1) + + return param_cdata + + +def _dh_cdata_to_parameters(dh_cdata, backend: "Backend") -> "_DHParameters": + param_cdata = _dh_params_dup(dh_cdata, backend) + return _DHParameters(backend, param_cdata) + + +class _DHParameters(dh.DHParameters): + def __init__(self, backend: "Backend", dh_cdata): + self._backend = backend + self._dh_cdata = dh_cdata + + def parameter_numbers(self) -> dh.DHParameterNumbers: + p = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + q_val: typing.Optional[int] + if q[0] == self._backend._ffi.NULL: + q_val = None + else: + q_val = self._backend._bn_to_int(q[0]) + return dh.DHParameterNumbers( + p=self._backend._bn_to_int(p[0]), + g=self._backend._bn_to_int(g[0]), + q=q_val, + ) + + def generate_private_key(self) -> dh.DHPrivateKey: + return self._backend.generate_dh_private_key(self) + + def parameter_bytes( + self, + encoding: serialization.Encoding, + format: serialization.ParameterFormat, + ) -> bytes: + if encoding is serialization.Encoding.OpenSSH: + raise TypeError("OpenSSH encoding is not supported") + + if format is not serialization.ParameterFormat.PKCS3: + raise ValueError("Only PKCS3 serialization is supported") + + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg( + self._dh_cdata, self._backend._ffi.NULL, q, self._backend._ffi.NULL + ) + if ( + q[0] != self._backend._ffi.NULL + and not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX + ): + raise UnsupportedAlgorithm( + "DH X9.42 serialization is not supported", + _Reasons.UNSUPPORTED_SERIALIZATION, + ) + + if encoding is serialization.Encoding.PEM: + if q[0] != self._backend._ffi.NULL: + write_bio = self._backend._lib.PEM_write_bio_DHxparams + else: + write_bio = self._backend._lib.PEM_write_bio_DHparams + elif encoding is serialization.Encoding.DER: + if q[0] != self._backend._ffi.NULL: + write_bio = self._backend._lib.i2d_DHxparams_bio + else: + write_bio = self._backend._lib.i2d_DHparams_bio + else: + raise TypeError("encoding must be an item from the Encoding enum") + + bio = self._backend._create_mem_bio_gc() + res = write_bio(bio, self._dh_cdata) + self._backend.openssl_assert(res == 1) + return self._backend._read_mem_bio(bio) + + +def _get_dh_num_bits(backend, dh_cdata) -> int: + p = backend._ffi.new("BIGNUM **") + backend._lib.DH_get0_pqg(dh_cdata, p, backend._ffi.NULL, backend._ffi.NULL) + backend.openssl_assert(p[0] != backend._ffi.NULL) + return backend._lib.BN_num_bits(p[0]) + + +class _DHPrivateKey(dh.DHPrivateKey): + def __init__(self, backend: "Backend", dh_cdata, evp_pkey): + self._backend = backend + self._dh_cdata = dh_cdata + self._evp_pkey = evp_pkey + self._key_size_bytes = self._backend._lib.DH_size(dh_cdata) + + @property + def key_size(self) -> int: + return _get_dh_num_bits(self._backend, self._dh_cdata) + + def private_numbers(self) -> dh.DHPrivateNumbers: + p = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + if q[0] == self._backend._ffi.NULL: + q_val = None + else: + q_val = self._backend._bn_to_int(q[0]) + pub_key = self._backend._ffi.new("BIGNUM **") + priv_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, priv_key) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL) + return dh.DHPrivateNumbers( + public_numbers=dh.DHPublicNumbers( + parameter_numbers=dh.DHParameterNumbers( + p=self._backend._bn_to_int(p[0]), + g=self._backend._bn_to_int(g[0]), + q=q_val, + ), + y=self._backend._bn_to_int(pub_key[0]), + ), + x=self._backend._bn_to_int(priv_key[0]), + ) + + def exchange(self, peer_public_key: dh.DHPublicKey) -> bytes: + if not isinstance(peer_public_key, _DHPublicKey): + raise TypeError("peer_public_key must be a DHPublicKey") + + ctx = self._backend._lib.EVP_PKEY_CTX_new( + self._evp_pkey, self._backend._ffi.NULL + ) + self._backend.openssl_assert(ctx != self._backend._ffi.NULL) + ctx = self._backend._ffi.gc(ctx, self._backend._lib.EVP_PKEY_CTX_free) + res = self._backend._lib.EVP_PKEY_derive_init(ctx) + self._backend.openssl_assert(res == 1) + res = self._backend._lib.EVP_PKEY_derive_set_peer( + ctx, peer_public_key._evp_pkey + ) + # Invalid kex errors here in OpenSSL 3.0 because checks were moved + # to EVP_PKEY_derive_set_peer + self._exchange_assert(res == 1) + keylen = self._backend._ffi.new("size_t *") + res = self._backend._lib.EVP_PKEY_derive( + ctx, self._backend._ffi.NULL, keylen + ) + # Invalid kex errors here in OpenSSL < 3 + self._exchange_assert(res == 1) + self._backend.openssl_assert(keylen[0] > 0) + buf = self._backend._ffi.new("unsigned char[]", keylen[0]) + res = self._backend._lib.EVP_PKEY_derive(ctx, buf, keylen) + self._backend.openssl_assert(res == 1) + + key = self._backend._ffi.buffer(buf, keylen[0])[:] + pad = self._key_size_bytes - len(key) + + if pad > 0: + key = (b"\x00" * pad) + key + + return key + + def _exchange_assert(self, ok: bool) -> None: + if not ok: + errors_with_text = self._backend._consume_errors_with_text() + raise ValueError( + "Error computing shared key.", + errors_with_text, + ) + + def public_key(self) -> dh.DHPublicKey: + dh_cdata = _dh_params_dup(self._dh_cdata, self._backend) + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key( + self._dh_cdata, pub_key, self._backend._ffi.NULL + ) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + pub_key_dup = self._backend._lib.BN_dup(pub_key[0]) + self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL) + + res = self._backend._lib.DH_set0_key( + dh_cdata, pub_key_dup, self._backend._ffi.NULL + ) + self._backend.openssl_assert(res == 1) + evp_pkey = self._backend._dh_cdata_to_evp_pkey(dh_cdata) + return _DHPublicKey(self._backend, dh_cdata, evp_pkey) + + def parameters(self) -> dh.DHParameters: + return _dh_cdata_to_parameters(self._dh_cdata, self._backend) + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + if format is not serialization.PrivateFormat.PKCS8: + raise ValueError( + "DH private keys support only PKCS8 serialization" + ) + if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX: + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg( + self._dh_cdata, + self._backend._ffi.NULL, + q, + self._backend._ffi.NULL, + ) + if q[0] != self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "DH X9.42 serialization is not supported", + _Reasons.UNSUPPORTED_SERIALIZATION, + ) + + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self, + self._evp_pkey, + self._dh_cdata, + ) + + +class _DHPublicKey(dh.DHPublicKey): + def __init__(self, backend: "Backend", dh_cdata, evp_pkey): + self._backend = backend + self._dh_cdata = dh_cdata + self._evp_pkey = evp_pkey + self._key_size_bits = _get_dh_num_bits(self._backend, self._dh_cdata) + + @property + def key_size(self) -> int: + return self._key_size_bits + + def public_numbers(self) -> dh.DHPublicNumbers: + p = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + if q[0] == self._backend._ffi.NULL: + q_val = None + else: + q_val = self._backend._bn_to_int(q[0]) + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_key( + self._dh_cdata, pub_key, self._backend._ffi.NULL + ) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + return dh.DHPublicNumbers( + parameter_numbers=dh.DHParameterNumbers( + p=self._backend._bn_to_int(p[0]), + g=self._backend._bn_to_int(g[0]), + q=q_val, + ), + y=self._backend._bn_to_int(pub_key[0]), + ) + + def parameters(self) -> dh.DHParameters: + return _dh_cdata_to_parameters(self._dh_cdata, self._backend) + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + if format is not serialization.PublicFormat.SubjectPublicKeyInfo: + raise ValueError( + "DH public keys support only " + "SubjectPublicKeyInfo serialization" + ) + + if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX: + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg( + self._dh_cdata, + self._backend._ffi.NULL, + q, + self._backend._ffi.NULL, + ) + if q[0] != self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "DH X9.42 serialization is not supported", + _Reasons.UNSUPPORTED_SERIALIZATION, + ) + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py new file mode 100644 index 000000000..15bd84a7b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py @@ -0,0 +1,236 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.backends.openssl.utils import ( + _calculate_digest_and_algorithm, +) +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import dsa +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +def _dsa_sig_sign( + backend: "Backend", private_key: "_DSAPrivateKey", data: bytes +) -> bytes: + sig_buf_len = backend._lib.DSA_size(private_key._dsa_cdata) + sig_buf = backend._ffi.new("unsigned char[]", sig_buf_len) + buflen = backend._ffi.new("unsigned int *") + + # The first parameter passed to DSA_sign is unused by OpenSSL but + # must be an integer. + res = backend._lib.DSA_sign( + 0, data, len(data), sig_buf, buflen, private_key._dsa_cdata + ) + backend.openssl_assert(res == 1) + backend.openssl_assert(buflen[0]) + + return backend._ffi.buffer(sig_buf)[: buflen[0]] + + +def _dsa_sig_verify( + backend: "Backend", + public_key: "_DSAPublicKey", + signature: bytes, + data: bytes, +) -> None: + # The first parameter passed to DSA_verify is unused by OpenSSL but + # must be an integer. + res = backend._lib.DSA_verify( + 0, data, len(data), signature, len(signature), public_key._dsa_cdata + ) + + if res != 1: + backend._consume_errors() + raise InvalidSignature + + +class _DSAParameters(dsa.DSAParameters): + def __init__(self, backend: "Backend", dsa_cdata): + self._backend = backend + self._dsa_cdata = dsa_cdata + + def parameter_numbers(self) -> dsa.DSAParameterNumbers: + p = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + return dsa.DSAParameterNumbers( + p=self._backend._bn_to_int(p[0]), + q=self._backend._bn_to_int(q[0]), + g=self._backend._bn_to_int(g[0]), + ) + + def generate_private_key(self) -> dsa.DSAPrivateKey: + return self._backend.generate_dsa_private_key(self) + + +class _DSAPrivateKey(dsa.DSAPrivateKey): + _key_size: int + + def __init__(self, backend: "Backend", dsa_cdata, evp_pkey): + self._backend = backend + self._dsa_cdata = dsa_cdata + self._evp_pkey = evp_pkey + + p = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DSA_get0_pqg( + dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL + ) + self._backend.openssl_assert(p[0] != backend._ffi.NULL) + self._key_size = self._backend._lib.BN_num_bits(p[0]) + + @property + def key_size(self) -> int: + return self._key_size + + def private_numbers(self) -> dsa.DSAPrivateNumbers: + p = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + pub_key = self._backend._ffi.new("BIGNUM **") + priv_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + self._backend._lib.DSA_get0_key(self._dsa_cdata, pub_key, priv_key) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL) + return dsa.DSAPrivateNumbers( + public_numbers=dsa.DSAPublicNumbers( + parameter_numbers=dsa.DSAParameterNumbers( + p=self._backend._bn_to_int(p[0]), + q=self._backend._bn_to_int(q[0]), + g=self._backend._bn_to_int(g[0]), + ), + y=self._backend._bn_to_int(pub_key[0]), + ), + x=self._backend._bn_to_int(priv_key[0]), + ) + + def public_key(self) -> dsa.DSAPublicKey: + dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) + self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL) + dsa_cdata = self._backend._ffi.gc( + dsa_cdata, self._backend._lib.DSA_free + ) + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DSA_get0_key( + self._dsa_cdata, pub_key, self._backend._ffi.NULL + ) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + pub_key_dup = self._backend._lib.BN_dup(pub_key[0]) + res = self._backend._lib.DSA_set0_key( + dsa_cdata, pub_key_dup, self._backend._ffi.NULL + ) + self._backend.openssl_assert(res == 1) + evp_pkey = self._backend._dsa_cdata_to_evp_pkey(dsa_cdata) + return _DSAPublicKey(self._backend, dsa_cdata, evp_pkey) + + def parameters(self) -> dsa.DSAParameters: + dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) + self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL) + dsa_cdata = self._backend._ffi.gc( + dsa_cdata, self._backend._lib.DSA_free + ) + return _DSAParameters(self._backend, dsa_cdata) + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self, + self._evp_pkey, + self._dsa_cdata, + ) + + def sign( + self, + data: bytes, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> bytes: + data, _ = _calculate_digest_and_algorithm(data, algorithm) + return _dsa_sig_sign(self._backend, self, data) + + +class _DSAPublicKey(dsa.DSAPublicKey): + _key_size: int + + def __init__(self, backend: "Backend", dsa_cdata, evp_pkey): + self._backend = backend + self._dsa_cdata = dsa_cdata + self._evp_pkey = evp_pkey + p = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DSA_get0_pqg( + dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL + ) + self._backend.openssl_assert(p[0] != backend._ffi.NULL) + self._key_size = self._backend._lib.BN_num_bits(p[0]) + + @property + def key_size(self) -> int: + return self._key_size + + def public_numbers(self) -> dsa.DSAPublicNumbers: + p = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + g = self._backend._ffi.new("BIGNUM **") + pub_key = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + self._backend._lib.DSA_get0_key( + self._dsa_cdata, pub_key, self._backend._ffi.NULL + ) + self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) + return dsa.DSAPublicNumbers( + parameter_numbers=dsa.DSAParameterNumbers( + p=self._backend._bn_to_int(p[0]), + q=self._backend._bn_to_int(q[0]), + g=self._backend._bn_to_int(g[0]), + ), + y=self._backend._bn_to_int(pub_key[0]), + ) + + def parameters(self) -> dsa.DSAParameters: + dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) + dsa_cdata = self._backend._ffi.gc( + dsa_cdata, self._backend._lib.DSA_free + ) + return _DSAParameters(self._backend, dsa_cdata) + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def verify( + self, + signature: bytes, + data: bytes, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> None: + data, _ = _calculate_digest_and_algorithm(data, algorithm) + return _dsa_sig_verify(self._backend, self, signature, data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py new file mode 100644 index 000000000..9bc6dd384 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py @@ -0,0 +1,315 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import ( + InvalidSignature, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.backends.openssl.utils import ( + _calculate_digest_and_algorithm, + _evp_pkey_derive, +) +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +def _check_signature_algorithm( + signature_algorithm: ec.EllipticCurveSignatureAlgorithm, +) -> None: + if not isinstance(signature_algorithm, ec.ECDSA): + raise UnsupportedAlgorithm( + "Unsupported elliptic curve signature algorithm.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + +def _ec_key_curve_sn(backend: "Backend", ec_key) -> str: + group = backend._lib.EC_KEY_get0_group(ec_key) + backend.openssl_assert(group != backend._ffi.NULL) + + nid = backend._lib.EC_GROUP_get_curve_name(group) + # The following check is to find EC keys with unnamed curves and raise + # an error for now. + if nid == backend._lib.NID_undef: + raise ValueError( + "ECDSA keys with explicit parameters are unsupported at this time" + ) + + # This is like the above check, but it also catches the case where you + # explicitly encoded a curve with the same parameters as a named curve. + # Don't do that. + if ( + not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL + and backend._lib.EC_GROUP_get_asn1_flag(group) == 0 + ): + raise ValueError( + "ECDSA keys with explicit parameters are unsupported at this time" + ) + + curve_name = backend._lib.OBJ_nid2sn(nid) + backend.openssl_assert(curve_name != backend._ffi.NULL) + + sn = backend._ffi.string(curve_name).decode("ascii") + return sn + + +def _mark_asn1_named_ec_curve(backend: "Backend", ec_cdata): + """ + Set the named curve flag on the EC_KEY. This causes OpenSSL to + serialize EC keys along with their curve OID which makes + deserialization easier. + """ + + backend._lib.EC_KEY_set_asn1_flag( + ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE + ) + + +def _check_key_infinity(backend: "Backend", ec_cdata) -> None: + point = backend._lib.EC_KEY_get0_public_key(ec_cdata) + backend.openssl_assert(point != backend._ffi.NULL) + group = backend._lib.EC_KEY_get0_group(ec_cdata) + backend.openssl_assert(group != backend._ffi.NULL) + if backend._lib.EC_POINT_is_at_infinity(group, point): + raise ValueError( + "Cannot load an EC public key where the point is at infinity" + ) + + +def _sn_to_elliptic_curve(backend: "Backend", sn: str) -> ec.EllipticCurve: + try: + return ec._CURVE_TYPES[sn]() + except KeyError: + raise UnsupportedAlgorithm( + "{} is not a supported elliptic curve".format(sn), + _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, + ) + + +def _ecdsa_sig_sign( + backend: "Backend", private_key: "_EllipticCurvePrivateKey", data: bytes +) -> bytes: + max_size = backend._lib.ECDSA_size(private_key._ec_key) + backend.openssl_assert(max_size > 0) + + sigbuf = backend._ffi.new("unsigned char[]", max_size) + siglen_ptr = backend._ffi.new("unsigned int[]", 1) + res = backend._lib.ECDSA_sign( + 0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key + ) + backend.openssl_assert(res == 1) + return backend._ffi.buffer(sigbuf)[: siglen_ptr[0]] + + +def _ecdsa_sig_verify( + backend: "Backend", + public_key: "_EllipticCurvePublicKey", + signature: bytes, + data: bytes, +) -> None: + res = backend._lib.ECDSA_verify( + 0, data, len(data), signature, len(signature), public_key._ec_key + ) + if res != 1: + backend._consume_errors() + raise InvalidSignature + + +class _EllipticCurvePrivateKey(ec.EllipticCurvePrivateKey): + def __init__(self, backend: "Backend", ec_key_cdata, evp_pkey): + self._backend = backend + self._ec_key = ec_key_cdata + self._evp_pkey = evp_pkey + + sn = _ec_key_curve_sn(backend, ec_key_cdata) + self._curve = _sn_to_elliptic_curve(backend, sn) + _mark_asn1_named_ec_curve(backend, ec_key_cdata) + _check_key_infinity(backend, ec_key_cdata) + + @property + def curve(self) -> ec.EllipticCurve: + return self._curve + + @property + def key_size(self) -> int: + return self.curve.key_size + + def exchange( + self, algorithm: ec.ECDH, peer_public_key: ec.EllipticCurvePublicKey + ) -> bytes: + if not ( + self._backend.elliptic_curve_exchange_algorithm_supported( + algorithm, self.curve + ) + ): + raise UnsupportedAlgorithm( + "This backend does not support the ECDH algorithm.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + if peer_public_key.curve.name != self.curve.name: + raise ValueError( + "peer_public_key and self are not on the same curve" + ) + + return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) + + def public_key(self) -> ec.EllipticCurvePublicKey: + group = self._backend._lib.EC_KEY_get0_group(self._ec_key) + self._backend.openssl_assert(group != self._backend._ffi.NULL) + + curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group) + public_ec_key = self._backend._ec_key_new_by_curve_nid(curve_nid) + + point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) + self._backend.openssl_assert(point != self._backend._ffi.NULL) + + res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point) + self._backend.openssl_assert(res == 1) + + evp_pkey = self._backend._ec_cdata_to_evp_pkey(public_ec_key) + + return _EllipticCurvePublicKey(self._backend, public_ec_key, evp_pkey) + + def private_numbers(self) -> ec.EllipticCurvePrivateNumbers: + bn = self._backend._lib.EC_KEY_get0_private_key(self._ec_key) + private_value = self._backend._bn_to_int(bn) + return ec.EllipticCurvePrivateNumbers( + private_value=private_value, + public_numbers=self.public_key().public_numbers(), + ) + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self, + self._evp_pkey, + self._ec_key, + ) + + def sign( + self, + data: bytes, + signature_algorithm: ec.EllipticCurveSignatureAlgorithm, + ) -> bytes: + _check_signature_algorithm(signature_algorithm) + data, _ = _calculate_digest_and_algorithm( + data, + signature_algorithm.algorithm, + ) + return _ecdsa_sig_sign(self._backend, self, data) + + +class _EllipticCurvePublicKey(ec.EllipticCurvePublicKey): + def __init__(self, backend: "Backend", ec_key_cdata, evp_pkey): + self._backend = backend + self._ec_key = ec_key_cdata + self._evp_pkey = evp_pkey + + sn = _ec_key_curve_sn(backend, ec_key_cdata) + self._curve = _sn_to_elliptic_curve(backend, sn) + _mark_asn1_named_ec_curve(backend, ec_key_cdata) + _check_key_infinity(backend, ec_key_cdata) + + @property + def curve(self) -> ec.EllipticCurve: + return self._curve + + @property + def key_size(self) -> int: + return self.curve.key_size + + def public_numbers(self) -> ec.EllipticCurvePublicNumbers: + get_func, group = self._backend._ec_key_determine_group_get_func( + self._ec_key + ) + point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) + self._backend.openssl_assert(point != self._backend._ffi.NULL) + + with self._backend._tmp_bn_ctx() as bn_ctx: + bn_x = self._backend._lib.BN_CTX_get(bn_ctx) + bn_y = self._backend._lib.BN_CTX_get(bn_ctx) + + res = get_func(group, point, bn_x, bn_y, bn_ctx) + self._backend.openssl_assert(res == 1) + + x = self._backend._bn_to_int(bn_x) + y = self._backend._bn_to_int(bn_y) + + return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve) + + def _encode_point(self, format: serialization.PublicFormat) -> bytes: + if format is serialization.PublicFormat.CompressedPoint: + conversion = self._backend._lib.POINT_CONVERSION_COMPRESSED + else: + assert format is serialization.PublicFormat.UncompressedPoint + conversion = self._backend._lib.POINT_CONVERSION_UNCOMPRESSED + + group = self._backend._lib.EC_KEY_get0_group(self._ec_key) + self._backend.openssl_assert(group != self._backend._ffi.NULL) + point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) + self._backend.openssl_assert(point != self._backend._ffi.NULL) + with self._backend._tmp_bn_ctx() as bn_ctx: + buflen = self._backend._lib.EC_POINT_point2oct( + group, point, conversion, self._backend._ffi.NULL, 0, bn_ctx + ) + self._backend.openssl_assert(buflen > 0) + buf = self._backend._ffi.new("char[]", buflen) + res = self._backend._lib.EC_POINT_point2oct( + group, point, conversion, buf, buflen, bn_ctx + ) + self._backend.openssl_assert(buflen == res) + + return self._backend._ffi.buffer(buf)[:] + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + if ( + encoding is serialization.Encoding.X962 + or format is serialization.PublicFormat.CompressedPoint + or format is serialization.PublicFormat.UncompressedPoint + ): + if encoding is not serialization.Encoding.X962 or format not in ( + serialization.PublicFormat.CompressedPoint, + serialization.PublicFormat.UncompressedPoint, + ): + raise ValueError( + "X962 encoding must be used with CompressedPoint or " + "UncompressedPoint format" + ) + + return self._encode_point(format) + else: + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def verify( + self, + signature: bytes, + data: bytes, + signature_algorithm: ec.EllipticCurveSignatureAlgorithm, + ) -> None: + _check_signature_algorithm(signature_algorithm) + data, _ = _calculate_digest_and_algorithm( + data, + signature_algorithm.algorithm, + ) + _ecdsa_sig_verify(self._backend, self, signature, data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py new file mode 100644 index 000000000..6f393e5b6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py @@ -0,0 +1,155 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import exceptions +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + _ED25519_KEY_SIZE, + _ED25519_SIG_SIZE, + Ed25519PrivateKey, + Ed25519PublicKey, +) + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +class _Ed25519PublicKey(Ed25519PublicKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw + or format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self) -> bytes: + buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] + + def verify(self, signature: bytes, data: bytes) -> None: + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestVerifyInit( + evp_md_ctx, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._evp_pkey, + ) + self._backend.openssl_assert(res == 1) + res = self._backend._lib.EVP_DigestVerify( + evp_md_ctx, signature, len(signature), data, len(data) + ) + if res != 1: + self._backend._consume_errors() + raise exceptions.InvalidSignature + + +class _Ed25519PrivateKey(Ed25519PrivateKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self) -> Ed25519PublicKey: + buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) + public_bytes = self._backend._ffi.buffer(buf)[:] + return self._backend.ed25519_load_public_bytes(public_bytes) + + def sign(self, data: bytes) -> bytes: + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestSignInit( + evp_md_ctx, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._evp_pkey, + ) + self._backend.openssl_assert(res == 1) + buf = self._backend._ffi.new("unsigned char[]", _ED25519_SIG_SIZE) + buflen = self._backend._ffi.new("size_t *", len(buf)) + res = self._backend._lib.EVP_DigestSign( + evp_md_ctx, buf, buflen, data, len(data) + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_SIG_SIZE) + return self._backend._ffi.buffer(buf, buflen[0])[:] + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PrivateFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw + or encoding is not serialization.Encoding.Raw + or not isinstance( + encryption_algorithm, serialization.NoEncryption + ) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self) -> bytes: + buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py new file mode 100644 index 000000000..0d27ea638 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py @@ -0,0 +1,156 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import exceptions +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.ed448 import ( + Ed448PrivateKey, + Ed448PublicKey, +) + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + +_ED448_KEY_SIZE = 57 +_ED448_SIG_SIZE = 114 + + +class _Ed448PublicKey(Ed448PublicKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw + or format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self) -> bytes: + buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] + + def verify(self, signature: bytes, data: bytes) -> None: + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestVerifyInit( + evp_md_ctx, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._evp_pkey, + ) + self._backend.openssl_assert(res == 1) + res = self._backend._lib.EVP_DigestVerify( + evp_md_ctx, signature, len(signature), data, len(data) + ) + if res != 1: + self._backend._consume_errors() + raise exceptions.InvalidSignature + + +class _Ed448PrivateKey(Ed448PrivateKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self) -> Ed448PublicKey: + buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) + public_bytes = self._backend._ffi.buffer(buf)[:] + return self._backend.ed448_load_public_bytes(public_bytes) + + def sign(self, data: bytes) -> bytes: + evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() + self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) + evp_md_ctx = self._backend._ffi.gc( + evp_md_ctx, self._backend._lib.EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestSignInit( + evp_md_ctx, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._evp_pkey, + ) + self._backend.openssl_assert(res == 1) + buf = self._backend._ffi.new("unsigned char[]", _ED448_SIG_SIZE) + buflen = self._backend._ffi.new("size_t *", len(buf)) + res = self._backend._lib.EVP_DigestSign( + evp_md_ctx, buf, buflen, data, len(data) + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_SIG_SIZE) + return self._backend._ffi.buffer(buf, buflen[0])[:] + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PrivateFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw + or encoding is not serialization.Encoding.Raw + or not isinstance( + encryption_algorithm, serialization.NoEncryption + ) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self) -> bytes: + buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py new file mode 100644 index 000000000..52d4646a7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py @@ -0,0 +1,86 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import hashes + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +class _HashContext(hashes.HashContext): + def __init__( + self, backend: "Backend", algorithm: hashes.HashAlgorithm, ctx=None + ) -> None: + self._algorithm = algorithm + + self._backend = backend + + if ctx is None: + ctx = self._backend._lib.EVP_MD_CTX_new() + ctx = self._backend._ffi.gc( + ctx, self._backend._lib.EVP_MD_CTX_free + ) + evp_md = self._backend._evp_md_from_algorithm(algorithm) + if evp_md == self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "{} is not a supported hash on this backend.".format( + algorithm.name + ), + _Reasons.UNSUPPORTED_HASH, + ) + res = self._backend._lib.EVP_DigestInit_ex( + ctx, evp_md, self._backend._ffi.NULL + ) + self._backend.openssl_assert(res != 0) + + self._ctx = ctx + + @property + def algorithm(self) -> hashes.HashAlgorithm: + return self._algorithm + + def copy(self) -> "_HashContext": + copied_ctx = self._backend._lib.EVP_MD_CTX_new() + copied_ctx = self._backend._ffi.gc( + copied_ctx, self._backend._lib.EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) + self._backend.openssl_assert(res != 0) + return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) + + def update(self, data: bytes) -> None: + data_ptr = self._backend._ffi.from_buffer(data) + res = self._backend._lib.EVP_DigestUpdate( + self._ctx, data_ptr, len(data) + ) + self._backend.openssl_assert(res != 0) + + def finalize(self) -> bytes: + if isinstance(self.algorithm, hashes.ExtendableOutputFunction): + # extendable output functions use a different finalize + return self._finalize_xof() + else: + buf = self._backend._ffi.new( + "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE + ) + outlen = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) + self._backend.openssl_assert(res != 0) + self._backend.openssl_assert( + outlen[0] == self.algorithm.digest_size + ) + return self._backend._ffi.buffer(buf)[: outlen[0]] + + def _finalize_xof(self) -> bytes: + buf = self._backend._ffi.new( + "unsigned char[]", self.algorithm.digest_size + ) + res = self._backend._lib.EVP_DigestFinalXOF( + self._ctx, buf, self.algorithm.digest_size + ) + self._backend.openssl_assert(res != 0) + return self._backend._ffi.buffer(buf)[: self.algorithm.digest_size] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py new file mode 100644 index 000000000..ba3dfb53f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py @@ -0,0 +1,84 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import ( + InvalidSignature, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.primitives import constant_time, hashes + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +class _HMACContext(hashes.HashContext): + def __init__( + self, + backend: "Backend", + key: bytes, + algorithm: hashes.HashAlgorithm, + ctx=None, + ): + self._algorithm = algorithm + self._backend = backend + + if ctx is None: + ctx = self._backend._lib.HMAC_CTX_new() + self._backend.openssl_assert(ctx != self._backend._ffi.NULL) + ctx = self._backend._ffi.gc(ctx, self._backend._lib.HMAC_CTX_free) + evp_md = self._backend._evp_md_from_algorithm(algorithm) + if evp_md == self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "{} is not a supported hash on this backend".format( + algorithm.name + ), + _Reasons.UNSUPPORTED_HASH, + ) + key_ptr = self._backend._ffi.from_buffer(key) + res = self._backend._lib.HMAC_Init_ex( + ctx, key_ptr, len(key), evp_md, self._backend._ffi.NULL + ) + self._backend.openssl_assert(res != 0) + + self._ctx = ctx + self._key = key + + @property + def algorithm(self) -> hashes.HashAlgorithm: + return self._algorithm + + def copy(self) -> "_HMACContext": + copied_ctx = self._backend._lib.HMAC_CTX_new() + self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL) + copied_ctx = self._backend._ffi.gc( + copied_ctx, self._backend._lib.HMAC_CTX_free + ) + res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx) + self._backend.openssl_assert(res != 0) + return _HMACContext( + self._backend, self._key, self.algorithm, ctx=copied_ctx + ) + + def update(self, data: bytes) -> None: + data_ptr = self._backend._ffi.from_buffer(data) + res = self._backend._lib.HMAC_Update(self._ctx, data_ptr, len(data)) + self._backend.openssl_assert(res != 0) + + def finalize(self) -> bytes: + buf = self._backend._ffi.new( + "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE + ) + outlen = self._backend._ffi.new("unsigned int *") + res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen) + self._backend.openssl_assert(res != 0) + self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) + return self._backend._ffi.buffer(buf)[: outlen[0]] + + def verify(self, signature: bytes) -> None: + digest = self.finalize() + if not constant_time.bytes_eq(digest, signature): + raise InvalidSignature("Signature did not match digest.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py new file mode 100644 index 000000000..d0d44f6fd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py @@ -0,0 +1,67 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives import constant_time + +_POLY1305_TAG_SIZE = 16 +_POLY1305_KEY_SIZE = 32 + + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +class _Poly1305Context: + def __init__(self, backend: "Backend", key: bytes) -> None: + self._backend = backend + + key_ptr = self._backend._ffi.from_buffer(key) + # This function copies the key into OpenSSL-owned memory so we don't + # need to retain it ourselves + evp_pkey = self._backend._lib.EVP_PKEY_new_raw_private_key( + self._backend._lib.NID_poly1305, + self._backend._ffi.NULL, + key_ptr, + len(key), + ) + self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) + self._evp_pkey = self._backend._ffi.gc( + evp_pkey, self._backend._lib.EVP_PKEY_free + ) + ctx = self._backend._lib.EVP_MD_CTX_new() + self._backend.openssl_assert(ctx != self._backend._ffi.NULL) + self._ctx = self._backend._ffi.gc( + ctx, self._backend._lib.EVP_MD_CTX_free + ) + res = self._backend._lib.EVP_DigestSignInit( + self._ctx, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + self._evp_pkey, + ) + self._backend.openssl_assert(res == 1) + + def update(self, data: bytes) -> None: + data_ptr = self._backend._ffi.from_buffer(data) + res = self._backend._lib.EVP_DigestSignUpdate( + self._ctx, data_ptr, len(data) + ) + self._backend.openssl_assert(res != 0) + + def finalize(self) -> bytes: + buf = self._backend._ffi.new("unsigned char[]", _POLY1305_TAG_SIZE) + outlen = self._backend._ffi.new("size_t *", _POLY1305_TAG_SIZE) + res = self._backend._lib.EVP_DigestSignFinal(self._ctx, buf, outlen) + self._backend.openssl_assert(res != 0) + self._backend.openssl_assert(outlen[0] == _POLY1305_TAG_SIZE) + return self._backend._ffi.buffer(buf)[: outlen[0]] + + def verify(self, tag: bytes) -> None: + mac = self.finalize() + if not constant_time.bytes_eq(mac, tag): + raise InvalidSignature("Value did not match computed tag.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py new file mode 100644 index 000000000..e18bab3ff --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py @@ -0,0 +1,588 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import threading +import typing + +from cryptography.exceptions import ( + InvalidSignature, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.backends.openssl.utils import ( + _calculate_digest_and_algorithm, +) +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils +from cryptography.hazmat.primitives.asymmetric.padding import ( + MGF1, + OAEP, + PSS, + AsymmetricPadding, + PKCS1v15, + _Auto, + _DigestLength, + _MaxLength, + calculate_max_pss_salt_length, +) +from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, + RSAPrivateNumbers, + RSAPublicKey, + RSAPublicNumbers, +) + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +def _get_rsa_pss_salt_length( + backend: "Backend", + pss: PSS, + key: typing.Union[RSAPrivateKey, RSAPublicKey], + hash_algorithm: hashes.HashAlgorithm, +) -> int: + salt = pss._salt_length + + if isinstance(salt, _MaxLength): + return calculate_max_pss_salt_length(key, hash_algorithm) + elif isinstance(salt, _DigestLength): + return hash_algorithm.digest_size + elif isinstance(salt, _Auto): + if isinstance(key, RSAPrivateKey): + raise ValueError( + "PSS salt length can only be set to AUTO when verifying" + ) + return backend._lib.RSA_PSS_SALTLEN_AUTO + else: + return salt + + +def _enc_dec_rsa( + backend: "Backend", + key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"], + data: bytes, + padding: AsymmetricPadding, +) -> bytes: + if not isinstance(padding, AsymmetricPadding): + raise TypeError("Padding must be an instance of AsymmetricPadding.") + + if isinstance(padding, PKCS1v15): + padding_enum = backend._lib.RSA_PKCS1_PADDING + elif isinstance(padding, OAEP): + padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING + + if not isinstance(padding._mgf, MGF1): + raise UnsupportedAlgorithm( + "Only MGF1 is supported by this backend.", + _Reasons.UNSUPPORTED_MGF, + ) + + if not backend.rsa_padding_supported(padding): + raise UnsupportedAlgorithm( + "This combination of padding and hash algorithm is not " + "supported by this backend.", + _Reasons.UNSUPPORTED_PADDING, + ) + + else: + raise UnsupportedAlgorithm( + "{} is not supported by this backend.".format(padding.name), + _Reasons.UNSUPPORTED_PADDING, + ) + + return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding) + + +def _enc_dec_rsa_pkey_ctx( + backend: "Backend", + key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"], + data: bytes, + padding_enum: int, + padding: AsymmetricPadding, +) -> bytes: + init: typing.Callable[[typing.Any], int] + crypt: typing.Callable[[typing.Any, typing.Any, int, bytes, int], int] + if isinstance(key, _RSAPublicKey): + init = backend._lib.EVP_PKEY_encrypt_init + crypt = backend._lib.EVP_PKEY_encrypt + else: + init = backend._lib.EVP_PKEY_decrypt_init + crypt = backend._lib.EVP_PKEY_decrypt + + pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL) + backend.openssl_assert(pkey_ctx != backend._ffi.NULL) + pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) + res = init(pkey_ctx) + backend.openssl_assert(res == 1) + res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) + backend.openssl_assert(res > 0) + buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey) + backend.openssl_assert(buf_size > 0) + if isinstance(padding, OAEP): + mgf1_md = backend._evp_md_non_null_from_algorithm( + padding._mgf._algorithm + ) + res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md) + backend.openssl_assert(res > 0) + oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm) + res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md) + backend.openssl_assert(res > 0) + + if ( + isinstance(padding, OAEP) + and padding._label is not None + and len(padding._label) > 0 + ): + # set0_rsa_oaep_label takes ownership of the char * so we need to + # copy it into some new memory + labelptr = backend._lib.OPENSSL_malloc(len(padding._label)) + backend.openssl_assert(labelptr != backend._ffi.NULL) + backend._ffi.memmove(labelptr, padding._label, len(padding._label)) + res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label( + pkey_ctx, labelptr, len(padding._label) + ) + backend.openssl_assert(res == 1) + + outlen = backend._ffi.new("size_t *", buf_size) + buf = backend._ffi.new("unsigned char[]", buf_size) + # Everything from this line onwards is written with the goal of being as + # constant-time as is practical given the constraints of Python and our + # API. See Bleichenbacher's '98 attack on RSA, and its many many variants. + # As such, you should not attempt to change this (particularly to "clean it + # up") without understanding why it was written this way (see + # Chesterton's Fence), and without measuring to verify you have not + # introduced observable time differences. + res = crypt(pkey_ctx, buf, outlen, data, len(data)) + resbuf = backend._ffi.buffer(buf)[: outlen[0]] + backend._lib.ERR_clear_error() + if res <= 0: + raise ValueError("Encryption/decryption failed.") + return resbuf + + +def _rsa_sig_determine_padding( + backend: "Backend", + key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"], + padding: AsymmetricPadding, + algorithm: typing.Optional[hashes.HashAlgorithm], +) -> int: + if not isinstance(padding, AsymmetricPadding): + raise TypeError("Expected provider of AsymmetricPadding.") + + pkey_size = backend._lib.EVP_PKEY_size(key._evp_pkey) + backend.openssl_assert(pkey_size > 0) + + if isinstance(padding, PKCS1v15): + # Hash algorithm is ignored for PKCS1v15-padding, may be None. + padding_enum = backend._lib.RSA_PKCS1_PADDING + elif isinstance(padding, PSS): + if not isinstance(padding._mgf, MGF1): + raise UnsupportedAlgorithm( + "Only MGF1 is supported by this backend.", + _Reasons.UNSUPPORTED_MGF, + ) + + # PSS padding requires a hash algorithm + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + # Size of key in bytes - 2 is the maximum + # PSS signature length (salt length is checked later) + if pkey_size - algorithm.digest_size - 2 < 0: + raise ValueError( + "Digest too large for key size. Use a larger " + "key or different digest." + ) + + padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING + else: + raise UnsupportedAlgorithm( + "{} is not supported by this backend.".format(padding.name), + _Reasons.UNSUPPORTED_PADDING, + ) + + return padding_enum + + +# Hash algorithm can be absent (None) to initialize the context without setting +# any message digest algorithm. This is currently only valid for the PKCS1v15 +# padding type, where it means that the signature data is encoded/decoded +# as provided, without being wrapped in a DigestInfo structure. +def _rsa_sig_setup( + backend: "Backend", + padding: AsymmetricPadding, + algorithm: typing.Optional[hashes.HashAlgorithm], + key: typing.Union["_RSAPublicKey", "_RSAPrivateKey"], + init_func: typing.Callable[[typing.Any], int], +): + padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm) + pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL) + backend.openssl_assert(pkey_ctx != backend._ffi.NULL) + pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) + res = init_func(pkey_ctx) + if res != 1: + errors = backend._consume_errors() + raise ValueError("Unable to sign/verify with this key", errors) + + if algorithm is not None: + evp_md = backend._evp_md_non_null_from_algorithm(algorithm) + res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md) + if res <= 0: + backend._consume_errors() + raise UnsupportedAlgorithm( + "{} is not supported by this backend for RSA signing.".format( + algorithm.name + ), + _Reasons.UNSUPPORTED_HASH, + ) + res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) + if res <= 0: + backend._consume_errors() + raise UnsupportedAlgorithm( + "{} is not supported for the RSA signature operation.".format( + padding.name + ), + _Reasons.UNSUPPORTED_PADDING, + ) + if isinstance(padding, PSS): + assert isinstance(algorithm, hashes.HashAlgorithm) + res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( + pkey_ctx, + _get_rsa_pss_salt_length(backend, padding, key, algorithm), + ) + backend.openssl_assert(res > 0) + + mgf1_md = backend._evp_md_non_null_from_algorithm( + padding._mgf._algorithm + ) + res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md) + backend.openssl_assert(res > 0) + + return pkey_ctx + + +def _rsa_sig_sign( + backend: "Backend", + padding: AsymmetricPadding, + algorithm: hashes.HashAlgorithm, + private_key: "_RSAPrivateKey", + data: bytes, +) -> bytes: + pkey_ctx = _rsa_sig_setup( + backend, + padding, + algorithm, + private_key, + backend._lib.EVP_PKEY_sign_init, + ) + buflen = backend._ffi.new("size_t *") + res = backend._lib.EVP_PKEY_sign( + pkey_ctx, backend._ffi.NULL, buflen, data, len(data) + ) + backend.openssl_assert(res == 1) + buf = backend._ffi.new("unsigned char[]", buflen[0]) + res = backend._lib.EVP_PKEY_sign(pkey_ctx, buf, buflen, data, len(data)) + if res != 1: + errors = backend._consume_errors_with_text() + raise ValueError( + "Digest or salt length too long for key size. Use a larger key " + "or shorter salt length if you are specifying a PSS salt", + errors, + ) + + return backend._ffi.buffer(buf)[:] + + +def _rsa_sig_verify( + backend: "Backend", + padding: AsymmetricPadding, + algorithm: hashes.HashAlgorithm, + public_key: "_RSAPublicKey", + signature: bytes, + data: bytes, +) -> None: + pkey_ctx = _rsa_sig_setup( + backend, + padding, + algorithm, + public_key, + backend._lib.EVP_PKEY_verify_init, + ) + res = backend._lib.EVP_PKEY_verify( + pkey_ctx, signature, len(signature), data, len(data) + ) + # The previous call can return negative numbers in the event of an + # error. This is not a signature failure but we need to fail if it + # occurs. + backend.openssl_assert(res >= 0) + if res == 0: + backend._consume_errors() + raise InvalidSignature + + +def _rsa_sig_recover( + backend: "Backend", + padding: AsymmetricPadding, + algorithm: typing.Optional[hashes.HashAlgorithm], + public_key: "_RSAPublicKey", + signature: bytes, +) -> bytes: + pkey_ctx = _rsa_sig_setup( + backend, + padding, + algorithm, + public_key, + backend._lib.EVP_PKEY_verify_recover_init, + ) + + # Attempt to keep the rest of the code in this function as constant/time + # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the + # buflen parameter is used even though its value may be undefined in the + # error case. Due to the tolerant nature of Python slicing this does not + # trigger any exceptions. + maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey) + backend.openssl_assert(maxlen > 0) + buf = backend._ffi.new("unsigned char[]", maxlen) + buflen = backend._ffi.new("size_t *", maxlen) + res = backend._lib.EVP_PKEY_verify_recover( + pkey_ctx, buf, buflen, signature, len(signature) + ) + resbuf = backend._ffi.buffer(buf)[: buflen[0]] + backend._lib.ERR_clear_error() + # Assume that all parameter errors are handled during the setup phase and + # any error here is due to invalid signature. + if res != 1: + raise InvalidSignature + return resbuf + + +class _RSAPrivateKey(RSAPrivateKey): + _evp_pkey: object + _rsa_cdata: object + _key_size: int + + def __init__( + self, + backend: "Backend", + rsa_cdata, + evp_pkey, + *, + unsafe_skip_rsa_key_validation: bool, + ): + res: int + # RSA_check_key is slower in OpenSSL 3.0.0 due to improved + # primality checking. In normal use this is unlikely to be a problem + # since users don't load new keys constantly, but for TESTING we've + # added an init arg that allows skipping the checks. You should not + # use this in production code unless you understand the consequences. + if not unsafe_skip_rsa_key_validation: + res = backend._lib.RSA_check_key(rsa_cdata) + if res != 1: + errors = backend._consume_errors_with_text() + raise ValueError("Invalid private key", errors) + # 2 is prime and passes an RSA key check, so we also check + # if p and q are odd just to be safe. + p = backend._ffi.new("BIGNUM **") + q = backend._ffi.new("BIGNUM **") + backend._lib.RSA_get0_factors(rsa_cdata, p, q) + backend.openssl_assert(p[0] != backend._ffi.NULL) + backend.openssl_assert(q[0] != backend._ffi.NULL) + p_odd = backend._lib.BN_is_odd(p[0]) + q_odd = backend._lib.BN_is_odd(q[0]) + if p_odd != 1 or q_odd != 1: + errors = backend._consume_errors_with_text() + raise ValueError("Invalid private key", errors) + + self._backend = backend + self._rsa_cdata = rsa_cdata + self._evp_pkey = evp_pkey + # Used for lazy blinding + self._blinded = False + self._blinding_lock = threading.Lock() + + n = self._backend._ffi.new("BIGNUM **") + self._backend._lib.RSA_get0_key( + self._rsa_cdata, + n, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + ) + self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) + self._key_size = self._backend._lib.BN_num_bits(n[0]) + + def _enable_blinding(self) -> None: + # If you call blind on an already blinded RSA key OpenSSL will turn + # it off and back on, which is a performance hit we want to avoid. + if not self._blinded: + with self._blinding_lock: + self._non_threadsafe_enable_blinding() + + def _non_threadsafe_enable_blinding(self) -> None: + # This is only a separate function to allow for testing to cover both + # branches. It should never be invoked except through _enable_blinding. + # Check if it's not True again in case another thread raced past the + # first non-locked check. + if not self._blinded: + res = self._backend._lib.RSA_blinding_on( + self._rsa_cdata, self._backend._ffi.NULL + ) + self._backend.openssl_assert(res == 1) + self._blinded = True + + @property + def key_size(self) -> int: + return self._key_size + + def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: + self._enable_blinding() + key_size_bytes = (self.key_size + 7) // 8 + if key_size_bytes != len(ciphertext): + raise ValueError("Ciphertext length must be equal to key size.") + + return _enc_dec_rsa(self._backend, self, ciphertext, padding) + + def public_key(self) -> RSAPublicKey: + ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata) + self._backend.openssl_assert(ctx != self._backend._ffi.NULL) + ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) + evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx) + return _RSAPublicKey(self._backend, ctx, evp_pkey) + + def private_numbers(self) -> RSAPrivateNumbers: + n = self._backend._ffi.new("BIGNUM **") + e = self._backend._ffi.new("BIGNUM **") + d = self._backend._ffi.new("BIGNUM **") + p = self._backend._ffi.new("BIGNUM **") + q = self._backend._ffi.new("BIGNUM **") + dmp1 = self._backend._ffi.new("BIGNUM **") + dmq1 = self._backend._ffi.new("BIGNUM **") + iqmp = self._backend._ffi.new("BIGNUM **") + self._backend._lib.RSA_get0_key(self._rsa_cdata, n, e, d) + self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(e[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(d[0] != self._backend._ffi.NULL) + self._backend._lib.RSA_get0_factors(self._rsa_cdata, p, q) + self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) + self._backend._lib.RSA_get0_crt_params( + self._rsa_cdata, dmp1, dmq1, iqmp + ) + self._backend.openssl_assert(dmp1[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(dmq1[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(iqmp[0] != self._backend._ffi.NULL) + return RSAPrivateNumbers( + p=self._backend._bn_to_int(p[0]), + q=self._backend._bn_to_int(q[0]), + d=self._backend._bn_to_int(d[0]), + dmp1=self._backend._bn_to_int(dmp1[0]), + dmq1=self._backend._bn_to_int(dmq1[0]), + iqmp=self._backend._bn_to_int(iqmp[0]), + public_numbers=RSAPublicNumbers( + e=self._backend._bn_to_int(e[0]), + n=self._backend._bn_to_int(n[0]), + ), + ) + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + return self._backend._private_key_bytes( + encoding, + format, + encryption_algorithm, + self, + self._evp_pkey, + self._rsa_cdata, + ) + + def sign( + self, + data: bytes, + padding: AsymmetricPadding, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> bytes: + self._enable_blinding() + data, algorithm = _calculate_digest_and_algorithm(data, algorithm) + return _rsa_sig_sign(self._backend, padding, algorithm, self, data) + + +class _RSAPublicKey(RSAPublicKey): + _evp_pkey: object + _rsa_cdata: object + _key_size: int + + def __init__(self, backend: "Backend", rsa_cdata, evp_pkey): + self._backend = backend + self._rsa_cdata = rsa_cdata + self._evp_pkey = evp_pkey + + n = self._backend._ffi.new("BIGNUM **") + self._backend._lib.RSA_get0_key( + self._rsa_cdata, + n, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + ) + self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) + self._key_size = self._backend._lib.BN_num_bits(n[0]) + + @property + def key_size(self) -> int: + return self._key_size + + def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: + return _enc_dec_rsa(self._backend, self, plaintext, padding) + + def public_numbers(self) -> RSAPublicNumbers: + n = self._backend._ffi.new("BIGNUM **") + e = self._backend._ffi.new("BIGNUM **") + self._backend._lib.RSA_get0_key( + self._rsa_cdata, n, e, self._backend._ffi.NULL + ) + self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) + self._backend.openssl_assert(e[0] != self._backend._ffi.NULL) + return RSAPublicNumbers( + e=self._backend._bn_to_int(e[0]), + n=self._backend._bn_to_int(n[0]), + ) + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, self._rsa_cdata + ) + + def verify( + self, + signature: bytes, + data: bytes, + padding: AsymmetricPadding, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> None: + data, algorithm = _calculate_digest_and_algorithm(data, algorithm) + _rsa_sig_verify( + self._backend, padding, algorithm, self, signature, data + ) + + def recover_data_from_signature( + self, + signature: bytes, + padding: AsymmetricPadding, + algorithm: typing.Optional[hashes.HashAlgorithm], + ) -> bytes: + if isinstance(algorithm, asym_utils.Prehashed): + raise TypeError( + "Prehashed is only supported in the sign and verify methods. " + "It cannot be used with recover_data_from_signature." + ) + return _rsa_sig_recover( + self._backend, padding, algorithm, self, signature + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py new file mode 100644 index 000000000..3a70a5818 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py @@ -0,0 +1,52 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.utils import Prehashed + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +def _evp_pkey_derive(backend: "Backend", evp_pkey, peer_public_key) -> bytes: + ctx = backend._lib.EVP_PKEY_CTX_new(evp_pkey, backend._ffi.NULL) + backend.openssl_assert(ctx != backend._ffi.NULL) + ctx = backend._ffi.gc(ctx, backend._lib.EVP_PKEY_CTX_free) + res = backend._lib.EVP_PKEY_derive_init(ctx) + backend.openssl_assert(res == 1) + res = backend._lib.EVP_PKEY_derive_set_peer(ctx, peer_public_key._evp_pkey) + backend.openssl_assert(res == 1) + keylen = backend._ffi.new("size_t *") + res = backend._lib.EVP_PKEY_derive(ctx, backend._ffi.NULL, keylen) + backend.openssl_assert(res == 1) + backend.openssl_assert(keylen[0] > 0) + buf = backend._ffi.new("unsigned char[]", keylen[0]) + res = backend._lib.EVP_PKEY_derive(ctx, buf, keylen) + if res != 1: + errors_with_text = backend._consume_errors_with_text() + raise ValueError("Error computing shared key.", errors_with_text) + + return backend._ffi.buffer(buf, keylen[0])[:] + + +def _calculate_digest_and_algorithm( + data: bytes, + algorithm: typing.Union[Prehashed, hashes.HashAlgorithm], +) -> typing.Tuple[bytes, hashes.HashAlgorithm]: + if not isinstance(algorithm, Prehashed): + hash_ctx = hashes.Hash(algorithm) + hash_ctx.update(data) + data = hash_ctx.finalize() + else: + algorithm = algorithm._algorithm + + if len(data) != algorithm.digest_size: + raise ValueError( + "The provided data must be the same length as the hash " + "algorithm's digest size." + ) + + return (data, algorithm) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py new file mode 100644 index 000000000..e3b41eced --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py @@ -0,0 +1,132 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.x25519 import ( + X25519PrivateKey, + X25519PublicKey, +) + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + + +_X25519_KEY_SIZE = 32 + + +class _X25519PublicKey(X25519PublicKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw + or format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self) -> bytes: + ucharpp = self._backend._ffi.new("unsigned char **") + res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint( + self._evp_pkey, ucharpp + ) + self._backend.openssl_assert(res == 32) + self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL) + data = self._backend._ffi.gc( + ucharpp[0], self._backend._lib.OPENSSL_free + ) + return self._backend._ffi.buffer(data, res)[:] + + +class _X25519PrivateKey(X25519PrivateKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self) -> X25519PublicKey: + bio = self._backend._create_mem_bio_gc() + res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey) + self._backend.openssl_assert(res == 1) + evp_pkey = self._backend._lib.d2i_PUBKEY_bio( + bio, self._backend._ffi.NULL + ) + self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) + evp_pkey = self._backend._ffi.gc( + evp_pkey, self._backend._lib.EVP_PKEY_free + ) + return _X25519PublicKey(self._backend, evp_pkey) + + def exchange(self, peer_public_key: X25519PublicKey) -> bytes: + if not isinstance(peer_public_key, X25519PublicKey): + raise TypeError("peer_public_key must be X25519PublicKey.") + + return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PrivateFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw + or encoding is not serialization.Encoding.Raw + or not isinstance( + encryption_algorithm, serialization.NoEncryption + ) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self) -> bytes: + # If/when LibreSSL adds support for EVP_PKEY_get_raw_private_key we + # can switch to it (Cryptography_HAS_RAW_KEY) + # The trick we use here is serializing to a PKCS8 key and just + # using the last 32 bytes, which is the key itself. + bio = self._backend._create_mem_bio_gc() + res = self._backend._lib.i2d_PKCS8PrivateKey_bio( + bio, + self._evp_pkey, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + 0, + self._backend._ffi.NULL, + self._backend._ffi.NULL, + ) + self._backend.openssl_assert(res == 1) + pkcs8 = self._backend._read_mem_bio(bio) + self._backend.openssl_assert(len(pkcs8) == 48) + return pkcs8[-_X25519_KEY_SIZE:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py new file mode 100644 index 000000000..d738188c7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py @@ -0,0 +1,117 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.x448 import ( + X448PrivateKey, + X448PublicKey, +) + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.backend import Backend + +_X448_KEY_SIZE = 56 + + +class _X448PublicKey(X448PublicKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PublicFormat, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PublicFormat.Raw + ): + if ( + encoding is not serialization.Encoding.Raw + or format is not serialization.PublicFormat.Raw + ): + raise ValueError( + "When using Raw both encoding and format must be Raw" + ) + + return self._raw_public_bytes() + + return self._backend._public_key_bytes( + encoding, format, self, self._evp_pkey, None + ) + + def _raw_public_bytes(self) -> bytes: + buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:] + + +class _X448PrivateKey(X448PrivateKey): + def __init__(self, backend: "Backend", evp_pkey): + self._backend = backend + self._evp_pkey = evp_pkey + + def public_key(self) -> X448PublicKey: + buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_public_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) + public_bytes = self._backend._ffi.buffer(buf)[:] + return self._backend.x448_load_public_bytes(public_bytes) + + def exchange(self, peer_public_key: X448PublicKey) -> bytes: + if not isinstance(peer_public_key, X448PublicKey): + raise TypeError("peer_public_key must be X448PublicKey.") + + return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) + + def private_bytes( + self, + encoding: serialization.Encoding, + format: serialization.PrivateFormat, + encryption_algorithm: serialization.KeySerializationEncryption, + ) -> bytes: + if ( + encoding is serialization.Encoding.Raw + or format is serialization.PrivateFormat.Raw + ): + if ( + format is not serialization.PrivateFormat.Raw + or encoding is not serialization.Encoding.Raw + or not isinstance( + encryption_algorithm, serialization.NoEncryption + ) + ): + raise ValueError( + "When using Raw both encoding and format must be Raw " + "and encryption_algorithm must be NoEncryption()" + ) + + return self._raw_private_bytes() + + return self._backend._private_key_bytes( + encoding, format, encryption_algorithm, self, self._evp_pkey, None + ) + + def _raw_private_bytes(self) -> bytes: + buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) + buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) + res = self._backend._lib.EVP_PKEY_get_raw_private_key( + self._evp_pkey, buf, buflen + ) + self._backend.openssl_assert(res == 1) + self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) + return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py new file mode 100644 index 000000000..b50933623 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyd b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyd new file mode 100644 index 0000000000000000000000000000000000000000..d76347c3887a0b69c982c8e4dd18078c6da3166c GIT binary patch literal 3964416 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%0AaU4?dK@lyDHnmGevv4ych;of(BQj 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

    9$rCkj}Q+PYxhbx%4o}nEGp{DuY4*!|aR}gKk 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�HoZ{oM+{t(7KLnBHR${OWft`fY&0D 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ℑ1V1RKNlkvq6PZlQ`O#o&J6F;E9eqldAD@6( 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 Y*PoD4w!36%VmP4L-wWt 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

    7d>ZG)Wb6VfuxfUK<#VgpZN4@AgNT1^RU=4q+- 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

    6Z8KQUd=hBN0%u5|@-dQ4dqx@)s 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

    odWN$*-{UdB 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�^9J&1 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

    OTBq)qWf+7&_SWS3xH@4uQo%U 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

      !Hj^jK#r_M259c182iQ+p@mT`I8O* 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 z
      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 literal 0 HcmV?d00001 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi new file mode 100644 index 000000000..d7642fcc4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi @@ -0,0 +1,34 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import types +import typing + +def check_pkcs7_padding(data: bytes) -> bool: ... +def check_ansix923_padding(data: bytes) -> bool: ... + +class ObjectIdentifier: + def __init__(self, val: str) -> None: ... + @property + def dotted_string(self) -> str: ... + @property + def _name(self) -> str: ... + +T = typing.TypeVar("T") + +class FixedPool(typing.Generic[T]): + def __init__( + self, + create: typing.Callable[[], T], + ) -> None: ... + def acquire(self) -> "PoolAcquisition[T]": ... + +class PoolAcquisition(typing.Generic[T]): + def __enter__(self) -> T: ... + def __exit__( + self, + exc_type: typing.Optional[typing.Type[BaseException]], + exc_value: typing.Optional[BaseException], + exc_tb: typing.Optional[types.TracebackType], + ) -> None: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi new file mode 100644 index 000000000..a8369ba83 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi @@ -0,0 +1,16 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +class TestCertificate: + not_after_tag: int + not_before_tag: int + issuer_value_tags: typing.List[int] + subject_value_tags: typing.List[int] + +def decode_dss_signature(signature: bytes) -> typing.Tuple[int, int]: ... +def encode_dss_signature(r: int, s: int) -> bytes: ... +def parse_spki_for_data(data: bytes) -> bytes: ... +def test_parse_certificate(data: bytes) -> TestCertificate: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi new file mode 100644 index 000000000..47a037ade --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi @@ -0,0 +1,25 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES +from cryptography.x509.ocsp import ( + OCSPRequest, + OCSPRequestBuilder, + OCSPResponse, + OCSPResponseBuilder, + OCSPResponseStatus, +) + +def load_der_ocsp_request(data: bytes) -> OCSPRequest: ... +def load_der_ocsp_response(data: bytes) -> OCSPResponse: ... +def create_ocsp_request(builder: OCSPRequestBuilder) -> OCSPRequest: ... +def create_ocsp_response( + status: OCSPResponseStatus, + builder: typing.Optional[OCSPResponseBuilder], + private_key: typing.Optional[PRIVATE_KEY_TYPES], + hash_algorithm: typing.Optional[hashes.HashAlgorithm], +) -> OCSPResponse: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi new file mode 100644 index 000000000..66bd85098 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi @@ -0,0 +1,15 @@ +import typing + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import pkcs7 + +def serialize_certificates( + certs: typing.List[x509.Certificate], + encoding: serialization.Encoding, +) -> bytes: ... +def sign_and_serialize( + builder: pkcs7.PKCS7SignatureBuilder, + encoding: serialization.Encoding, + options: typing.Iterable[pkcs7.PKCS7Options], +) -> bytes: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi new file mode 100644 index 000000000..1bbde8005 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi @@ -0,0 +1,42 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES + +def load_pem_x509_certificate(data: bytes) -> x509.Certificate: ... +def load_pem_x509_certificates( + data: bytes, +) -> typing.List[x509.Certificate]: ... +def load_der_x509_certificate(data: bytes) -> x509.Certificate: ... +def load_pem_x509_crl(data: bytes) -> x509.CertificateRevocationList: ... +def load_der_x509_crl(data: bytes) -> x509.CertificateRevocationList: ... +def load_pem_x509_csr(data: bytes) -> x509.CertificateSigningRequest: ... +def load_der_x509_csr(data: bytes) -> x509.CertificateSigningRequest: ... +def encode_name_bytes(name: x509.Name) -> bytes: ... +def encode_extension_value(extension: x509.ExtensionType) -> bytes: ... +def create_x509_certificate( + builder: x509.CertificateBuilder, + private_key: PRIVATE_KEY_TYPES, + hash_algorithm: typing.Optional[hashes.HashAlgorithm], +) -> x509.Certificate: ... +def create_x509_csr( + builder: x509.CertificateSigningRequestBuilder, + private_key: PRIVATE_KEY_TYPES, + hash_algorithm: typing.Optional[hashes.HashAlgorithm], +) -> x509.CertificateSigningRequest: ... +def create_x509_crl( + builder: x509.CertificateRevocationListBuilder, + private_key: PRIVATE_KEY_TYPES, + hash_algorithm: typing.Optional[hashes.HashAlgorithm], +) -> x509.CertificateRevocationList: ... + +class Sct: ... +class Certificate: ... +class RevokedCertificate: ... +class CertificateRevocationList: ... +class CertificateSigningRequest: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py new file mode 100644 index 000000000..b50933623 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py new file mode 100644 index 000000000..7903a9bb4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py @@ -0,0 +1,360 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + + +def cryptography_has_ec2m() -> typing.List[str]: + return [ + "EC_POINT_get_affine_coordinates_GF2m", + ] + + +def cryptography_has_ssl3_method() -> typing.List[str]: + return [ + "SSLv3_method", + "SSLv3_client_method", + "SSLv3_server_method", + ] + + +def cryptography_has_set_cert_cb() -> typing.List[str]: + return [ + "SSL_CTX_set_cert_cb", + "SSL_set_cert_cb", + ] + + +def cryptography_has_ssl_st() -> typing.List[str]: + return [ + "SSL_ST_BEFORE", + "SSL_ST_OK", + "SSL_ST_INIT", + "SSL_ST_RENEGOTIATE", + ] + + +def cryptography_has_tls_st() -> typing.List[str]: + return [ + "TLS_ST_BEFORE", + "TLS_ST_OK", + ] + + +def cryptography_has_evp_pkey_set_alias_type() -> typing.List[str]: + return [ + "EVP_PKEY_set_alias_type", + ] + + +def cryptography_has_scrypt() -> typing.List[str]: + return [ + "EVP_PBE_scrypt", + ] + + +def cryptography_has_evp_pkey_dhx() -> typing.List[str]: + return [ + "EVP_PKEY_DHX", + "d2i_DHxparams_bio", + "i2d_DHxparams_bio", + ] + + +def cryptography_has_mem_functions() -> typing.List[str]: + return [ + "Cryptography_CRYPTO_set_mem_functions", + ] + + +def cryptography_has_x509_store_ctx_get_issuer() -> typing.List[str]: + return [ + "X509_STORE_set_get_issuer", + ] + + +def cryptography_has_ed448() -> typing.List[str]: + return [ + "EVP_PKEY_ED448", + "NID_ED448", + ] + + +def cryptography_has_ed25519() -> typing.List[str]: + return [ + "NID_ED25519", + "EVP_PKEY_ED25519", + ] + + +def cryptography_has_poly1305() -> typing.List[str]: + return [ + "NID_poly1305", + "EVP_PKEY_POLY1305", + ] + + +def cryptography_has_evp_digestfinal_xof() -> typing.List[str]: + return [ + "EVP_DigestFinalXOF", + ] + + +def cryptography_has_evp_pkey_get_set_tls_encodedpoint() -> typing.List[str]: + return [ + "EVP_PKEY_get1_tls_encodedpoint", + "EVP_PKEY_set1_tls_encodedpoint", + ] + + +def cryptography_has_fips() -> typing.List[str]: + return [ + "FIPS_mode_set", + "FIPS_mode", + ] + + +def cryptography_has_ssl_sigalgs() -> typing.List[str]: + return [ + "SSL_CTX_set1_sigalgs_list", + ] + + +def cryptography_has_psk() -> typing.List[str]: + return [ + "SSL_CTX_use_psk_identity_hint", + "SSL_CTX_set_psk_server_callback", + "SSL_CTX_set_psk_client_callback", + ] + + +def cryptography_has_psk_tlsv13() -> typing.List[str]: + return [ + "SSL_CTX_set_psk_find_session_callback", + "SSL_CTX_set_psk_use_session_callback", + "Cryptography_SSL_SESSION_new", + "SSL_CIPHER_find", + "SSL_SESSION_set1_master_key", + "SSL_SESSION_set_cipher", + "SSL_SESSION_set_protocol_version", + ] + + +def cryptography_has_custom_ext() -> typing.List[str]: + return [ + "SSL_CTX_add_client_custom_ext", + "SSL_CTX_add_server_custom_ext", + "SSL_extension_supported", + ] + + +def cryptography_has_openssl_cleanup() -> typing.List[str]: + return [ + "OPENSSL_cleanup", + ] + + +def cryptography_has_tlsv13_functions() -> typing.List[str]: + return [ + "SSL_VERIFY_POST_HANDSHAKE", + "SSL_CTX_set_ciphersuites", + "SSL_verify_client_post_handshake", + "SSL_CTX_set_post_handshake_auth", + "SSL_set_post_handshake_auth", + "SSL_SESSION_get_max_early_data", + "SSL_write_early_data", + "SSL_read_early_data", + "SSL_CTX_set_max_early_data", + ] + + +def cryptography_has_raw_key() -> typing.List[str]: + return [ + "EVP_PKEY_new_raw_private_key", + "EVP_PKEY_new_raw_public_key", + "EVP_PKEY_get_raw_private_key", + "EVP_PKEY_get_raw_public_key", + ] + + +def cryptography_has_engine() -> typing.List[str]: + return [ + "ENGINE_by_id", + "ENGINE_init", + "ENGINE_finish", + "ENGINE_get_default_RAND", + "ENGINE_set_default_RAND", + "ENGINE_unregister_RAND", + "ENGINE_ctrl_cmd", + "ENGINE_free", + "ENGINE_get_name", + "Cryptography_add_osrandom_engine", + "ENGINE_ctrl_cmd_string", + "ENGINE_load_builtin_engines", + "ENGINE_load_private_key", + "ENGINE_load_public_key", + "SSL_CTX_set_client_cert_engine", + ] + + +def cryptography_has_verified_chain() -> typing.List[str]: + return [ + "SSL_get0_verified_chain", + ] + + +def cryptography_has_srtp() -> typing.List[str]: + return [ + "SSL_CTX_set_tlsext_use_srtp", + "SSL_set_tlsext_use_srtp", + "SSL_get_selected_srtp_profile", + ] + + +def cryptography_has_providers() -> typing.List[str]: + return [ + "OSSL_PROVIDER_load", + "OSSL_PROVIDER_unload", + "ERR_LIB_PROV", + "PROV_R_WRONG_FINAL_BLOCK_LENGTH", + "PROV_R_BAD_DECRYPT", + ] + + +def cryptography_has_op_no_renegotiation() -> typing.List[str]: + return [ + "SSL_OP_NO_RENEGOTIATION", + ] + + +def cryptography_has_dtls_get_data_mtu() -> typing.List[str]: + return [ + "DTLS_get_data_mtu", + ] + + +def cryptography_has_300_fips() -> typing.List[str]: + return [ + "EVP_default_properties_is_fips_enabled", + "EVP_default_properties_enable_fips", + ] + + +def cryptography_has_ssl_cookie() -> typing.List[str]: + return [ + "SSL_OP_COOKIE_EXCHANGE", + "DTLSv1_listen", + "SSL_CTX_set_cookie_generate_cb", + "SSL_CTX_set_cookie_verify_cb", + ] + + +def cryptography_has_pkcs7_funcs() -> typing.List[str]: + return [ + "SMIME_write_PKCS7", + "PEM_write_bio_PKCS7_stream", + "PKCS7_sign_add_signer", + "PKCS7_final", + "PKCS7_verify", + "SMIME_read_PKCS7", + "PKCS7_get0_signers", + ] + + +def cryptography_has_bn_flags() -> typing.List[str]: + return [ + "BN_FLG_CONSTTIME", + "BN_set_flags", + "BN_prime_checks_for_size", + ] + + +def cryptography_has_evp_pkey_dh() -> typing.List[str]: + return [ + "EVP_PKEY_set1_DH", + ] + + +def cryptography_has_300_evp_cipher() -> typing.List[str]: + return ["EVP_CIPHER_fetch", "EVP_CIPHER_free"] + + +def cryptography_has_unexpected_eof_while_reading() -> typing.List[str]: + return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"] + + +def cryptography_has_pkcs12_set_mac() -> typing.List[str]: + return ["PKCS12_set_mac"] + + +def cryptography_has_ssl_op_ignore_unexpected_eof() -> typing.List[str]: + return [ + "SSL_OP_IGNORE_UNEXPECTED_EOF", + ] + + +def cryptography_has_get_extms_support() -> typing.List[str]: + return ["SSL_get_extms_support"] + + +# This is a mapping of +# {condition: function-returning-names-dependent-on-that-condition} so we can +# loop over them and delete unsupported names at runtime. It will be removed +# when cffi supports #if in cdef. We use functions instead of just a dict of +# lists so we can use coverage to measure which are used. +CONDITIONAL_NAMES = { + "Cryptography_HAS_EC2M": cryptography_has_ec2m, + "Cryptography_HAS_SSL3_METHOD": cryptography_has_ssl3_method, + "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, + "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, + "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, + "Cryptography_HAS_EVP_PKEY_set_alias_type": ( + cryptography_has_evp_pkey_set_alias_type + ), + "Cryptography_HAS_SCRYPT": cryptography_has_scrypt, + "Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx, + "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions, + "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": ( + cryptography_has_x509_store_ctx_get_issuer + ), + "Cryptography_HAS_ED448": cryptography_has_ed448, + "Cryptography_HAS_ED25519": cryptography_has_ed25519, + "Cryptography_HAS_POLY1305": cryptography_has_poly1305, + "Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint": ( + cryptography_has_evp_pkey_get_set_tls_encodedpoint + ), + "Cryptography_HAS_FIPS": cryptography_has_fips, + "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs, + "Cryptography_HAS_PSK": cryptography_has_psk, + "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13, + "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, + "Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup, + "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions, + "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key, + "Cryptography_HAS_EVP_DIGESTFINAL_XOF": ( + cryptography_has_evp_digestfinal_xof + ), + "Cryptography_HAS_ENGINE": cryptography_has_engine, + "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, + "Cryptography_HAS_SRTP": cryptography_has_srtp, + "Cryptography_HAS_PROVIDERS": cryptography_has_providers, + "Cryptography_HAS_OP_NO_RENEGOTIATION": ( + cryptography_has_op_no_renegotiation + ), + "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, + "Cryptography_HAS_300_FIPS": cryptography_has_300_fips, + "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie, + "Cryptography_HAS_PKCS7_FUNCS": cryptography_has_pkcs7_funcs, + "Cryptography_HAS_BN_FLAGS": cryptography_has_bn_flags, + "Cryptography_HAS_EVP_PKEY_DH": cryptography_has_evp_pkey_dh, + "Cryptography_HAS_300_EVP_CIPHER": cryptography_has_300_evp_cipher, + "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": ( + cryptography_has_unexpected_eof_while_reading + ), + "Cryptography_HAS_PKCS12_SET_MAC": cryptography_has_pkcs12_set_mac, + "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": ( + cryptography_has_ssl_op_ignore_unexpected_eof + ), + "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support, +} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py new file mode 100644 index 000000000..a1602164d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py @@ -0,0 +1,244 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import os +import sys +import threading +import types +import typing +import warnings + +import cryptography +from cryptography.exceptions import InternalError +from cryptography.hazmat.bindings._openssl import ffi, lib +from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES + +_OpenSSLErrorWithText = typing.NamedTuple( + "_OpenSSLErrorWithText", + [("code", int), ("lib", int), ("reason", int), ("reason_text", bytes)], +) + + +class _OpenSSLError: + def __init__(self, code: int, lib: int, reason: int): + self._code = code + self._lib = lib + self._reason = reason + + def _lib_reason_match(self, lib: int, reason: int) -> bool: + return lib == self.lib and reason == self.reason + + @property + def code(self) -> int: + return self._code + + @property + def lib(self) -> int: + return self._lib + + @property + def reason(self) -> int: + return self._reason + + +def _consume_errors(lib) -> typing.List[_OpenSSLError]: + errors = [] + while True: + code: int = lib.ERR_get_error() + if code == 0: + break + + err_lib: int = lib.ERR_GET_LIB(code) + err_reason: int = lib.ERR_GET_REASON(code) + + errors.append(_OpenSSLError(code, err_lib, err_reason)) + + return errors + + +def _errors_with_text( + errors: typing.List[_OpenSSLError], +) -> typing.List[_OpenSSLErrorWithText]: + errors_with_text = [] + for err in errors: + buf = ffi.new("char[]", 256) + lib.ERR_error_string_n(err.code, buf, len(buf)) + err_text_reason: bytes = ffi.string(buf) + + errors_with_text.append( + _OpenSSLErrorWithText( + err.code, err.lib, err.reason, err_text_reason + ) + ) + + return errors_with_text + + +def _consume_errors_with_text(lib): + return _errors_with_text(_consume_errors(lib)) + + +def _openssl_assert( + lib, ok: bool, errors: typing.Optional[typing.List[_OpenSSLError]] = None +) -> None: + if not ok: + if errors is None: + errors = _consume_errors(lib) + errors_with_text = _errors_with_text(errors) + + raise InternalError( + "Unknown OpenSSL error. This error is commonly encountered when " + "another library is not cleaning up the OpenSSL error stack. If " + "you are using cryptography with another library that uses " + "OpenSSL try disabling it before reporting a bug. Otherwise " + "please file an issue at https://github.com/pyca/cryptography/" + "issues with information on how to reproduce " + "this. ({0!r})".format(errors_with_text), + errors_with_text, + ) + + +def _legacy_provider_error(loaded: bool) -> None: + if not loaded: + raise RuntimeError( + "OpenSSL 3.0's legacy provider failed to load. This is a fatal " + "error by default, but cryptography supports running without " + "legacy algorithms by setting the environment variable " + "CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error," + " you have likely made a mistake with your OpenSSL configuration." + ) + + +def build_conditional_library( + lib: typing.Any, + conditional_names: typing.Dict[str, typing.Callable[[], typing.List[str]]], +) -> typing.Any: + conditional_lib = types.ModuleType("lib") + conditional_lib._original_lib = lib # type: ignore[attr-defined] + excluded_names = set() + for condition, names_cb in conditional_names.items(): + if not getattr(lib, condition): + excluded_names.update(names_cb()) + + for attr in dir(lib): + if attr not in excluded_names: + setattr(conditional_lib, attr, getattr(lib, attr)) + + return conditional_lib + + +class Binding: + """ + OpenSSL API wrapper. + """ + + lib: typing.ClassVar = None + ffi = ffi + _lib_loaded = False + _init_lock = threading.Lock() + _legacy_provider: typing.Any = ffi.NULL + _legacy_provider_loaded = False + _default_provider: typing.Any = ffi.NULL + + def __init__(self) -> None: + self._ensure_ffi_initialized() + + def _enable_fips(self) -> None: + # This function enables FIPS mode for OpenSSL 3.0.0 on installs that + # have the FIPS provider installed properly. + _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER) + self._base_provider = self.lib.OSSL_PROVIDER_load( + self.ffi.NULL, b"base" + ) + _openssl_assert(self.lib, self._base_provider != self.ffi.NULL) + self.lib._fips_provider = self.lib.OSSL_PROVIDER_load( + self.ffi.NULL, b"fips" + ) + _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL) + + res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1) + _openssl_assert(self.lib, res == 1) + + @classmethod + def _register_osrandom_engine(cls) -> None: + # Clear any errors extant in the queue before we start. In many + # scenarios other things may be interacting with OpenSSL in the same + # process space and it has proven untenable to assume that they will + # reliably clear the error queue. Once we clear it here we will + # error on any subsequent unexpected item in the stack. + cls.lib.ERR_clear_error() + if cls.lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: + result = cls.lib.Cryptography_add_osrandom_engine() + _openssl_assert(cls.lib, result in (1, 2)) + + @classmethod + def _ensure_ffi_initialized(cls) -> None: + with cls._init_lock: + if not cls._lib_loaded: + cls.lib = build_conditional_library(lib, CONDITIONAL_NAMES) + cls._lib_loaded = True + cls._register_osrandom_engine() + # As of OpenSSL 3.0.0 we must register a legacy cipher provider + # to get RC2 (needed for junk asymmetric private key + # serialization), RC4, Blowfish, IDEA, SEED, etc. These things + # are ugly legacy, but we aren't going to get rid of them + # any time soon. + if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: + if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"): + cls._legacy_provider = cls.lib.OSSL_PROVIDER_load( + cls.ffi.NULL, b"legacy" + ) + cls._legacy_provider_loaded = ( + cls._legacy_provider != cls.ffi.NULL + ) + _legacy_provider_error(cls._legacy_provider_loaded) + + cls._default_provider = cls.lib.OSSL_PROVIDER_load( + cls.ffi.NULL, b"default" + ) + _openssl_assert( + cls.lib, cls._default_provider != cls.ffi.NULL + ) + + @classmethod + def init_static_locks(cls) -> None: + cls._ensure_ffi_initialized() + + +def _verify_package_version(version: str) -> None: + # Occasionally we run into situations where the version of the Python + # package does not match the version of the shared object that is loaded. + # This may occur in environments where multiple versions of cryptography + # are installed and available in the python path. To avoid errors cropping + # up later this code checks that the currently imported package and the + # shared object that were loaded have the same version and raise an + # ImportError if they do not + so_package_version = ffi.string(lib.CRYPTOGRAPHY_PACKAGE_VERSION) + if version.encode("ascii") != so_package_version: + raise ImportError( + "The version of cryptography does not match the loaded " + "shared object. This can happen if you have multiple copies of " + "cryptography installed in your Python path. Please try creating " + "a new virtual environment to resolve this issue. " + "Loaded python version: {}, shared object version: {}".format( + version, so_package_version + ) + ) + + +_verify_package_version(cryptography.__version__) + +Binding.init_static_locks() + +if ( + sys.platform == "win32" + and os.environ.get("PROCESSOR_ARCHITEW6432") is not None +): + warnings.warn( + "You are using cryptography on a 32-bit Python on a 64-bit Windows " + "Operating System. Cryptography will be significantly faster if you " + "switch to using a 64-bit Python.", + UserWarning, + stacklevel=2, + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py new file mode 100644 index 000000000..b50933623 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py new file mode 100644 index 000000000..fb815a0e9 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py @@ -0,0 +1,17 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import abc + +# This exists to break an import cycle. It is normally accessible from the +# asymmetric padding module. + + +class AsymmetricPadding(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this padding (e.g. "PSS", "PKCS1"). + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py new file mode 100644 index 000000000..138a104e2 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py @@ -0,0 +1,43 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import abc +import typing + +# This exists to break an import cycle. It is normally accessible from the +# ciphers module. + + +class CipherAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this mode (e.g. "AES", "Camellia"). + """ + + @property + @abc.abstractmethod + def key_sizes(self) -> typing.FrozenSet[int]: + """ + Valid key sizes for this algorithm in bits + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The size of the key being used as an integer in bits (e.g. 128, 256). + """ + + +class BlockCipherAlgorithm(metaclass=abc.ABCMeta): + key: bytes + + @property + @abc.abstractmethod + def block_size(self) -> int: + """ + The size of a block as an integer in bits (e.g. 64, 128). + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py new file mode 100644 index 000000000..fddb4c85e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py @@ -0,0 +1,168 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import abc +import typing + +from cryptography import utils +from cryptography.hazmat.primitives.hashes import HashAlgorithm + +# This exists to break an import cycle. These classes are normally accessible +# from the serialization module. + + +class PBES(utils.Enum): + PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" + PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" + + +class Encoding(utils.Enum): + PEM = "PEM" + DER = "DER" + OpenSSH = "OpenSSH" + Raw = "Raw" + X962 = "ANSI X9.62" + SMIME = "S/MIME" + + +class PrivateFormat(utils.Enum): + PKCS8 = "PKCS8" + TraditionalOpenSSL = "TraditionalOpenSSL" + Raw = "Raw" + OpenSSH = "OpenSSH" + PKCS12 = "PKCS12" + + def encryption_builder(self) -> "KeySerializationEncryptionBuilder": + if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12): + raise ValueError( + "encryption_builder only supported with PrivateFormat.OpenSSH" + " and PrivateFormat.PKCS12" + ) + return KeySerializationEncryptionBuilder(self) + + +class PublicFormat(utils.Enum): + SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" + PKCS1 = "Raw PKCS#1" + OpenSSH = "OpenSSH" + Raw = "Raw" + CompressedPoint = "X9.62 Compressed Point" + UncompressedPoint = "X9.62 Uncompressed Point" + + +class ParameterFormat(utils.Enum): + PKCS3 = "PKCS3" + + +class KeySerializationEncryption(metaclass=abc.ABCMeta): + pass + + +class BestAvailableEncryption(KeySerializationEncryption): + def __init__(self, password: bytes): + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + self.password = password + + +class NoEncryption(KeySerializationEncryption): + pass + + +class KeySerializationEncryptionBuilder(object): + def __init__( + self, + format: PrivateFormat, + *, + _kdf_rounds: typing.Optional[int] = None, + _hmac_hash: typing.Optional[HashAlgorithm] = None, + _key_cert_algorithm: typing.Optional[PBES] = None, + ) -> None: + self._format = format + + self._kdf_rounds = _kdf_rounds + self._hmac_hash = _hmac_hash + self._key_cert_algorithm = _key_cert_algorithm + + def kdf_rounds(self, rounds: int) -> "KeySerializationEncryptionBuilder": + if self._kdf_rounds is not None: + raise ValueError("kdf_rounds already set") + + if not isinstance(rounds, int): + raise TypeError("kdf_rounds must be an integer") + + if rounds < 1: + raise ValueError("kdf_rounds must be a positive integer") + + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def hmac_hash( + self, algorithm: HashAlgorithm + ) -> "KeySerializationEncryptionBuilder": + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "hmac_hash only supported with PrivateFormat.PKCS12" + ) + + if self._hmac_hash is not None: + raise ValueError("hmac_hash already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=algorithm, + _key_cert_algorithm=self._key_cert_algorithm, + ) + + def key_cert_algorithm( + self, algorithm: PBES + ) -> "KeySerializationEncryptionBuilder": + if self._format is not PrivateFormat.PKCS12: + raise TypeError( + "key_cert_algorithm only supported with " + "PrivateFormat.PKCS12" + ) + if self._key_cert_algorithm is not None: + raise ValueError("key_cert_algorithm already set") + return KeySerializationEncryptionBuilder( + self._format, + _kdf_rounds=self._kdf_rounds, + _hmac_hash=self._hmac_hash, + _key_cert_algorithm=algorithm, + ) + + def build(self, password: bytes) -> KeySerializationEncryption: + if not isinstance(password, bytes) or len(password) == 0: + raise ValueError("Password must be 1 or more bytes.") + + return _KeySerializationEncryption( + self._format, + password, + kdf_rounds=self._kdf_rounds, + hmac_hash=self._hmac_hash, + key_cert_algorithm=self._key_cert_algorithm, + ) + + +class _KeySerializationEncryption(KeySerializationEncryption): + def __init__( + self, + format: PrivateFormat, + password: bytes, + *, + kdf_rounds: typing.Optional[int], + hmac_hash: typing.Optional[HashAlgorithm], + key_cert_algorithm: typing.Optional[PBES], + ): + self._format = format + self.password = password + + self._kdf_rounds = kdf_rounds + self._hmac_hash = hmac_hash + self._key_cert_algorithm = key_cert_algorithm diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py new file mode 100644 index 000000000..b50933623 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py @@ -0,0 +1,3 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py new file mode 100644 index 000000000..33de0e551 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py @@ -0,0 +1,251 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography.hazmat.primitives import _serialization + +_MIN_MODULUS_SIZE = 512 + + +def generate_parameters( + generator: int, key_size: int, backend: typing.Any = None +) -> "DHParameters": + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.generate_dh_parameters(generator, key_size) + + +class DHParameterNumbers: + def __init__(self, p: int, g: int, q: typing.Optional[int] = None) -> None: + if not isinstance(p, int) or not isinstance(g, int): + raise TypeError("p and g must be integers") + if q is not None and not isinstance(q, int): + raise TypeError("q must be integer or None") + + if g < 2: + raise ValueError("DH generator must be 2 or greater") + + if p.bit_length() < _MIN_MODULUS_SIZE: + raise ValueError( + "p (modulus) must be at least {}-bit".format(_MIN_MODULUS_SIZE) + ) + + self._p = p + self._g = g + self._q = q + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DHParameterNumbers): + return NotImplemented + + return ( + self._p == other._p and self._g == other._g and self._q == other._q + ) + + def parameters(self, backend: typing.Any = None) -> "DHParameters": + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_dh_parameter_numbers(self) + + @property + def p(self) -> int: + return self._p + + @property + def g(self) -> int: + return self._g + + @property + def q(self) -> typing.Optional[int]: + return self._q + + +class DHPublicNumbers: + def __init__(self, y: int, parameter_numbers: DHParameterNumbers) -> None: + if not isinstance(y, int): + raise TypeError("y must be an integer.") + + if not isinstance(parameter_numbers, DHParameterNumbers): + raise TypeError( + "parameters must be an instance of DHParameterNumbers." + ) + + self._y = y + self._parameter_numbers = parameter_numbers + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DHPublicNumbers): + return NotImplemented + + return ( + self._y == other._y + and self._parameter_numbers == other._parameter_numbers + ) + + def public_key(self, backend: typing.Any = None) -> "DHPublicKey": + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_dh_public_numbers(self) + + @property + def y(self) -> int: + return self._y + + @property + def parameter_numbers(self) -> DHParameterNumbers: + return self._parameter_numbers + + +class DHPrivateNumbers: + def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: + if not isinstance(x, int): + raise TypeError("x must be an integer.") + + if not isinstance(public_numbers, DHPublicNumbers): + raise TypeError( + "public_numbers must be an instance of " "DHPublicNumbers." + ) + + self._x = x + self._public_numbers = public_numbers + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DHPrivateNumbers): + return NotImplemented + + return ( + self._x == other._x + and self._public_numbers == other._public_numbers + ) + + def private_key(self, backend: typing.Any = None) -> "DHPrivateKey": + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_dh_private_numbers(self) + + @property + def public_numbers(self) -> DHPublicNumbers: + return self._public_numbers + + @property + def x(self) -> int: + return self._x + + +class DHParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> "DHPrivateKey": + """ + Generates and returns a DHPrivateKey. + """ + + @abc.abstractmethod + def parameter_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.ParameterFormat, + ) -> bytes: + """ + Returns the parameters serialized as bytes. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> DHParameterNumbers: + """ + Returns a DHParameterNumbers. + """ + + +DHParametersWithSerialization = DHParameters + + +class DHPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> DHPublicNumbers: + """ + Returns a DHPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +DHPublicKeyWithSerialization = DHPublicKey + + +class DHPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> DHPublicKey: + """ + The DHPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DHParameters: + """ + The DHParameters object associated with this private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: DHPublicKey) -> bytes: + """ + Given peer's DHPublicKey, carry out the key exchange and + return shared key as bytes. + """ + + @abc.abstractmethod + def private_numbers(self) -> DHPrivateNumbers: + """ + Returns a DHPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +DHPrivateKeyWithSerialization = DHPrivateKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 000000000..6103d8093 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,288 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class DSAParameters(metaclass=abc.ABCMeta): + @abc.abstractmethod + def generate_private_key(self) -> "DSAPrivateKey": + """ + Generates and returns a DSAPrivateKey. + """ + + @abc.abstractmethod + def parameter_numbers(self) -> "DSAParameterNumbers": + """ + Returns a DSAParameterNumbers. + """ + + +DSAParametersWithNumbers = DSAParameters + + +class DSAPrivateKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def public_key(self) -> "DSAPublicKey": + """ + The DSAPublicKey associated with this private key. + """ + + @abc.abstractmethod + def parameters(self) -> DSAParameters: + """ + The DSAParameters object associated with this private key. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> bytes: + """ + Signs the data + """ + + @abc.abstractmethod + def private_numbers(self) -> "DSAPrivateNumbers": + """ + Returns a DSAPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +DSAPrivateKeyWithSerialization = DSAPrivateKey + + +class DSAPublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the prime modulus. + """ + + @abc.abstractmethod + def parameters(self) -> DSAParameters: + """ + The DSAParameters object associated with this public key. + """ + + @abc.abstractmethod + def public_numbers(self) -> "DSAPublicNumbers": + """ + Returns a DSAPublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> None: + """ + Verifies the signature of the data. + """ + + +DSAPublicKeyWithSerialization = DSAPublicKey + + +class DSAParameterNumbers: + def __init__(self, p: int, q: int, g: int): + if ( + not isinstance(p, int) + or not isinstance(q, int) + or not isinstance(g, int) + ): + raise TypeError( + "DSAParameterNumbers p, q, and g arguments must be integers." + ) + + self._p = p + self._q = q + self._g = g + + @property + def p(self) -> int: + return self._p + + @property + def q(self) -> int: + return self._q + + @property + def g(self) -> int: + return self._g + + def parameters(self, backend: typing.Any = None) -> DSAParameters: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_dsa_parameter_numbers(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DSAParameterNumbers): + return NotImplemented + + return self.p == other.p and self.q == other.q and self.g == other.g + + def __repr__(self) -> str: + return ( + "".format(self=self) + ) + + +class DSAPublicNumbers: + def __init__(self, y: int, parameter_numbers: DSAParameterNumbers): + if not isinstance(y, int): + raise TypeError("DSAPublicNumbers y argument must be an integer.") + + if not isinstance(parameter_numbers, DSAParameterNumbers): + raise TypeError( + "parameter_numbers must be a DSAParameterNumbers instance." + ) + + self._y = y + self._parameter_numbers = parameter_numbers + + @property + def y(self) -> int: + return self._y + + @property + def parameter_numbers(self) -> DSAParameterNumbers: + return self._parameter_numbers + + def public_key(self, backend: typing.Any = None) -> DSAPublicKey: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_dsa_public_numbers(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DSAPublicNumbers): + return NotImplemented + + return ( + self.y == other.y + and self.parameter_numbers == other.parameter_numbers + ) + + def __repr__(self) -> str: + return ( + "".format(self=self) + ) + + +class DSAPrivateNumbers: + def __init__(self, x: int, public_numbers: DSAPublicNumbers): + if not isinstance(x, int): + raise TypeError("DSAPrivateNumbers x argument must be an integer.") + + if not isinstance(public_numbers, DSAPublicNumbers): + raise TypeError( + "public_numbers must be a DSAPublicNumbers instance." + ) + self._public_numbers = public_numbers + self._x = x + + @property + def x(self) -> int: + return self._x + + @property + def public_numbers(self) -> DSAPublicNumbers: + return self._public_numbers + + def private_key(self, backend: typing.Any = None) -> DSAPrivateKey: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_dsa_private_numbers(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DSAPrivateNumbers): + return NotImplemented + + return ( + self.x == other.x and self.public_numbers == other.public_numbers + ) + + +def generate_parameters( + key_size: int, backend: typing.Any = None +) -> DSAParameters: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.generate_dsa_parameters(key_size) + + +def generate_private_key( + key_size: int, backend: typing.Any = None +) -> DSAPrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.generate_dsa_private_key_and_parameters(key_size) + + +def _check_dsa_parameters(parameters: DSAParameterNumbers) -> None: + if parameters.p.bit_length() not in [1024, 2048, 3072, 4096]: + raise ValueError( + "p must be exactly 1024, 2048, 3072, or 4096 bits long" + ) + if parameters.q.bit_length() not in [160, 224, 256]: + raise ValueError("q must be exactly 160, 224, or 256 bits long") + + if not (1 < parameters.g < parameters.p): + raise ValueError("g, p don't satisfy 1 < g < p.") + + +def _check_dsa_private_numbers(numbers: DSAPrivateNumbers) -> None: + parameters = numbers.public_numbers.parameter_numbers + _check_dsa_parameters(parameters) + if numbers.x <= 0 or numbers.x >= parameters.q: + raise ValueError("x must be > 0 and < q.") + + if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p): + raise ValueError("y must be equal to (g ** x % p).") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py new file mode 100644 index 000000000..c5df2c27a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py @@ -0,0 +1,483 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography import utils +from cryptography.hazmat._oid import ObjectIdentifier +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class EllipticCurveOID: + SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1") + SECP224R1 = ObjectIdentifier("1.3.132.0.33") + SECP256K1 = ObjectIdentifier("1.3.132.0.10") + SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7") + SECP384R1 = ObjectIdentifier("1.3.132.0.34") + SECP521R1 = ObjectIdentifier("1.3.132.0.35") + BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") + BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") + BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") + SECT163K1 = ObjectIdentifier("1.3.132.0.1") + SECT163R2 = ObjectIdentifier("1.3.132.0.15") + SECT233K1 = ObjectIdentifier("1.3.132.0.26") + SECT233R1 = ObjectIdentifier("1.3.132.0.27") + SECT283K1 = ObjectIdentifier("1.3.132.0.16") + SECT283R1 = ObjectIdentifier("1.3.132.0.17") + SECT409K1 = ObjectIdentifier("1.3.132.0.36") + SECT409R1 = ObjectIdentifier("1.3.132.0.37") + SECT571K1 = ObjectIdentifier("1.3.132.0.38") + SECT571R1 = ObjectIdentifier("1.3.132.0.39") + + +class EllipticCurve(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + The name of the curve. e.g. secp256r1. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + +class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def algorithm( + self, + ) -> typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm]: + """ + The digest algorithm used with this signature. + """ + + +class EllipticCurvePrivateKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def exchange( + self, algorithm: "ECDH", peer_public_key: "EllipticCurvePublicKey" + ) -> bytes: + """ + Performs a key exchange operation using the provided algorithm with the + provided peer's public key. + """ + + @abc.abstractmethod + def public_key(self) -> "EllipticCurvePublicKey": + """ + The EllipticCurvePublicKey for this private key. + """ + + @property + @abc.abstractmethod + def curve(self) -> EllipticCurve: + """ + The EllipticCurve that this key is on. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + signature_algorithm: EllipticCurveSignatureAlgorithm, + ) -> bytes: + """ + Signs the data + """ + + @abc.abstractmethod + def private_numbers(self) -> "EllipticCurvePrivateNumbers": + """ + Returns an EllipticCurvePrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey + + +class EllipticCurvePublicKey(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def curve(self) -> EllipticCurve: + """ + The EllipticCurve that this key is on. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + Bit size of a secret scalar for the curve. + """ + + @abc.abstractmethod + def public_numbers(self) -> "EllipticCurvePublicNumbers": + """ + Returns an EllipticCurvePublicNumbers. + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + signature_algorithm: EllipticCurveSignatureAlgorithm, + ) -> None: + """ + Verifies the signature of the data. + """ + + @classmethod + def from_encoded_point( + cls, curve: EllipticCurve, data: bytes + ) -> "EllipticCurvePublicKey": + utils._check_bytes("data", data) + + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must be an EllipticCurve instance") + + if len(data) == 0: + raise ValueError("data must not be an empty byte string") + + if data[0] not in [0x02, 0x03, 0x04]: + raise ValueError("Unsupported elliptic curve point type") + + from cryptography.hazmat.backends.openssl.backend import backend + + return backend.load_elliptic_curve_public_bytes(curve, data) + + +EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey + + +class SECT571R1(EllipticCurve): + name = "sect571r1" + key_size = 570 + + +class SECT409R1(EllipticCurve): + name = "sect409r1" + key_size = 409 + + +class SECT283R1(EllipticCurve): + name = "sect283r1" + key_size = 283 + + +class SECT233R1(EllipticCurve): + name = "sect233r1" + key_size = 233 + + +class SECT163R2(EllipticCurve): + name = "sect163r2" + key_size = 163 + + +class SECT571K1(EllipticCurve): + name = "sect571k1" + key_size = 571 + + +class SECT409K1(EllipticCurve): + name = "sect409k1" + key_size = 409 + + +class SECT283K1(EllipticCurve): + name = "sect283k1" + key_size = 283 + + +class SECT233K1(EllipticCurve): + name = "sect233k1" + key_size = 233 + + +class SECT163K1(EllipticCurve): + name = "sect163k1" + key_size = 163 + + +class SECP521R1(EllipticCurve): + name = "secp521r1" + key_size = 521 + + +class SECP384R1(EllipticCurve): + name = "secp384r1" + key_size = 384 + + +class SECP256R1(EllipticCurve): + name = "secp256r1" + key_size = 256 + + +class SECP256K1(EllipticCurve): + name = "secp256k1" + key_size = 256 + + +class SECP224R1(EllipticCurve): + name = "secp224r1" + key_size = 224 + + +class SECP192R1(EllipticCurve): + name = "secp192r1" + key_size = 192 + + +class BrainpoolP256R1(EllipticCurve): + name = "brainpoolP256r1" + key_size = 256 + + +class BrainpoolP384R1(EllipticCurve): + name = "brainpoolP384r1" + key_size = 384 + + +class BrainpoolP512R1(EllipticCurve): + name = "brainpoolP512r1" + key_size = 512 + + +_CURVE_TYPES: typing.Dict[str, typing.Type[EllipticCurve]] = { + "prime192v1": SECP192R1, + "prime256v1": SECP256R1, + "secp192r1": SECP192R1, + "secp224r1": SECP224R1, + "secp256r1": SECP256R1, + "secp384r1": SECP384R1, + "secp521r1": SECP521R1, + "secp256k1": SECP256K1, + "sect163k1": SECT163K1, + "sect233k1": SECT233K1, + "sect283k1": SECT283K1, + "sect409k1": SECT409K1, + "sect571k1": SECT571K1, + "sect163r2": SECT163R2, + "sect233r1": SECT233R1, + "sect283r1": SECT283R1, + "sect409r1": SECT409R1, + "sect571r1": SECT571R1, + "brainpoolP256r1": BrainpoolP256R1, + "brainpoolP384r1": BrainpoolP384R1, + "brainpoolP512r1": BrainpoolP512R1, +} + + +class ECDSA(EllipticCurveSignatureAlgorithm): + def __init__( + self, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ): + self._algorithm = algorithm + + @property + def algorithm( + self, + ) -> typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm]: + return self._algorithm + + +def generate_private_key( + curve: EllipticCurve, backend: typing.Any = None +) -> EllipticCurvePrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.generate_elliptic_curve_private_key(curve) + + +def derive_private_key( + private_value: int, + curve: EllipticCurve, + backend: typing.Any = None, +) -> EllipticCurvePrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + if not isinstance(private_value, int): + raise TypeError("private_value must be an integer type.") + + if private_value <= 0: + raise ValueError("private_value must be a positive integer.") + + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must provide the EllipticCurve interface.") + + return ossl.derive_elliptic_curve_private_key(private_value, curve) + + +class EllipticCurvePublicNumbers: + def __init__(self, x: int, y: int, curve: EllipticCurve): + if not isinstance(x, int) or not isinstance(y, int): + raise TypeError("x and y must be integers.") + + if not isinstance(curve, EllipticCurve): + raise TypeError("curve must provide the EllipticCurve interface.") + + self._y = y + self._x = x + self._curve = curve + + def public_key(self, backend: typing.Any = None) -> EllipticCurvePublicKey: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_elliptic_curve_public_numbers(self) + + @property + def curve(self) -> EllipticCurve: + return self._curve + + @property + def x(self) -> int: + return self._x + + @property + def y(self) -> int: + return self._y + + def __eq__(self, other: object) -> bool: + if not isinstance(other, EllipticCurvePublicNumbers): + return NotImplemented + + return ( + self.x == other.x + and self.y == other.y + and self.curve.name == other.curve.name + and self.curve.key_size == other.curve.key_size + ) + + def __hash__(self) -> int: + return hash((self.x, self.y, self.curve.name, self.curve.key_size)) + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + +class EllipticCurvePrivateNumbers: + def __init__( + self, private_value: int, public_numbers: EllipticCurvePublicNumbers + ): + if not isinstance(private_value, int): + raise TypeError("private_value must be an integer.") + + if not isinstance(public_numbers, EllipticCurvePublicNumbers): + raise TypeError( + "public_numbers must be an EllipticCurvePublicNumbers " + "instance." + ) + + self._private_value = private_value + self._public_numbers = public_numbers + + def private_key( + self, backend: typing.Any = None + ) -> EllipticCurvePrivateKey: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_elliptic_curve_private_numbers(self) + + @property + def private_value(self) -> int: + return self._private_value + + @property + def public_numbers(self) -> EllipticCurvePublicNumbers: + return self._public_numbers + + def __eq__(self, other: object) -> bool: + if not isinstance(other, EllipticCurvePrivateNumbers): + return NotImplemented + + return ( + self.private_value == other.private_value + and self.public_numbers == other.public_numbers + ) + + def __hash__(self) -> int: + return hash((self.private_value, self.public_numbers)) + + +class ECDH: + pass + + +_OID_TO_CURVE = { + EllipticCurveOID.SECP192R1: SECP192R1, + EllipticCurveOID.SECP224R1: SECP224R1, + EllipticCurveOID.SECP256K1: SECP256K1, + EllipticCurveOID.SECP256R1: SECP256R1, + EllipticCurveOID.SECP384R1: SECP384R1, + EllipticCurveOID.SECP521R1: SECP521R1, + EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, + EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, + EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, + EllipticCurveOID.SECT163K1: SECT163K1, + EllipticCurveOID.SECT163R2: SECT163R2, + EllipticCurveOID.SECT233K1: SECT233K1, + EllipticCurveOID.SECT233R1: SECT233R1, + EllipticCurveOID.SECT283K1: SECT283K1, + EllipticCurveOID.SECT283R1: SECT283R1, + EllipticCurveOID.SECT409K1: SECT409K1, + EllipticCurveOID.SECT409R1: SECT409R1, + EllipticCurveOID.SECT571K1: SECT571K1, + EllipticCurveOID.SECT571R1: SECT571R1, +} + + +def get_curve_for_oid(oid: ObjectIdentifier) -> typing.Type[EllipticCurve]: + try: + return _OID_TO_CURVE[oid] + except KeyError: + raise LookupError( + "The provided object identifier has no matching elliptic " + "curve class" + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py new file mode 100644 index 000000000..220bf592c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py @@ -0,0 +1,91 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import _serialization + +_ED25519_KEY_SIZE = 32 +_ED25519_SIG_SIZE = 64 + + +class Ed25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> "Ed25519PublicKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return backend.ed25519_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def verify(self, signature: bytes, data: bytes) -> None: + """ + Verify the signature. + """ + + +class Ed25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> "Ed25519PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return backend.ed25519_generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> "Ed25519PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed25519_supported(): + raise UnsupportedAlgorithm( + "ed25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return backend.ed25519_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> Ed25519PublicKey: + """ + The Ed25519PublicKey derived from the private key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def sign(self, data: bytes) -> bytes: + """ + Signs the data. + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py new file mode 100644 index 000000000..27bc27c69 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py @@ -0,0 +1,87 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import _serialization + + +class Ed448PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> "Ed448PublicKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return backend.ed448_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def verify(self, signature: bytes, data: bytes) -> None: + """ + Verify the signature. + """ + + +class Ed448PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> "Ed448PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + return backend.ed448_generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> "Ed448PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.ed448_supported(): + raise UnsupportedAlgorithm( + "ed448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, + ) + + return backend.ed448_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> Ed448PublicKey: + """ + The Ed448PublicKey derived from the private key. + """ + + @abc.abstractmethod + def sign(self, data: bytes) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py new file mode 100644 index 000000000..dd3c648f1 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py @@ -0,0 +1,101 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives._asymmetric import ( + AsymmetricPadding as AsymmetricPadding, +) +from cryptography.hazmat.primitives.asymmetric import rsa + + +class PKCS1v15(AsymmetricPadding): + name = "EMSA-PKCS1-v1_5" + + +class _MaxLength: + "Sentinel value for `MAX_LENGTH`." + + +class _Auto: + "Sentinel value for `AUTO`." + + +class _DigestLength: + "Sentinel value for `DIGEST_LENGTH`." + + +class PSS(AsymmetricPadding): + MAX_LENGTH = _MaxLength() + AUTO = _Auto() + DIGEST_LENGTH = _DigestLength() + name = "EMSA-PSS" + _salt_length: typing.Union[int, _MaxLength, _Auto, _DigestLength] + + def __init__( + self, + mgf: "MGF", + salt_length: typing.Union[int, _MaxLength, _Auto, _DigestLength], + ) -> None: + self._mgf = mgf + + if not isinstance( + salt_length, (int, _MaxLength, _Auto, _DigestLength) + ): + raise TypeError( + "salt_length must be an integer, MAX_LENGTH, " + "DIGEST_LENGTH, or AUTO" + ) + + if isinstance(salt_length, int) and salt_length < 0: + raise ValueError("salt_length must be zero or greater.") + + self._salt_length = salt_length + + +class OAEP(AsymmetricPadding): + name = "EME-OAEP" + + def __init__( + self, + mgf: "MGF", + algorithm: hashes.HashAlgorithm, + label: typing.Optional[bytes], + ): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + self._mgf = mgf + self._algorithm = algorithm + self._label = label + + +class MGF(metaclass=abc.ABCMeta): + _algorithm: hashes.HashAlgorithm + + +class MGF1(MGF): + MAX_LENGTH = _MaxLength() + + def __init__(self, algorithm: hashes.HashAlgorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + + self._algorithm = algorithm + + +def calculate_max_pss_salt_length( + key: typing.Union["rsa.RSAPrivateKey", "rsa.RSAPublicKey"], + hash_algorithm: hashes.HashAlgorithm, +) -> int: + if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): + raise TypeError("key must be an RSA public or private key") + # bit length - 1 per RFC 3447 + emlen = (key.key_size + 6) // 8 + salt_length = emlen - hash_algorithm.digest_size - 2 + assert salt_length >= 0 + return salt_length diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py new file mode 100644 index 000000000..81f5a0ec6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py @@ -0,0 +1,432 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing +from math import gcd + +from cryptography.hazmat.primitives import _serialization, hashes +from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding +from cryptography.hazmat.primitives.asymmetric import utils as asym_utils + + +class RSAPrivateKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: + """ + Decrypts the provided ciphertext. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the public modulus. + """ + + @abc.abstractmethod + def public_key(self) -> "RSAPublicKey": + """ + The RSAPublicKey associated with this private key. + """ + + @abc.abstractmethod + def sign( + self, + data: bytes, + padding: AsymmetricPadding, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> bytes: + """ + Signs the data. + """ + + @abc.abstractmethod + def private_numbers(self) -> "RSAPrivateNumbers": + """ + Returns an RSAPrivateNumbers. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + +RSAPrivateKeyWithSerialization = RSAPrivateKey + + +class RSAPublicKey(metaclass=abc.ABCMeta): + @abc.abstractmethod + def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: + """ + Encrypts the given plaintext. + """ + + @property + @abc.abstractmethod + def key_size(self) -> int: + """ + The bit length of the public modulus. + """ + + @abc.abstractmethod + def public_numbers(self) -> "RSAPublicNumbers": + """ + Returns an RSAPublicNumbers + """ + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + Returns the key serialized as bytes. + """ + + @abc.abstractmethod + def verify( + self, + signature: bytes, + data: bytes, + padding: AsymmetricPadding, + algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], + ) -> None: + """ + Verifies the signature of the data. + """ + + @abc.abstractmethod + def recover_data_from_signature( + self, + signature: bytes, + padding: AsymmetricPadding, + algorithm: typing.Optional[hashes.HashAlgorithm], + ) -> bytes: + """ + Recovers the original data from the signature. + """ + + +RSAPublicKeyWithSerialization = RSAPublicKey + + +def generate_private_key( + public_exponent: int, + key_size: int, + backend: typing.Any = None, +) -> RSAPrivateKey: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + _verify_rsa_parameters(public_exponent, key_size) + return ossl.generate_rsa_private_key(public_exponent, key_size) + + +def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None: + if public_exponent not in (3, 65537): + raise ValueError( + "public_exponent must be either 3 (for legacy compatibility) or " + "65537. Almost everyone should choose 65537 here!" + ) + + if key_size < 512: + raise ValueError("key_size must be at least 512-bits.") + + +def _check_private_key_components( + p: int, + q: int, + private_exponent: int, + dmp1: int, + dmq1: int, + iqmp: int, + public_exponent: int, + modulus: int, +) -> None: + if modulus < 3: + raise ValueError("modulus must be >= 3.") + + if p >= modulus: + raise ValueError("p must be < modulus.") + + if q >= modulus: + raise ValueError("q must be < modulus.") + + if dmp1 >= modulus: + raise ValueError("dmp1 must be < modulus.") + + if dmq1 >= modulus: + raise ValueError("dmq1 must be < modulus.") + + if iqmp >= modulus: + raise ValueError("iqmp must be < modulus.") + + if private_exponent >= modulus: + raise ValueError("private_exponent must be < modulus.") + + if public_exponent < 3 or public_exponent >= modulus: + raise ValueError("public_exponent must be >= 3 and < modulus.") + + if public_exponent & 1 == 0: + raise ValueError("public_exponent must be odd.") + + if dmp1 & 1 == 0: + raise ValueError("dmp1 must be odd.") + + if dmq1 & 1 == 0: + raise ValueError("dmq1 must be odd.") + + if p * q != modulus: + raise ValueError("p*q must equal modulus.") + + +def _check_public_key_components(e: int, n: int) -> None: + if n < 3: + raise ValueError("n must be >= 3.") + + if e < 3 or e >= n: + raise ValueError("e must be >= 3 and < n.") + + if e & 1 == 0: + raise ValueError("e must be odd.") + + +def _modinv(e: int, m: int) -> int: + """ + Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 + """ + x1, x2 = 1, 0 + a, b = e, m + while b > 0: + q, r = divmod(a, b) + xn = x1 - q * x2 + a, b, x1, x2 = b, r, x2, xn + return x1 % m + + +def rsa_crt_iqmp(p: int, q: int) -> int: + """ + Compute the CRT (q ** -1) % p value from RSA primes p and q. + """ + return _modinv(q, p) + + +def rsa_crt_dmp1(private_exponent: int, p: int) -> int: + """ + Compute the CRT private_exponent % (p - 1) value from the RSA + private_exponent (d) and p. + """ + return private_exponent % (p - 1) + + +def rsa_crt_dmq1(private_exponent: int, q: int) -> int: + """ + Compute the CRT private_exponent % (q - 1) value from the RSA + private_exponent (d) and q. + """ + return private_exponent % (q - 1) + + +# Controls the number of iterations rsa_recover_prime_factors will perform +# to obtain the prime factors. Each iteration increments by 2 so the actual +# maximum attempts is half this number. +_MAX_RECOVERY_ATTEMPTS = 1000 + + +def rsa_recover_prime_factors( + n: int, e: int, d: int +) -> typing.Tuple[int, int]: + """ + Compute factors p and q from the private exponent d. We assume that n has + no more than two factors. This function is adapted from code in PyCrypto. + """ + # See 8.2.2(i) in Handbook of Applied Cryptography. + ktot = d * e - 1 + # The quantity d*e-1 is a multiple of phi(n), even, + # and can be represented as t*2^s. + t = ktot + while t % 2 == 0: + t = t // 2 + # Cycle through all multiplicative inverses in Zn. + # The algorithm is non-deterministic, but there is a 50% chance + # any candidate a leads to successful factoring. + # See "Digitalized Signatures and Public Key Functions as Intractable + # as Factorization", M. Rabin, 1979 + spotted = False + a = 2 + while not spotted and a < _MAX_RECOVERY_ATTEMPTS: + k = t + # Cycle through all values a^{t*2^i}=a^k + while k < ktot: + cand = pow(a, k, n) + # Check if a^k is a non-trivial root of unity (mod n) + if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: + # We have found a number such that (cand-1)(cand+1)=0 (mod n). + # Either of the terms divides n. + p = gcd(cand + 1, n) + spotted = True + break + k *= 2 + # This value was not any good... let's try another! + a += 2 + if not spotted: + raise ValueError("Unable to compute factors p and q from exponent d.") + # Found ! + q, r = divmod(n, p) + assert r == 0 + p, q = sorted((p, q), reverse=True) + return (p, q) + + +class RSAPrivateNumbers: + def __init__( + self, + p: int, + q: int, + d: int, + dmp1: int, + dmq1: int, + iqmp: int, + public_numbers: "RSAPublicNumbers", + ): + if ( + not isinstance(p, int) + or not isinstance(q, int) + or not isinstance(d, int) + or not isinstance(dmp1, int) + or not isinstance(dmq1, int) + or not isinstance(iqmp, int) + ): + raise TypeError( + "RSAPrivateNumbers p, q, d, dmp1, dmq1, iqmp arguments must" + " all be an integers." + ) + + if not isinstance(public_numbers, RSAPublicNumbers): + raise TypeError( + "RSAPrivateNumbers public_numbers must be an RSAPublicNumbers" + " instance." + ) + + self._p = p + self._q = q + self._d = d + self._dmp1 = dmp1 + self._dmq1 = dmq1 + self._iqmp = iqmp + self._public_numbers = public_numbers + + @property + def p(self) -> int: + return self._p + + @property + def q(self) -> int: + return self._q + + @property + def d(self) -> int: + return self._d + + @property + def dmp1(self) -> int: + return self._dmp1 + + @property + def dmq1(self) -> int: + return self._dmq1 + + @property + def iqmp(self) -> int: + return self._iqmp + + @property + def public_numbers(self) -> "RSAPublicNumbers": + return self._public_numbers + + def private_key( + self, + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, + ) -> RSAPrivateKey: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_rsa_private_numbers( + self, unsafe_skip_rsa_key_validation + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RSAPrivateNumbers): + return NotImplemented + + return ( + self.p == other.p + and self.q == other.q + and self.d == other.d + and self.dmp1 == other.dmp1 + and self.dmq1 == other.dmq1 + and self.iqmp == other.iqmp + and self.public_numbers == other.public_numbers + ) + + def __hash__(self) -> int: + return hash( + ( + self.p, + self.q, + self.d, + self.dmp1, + self.dmq1, + self.iqmp, + self.public_numbers, + ) + ) + + +class RSAPublicNumbers: + def __init__(self, e: int, n: int): + if not isinstance(e, int) or not isinstance(n, int): + raise TypeError("RSAPublicNumbers arguments must be integers.") + + self._e = e + self._n = n + + @property + def e(self) -> int: + return self._e + + @property + def n(self) -> int: + return self._n + + def public_key(self, backend: typing.Any = None) -> RSAPublicKey: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + return ossl.load_rsa_public_numbers(self) + + def __repr__(self) -> str: + return "".format(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RSAPublicNumbers): + return NotImplemented + + return self.e == other.e and self.n == other.n + + def __hash__(self) -> int: + return hash((self.e, self.n)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py new file mode 100644 index 000000000..6b5ff0801 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py @@ -0,0 +1,68 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives.asymmetric import ( + dh, + dsa, + ec, + ed448, + ed25519, + rsa, + x448, + x25519, +) + +# Every asymmetric key type +PUBLIC_KEY_TYPES = typing.Union[ + dh.DHPublicKey, + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, +] +# Every asymmetric key type +PRIVATE_KEY_TYPES = typing.Union[ + dh.DHPrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + x25519.X25519PrivateKey, + x448.X448PrivateKey, +] +# Just the key types we allow to be used for x509 signing. This mirrors +# the certificate public key types +CERTIFICATE_PRIVATE_KEY_TYPES = typing.Union[ + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, +] +# Just the key types we allow to be used for x509 signing. This mirrors +# the certificate private key types +CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = typing.Union[ + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, +] +# This type removes DHPublicKey. x448/x25519 can be a public key +# but cannot be used in signing so they are allowed here. +CERTIFICATE_PUBLIC_KEY_TYPES = typing.Union[ + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py new file mode 100644 index 000000000..140ca1960 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py @@ -0,0 +1,23 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography.hazmat.bindings._rust import asn1 +from cryptography.hazmat.primitives import hashes + +decode_dss_signature = asn1.decode_dss_signature +encode_dss_signature = asn1.encode_dss_signature + + +class Prehashed: + def __init__(self, algorithm: hashes.HashAlgorithm): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of HashAlgorithm.") + + self._algorithm = algorithm + self._digest_size = algorithm.digest_size + + @property + def digest_size(self) -> int: + return self._digest_size diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py new file mode 100644 index 000000000..690af78c2 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py @@ -0,0 +1,81 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import _serialization + + +class X25519PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> "X25519PublicKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return backend.x25519_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + +class X25519PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> "X25519PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + return backend.x25519_generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> "X25519PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x25519_supported(): + raise UnsupportedAlgorithm( + "X25519 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return backend.x25519_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> X25519PublicKey: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: X25519PublicKey) -> bytes: + """ + Performs a key exchange operation using the provided peer's public key. + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py new file mode 100644 index 000000000..7f71c2722 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py @@ -0,0 +1,81 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc + +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives import _serialization + + +class X448PublicKey(metaclass=abc.ABCMeta): + @classmethod + def from_public_bytes(cls, data: bytes) -> "X448PublicKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return backend.x448_load_public_bytes(data) + + @abc.abstractmethod + def public_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PublicFormat, + ) -> bytes: + """ + The serialized bytes of the public key. + """ + + +class X448PrivateKey(metaclass=abc.ABCMeta): + @classmethod + def generate(cls) -> "X448PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + return backend.x448_generate_key() + + @classmethod + def from_private_bytes(cls, data: bytes) -> "X448PrivateKey": + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.x448_supported(): + raise UnsupportedAlgorithm( + "X448 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, + ) + + return backend.x448_load_private_bytes(data) + + @abc.abstractmethod + def public_key(self) -> X448PublicKey: + """ + The serialized bytes of the public key. + """ + + @abc.abstractmethod + def private_bytes( + self, + encoding: _serialization.Encoding, + format: _serialization.PrivateFormat, + encryption_algorithm: _serialization.KeySerializationEncryption, + ) -> bytes: + """ + The serialized bytes of the private key. + """ + + @abc.abstractmethod + def exchange(self, peer_public_key: X448PublicKey) -> bytes: + """ + Performs a key exchange operation using the provided peer's public key. + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py new file mode 100644 index 000000000..95f02842a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py @@ -0,0 +1,26 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers.base import ( + AEADCipherContext, + AEADDecryptionContext, + AEADEncryptionContext, + Cipher, + CipherContext, +) + +__all__ = [ + "Cipher", + "CipherAlgorithm", + "BlockCipherAlgorithm", + "CipherContext", + "AEADCipherContext", + "AEADDecryptionContext", + "AEADEncryptionContext", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py new file mode 100644 index 000000000..567301acc --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py @@ -0,0 +1,374 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import os +import typing + +from cryptography import exceptions, utils +from cryptography.hazmat.backends.openssl import aead +from cryptography.hazmat.backends.openssl.backend import backend +from cryptography.hazmat.bindings._rust import FixedPool + + +class ChaCha20Poly1305: + _MAX_SIZE = 2**31 - 1 + + def __init__(self, key: bytes): + if not backend.aead_cipher_supported(self): + raise exceptions.UnsupportedAlgorithm( + "ChaCha20Poly1305 is not supported by this version of OpenSSL", + exceptions._Reasons.UNSUPPORTED_CIPHER, + ) + utils._check_byteslike("key", key) + + if len(key) != 32: + raise ValueError("ChaCha20Poly1305 key must be 32 bytes.") + + self._key = key + self._pool = FixedPool(self._create_fn) + + @classmethod + def generate_key(cls) -> bytes: + return os.urandom(32) + + def _create_fn(self): + return aead._aead_create_ctx(backend, self, self._key) + + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**31 - 1 bytes" + ) + + self._check_params(nonce, data, associated_data) + with self._pool.acquire() as ctx: + return aead._encrypt( + backend, self, nonce, data, [associated_data], 16, ctx + ) + + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + with self._pool.acquire() as ctx: + return aead._decrypt( + backend, self, nonce, data, [associated_data], 16, ctx + ) + + def _check_params( + self, + nonce: bytes, + data: bytes, + associated_data: bytes, + ) -> None: + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if len(nonce) != 12: + raise ValueError("Nonce must be 12 bytes") + + +class AESCCM: + _MAX_SIZE = 2**31 - 1 + + def __init__(self, key: bytes, tag_length: int = 16): + utils._check_byteslike("key", key) + if len(key) not in (16, 24, 32): + raise ValueError("AESCCM key must be 128, 192, or 256 bits.") + + self._key = key + if not isinstance(tag_length, int): + raise TypeError("tag_length must be an integer") + + if tag_length not in (4, 6, 8, 10, 12, 14, 16): + raise ValueError("Invalid tag_length") + + self._tag_length = tag_length + + if not backend.aead_cipher_supported(self): + raise exceptions.UnsupportedAlgorithm( + "AESCCM is not supported by this version of OpenSSL", + exceptions._Reasons.UNSUPPORTED_CIPHER, + ) + + @classmethod + def generate_key(cls, bit_length: int) -> bytes: + if not isinstance(bit_length, int): + raise TypeError("bit_length must be an integer") + + if bit_length not in (128, 192, 256): + raise ValueError("bit_length must be 128, 192, or 256") + + return os.urandom(bit_length // 8) + + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**31 - 1 bytes" + ) + + self._check_params(nonce, data, associated_data) + self._validate_lengths(nonce, len(data)) + return aead._encrypt( + backend, self, nonce, data, [associated_data], self._tag_length + ) + + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + return aead._decrypt( + backend, self, nonce, data, [associated_data], self._tag_length + ) + + def _validate_lengths(self, nonce: bytes, data_len: int) -> None: + # For information about computing this, see + # https://tools.ietf.org/html/rfc3610#section-2.1 + l_val = 15 - len(nonce) + if 2 ** (8 * l_val) < data_len: + raise ValueError("Data too long for nonce") + + def _check_params( + self, nonce: bytes, data: bytes, associated_data: bytes + ) -> None: + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if not 7 <= len(nonce) <= 13: + raise ValueError("Nonce must be between 7 and 13 bytes") + + +class AESGCM: + _MAX_SIZE = 2**31 - 1 + + def __init__(self, key: bytes): + utils._check_byteslike("key", key) + if len(key) not in (16, 24, 32): + raise ValueError("AESGCM key must be 128, 192, or 256 bits.") + + self._key = key + + @classmethod + def generate_key(cls, bit_length: int) -> bytes: + if not isinstance(bit_length, int): + raise TypeError("bit_length must be an integer") + + if bit_length not in (128, 192, 256): + raise ValueError("bit_length must be 128, 192, or 256") + + return os.urandom(bit_length // 8) + + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**31 - 1 bytes" + ) + + self._check_params(nonce, data, associated_data) + return aead._encrypt(backend, self, nonce, data, [associated_data], 16) + + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + return aead._decrypt(backend, self, nonce, data, [associated_data], 16) + + def _check_params( + self, + nonce: bytes, + data: bytes, + associated_data: bytes, + ) -> None: + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if len(nonce) < 8 or len(nonce) > 128: + raise ValueError("Nonce must be between 8 and 128 bytes") + + +class AESOCB3: + _MAX_SIZE = 2**31 - 1 + + def __init__(self, key: bytes): + utils._check_byteslike("key", key) + if len(key) not in (16, 24, 32): + raise ValueError("AESOCB3 key must be 128, 192, or 256 bits.") + + self._key = key + + if not backend.aead_cipher_supported(self): + raise exceptions.UnsupportedAlgorithm( + "OCB3 is not supported by this version of OpenSSL", + exceptions._Reasons.UNSUPPORTED_CIPHER, + ) + + @classmethod + def generate_key(cls, bit_length: int) -> bytes: + if not isinstance(bit_length, int): + raise TypeError("bit_length must be an integer") + + if bit_length not in (128, 192, 256): + raise ValueError("bit_length must be 128, 192, or 256") + + return os.urandom(bit_length // 8) + + def encrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**31 - 1 bytes" + ) + + self._check_params(nonce, data, associated_data) + return aead._encrypt(backend, self, nonce, data, [associated_data], 16) + + def decrypt( + self, + nonce: bytes, + data: bytes, + associated_data: typing.Optional[bytes], + ) -> bytes: + if associated_data is None: + associated_data = b"" + + self._check_params(nonce, data, associated_data) + return aead._decrypt(backend, self, nonce, data, [associated_data], 16) + + def _check_params( + self, + nonce: bytes, + data: bytes, + associated_data: bytes, + ) -> None: + utils._check_byteslike("nonce", nonce) + utils._check_bytes("data", data) + utils._check_bytes("associated_data", associated_data) + if len(nonce) < 12 or len(nonce) > 15: + raise ValueError("Nonce must be between 12 and 15 bytes") + + +class AESSIV(object): + _MAX_SIZE = 2**31 - 1 + + def __init__(self, key: bytes): + utils._check_byteslike("key", key) + if len(key) not in (32, 48, 64): + raise ValueError("AESSIV key must be 256, 384, or 512 bits.") + + self._key = key + + if not backend.aead_cipher_supported(self): + raise exceptions.UnsupportedAlgorithm( + "AES-SIV is not supported by this version of OpenSSL", + exceptions._Reasons.UNSUPPORTED_CIPHER, + ) + + @classmethod + def generate_key(cls, bit_length: int) -> bytes: + if not isinstance(bit_length, int): + raise TypeError("bit_length must be an integer") + + if bit_length not in (256, 384, 512): + raise ValueError("bit_length must be 256, 384, or 512") + + return os.urandom(bit_length // 8) + + def encrypt( + self, + data: bytes, + associated_data: typing.Optional[typing.List[bytes]], + ) -> bytes: + if associated_data is None: + associated_data = [] + + self._check_params(data, associated_data) + + if len(data) > self._MAX_SIZE or any( + len(ad) > self._MAX_SIZE for ad in associated_data + ): + # This is OverflowError to match what cffi would raise + raise OverflowError( + "Data or associated data too long. Max 2**31 - 1 bytes" + ) + + return aead._encrypt(backend, self, b"", data, associated_data, 16) + + def decrypt( + self, + data: bytes, + associated_data: typing.Optional[typing.List[bytes]], + ) -> bytes: + if associated_data is None: + associated_data = [] + + self._check_params(data, associated_data) + + return aead._decrypt(backend, self, b"", data, associated_data, 16) + + def _check_params( + self, + data: bytes, + associated_data: typing.List[bytes], + ) -> None: + utils._check_bytes("data", data) + if len(data) == 0: + raise ValueError("data must not be zero length") + if not isinstance(associated_data, list) or not all( + isinstance(x, bytes) for x in associated_data + ): + raise TypeError("associated_data must be a list of bytes or None") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py new file mode 100644 index 000000000..613854261 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py @@ -0,0 +1,227 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography import utils +from cryptography.hazmat.primitives.ciphers import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) + + +def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes: + # Verify that the key is instance of bytes + utils._check_byteslike("key", key) + + # Verify that the key size matches the expected key size + if len(key) * 8 not in algorithm.key_sizes: + raise ValueError( + "Invalid key size ({}) for {}.".format( + len(key) * 8, algorithm.name + ) + ) + return key + + +class AES(CipherAlgorithm, BlockCipherAlgorithm): + name = "AES" + block_size = 128 + # 512 added to support AES-256-XTS, which uses 512-bit keys + key_sizes = frozenset([128, 192, 256, 512]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class AES128(CipherAlgorithm, BlockCipherAlgorithm): + name = "AES" + block_size = 128 + key_sizes = frozenset([128]) + key_size = 128 + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + +class AES256(CipherAlgorithm, BlockCipherAlgorithm): + name = "AES" + block_size = 128 + key_sizes = frozenset([256]) + key_size = 256 + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + +class Camellia(CipherAlgorithm, BlockCipherAlgorithm): + name = "camellia" + block_size = 128 + key_sizes = frozenset([128, 192, 256]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class TripleDES(CipherAlgorithm, BlockCipherAlgorithm): + name = "3DES" + block_size = 64 + key_sizes = frozenset([64, 128, 192]) + + def __init__(self, key: bytes): + if len(key) == 8: + key += key + key + elif len(key) == 16: + key += key[:8] + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class Blowfish(CipherAlgorithm, BlockCipherAlgorithm): + name = "Blowfish" + block_size = 64 + key_sizes = frozenset(range(32, 449, 8)) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +_BlowfishInternal = Blowfish +utils.deprecated( + Blowfish, + __name__, + "Blowfish has been deprecated", + utils.DeprecatedIn37, + name="Blowfish", +) + + +class CAST5(CipherAlgorithm, BlockCipherAlgorithm): + name = "CAST5" + block_size = 64 + key_sizes = frozenset(range(40, 129, 8)) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +_CAST5Internal = CAST5 +utils.deprecated( + CAST5, + __name__, + "CAST5 has been deprecated", + utils.DeprecatedIn37, + name="CAST5", +) + + +class ARC4(CipherAlgorithm): + name = "RC4" + key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class IDEA(CipherAlgorithm, BlockCipherAlgorithm): + name = "IDEA" + block_size = 64 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +_IDEAInternal = IDEA +utils.deprecated( + IDEA, + __name__, + "IDEA has been deprecated", + utils.DeprecatedIn37, + name="IDEA", +) + + +class SEED(CipherAlgorithm, BlockCipherAlgorithm): + name = "SEED" + block_size = 128 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +_SEEDInternal = SEED +utils.deprecated( + SEED, + __name__, + "SEED has been deprecated", + utils.DeprecatedIn37, + name="SEED", +) + + +class ChaCha20(CipherAlgorithm): + name = "ChaCha20" + key_sizes = frozenset([256]) + + def __init__(self, key: bytes, nonce: bytes): + self.key = _verify_key_size(self, key) + utils._check_byteslike("nonce", nonce) + + if len(nonce) != 16: + raise ValueError("nonce must be 128-bits (16 bytes)") + + self._nonce = nonce + + @property + def nonce(self) -> bytes: + return self._nonce + + @property + def key_size(self) -> int: + return len(self.key) * 8 + + +class SM4(CipherAlgorithm, BlockCipherAlgorithm): + name = "SM4" + block_size = 128 + key_sizes = frozenset([128]) + + def __init__(self, key: bytes): + self.key = _verify_key_size(self, key) + + @property + def key_size(self) -> int: + return len(self.key) * 8 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py new file mode 100644 index 000000000..d7c4f096d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py @@ -0,0 +1,269 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography.exceptions import ( + AlreadyFinalized, + AlreadyUpdated, + NotYetFinalized, +) +from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm +from cryptography.hazmat.primitives.ciphers import modes + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.ciphers import ( + _CipherContext as _BackendCipherContext, + ) + + +class CipherContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: bytes) -> bytes: + """ + Processes the provided bytes through the cipher and returns the results + as bytes. + """ + + @abc.abstractmethod + def update_into(self, data: bytes, buf: bytes) -> int: + """ + Processes the provided bytes and writes the resulting data into the + provided buffer. Returns the number of bytes written. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Returns the results of processing the final block as bytes. + """ + + +class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta): + @abc.abstractmethod + def authenticate_additional_data(self, data: bytes) -> None: + """ + Authenticates the provided bytes. + """ + + +class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): + @abc.abstractmethod + def finalize_with_tag(self, tag: bytes) -> bytes: + """ + Returns the results of processing the final block as bytes and allows + delayed passing of the authentication tag. + """ + + +class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tag(self) -> bytes: + """ + Returns tag bytes. This is only available after encryption is + finalized. + """ + + +Mode = typing.TypeVar( + "Mode", bound=typing.Optional[modes.Mode], covariant=True +) + + +class Cipher(typing.Generic[Mode]): + def __init__( + self, + algorithm: CipherAlgorithm, + mode: Mode, + backend: typing.Any = None, + ) -> None: + + if not isinstance(algorithm, CipherAlgorithm): + raise TypeError("Expected interface of CipherAlgorithm.") + + if mode is not None: + # mypy needs this assert to narrow the type from our generic + # type. Maybe it won't some time in the future. + assert isinstance(mode, modes.Mode) + mode.validate_for_algorithm(algorithm) + + self.algorithm = algorithm + self.mode = mode + + @typing.overload + def encryptor( + self: "Cipher[modes.ModeWithAuthenticationTag]", + ) -> AEADEncryptionContext: + ... + + @typing.overload + def encryptor( + self: "_CIPHER_TYPE", + ) -> CipherContext: + ... + + def encryptor(self): + if isinstance(self.mode, modes.ModeWithAuthenticationTag): + if self.mode.tag is not None: + raise ValueError( + "Authentication tag must be None when encrypting." + ) + from cryptography.hazmat.backends.openssl.backend import backend + + ctx = backend.create_symmetric_encryption_ctx( + self.algorithm, self.mode + ) + return self._wrap_ctx(ctx, encrypt=True) + + @typing.overload + def decryptor( + self: "Cipher[modes.ModeWithAuthenticationTag]", + ) -> AEADDecryptionContext: + ... + + @typing.overload + def decryptor( + self: "_CIPHER_TYPE", + ) -> CipherContext: + ... + + def decryptor(self): + from cryptography.hazmat.backends.openssl.backend import backend + + ctx = backend.create_symmetric_decryption_ctx( + self.algorithm, self.mode + ) + return self._wrap_ctx(ctx, encrypt=False) + + def _wrap_ctx( + self, ctx: "_BackendCipherContext", encrypt: bool + ) -> typing.Union[ + AEADEncryptionContext, AEADDecryptionContext, CipherContext + ]: + if isinstance(self.mode, modes.ModeWithAuthenticationTag): + if encrypt: + return _AEADEncryptionContext(ctx) + else: + return _AEADDecryptionContext(ctx) + else: + return _CipherContext(ctx) + + +_CIPHER_TYPE = Cipher[ + typing.Union[ + modes.ModeWithNonce, + modes.ModeWithTweak, + None, + modes.ECB, + modes.ModeWithInitializationVector, + ] +] + + +class _CipherContext(CipherContext): + _ctx: typing.Optional["_BackendCipherContext"] + + def __init__(self, ctx: "_BackendCipherContext") -> None: + self._ctx = ctx + + def update(self, data: bytes) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + return self._ctx.update(data) + + def update_into(self, data: bytes, buf: bytes) -> int: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + return self._ctx.update_into(data, buf) + + def finalize(self) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + data = self._ctx.finalize() + self._ctx = None + return data + + +class _AEADCipherContext(AEADCipherContext): + _ctx: typing.Optional["_BackendCipherContext"] + _tag: typing.Optional[bytes] + + def __init__(self, ctx: "_BackendCipherContext") -> None: + self._ctx = ctx + self._bytes_processed = 0 + self._aad_bytes_processed = 0 + self._tag = None + self._updated = False + + def _check_limit(self, data_size: int) -> None: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + self._updated = True + self._bytes_processed += data_size + if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES: + raise ValueError( + "{} has a maximum encrypted byte limit of {}".format( + self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES + ) + ) + + def update(self, data: bytes) -> bytes: + self._check_limit(len(data)) + # mypy needs this assert even though _check_limit already checked + assert self._ctx is not None + return self._ctx.update(data) + + def update_into(self, data: bytes, buf: bytes) -> int: + self._check_limit(len(data)) + # mypy needs this assert even though _check_limit already checked + assert self._ctx is not None + return self._ctx.update_into(data, buf) + + def finalize(self) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + data = self._ctx.finalize() + self._tag = self._ctx.tag + self._ctx = None + return data + + def authenticate_additional_data(self, data: bytes) -> None: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + if self._updated: + raise AlreadyUpdated("Update has been called on this context.") + + self._aad_bytes_processed += len(data) + if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES: + raise ValueError( + "{} has a maximum AAD byte limit of {}".format( + self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES + ) + ) + + self._ctx.authenticate_additional_data(data) + + +class _AEADDecryptionContext(_AEADCipherContext, AEADDecryptionContext): + def finalize_with_tag(self, tag: bytes) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + data = self._ctx.finalize_with_tag(tag) + self._tag = self._ctx.tag + self._ctx = None + return data + + +class _AEADEncryptionContext(_AEADCipherContext, AEADEncryptionContext): + @property + def tag(self) -> bytes: + if self._ctx is not None: + raise NotYetFinalized( + "You must finalize encryption before " "getting the tag." + ) + assert self._tag is not None + return self._tag diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py new file mode 100644 index 000000000..b7468b1bd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py @@ -0,0 +1,275 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.primitives._cipheralgorithm import ( + BlockCipherAlgorithm, + CipherAlgorithm, +) +from cryptography.hazmat.primitives.ciphers import algorithms + + +class Mode(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this mode (e.g. "ECB", "CBC"). + """ + + @abc.abstractmethod + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + """ + Checks that all the necessary invariants of this (mode, algorithm) + combination are met. + """ + + +class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def initialization_vector(self) -> bytes: + """ + The value of the initialization vector for this mode as bytes. + """ + + +class ModeWithTweak(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tweak(self) -> bytes: + """ + The value of the tweak for this mode as bytes. + """ + + +class ModeWithNonce(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def nonce(self) -> bytes: + """ + The value of the nonce for this mode as bytes. + """ + + +class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def tag(self) -> typing.Optional[bytes]: + """ + The value of the tag supplied to the constructor of this mode. + """ + + +def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: + if algorithm.key_size > 256 and algorithm.name == "AES": + raise ValueError( + "Only 128, 192, and 256 bit keys are allowed for this AES mode" + ) + + +def _check_iv_length( + self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm +) -> None: + if len(self.initialization_vector) * 8 != algorithm.block_size: + raise ValueError( + "Invalid IV size ({}) for {}.".format( + len(self.initialization_vector), self.name + ) + ) + + +def _check_nonce_length( + nonce: bytes, name: str, algorithm: CipherAlgorithm +) -> None: + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + f"{name} requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + if len(nonce) * 8 != algorithm.block_size: + raise ValueError( + "Invalid nonce size ({}) for {}.".format(len(nonce), name) + ) + + +def _check_iv_and_key_length( + self: ModeWithInitializationVector, algorithm: CipherAlgorithm +) -> None: + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + f"{self} requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + _check_aes_key_length(self, algorithm) + _check_iv_length(self, algorithm) + + +class CBC(ModeWithInitializationVector): + name = "CBC" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class XTS(ModeWithTweak): + name = "XTS" + + def __init__(self, tweak: bytes): + utils._check_byteslike("tweak", tweak) + + if len(tweak) != 16: + raise ValueError("tweak must be 128-bits (16 bytes)") + + self._tweak = tweak + + @property + def tweak(self) -> bytes: + return self._tweak + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): + raise TypeError( + "The AES128 and AES256 classes do not support XTS, please use " + "the standard AES class instead." + ) + + if algorithm.key_size not in (256, 512): + raise ValueError( + "The XTS specification requires a 256-bit key for AES-128-XTS" + " and 512-bit key for AES-256-XTS" + ) + + +class ECB(Mode): + name = "ECB" + + validate_for_algorithm = _check_aes_key_length + + +class OFB(ModeWithInitializationVector): + name = "OFB" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB(ModeWithInitializationVector): + name = "CFB" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CFB8(ModeWithInitializationVector): + name = "CFB8" + + def __init__(self, initialization_vector: bytes): + utils._check_byteslike("initialization_vector", initialization_vector) + self._initialization_vector = initialization_vector + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + validate_for_algorithm = _check_iv_and_key_length + + +class CTR(ModeWithNonce): + name = "CTR" + + def __init__(self, nonce: bytes): + utils._check_byteslike("nonce", nonce) + self._nonce = nonce + + @property + def nonce(self) -> bytes: + return self._nonce + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + _check_aes_key_length(self, algorithm) + _check_nonce_length(self.nonce, self.name, algorithm) + + +class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): + name = "GCM" + _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 + _MAX_AAD_BYTES = (2**64) // 8 + + def __init__( + self, + initialization_vector: bytes, + tag: typing.Optional[bytes] = None, + min_tag_length: int = 16, + ): + # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive + # This is a sane limit anyway so we'll enforce it here. + utils._check_byteslike("initialization_vector", initialization_vector) + if len(initialization_vector) < 8 or len(initialization_vector) > 128: + raise ValueError( + "initialization_vector must be between 8 and 128 bytes (64 " + "and 1024 bits)." + ) + self._initialization_vector = initialization_vector + if tag is not None: + utils._check_bytes("tag", tag) + if min_tag_length < 4: + raise ValueError("min_tag_length must be >= 4") + if len(tag) < min_tag_length: + raise ValueError( + "Authentication tag must be {} bytes or longer.".format( + min_tag_length + ) + ) + self._tag = tag + self._min_tag_length = min_tag_length + + @property + def tag(self) -> typing.Optional[bytes]: + return self._tag + + @property + def initialization_vector(self) -> bytes: + return self._initialization_vector + + def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: + _check_aes_key_length(self, algorithm) + if not isinstance(algorithm, BlockCipherAlgorithm): + raise UnsupportedAlgorithm( + "GCM requires a block cipher algorithm", + _Reasons.UNSUPPORTED_CIPHER, + ) + block_size_bytes = algorithm.block_size // 8 + if self._tag is not None and len(self._tag) > block_size_bytes: + raise ValueError( + "Authentication tag cannot be more than {} bytes.".format( + block_size_bytes + ) + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py new file mode 100644 index 000000000..00c4bd11d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py @@ -0,0 +1,64 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized +from cryptography.hazmat.primitives import ciphers + +if typing.TYPE_CHECKING: + from cryptography.hazmat.backends.openssl.cmac import _CMACContext + + +class CMAC: + _ctx: typing.Optional["_CMACContext"] + _algorithm: ciphers.BlockCipherAlgorithm + + def __init__( + self, + algorithm: ciphers.BlockCipherAlgorithm, + backend: typing.Any = None, + ctx: typing.Optional["_CMACContext"] = None, + ) -> None: + if not isinstance(algorithm, ciphers.BlockCipherAlgorithm): + raise TypeError("Expected instance of BlockCipherAlgorithm.") + self._algorithm = algorithm + + if ctx is None: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + self._ctx = ossl.create_cmac_ctx(self._algorithm) + else: + self._ctx = ctx + + def update(self, data: bytes) -> None: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_bytes("data", data) + self._ctx.update(data) + + def finalize(self) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + digest = self._ctx.finalize() + self._ctx = None + return digest + + def verify(self, signature: bytes) -> None: + utils._check_bytes("signature", signature) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + + ctx, self._ctx = self._ctx, None + ctx.verify(signature) + + def copy(self) -> "CMAC": + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + return CMAC(self._algorithm, ctx=self._ctx.copy()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py new file mode 100644 index 000000000..a02fa9c45 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py @@ -0,0 +1,13 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import hmac + + +def bytes_eq(a: bytes, b: bytes) -> bool: + if not isinstance(a, bytes) or not isinstance(b, bytes): + raise TypeError("a and b must be bytes.") + + return hmac.compare_digest(a, b) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py new file mode 100644 index 000000000..330c08dfa --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py @@ -0,0 +1,261 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import abc +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized + + +class HashAlgorithm(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def name(self) -> str: + """ + A string naming this algorithm (e.g. "sha256", "md5"). + """ + + @property + @abc.abstractmethod + def digest_size(self) -> int: + """ + The size of the resulting digest in bytes. + """ + + @property + @abc.abstractmethod + def block_size(self) -> typing.Optional[int]: + """ + The internal block size of the hash function, or None if the hash + function does not use blocks internally (e.g. SHA3). + """ + + +class HashContext(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def algorithm(self) -> HashAlgorithm: + """ + A HashAlgorithm that will be used by this context. + """ + + @abc.abstractmethod + def update(self, data: bytes) -> None: + """ + Processes the provided bytes through the hash. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Finalizes the hash context and returns the hash digest as bytes. + """ + + @abc.abstractmethod + def copy(self) -> "HashContext": + """ + Return a HashContext that is a copy of the current context. + """ + + +class ExtendableOutputFunction(metaclass=abc.ABCMeta): + """ + An interface for extendable output functions. + """ + + +class Hash(HashContext): + _ctx: typing.Optional[HashContext] + + def __init__( + self, + algorithm: HashAlgorithm, + backend: typing.Any = None, + ctx: typing.Optional["HashContext"] = None, + ) -> None: + if not isinstance(algorithm, HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + self._algorithm = algorithm + + if ctx is None: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + self._ctx = ossl.create_hash_ctx(self.algorithm) + else: + self._ctx = ctx + + @property + def algorithm(self) -> HashAlgorithm: + return self._algorithm + + def update(self, data: bytes) -> None: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + utils._check_byteslike("data", data) + self._ctx.update(data) + + def copy(self) -> "Hash": + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + return Hash(self.algorithm, ctx=self._ctx.copy()) + + def finalize(self) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + digest = self._ctx.finalize() + self._ctx = None + return digest + + +class SHA1(HashAlgorithm): + name = "sha1" + digest_size = 20 + block_size = 64 + + +class SHA512_224(HashAlgorithm): # noqa: N801 + name = "sha512-224" + digest_size = 28 + block_size = 128 + + +class SHA512_256(HashAlgorithm): # noqa: N801 + name = "sha512-256" + digest_size = 32 + block_size = 128 + + +class SHA224(HashAlgorithm): + name = "sha224" + digest_size = 28 + block_size = 64 + + +class SHA256(HashAlgorithm): + name = "sha256" + digest_size = 32 + block_size = 64 + + +class SHA384(HashAlgorithm): + name = "sha384" + digest_size = 48 + block_size = 128 + + +class SHA512(HashAlgorithm): + name = "sha512" + digest_size = 64 + block_size = 128 + + +class SHA3_224(HashAlgorithm): # noqa: N801 + name = "sha3-224" + digest_size = 28 + block_size = None + + +class SHA3_256(HashAlgorithm): # noqa: N801 + name = "sha3-256" + digest_size = 32 + block_size = None + + +class SHA3_384(HashAlgorithm): # noqa: N801 + name = "sha3-384" + digest_size = 48 + block_size = None + + +class SHA3_512(HashAlgorithm): # noqa: N801 + name = "sha3-512" + digest_size = 64 + block_size = None + + +class SHAKE128(HashAlgorithm, ExtendableOutputFunction): + name = "shake128" + block_size = None + + def __init__(self, digest_size: int): + if not isinstance(digest_size, int): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class SHAKE256(HashAlgorithm, ExtendableOutputFunction): + name = "shake256" + block_size = None + + def __init__(self, digest_size: int): + if not isinstance(digest_size, int): + raise TypeError("digest_size must be an integer") + + if digest_size < 1: + raise ValueError("digest_size must be a positive integer") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class MD5(HashAlgorithm): + name = "md5" + digest_size = 16 + block_size = 64 + + +class BLAKE2b(HashAlgorithm): + name = "blake2b" + _max_digest_size = 64 + _min_digest_size = 1 + block_size = 128 + + def __init__(self, digest_size: int): + + if digest_size != 64: + raise ValueError("Digest size must be 64") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class BLAKE2s(HashAlgorithm): + name = "blake2s" + block_size = 64 + _max_digest_size = 32 + _min_digest_size = 1 + + def __init__(self, digest_size: int): + + if digest_size != 32: + raise ValueError("Digest size must be 32") + + self._digest_size = digest_size + + @property + def digest_size(self) -> int: + return self._digest_size + + +class SM3(HashAlgorithm): + name = "sm3" + digest_size = 32 + block_size = 64 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py new file mode 100644 index 000000000..8f1c0eae6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py @@ -0,0 +1,70 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized +from cryptography.hazmat.backends.openssl.hmac import _HMACContext +from cryptography.hazmat.primitives import hashes + + +class HMAC(hashes.HashContext): + _ctx: typing.Optional[_HMACContext] + + def __init__( + self, + key: bytes, + algorithm: hashes.HashAlgorithm, + backend: typing.Any = None, + ctx=None, + ): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise TypeError("Expected instance of hashes.HashAlgorithm.") + self._algorithm = algorithm + + self._key = key + if ctx is None: + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + self._ctx = ossl.create_hmac_ctx(key, self.algorithm) + else: + self._ctx = ctx + + @property + def algorithm(self) -> hashes.HashAlgorithm: + return self._algorithm + + def update(self, data: bytes) -> None: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + utils._check_byteslike("data", data) + self._ctx.update(data) + + def copy(self) -> "HMAC": + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + return HMAC( + self._key, + self.algorithm, + ctx=self._ctx.copy(), + ) + + def finalize(self) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + digest = self._ctx.finalize() + self._ctx = None + return digest + + def verify(self, signature: bytes) -> None: + utils._check_bytes("signature", signature) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + + ctx, self._ctx = self._ctx, None + ctx.verify(signature) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py new file mode 100644 index 000000000..38e2f8bc4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py @@ -0,0 +1,22 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc + + +class KeyDerivationFunction(metaclass=abc.ABCMeta): + @abc.abstractmethod + def derive(self, key_material: bytes) -> bytes: + """ + Deterministically generates and returns a new key based on the existing + key material. + """ + + @abc.abstractmethod + def verify(self, key_material: bytes, expected_key: bytes) -> None: + """ + Checks whether the key generated by the key material matches the + expected derived key. Raises an exception if they do not match. + """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py new file mode 100644 index 000000000..94312fec3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py @@ -0,0 +1,127 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized, InvalidKey +from cryptography.hazmat.primitives import constant_time, hashes, hmac +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +def _int_to_u32be(n: int) -> bytes: + return n.to_bytes(length=4, byteorder="big") + + +def _common_args_checks( + algorithm: hashes.HashAlgorithm, + length: int, + otherinfo: typing.Optional[bytes], +) -> None: + max_length = algorithm.digest_size * (2**32 - 1) + if length > max_length: + raise ValueError( + "Cannot derive keys larger than {} bits.".format(max_length) + ) + if otherinfo is not None: + utils._check_bytes("otherinfo", otherinfo) + + +def _concatkdf_derive( + key_material: bytes, + length: int, + auxfn: typing.Callable[[], hashes.HashContext], + otherinfo: bytes, +) -> bytes: + utils._check_byteslike("key_material", key_material) + output = [b""] + outlen = 0 + counter = 1 + + while length > outlen: + h = auxfn() + h.update(_int_to_u32be(counter)) + h.update(key_material) + h.update(otherinfo) + output.append(h.finalize()) + outlen += len(output[-1]) + counter += 1 + + return b"".join(output)[:length] + + +class ConcatKDFHash(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + otherinfo: typing.Optional[bytes], + backend: typing.Any = None, + ): + _common_args_checks(algorithm, length, otherinfo) + self._algorithm = algorithm + self._length = length + self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" + + self._used = False + + def _hash(self) -> hashes.Hash: + return hashes.Hash(self._algorithm) + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized + self._used = True + return _concatkdf_derive( + key_material, self._length, self._hash, self._otherinfo + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +class ConcatKDFHMAC(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: typing.Optional[bytes], + otherinfo: typing.Optional[bytes], + backend: typing.Any = None, + ): + _common_args_checks(algorithm, length, otherinfo) + self._algorithm = algorithm + self._length = length + self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" + + if algorithm.block_size is None: + raise TypeError( + "{} is unsupported for ConcatKDF".format(algorithm.name) + ) + + if salt is None: + salt = b"\x00" * algorithm.block_size + else: + utils._check_bytes("salt", salt) + + self._salt = salt + + self._used = False + + def _hmac(self) -> hmac.HMAC: + return hmac.HMAC(self._salt, self._algorithm) + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized + self._used = True + return _concatkdf_derive( + key_material, self._length, self._hmac, self._otherinfo + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py new file mode 100644 index 000000000..2152ae220 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py @@ -0,0 +1,100 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized, InvalidKey +from cryptography.hazmat.primitives import constant_time, hashes, hmac +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class HKDF(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: typing.Optional[bytes], + info: typing.Optional[bytes], + backend: typing.Any = None, + ): + self._algorithm = algorithm + + if salt is None: + salt = b"\x00" * self._algorithm.digest_size + else: + utils._check_bytes("salt", salt) + + self._salt = salt + + self._hkdf_expand = HKDFExpand(self._algorithm, length, info) + + def _extract(self, key_material: bytes) -> bytes: + h = hmac.HMAC(self._salt, self._algorithm) + h.update(key_material) + return h.finalize() + + def derive(self, key_material: bytes) -> bytes: + utils._check_byteslike("key_material", key_material) + return self._hkdf_expand.derive(self._extract(key_material)) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +class HKDFExpand(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + info: typing.Optional[bytes], + backend: typing.Any = None, + ): + self._algorithm = algorithm + + max_length = 255 * algorithm.digest_size + + if length > max_length: + raise ValueError( + "Cannot derive keys larger than {} octets.".format(max_length) + ) + + self._length = length + + if info is None: + info = b"" + else: + utils._check_bytes("info", info) + + self._info = info + + self._used = False + + def _expand(self, key_material: bytes) -> bytes: + output = [b""] + counter = 1 + + while self._algorithm.digest_size * (len(output) - 1) < self._length: + h = hmac.HMAC(key_material, self._algorithm) + h.update(output[-1]) + h.update(self._info) + h.update(bytes([counter])) + output.append(h.finalize()) + counter += 1 + + return b"".join(output)[: self._length] + + def derive(self, key_material: bytes) -> bytes: + utils._check_byteslike("key_material", key_material) + if self._used: + raise AlreadyFinalized + + self._used = True + return self._expand(key_material) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py new file mode 100644 index 000000000..7f185a9af --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py @@ -0,0 +1,297 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + InvalidKey, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.primitives import ( + ciphers, + cmac, + constant_time, + hashes, + hmac, +) +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class Mode(utils.Enum): + CounterMode = "ctr" + + +class CounterLocation(utils.Enum): + BeforeFixed = "before_fixed" + AfterFixed = "after_fixed" + MiddleFixed = "middle_fixed" + + +class _KBKDFDeriver: + def __init__( + self, + prf: typing.Callable, + mode: Mode, + length: int, + rlen: int, + llen: typing.Optional[int], + location: CounterLocation, + break_location: typing.Optional[int], + label: typing.Optional[bytes], + context: typing.Optional[bytes], + fixed: typing.Optional[bytes], + ): + assert callable(prf) + + if not isinstance(mode, Mode): + raise TypeError("mode must be of type Mode") + + if not isinstance(location, CounterLocation): + raise TypeError("location must be of type CounterLocation") + + if break_location is None and location is CounterLocation.MiddleFixed: + raise ValueError("Please specify a break_location") + + if ( + break_location is not None + and location != CounterLocation.MiddleFixed + ): + raise ValueError( + "break_location is ignored when location is not" + " CounterLocation.MiddleFixed" + ) + + if break_location is not None and not isinstance(break_location, int): + raise TypeError("break_location must be an integer") + + if break_location is not None and break_location < 0: + raise ValueError("break_location must be a positive integer") + + if (label or context) and fixed: + raise ValueError( + "When supplying fixed data, " "label and context are ignored." + ) + + if rlen is None or not self._valid_byte_length(rlen): + raise ValueError("rlen must be between 1 and 4") + + if llen is None and fixed is None: + raise ValueError("Please specify an llen") + + if llen is not None and not isinstance(llen, int): + raise TypeError("llen must be an integer") + + if label is None: + label = b"" + + if context is None: + context = b"" + + utils._check_bytes("label", label) + utils._check_bytes("context", context) + self._prf = prf + self._mode = mode + self._length = length + self._rlen = rlen + self._llen = llen + self._location = location + self._break_location = break_location + self._label = label + self._context = context + self._used = False + self._fixed_data = fixed + + @staticmethod + def _valid_byte_length(value: int) -> bool: + if not isinstance(value, int): + raise TypeError("value must be of type int") + + value_bin = utils.int_to_bytes(1, value) + if not 1 <= len(value_bin) <= 4: + return False + return True + + def derive(self, key_material: bytes, prf_output_size: int) -> bytes: + if self._used: + raise AlreadyFinalized + + utils._check_byteslike("key_material", key_material) + self._used = True + + # inverse floor division (equivalent to ceiling) + rounds = -(-self._length // prf_output_size) + + output = [b""] + + # For counter mode, the number of iterations shall not be + # larger than 2^r-1, where r <= 32 is the binary length of the counter + # This ensures that the counter values used as an input to the + # PRF will not repeat during a particular call to the KDF function. + r_bin = utils.int_to_bytes(1, self._rlen) + if rounds > pow(2, len(r_bin) * 8) - 1: + raise ValueError("There are too many iterations.") + + fixed = self._generate_fixed_input() + + if self._location == CounterLocation.BeforeFixed: + data_before_ctr = b"" + data_after_ctr = fixed + elif self._location == CounterLocation.AfterFixed: + data_before_ctr = fixed + data_after_ctr = b"" + else: + if isinstance( + self._break_location, int + ) and self._break_location > len(fixed): + raise ValueError("break_location offset > len(fixed)") + data_before_ctr = fixed[: self._break_location] + data_after_ctr = fixed[self._break_location :] + + for i in range(1, rounds + 1): + h = self._prf(key_material) + + counter = utils.int_to_bytes(i, self._rlen) + input_data = data_before_ctr + counter + data_after_ctr + + h.update(input_data) + + output.append(h.finalize()) + + return b"".join(output)[: self._length] + + def _generate_fixed_input(self) -> bytes: + if self._fixed_data and isinstance(self._fixed_data, bytes): + return self._fixed_data + + l_val = utils.int_to_bytes(self._length * 8, self._llen) + + return b"".join([self._label, b"\x00", self._context, l_val]) + + +class KBKDFHMAC(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + mode: Mode, + length: int, + rlen: int, + llen: typing.Optional[int], + location: CounterLocation, + label: typing.Optional[bytes], + context: typing.Optional[bytes], + fixed: typing.Optional[bytes], + backend: typing.Any = None, + *, + break_location: typing.Optional[int] = None, + ): + if not isinstance(algorithm, hashes.HashAlgorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported hash algorithm.", + _Reasons.UNSUPPORTED_HASH, + ) + + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.hmac_supported(algorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported hmac algorithm.", + _Reasons.UNSUPPORTED_HASH, + ) + + self._algorithm = algorithm + + self._deriver = _KBKDFDeriver( + self._prf, + mode, + length, + rlen, + llen, + location, + break_location, + label, + context, + fixed, + ) + + def _prf(self, key_material: bytes) -> hmac.HMAC: + return hmac.HMAC(key_material, self._algorithm) + + def derive(self, key_material: bytes) -> bytes: + return self._deriver.derive(key_material, self._algorithm.digest_size) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey + + +class KBKDFCMAC(KeyDerivationFunction): + def __init__( + self, + algorithm, + mode: Mode, + length: int, + rlen: int, + llen: typing.Optional[int], + location: CounterLocation, + label: typing.Optional[bytes], + context: typing.Optional[bytes], + fixed: typing.Optional[bytes], + backend: typing.Any = None, + *, + break_location: typing.Optional[int] = None, + ): + if not issubclass( + algorithm, ciphers.BlockCipherAlgorithm + ) or not issubclass(algorithm, ciphers.CipherAlgorithm): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported cipher algorithm.", + _Reasons.UNSUPPORTED_CIPHER, + ) + + self._algorithm = algorithm + self._cipher: typing.Optional[ciphers.BlockCipherAlgorithm] = None + + self._deriver = _KBKDFDeriver( + self._prf, + mode, + length, + rlen, + llen, + location, + break_location, + label, + context, + fixed, + ) + + def _prf(self, _: bytes) -> cmac.CMAC: + assert self._cipher is not None + + return cmac.CMAC(self._cipher) + + def derive(self, key_material: bytes) -> bytes: + self._cipher = self._algorithm(key_material) + + assert self._cipher is not None + + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.cmac_algorithm_supported(self._cipher): + raise UnsupportedAlgorithm( + "Algorithm supplied is not a supported cipher algorithm.", + _Reasons.UNSUPPORTED_CIPHER, + ) + + return self._deriver.derive(key_material, self._cipher.block_size // 8) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py new file mode 100644 index 000000000..8d23f8c25 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py @@ -0,0 +1,65 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + InvalidKey, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.primitives import constant_time, hashes +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +class PBKDF2HMAC(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + salt: bytes, + iterations: int, + backend: typing.Any = None, + ): + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.pbkdf2_hmac_supported(algorithm): + raise UnsupportedAlgorithm( + "{} is not supported for PBKDF2 by this backend.".format( + algorithm.name + ), + _Reasons.UNSUPPORTED_HASH, + ) + self._used = False + self._algorithm = algorithm + self._length = length + utils._check_bytes("salt", salt) + self._salt = salt + self._iterations = iterations + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized("PBKDF2 instances can only be used once.") + self._used = True + + utils._check_byteslike("key_material", key_material) + from cryptography.hazmat.backends.openssl.backend import backend + + return backend.derive_pbkdf2_hmac( + self._algorithm, + self._length, + self._salt, + self._iterations, + key_material, + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + derived_key = self.derive(key_material) + if not constant_time.bytes_eq(derived_key, expected_key): + raise InvalidKey("Keys do not match.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py new file mode 100644 index 000000000..286f4388c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py @@ -0,0 +1,73 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import sys +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + InvalidKey, + UnsupportedAlgorithm, +) +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + +# This is used by the scrypt tests to skip tests that require more memory +# than the MEM_LIMIT +_MEM_LIMIT = sys.maxsize // 2 + + +class Scrypt(KeyDerivationFunction): + def __init__( + self, + salt: bytes, + length: int, + n: int, + r: int, + p: int, + backend: typing.Any = None, + ): + from cryptography.hazmat.backends.openssl.backend import ( + backend as ossl, + ) + + if not ossl.scrypt_supported(): + raise UnsupportedAlgorithm( + "This version of OpenSSL does not support scrypt" + ) + self._length = length + utils._check_bytes("salt", salt) + if n < 2 or (n & (n - 1)) != 0: + raise ValueError("n must be greater than 1 and be a power of 2.") + + if r < 1: + raise ValueError("r must be greater than or equal to 1.") + + if p < 1: + raise ValueError("p must be greater than or equal to 1.") + + self._used = False + self._salt = salt + self._n = n + self._r = r + self._p = p + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized("Scrypt instances can only be used once.") + self._used = True + + utils._check_byteslike("key_material", key_material) + from cryptography.hazmat.backends.openssl.backend import backend + + return backend.derive_scrypt( + key_material, self._salt, self._length, self._n, self._r, self._p + ) + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + derived_key = self.derive(key_material) + if not constant_time.bytes_eq(derived_key, expected_key): + raise InvalidKey("Keys do not match.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py new file mode 100644 index 000000000..651e691aa --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py @@ -0,0 +1,62 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized, InvalidKey +from cryptography.hazmat.primitives import constant_time, hashes +from cryptography.hazmat.primitives.kdf import KeyDerivationFunction + + +def _int_to_u32be(n: int) -> bytes: + return n.to_bytes(length=4, byteorder="big") + + +class X963KDF(KeyDerivationFunction): + def __init__( + self, + algorithm: hashes.HashAlgorithm, + length: int, + sharedinfo: typing.Optional[bytes], + backend: typing.Any = None, + ): + max_len = algorithm.digest_size * (2**32 - 1) + if length > max_len: + raise ValueError( + "Cannot derive keys larger than {} bits.".format(max_len) + ) + if sharedinfo is not None: + utils._check_bytes("sharedinfo", sharedinfo) + + self._algorithm = algorithm + self._length = length + self._sharedinfo = sharedinfo + self._used = False + + def derive(self, key_material: bytes) -> bytes: + if self._used: + raise AlreadyFinalized + self._used = True + utils._check_byteslike("key_material", key_material) + output = [b""] + outlen = 0 + counter = 1 + + while self._length > outlen: + h = hashes.Hash(self._algorithm) + h.update(key_material) + h.update(_int_to_u32be(counter)) + if self._sharedinfo is not None: + h.update(self._sharedinfo) + output.append(h.finalize()) + outlen += len(output[-1]) + counter += 1 + + return b"".join(output)[: self._length] + + def verify(self, key_material: bytes, expected_key: bytes) -> None: + if not constant_time.bytes_eq(self.derive(key_material), expected_key): + raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py new file mode 100644 index 000000000..64771ca3c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py @@ -0,0 +1,176 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography.hazmat.primitives.ciphers import Cipher +from cryptography.hazmat.primitives.ciphers.algorithms import AES +from cryptography.hazmat.primitives.ciphers.modes import ECB +from cryptography.hazmat.primitives.constant_time import bytes_eq + + +def _wrap_core( + wrapping_key: bytes, + a: bytes, + r: typing.List[bytes], +) -> bytes: + # RFC 3394 Key Wrap - 2.2.1 (index method) + encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() + n = len(r) + for j in range(6): + for i in range(n): + # every encryption operation is a discrete 16 byte chunk (because + # AES has a 128-bit block size) and since we're using ECB it is + # safe to reuse the encryptor for the entire operation + b = encryptor.update(a + r[i]) + a = ( + int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1) + ).to_bytes(length=8, byteorder="big") + r[i] = b[-8:] + + assert encryptor.finalize() == b"" + + return a + b"".join(r) + + +def aes_key_wrap( + wrapping_key: bytes, + key_to_wrap: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + if len(key_to_wrap) < 16: + raise ValueError("The key to wrap must be at least 16 bytes") + + if len(key_to_wrap) % 8 != 0: + raise ValueError("The key to wrap must be a multiple of 8 bytes") + + a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] + return _wrap_core(wrapping_key, a, r) + + +def _unwrap_core( + wrapping_key: bytes, + a: bytes, + r: typing.List[bytes], +) -> typing.Tuple[bytes, typing.List[bytes]]: + # Implement RFC 3394 Key Unwrap - 2.2.2 (index method) + decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() + n = len(r) + for j in reversed(range(6)): + for i in reversed(range(n)): + atr = ( + int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1) + ).to_bytes(length=8, byteorder="big") + r[i] + # every decryption operation is a discrete 16 byte chunk so + # it is safe to reuse the decryptor for the entire operation + b = decryptor.update(atr) + a = b[:8] + r[i] = b[-8:] + + assert decryptor.finalize() == b"" + return a, r + + +def aes_key_wrap_with_padding( + wrapping_key: bytes, + key_to_wrap: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + aiv = b"\xA6\x59\x59\xA6" + len(key_to_wrap).to_bytes( + length=4, byteorder="big" + ) + # pad the key to wrap if necessary + pad = (8 - (len(key_to_wrap) % 8)) % 8 + key_to_wrap = key_to_wrap + b"\x00" * pad + if len(key_to_wrap) == 8: + # RFC 5649 - 4.1 - exactly 8 octets after padding + encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() + b = encryptor.update(aiv + key_to_wrap) + assert encryptor.finalize() == b"" + return b + else: + r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] + return _wrap_core(wrapping_key, aiv, r) + + +def aes_key_unwrap_with_padding( + wrapping_key: bytes, + wrapped_key: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapped_key) < 16: + raise InvalidUnwrap("Must be at least 16 bytes") + + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + if len(wrapped_key) == 16: + # RFC 5649 - 4.2 - exactly two 64-bit blocks + decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() + out = decryptor.update(wrapped_key) + assert decryptor.finalize() == b"" + a = out[:8] + data = out[8:] + n = 1 + else: + r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] + encrypted_aiv = r.pop(0) + n = len(r) + a, r = _unwrap_core(wrapping_key, encrypted_aiv, r) + data = b"".join(r) + + # 1) Check that MSB(32,A) = A65959A6. + # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let + # MLI = LSB(32,A). + # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of + # the output data are zero. + mli = int.from_bytes(a[4:], byteorder="big") + b = (8 * n) - mli + if ( + not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") + or not 8 * (n - 1) < mli <= 8 * n + or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b)) + ): + raise InvalidUnwrap() + + if b == 0: + return data + else: + return data[:-b] + + +def aes_key_unwrap( + wrapping_key: bytes, + wrapped_key: bytes, + backend: typing.Any = None, +) -> bytes: + if len(wrapped_key) < 24: + raise InvalidUnwrap("Must be at least 24 bytes") + + if len(wrapped_key) % 8 != 0: + raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes") + + if len(wrapping_key) not in [16, 24, 32]: + raise ValueError("The wrapping key must be a valid AES key length") + + aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" + r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] + a = r.pop(0) + a, r = _unwrap_core(wrapping_key, a, r) + if not bytes_eq(a, aiv): + raise InvalidUnwrap() + + return b"".join(r) + + +class InvalidUnwrap(Exception): + pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py new file mode 100644 index 000000000..d6c1d9152 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py @@ -0,0 +1,224 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import typing + +from cryptography import utils +from cryptography.exceptions import AlreadyFinalized +from cryptography.hazmat.bindings._rust import ( + check_ansix923_padding, + check_pkcs7_padding, +) + + +class PaddingContext(metaclass=abc.ABCMeta): + @abc.abstractmethod + def update(self, data: bytes) -> bytes: + """ + Pads the provided bytes and returns any available data as bytes. + """ + + @abc.abstractmethod + def finalize(self) -> bytes: + """ + Finalize the padding, returns bytes. + """ + + +def _byte_padding_check(block_size: int) -> None: + if not (0 <= block_size <= 2040): + raise ValueError("block_size must be in range(0, 2041).") + + if block_size % 8 != 0: + raise ValueError("block_size must be a multiple of 8.") + + +def _byte_padding_update( + buffer_: typing.Optional[bytes], data: bytes, block_size: int +) -> typing.Tuple[bytes, bytes]: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_byteslike("data", data) + + buffer_ += bytes(data) + + finished_blocks = len(buffer_) // (block_size // 8) + + result = buffer_[: finished_blocks * (block_size // 8)] + buffer_ = buffer_[finished_blocks * (block_size // 8) :] + + return buffer_, result + + +def _byte_padding_pad( + buffer_: typing.Optional[bytes], + block_size: int, + paddingfn: typing.Callable[[int], bytes], +) -> bytes: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + pad_size = block_size // 8 - len(buffer_) + return buffer_ + paddingfn(pad_size) + + +def _byte_unpadding_update( + buffer_: typing.Optional[bytes], data: bytes, block_size: int +) -> typing.Tuple[bytes, bytes]: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + utils._check_byteslike("data", data) + + buffer_ += bytes(data) + + finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) + + result = buffer_[: finished_blocks * (block_size // 8)] + buffer_ = buffer_[finished_blocks * (block_size // 8) :] + + return buffer_, result + + +def _byte_unpadding_check( + buffer_: typing.Optional[bytes], + block_size: int, + checkfn: typing.Callable[[bytes], int], +) -> bytes: + if buffer_ is None: + raise AlreadyFinalized("Context was already finalized.") + + if len(buffer_) != block_size // 8: + raise ValueError("Invalid padding bytes.") + + valid = checkfn(buffer_) + + if not valid: + raise ValueError("Invalid padding bytes.") + + pad_size = buffer_[-1] + return buffer_[:-pad_size] + + +class PKCS7: + def __init__(self, block_size: int): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self) -> PaddingContext: + return _PKCS7PaddingContext(self.block_size) + + def unpadder(self) -> PaddingContext: + return _PKCS7UnpaddingContext(self.block_size) + + +class _PKCS7PaddingContext(PaddingContext): + _buffer: typing.Optional[bytes] + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_padding_update( + self._buffer, data, self.block_size + ) + return result + + def _padding(self, size: int) -> bytes: + return bytes([size]) * size + + def finalize(self) -> bytes: + result = _byte_padding_pad( + self._buffer, self.block_size, self._padding + ) + self._buffer = None + return result + + +class _PKCS7UnpaddingContext(PaddingContext): + _buffer: typing.Optional[bytes] + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_unpadding_update( + self._buffer, data, self.block_size + ) + return result + + def finalize(self) -> bytes: + result = _byte_unpadding_check( + self._buffer, self.block_size, check_pkcs7_padding + ) + self._buffer = None + return result + + +class ANSIX923: + def __init__(self, block_size: int): + _byte_padding_check(block_size) + self.block_size = block_size + + def padder(self) -> PaddingContext: + return _ANSIX923PaddingContext(self.block_size) + + def unpadder(self) -> PaddingContext: + return _ANSIX923UnpaddingContext(self.block_size) + + +class _ANSIX923PaddingContext(PaddingContext): + _buffer: typing.Optional[bytes] + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_padding_update( + self._buffer, data, self.block_size + ) + return result + + def _padding(self, size: int) -> bytes: + return bytes([0]) * (size - 1) + bytes([size]) + + def finalize(self) -> bytes: + result = _byte_padding_pad( + self._buffer, self.block_size, self._padding + ) + self._buffer = None + return result + + +class _ANSIX923UnpaddingContext(PaddingContext): + _buffer: typing.Optional[bytes] + + def __init__(self, block_size: int): + self.block_size = block_size + # TODO: more copies than necessary, we should use zero-buffer (#193) + self._buffer = b"" + + def update(self, data: bytes) -> bytes: + self._buffer, result = _byte_unpadding_update( + self._buffer, data, self.block_size + ) + return result + + def finalize(self) -> bytes: + result = _byte_unpadding_check( + self._buffer, + self.block_size, + check_ansix923_padding, + ) + self._buffer = None + return result diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py new file mode 100644 index 000000000..7fcf4a50f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py @@ -0,0 +1,60 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import utils +from cryptography.exceptions import ( + AlreadyFinalized, + UnsupportedAlgorithm, + _Reasons, +) +from cryptography.hazmat.backends.openssl.poly1305 import _Poly1305Context + + +class Poly1305: + _ctx: typing.Optional[_Poly1305Context] + + def __init__(self, key: bytes): + from cryptography.hazmat.backends.openssl.backend import backend + + if not backend.poly1305_supported(): + raise UnsupportedAlgorithm( + "poly1305 is not supported by this version of OpenSSL.", + _Reasons.UNSUPPORTED_MAC, + ) + self._ctx = backend.create_poly1305_ctx(key) + + def update(self, data: bytes) -> None: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + utils._check_byteslike("data", data) + self._ctx.update(data) + + def finalize(self) -> bytes: + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + mac = self._ctx.finalize() + self._ctx = None + return mac + + def verify(self, tag: bytes) -> None: + utils._check_bytes("tag", tag) + if self._ctx is None: + raise AlreadyFinalized("Context was already finalized.") + + ctx, self._ctx = self._ctx, None + ctx.verify(tag) + + @classmethod + def generate_tag(cls, key: bytes, data: bytes) -> bytes: + p = Poly1305(key) + p.update(data) + return p.finalize() + + @classmethod + def verify_tag(cls, key: bytes, data: bytes, tag: bytes) -> None: + p = Poly1305(key) + p.update(data) + p.verify(tag) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py new file mode 100644 index 000000000..af4112f39 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py @@ -0,0 +1,46 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography.hazmat.primitives._serialization import ( + BestAvailableEncryption, + Encoding, + KeySerializationEncryption, + NoEncryption, + ParameterFormat, + PrivateFormat, + PublicFormat, + _KeySerializationEncryption, +) +from cryptography.hazmat.primitives.serialization.base import ( + load_der_parameters, + load_der_private_key, + load_der_public_key, + load_pem_parameters, + load_pem_private_key, + load_pem_public_key, +) +from cryptography.hazmat.primitives.serialization.ssh import ( + load_ssh_private_key, + load_ssh_public_key, +) + +__all__ = [ + "load_der_parameters", + "load_der_private_key", + "load_der_public_key", + "load_pem_parameters", + "load_pem_private_key", + "load_pem_public_key", + "load_ssh_private_key", + "load_ssh_public_key", + "Encoding", + "PrivateFormat", + "PublicFormat", + "ParameterFormat", + "KeySerializationEncryption", + "BestAvailableEncryption", + "NoEncryption", + "_KeySerializationEncryption", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py new file mode 100644 index 000000000..8a8417664 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py @@ -0,0 +1,72 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import typing + +from cryptography.hazmat.primitives.asymmetric import dh +from cryptography.hazmat.primitives.asymmetric.types import ( + PRIVATE_KEY_TYPES, + PUBLIC_KEY_TYPES, +) + + +def load_pem_private_key( + data: bytes, + password: typing.Optional[bytes], + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PRIVATE_KEY_TYPES: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_pem_private_key( + data, password, unsafe_skip_rsa_key_validation + ) + + +def load_pem_public_key( + data: bytes, backend: typing.Any = None +) -> PUBLIC_KEY_TYPES: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_pem_public_key(data) + + +def load_pem_parameters( + data: bytes, backend: typing.Any = None +) -> "dh.DHParameters": + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_pem_parameters(data) + + +def load_der_private_key( + data: bytes, + password: typing.Optional[bytes], + backend: typing.Any = None, + *, + unsafe_skip_rsa_key_validation: bool = False, +) -> PRIVATE_KEY_TYPES: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_der_private_key( + data, password, unsafe_skip_rsa_key_validation + ) + + +def load_der_public_key( + data: bytes, backend: typing.Any = None +) -> PUBLIC_KEY_TYPES: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_der_public_key(data) + + +def load_der_parameters( + data: bytes, backend: typing.Any = None +) -> "dh.DHParameters": + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_der_parameters(data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py new file mode 100644 index 000000000..05212257d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py @@ -0,0 +1,226 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives._serialization import PBES as PBES +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed448, + ed25519, + rsa, +) +from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES + +__all__ = [ + "PBES", + "PKCS12Certificate", + "PKCS12KeyAndCertificates", + "load_key_and_certificates", + "load_pkcs12", + "serialize_key_and_certificates", +] + +_ALLOWED_PKCS12_TYPES = typing.Union[ + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, +] + + +class PKCS12Certificate: + def __init__( + self, + cert: x509.Certificate, + friendly_name: typing.Optional[bytes], + ): + if not isinstance(cert, x509.Certificate): + raise TypeError("Expecting x509.Certificate object") + if friendly_name is not None and not isinstance(friendly_name, bytes): + raise TypeError("friendly_name must be bytes or None") + self._cert = cert + self._friendly_name = friendly_name + + @property + def friendly_name(self) -> typing.Optional[bytes]: + return self._friendly_name + + @property + def certificate(self) -> x509.Certificate: + return self._cert + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PKCS12Certificate): + return NotImplemented + + return ( + self.certificate == other.certificate + and self.friendly_name == other.friendly_name + ) + + def __hash__(self) -> int: + return hash((self.certificate, self.friendly_name)) + + def __repr__(self) -> str: + return "".format( + self.certificate, self.friendly_name + ) + + +class PKCS12KeyAndCertificates: + def __init__( + self, + key: typing.Optional[PRIVATE_KEY_TYPES], + cert: typing.Optional[PKCS12Certificate], + additional_certs: typing.List[PKCS12Certificate], + ): + if key is not None and not isinstance( + key, + ( + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + ), + ): + raise TypeError( + "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" + " private key, or None." + ) + if cert is not None and not isinstance(cert, PKCS12Certificate): + raise TypeError("cert must be a PKCS12Certificate object or None") + if not all( + isinstance(add_cert, PKCS12Certificate) + for add_cert in additional_certs + ): + raise TypeError( + "all values in additional_certs must be PKCS12Certificate" + " objects" + ) + self._key = key + self._cert = cert + self._additional_certs = additional_certs + + @property + def key(self) -> typing.Optional[PRIVATE_KEY_TYPES]: + return self._key + + @property + def cert(self) -> typing.Optional[PKCS12Certificate]: + return self._cert + + @property + def additional_certs(self) -> typing.List[PKCS12Certificate]: + return self._additional_certs + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PKCS12KeyAndCertificates): + return NotImplemented + + return ( + self.key == other.key + and self.cert == other.cert + and self.additional_certs == other.additional_certs + ) + + def __hash__(self) -> int: + return hash((self.key, self.cert, tuple(self.additional_certs))) + + def __repr__(self) -> str: + fmt = ( + "" + ) + return fmt.format(self.key, self.cert, self.additional_certs) + + +def load_key_and_certificates( + data: bytes, + password: typing.Optional[bytes], + backend: typing.Any = None, +) -> typing.Tuple[ + typing.Optional[PRIVATE_KEY_TYPES], + typing.Optional[x509.Certificate], + typing.List[x509.Certificate], +]: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_key_and_certificates_from_pkcs12(data, password) + + +def load_pkcs12( + data: bytes, + password: typing.Optional[bytes], + backend: typing.Any = None, +) -> PKCS12KeyAndCertificates: + from cryptography.hazmat.backends.openssl.backend import backend as ossl + + return ossl.load_pkcs12(data, password) + + +_PKCS12_CAS_TYPES = typing.Union[ + x509.Certificate, + PKCS12Certificate, +] + + +def serialize_key_and_certificates( + name: typing.Optional[bytes], + key: typing.Optional[_ALLOWED_PKCS12_TYPES], + cert: typing.Optional[x509.Certificate], + cas: typing.Optional[typing.Iterable[_PKCS12_CAS_TYPES]], + encryption_algorithm: serialization.KeySerializationEncryption, +) -> bytes: + if key is not None and not isinstance( + key, + ( + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ec.EllipticCurvePrivateKey, + ed25519.Ed25519PrivateKey, + ed448.Ed448PrivateKey, + ), + ): + raise TypeError( + "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" + " private key, or None." + ) + if cert is not None and not isinstance(cert, x509.Certificate): + raise TypeError("cert must be a certificate or None") + + if cas is not None: + cas = list(cas) + if not all( + isinstance( + val, + ( + x509.Certificate, + PKCS12Certificate, + ), + ) + for val in cas + ): + raise TypeError("all values in cas must be certificates") + + if not isinstance( + encryption_algorithm, serialization.KeySerializationEncryption + ): + raise TypeError( + "Key encryption algorithm must be a " + "KeySerializationEncryption instance" + ) + + if key is None and cert is None and not cas: + raise ValueError("You must supply at least one of key, cert, or cas") + + from cryptography.hazmat.backends.openssl.backend import backend + + return backend.serialize_key_and_certificates_to_pkcs12( + name, key, cert, cas, encryption_algorithm + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py new file mode 100644 index 000000000..7e593e719 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py @@ -0,0 +1,219 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import email.base64mime +import email.generator +import email.message +import io +import typing + +from cryptography import utils, x509 +from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ec, rsa +from cryptography.utils import _check_byteslike + + +def load_pem_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]: + from cryptography.hazmat.backends.openssl.backend import backend + + return backend.load_pem_pkcs7_certificates(data) + + +def load_der_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]: + from cryptography.hazmat.backends.openssl.backend import backend + + return backend.load_der_pkcs7_certificates(data) + + +def serialize_certificates( + certs: typing.List[x509.Certificate], + encoding: serialization.Encoding, +) -> bytes: + return rust_pkcs7.serialize_certificates(certs, encoding) + + +_ALLOWED_PKCS7_HASH_TYPES = typing.Union[ + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, +] + +_ALLOWED_PRIVATE_KEY_TYPES = typing.Union[ + rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey +] + + +class PKCS7Options(utils.Enum): + Text = "Add text/plain MIME type" + Binary = "Don't translate input data into canonical MIME format" + DetachedSignature = "Don't embed data in the PKCS7 structure" + NoCapabilities = "Don't embed SMIME capabilities" + NoAttributes = "Don't embed authenticatedAttributes" + NoCerts = "Don't embed signer certificate" + + +class PKCS7SignatureBuilder: + def __init__( + self, + data: typing.Optional[bytes] = None, + signers: typing.List[ + typing.Tuple[ + x509.Certificate, + _ALLOWED_PRIVATE_KEY_TYPES, + _ALLOWED_PKCS7_HASH_TYPES, + ] + ] = [], + additional_certs: typing.List[x509.Certificate] = [], + ): + self._data = data + self._signers = signers + self._additional_certs = additional_certs + + def set_data(self, data: bytes) -> "PKCS7SignatureBuilder": + _check_byteslike("data", data) + if self._data is not None: + raise ValueError("data may only be set once") + + return PKCS7SignatureBuilder(bytes(data), self._signers) + + def add_signer( + self, + certificate: x509.Certificate, + private_key: _ALLOWED_PRIVATE_KEY_TYPES, + hash_algorithm: _ALLOWED_PKCS7_HASH_TYPES, + ) -> "PKCS7SignatureBuilder": + if not isinstance( + hash_algorithm, + ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, + ), + ): + raise TypeError( + "hash_algorithm must be one of hashes.SHA1, SHA224, " + "SHA256, SHA384, or SHA512" + ) + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + if not isinstance( + private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) + ): + raise TypeError("Only RSA & EC keys are supported at this time.") + + return PKCS7SignatureBuilder( + self._data, + self._signers + [(certificate, private_key, hash_algorithm)], + ) + + def add_certificate( + self, certificate: x509.Certificate + ) -> "PKCS7SignatureBuilder": + if not isinstance(certificate, x509.Certificate): + raise TypeError("certificate must be a x509.Certificate") + + return PKCS7SignatureBuilder( + self._data, self._signers, self._additional_certs + [certificate] + ) + + def sign( + self, + encoding: serialization.Encoding, + options: typing.Iterable[PKCS7Options], + backend: typing.Any = None, + ) -> bytes: + if len(self._signers) == 0: + raise ValueError("Must have at least one signer") + if self._data is None: + raise ValueError("You must add data to sign") + options = list(options) + if not all(isinstance(x, PKCS7Options) for x in options): + raise ValueError("options must be from the PKCS7Options enum") + if encoding not in ( + serialization.Encoding.PEM, + serialization.Encoding.DER, + serialization.Encoding.SMIME, + ): + raise ValueError( + "Must be PEM, DER, or SMIME from the Encoding enum" + ) + + # Text is a meaningless option unless it is accompanied by + # DetachedSignature + if ( + PKCS7Options.Text in options + and PKCS7Options.DetachedSignature not in options + ): + raise ValueError( + "When passing the Text option you must also pass " + "DetachedSignature" + ) + + if PKCS7Options.Text in options and encoding in ( + serialization.Encoding.DER, + serialization.Encoding.PEM, + ): + raise ValueError( + "The Text option is only available for SMIME serialization" + ) + + # No attributes implies no capabilities so we'll error if you try to + # pass both. + if ( + PKCS7Options.NoAttributes in options + and PKCS7Options.NoCapabilities in options + ): + raise ValueError( + "NoAttributes is a superset of NoCapabilities. Do not pass " + "both values." + ) + + return rust_pkcs7.sign_and_serialize(self, encoding, options) + + +def _smime_encode(data: bytes, signature: bytes, micalg: str) -> bytes: + # This function works pretty hard to replicate what OpenSSL does + # precisely. For good and for ill. + + m = email.message.Message() + m.add_header("MIME-Version", "1.0") + m.add_header( + "Content-Type", + "multipart/signed", + protocol="application/x-pkcs7-signature", + micalg=micalg, + ) + + m.preamble = "This is an S/MIME signed message\n" + + msg_part = email.message.MIMEPart() + msg_part.set_payload(data) + msg_part.add_header("Content-Type", "text/plain") + m.attach(msg_part) + + sig_part = email.message.MIMEPart() + sig_part.add_header( + "Content-Type", "application/x-pkcs7-signature", name="smime.p7s" + ) + sig_part.add_header("Content-Transfer-Encoding", "base64") + sig_part.add_header( + "Content-Disposition", "attachment", filename="smime.p7s" + ) + sig_part.set_payload( + email.base64mime.body_encode(signature, maxlinelen=65) + ) + del sig_part["MIME-Version"] + m.attach(sig_part) + + fp = io.BytesIO() + g = email.generator.BytesGenerator( + fp, maxheaderlen=0, mangle_from_=False, policy=m.policy + ) + g.flatten(m) + return fp.getvalue() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py new file mode 100644 index 000000000..7125badb4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py @@ -0,0 +1,758 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import binascii +import os +import re +import typing +from base64 import encodebytes as _base64_encode + +from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm +from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.serialization import ( + Encoding, + KeySerializationEncryption, + NoEncryption, + PrivateFormat, + PublicFormat, + _KeySerializationEncryption, +) + +try: + from bcrypt import kdf as _bcrypt_kdf + + _bcrypt_supported = True +except ImportError: + _bcrypt_supported = False + + def _bcrypt_kdf( + password: bytes, + salt: bytes, + desired_key_bytes: int, + rounds: int, + ignore_few_rounds: bool = False, + ) -> bytes: + raise UnsupportedAlgorithm("Need bcrypt module") + + +_SSH_ED25519 = b"ssh-ed25519" +_SSH_RSA = b"ssh-rsa" +_SSH_DSA = b"ssh-dss" +_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256" +_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384" +_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521" +_CERT_SUFFIX = b"-cert-v01@openssh.com" + +_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") +_SK_MAGIC = b"openssh-key-v1\0" +_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----" +_SK_END = b"-----END OPENSSH PRIVATE KEY-----" +_BCRYPT = b"bcrypt" +_NONE = b"none" +_DEFAULT_CIPHER = b"aes256-ctr" +_DEFAULT_ROUNDS = 16 + +# re is only way to work on bytes-like data +_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL) + +# padding for max blocksize +_PADDING = memoryview(bytearray(range(1, 1 + 16))) + +# ciphers that are actually used in key wrapping +_SSH_CIPHERS: typing.Dict[ + bytes, + typing.Tuple[ + typing.Type[algorithms.AES], + int, + typing.Union[typing.Type[modes.CTR], typing.Type[modes.CBC]], + int, + ], +] = { + b"aes256-ctr": (algorithms.AES, 32, modes.CTR, 16), + b"aes256-cbc": (algorithms.AES, 32, modes.CBC, 16), +} + +# map local curve name to key type +_ECDSA_KEY_TYPE = { + "secp256r1": _ECDSA_NISTP256, + "secp384r1": _ECDSA_NISTP384, + "secp521r1": _ECDSA_NISTP521, +} + + +def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes: + """Return SSH key_type and curve_name for private key.""" + curve = public_key.curve + if curve.name not in _ECDSA_KEY_TYPE: + raise ValueError( + f"Unsupported curve for ssh private key: {curve.name!r}" + ) + return _ECDSA_KEY_TYPE[curve.name] + + +def _ssh_pem_encode( + data: bytes, + prefix: bytes = _SK_START + b"\n", + suffix: bytes = _SK_END + b"\n", +) -> bytes: + return b"".join([prefix, _base64_encode(data), suffix]) + + +def _check_block_size(data: bytes, block_len: int) -> None: + """Require data to be full blocks""" + if not data or len(data) % block_len != 0: + raise ValueError("Corrupt data: missing padding") + + +def _check_empty(data: bytes) -> None: + """All data should have been parsed.""" + if data: + raise ValueError("Corrupt data: unparsed data") + + +def _init_cipher( + ciphername: bytes, + password: typing.Optional[bytes], + salt: bytes, + rounds: int, +) -> Cipher[typing.Union[modes.CBC, modes.CTR]]: + """Generate key + iv and return cipher.""" + if not password: + raise ValueError("Key is password-protected.") + + algo, key_len, mode, iv_len = _SSH_CIPHERS[ciphername] + seed = _bcrypt_kdf(password, salt, key_len + iv_len, rounds, True) + return Cipher(algo(seed[:key_len]), mode(seed[key_len:])) + + +def _get_u32(data: memoryview) -> typing.Tuple[int, memoryview]: + """Uint32""" + if len(data) < 4: + raise ValueError("Invalid data") + return int.from_bytes(data[:4], byteorder="big"), data[4:] + + +def _get_u64(data: memoryview) -> typing.Tuple[int, memoryview]: + """Uint64""" + if len(data) < 8: + raise ValueError("Invalid data") + return int.from_bytes(data[:8], byteorder="big"), data[8:] + + +def _get_sshstr(data: memoryview) -> typing.Tuple[memoryview, memoryview]: + """Bytes with u32 length prefix""" + n, data = _get_u32(data) + if n > len(data): + raise ValueError("Invalid data") + return data[:n], data[n:] + + +def _get_mpint(data: memoryview) -> typing.Tuple[int, memoryview]: + """Big integer.""" + val, data = _get_sshstr(data) + if val and val[0] > 0x7F: + raise ValueError("Invalid data") + return int.from_bytes(val, "big"), data + + +def _to_mpint(val: int) -> bytes: + """Storage format for signed bigint.""" + if val < 0: + raise ValueError("negative mpint not allowed") + if not val: + return b"" + nbytes = (val.bit_length() + 8) // 8 + return utils.int_to_bytes(val, nbytes) + + +class _FragList: + """Build recursive structure without data copy.""" + + flist: typing.List[bytes] + + def __init__( + self, init: typing.Optional[typing.List[bytes]] = None + ) -> None: + self.flist = [] + if init: + self.flist.extend(init) + + def put_raw(self, val: bytes) -> None: + """Add plain bytes""" + self.flist.append(val) + + def put_u32(self, val: int) -> None: + """Big-endian uint32""" + self.flist.append(val.to_bytes(length=4, byteorder="big")) + + def put_sshstr(self, val: typing.Union[bytes, "_FragList"]) -> None: + """Bytes prefixed with u32 length""" + if isinstance(val, (bytes, memoryview, bytearray)): + self.put_u32(len(val)) + self.flist.append(val) + else: + self.put_u32(val.size()) + self.flist.extend(val.flist) + + def put_mpint(self, val: int) -> None: + """Big-endian bigint prefixed with u32 length""" + self.put_sshstr(_to_mpint(val)) + + def size(self) -> int: + """Current number of bytes""" + return sum(map(len, self.flist)) + + def render(self, dstbuf: memoryview, pos: int = 0) -> int: + """Write into bytearray""" + for frag in self.flist: + flen = len(frag) + start, pos = pos, pos + flen + dstbuf[start:pos] = frag + return pos + + def tobytes(self) -> bytes: + """Return as bytes""" + buf = memoryview(bytearray(self.size())) + self.render(buf) + return buf.tobytes() + + +class _SSHFormatRSA: + """Format for RSA keys. + + Public: + mpint e, n + Private: + mpint n, e, d, iqmp, p, q + """ + + def get_public(self, data: memoryview): + """RSA public fields""" + e, data = _get_mpint(data) + n, data = _get_mpint(data) + return (e, n), data + + def load_public( + self, data: memoryview + ) -> typing.Tuple[rsa.RSAPublicKey, memoryview]: + """Make RSA public key from data.""" + (e, n), data = self.get_public(data) + public_numbers = rsa.RSAPublicNumbers(e, n) + public_key = public_numbers.public_key() + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> typing.Tuple[rsa.RSAPrivateKey, memoryview]: + """Make RSA private key from data.""" + n, data = _get_mpint(data) + e, data = _get_mpint(data) + d, data = _get_mpint(data) + iqmp, data = _get_mpint(data) + p, data = _get_mpint(data) + q, data = _get_mpint(data) + + if (e, n) != pubfields: + raise ValueError("Corrupt data: rsa field mismatch") + dmp1 = rsa.rsa_crt_dmp1(d, p) + dmq1 = rsa.rsa_crt_dmq1(d, q) + public_numbers = rsa.RSAPublicNumbers(e, n) + private_numbers = rsa.RSAPrivateNumbers( + p, q, d, dmp1, dmq1, iqmp, public_numbers + ) + private_key = private_numbers.private_key() + return private_key, data + + def encode_public( + self, public_key: rsa.RSAPublicKey, f_pub: _FragList + ) -> None: + """Write RSA public key""" + pubn = public_key.public_numbers() + f_pub.put_mpint(pubn.e) + f_pub.put_mpint(pubn.n) + + def encode_private( + self, private_key: rsa.RSAPrivateKey, f_priv: _FragList + ) -> None: + """Write RSA private key""" + private_numbers = private_key.private_numbers() + public_numbers = private_numbers.public_numbers + + f_priv.put_mpint(public_numbers.n) + f_priv.put_mpint(public_numbers.e) + + f_priv.put_mpint(private_numbers.d) + f_priv.put_mpint(private_numbers.iqmp) + f_priv.put_mpint(private_numbers.p) + f_priv.put_mpint(private_numbers.q) + + +class _SSHFormatDSA: + """Format for DSA keys. + + Public: + mpint p, q, g, y + Private: + mpint p, q, g, y, x + """ + + def get_public( + self, data: memoryview + ) -> typing.Tuple[typing.Tuple, memoryview]: + """DSA public fields""" + p, data = _get_mpint(data) + q, data = _get_mpint(data) + g, data = _get_mpint(data) + y, data = _get_mpint(data) + return (p, q, g, y), data + + def load_public( + self, data: memoryview + ) -> typing.Tuple[dsa.DSAPublicKey, memoryview]: + """Make DSA public key from data.""" + (p, q, g, y), data = self.get_public(data) + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + self._validate(public_numbers) + public_key = public_numbers.public_key() + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> typing.Tuple[dsa.DSAPrivateKey, memoryview]: + """Make DSA private key from data.""" + (p, q, g, y), data = self.get_public(data) + x, data = _get_mpint(data) + + if (p, q, g, y) != pubfields: + raise ValueError("Corrupt data: dsa field mismatch") + parameter_numbers = dsa.DSAParameterNumbers(p, q, g) + public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) + self._validate(public_numbers) + private_numbers = dsa.DSAPrivateNumbers(x, public_numbers) + private_key = private_numbers.private_key() + return private_key, data + + def encode_public( + self, public_key: dsa.DSAPublicKey, f_pub: _FragList + ) -> None: + """Write DSA public key""" + public_numbers = public_key.public_numbers() + parameter_numbers = public_numbers.parameter_numbers + self._validate(public_numbers) + + f_pub.put_mpint(parameter_numbers.p) + f_pub.put_mpint(parameter_numbers.q) + f_pub.put_mpint(parameter_numbers.g) + f_pub.put_mpint(public_numbers.y) + + def encode_private( + self, private_key: dsa.DSAPrivateKey, f_priv: _FragList + ) -> None: + """Write DSA private key""" + self.encode_public(private_key.public_key(), f_priv) + f_priv.put_mpint(private_key.private_numbers().x) + + def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None: + parameter_numbers = public_numbers.parameter_numbers + if parameter_numbers.p.bit_length() != 1024: + raise ValueError("SSH supports only 1024 bit DSA keys") + + +class _SSHFormatECDSA: + """Format for ECDSA keys. + + Public: + str curve + bytes point + Private: + str curve + bytes point + mpint secret + """ + + def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve): + self.ssh_curve_name = ssh_curve_name + self.curve = curve + + def get_public( + self, data: memoryview + ) -> typing.Tuple[typing.Tuple, memoryview]: + """ECDSA public fields""" + curve, data = _get_sshstr(data) + point, data = _get_sshstr(data) + if curve != self.ssh_curve_name: + raise ValueError("Curve name mismatch") + if point[0] != 4: + raise NotImplementedError("Need uncompressed point") + return (curve, point), data + + def load_public( + self, data: memoryview + ) -> typing.Tuple[ec.EllipticCurvePublicKey, memoryview]: + """Make ECDSA public key from data.""" + (curve_name, point), data = self.get_public(data) + public_key = ec.EllipticCurvePublicKey.from_encoded_point( + self.curve, point.tobytes() + ) + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> typing.Tuple[ec.EllipticCurvePrivateKey, memoryview]: + """Make ECDSA private key from data.""" + (curve_name, point), data = self.get_public(data) + secret, data = _get_mpint(data) + + if (curve_name, point) != pubfields: + raise ValueError("Corrupt data: ecdsa field mismatch") + private_key = ec.derive_private_key(secret, self.curve) + return private_key, data + + def encode_public( + self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList + ) -> None: + """Write ECDSA public key""" + point = public_key.public_bytes( + Encoding.X962, PublicFormat.UncompressedPoint + ) + f_pub.put_sshstr(self.ssh_curve_name) + f_pub.put_sshstr(point) + + def encode_private( + self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList + ) -> None: + """Write ECDSA private key""" + public_key = private_key.public_key() + private_numbers = private_key.private_numbers() + + self.encode_public(public_key, f_priv) + f_priv.put_mpint(private_numbers.private_value) + + +class _SSHFormatEd25519: + """Format for Ed25519 keys. + + Public: + bytes point + Private: + bytes point + bytes secret_and_point + """ + + def get_public( + self, data: memoryview + ) -> typing.Tuple[typing.Tuple, memoryview]: + """Ed25519 public fields""" + point, data = _get_sshstr(data) + return (point,), data + + def load_public( + self, data: memoryview + ) -> typing.Tuple[ed25519.Ed25519PublicKey, memoryview]: + """Make Ed25519 public key from data.""" + (point,), data = self.get_public(data) + public_key = ed25519.Ed25519PublicKey.from_public_bytes( + point.tobytes() + ) + return public_key, data + + def load_private( + self, data: memoryview, pubfields + ) -> typing.Tuple[ed25519.Ed25519PrivateKey, memoryview]: + """Make Ed25519 private key from data.""" + (point,), data = self.get_public(data) + keypair, data = _get_sshstr(data) + + secret = keypair[:32] + point2 = keypair[32:] + if point != point2 or (point,) != pubfields: + raise ValueError("Corrupt data: ed25519 field mismatch") + private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) + return private_key, data + + def encode_public( + self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList + ) -> None: + """Write Ed25519 public key""" + raw_public_key = public_key.public_bytes( + Encoding.Raw, PublicFormat.Raw + ) + f_pub.put_sshstr(raw_public_key) + + def encode_private( + self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList + ) -> None: + """Write Ed25519 private key""" + public_key = private_key.public_key() + raw_private_key = private_key.private_bytes( + Encoding.Raw, PrivateFormat.Raw, NoEncryption() + ) + raw_public_key = public_key.public_bytes( + Encoding.Raw, PublicFormat.Raw + ) + f_keypair = _FragList([raw_private_key, raw_public_key]) + + self.encode_public(public_key, f_priv) + f_priv.put_sshstr(f_keypair) + + +_KEY_FORMATS = { + _SSH_RSA: _SSHFormatRSA(), + _SSH_DSA: _SSHFormatDSA(), + _SSH_ED25519: _SSHFormatEd25519(), + _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()), + _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()), + _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()), +} + + +def _lookup_kformat(key_type: bytes): + """Return valid format or throw error""" + if not isinstance(key_type, bytes): + key_type = memoryview(key_type).tobytes() + if key_type in _KEY_FORMATS: + return _KEY_FORMATS[key_type] + raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}") + + +_SSH_PRIVATE_KEY_TYPES = typing.Union[ + ec.EllipticCurvePrivateKey, + rsa.RSAPrivateKey, + dsa.DSAPrivateKey, + ed25519.Ed25519PrivateKey, +] + + +def load_ssh_private_key( + data: bytes, + password: typing.Optional[bytes], + backend: typing.Any = None, +) -> _SSH_PRIVATE_KEY_TYPES: + """Load private key from OpenSSH custom encoding.""" + utils._check_byteslike("data", data) + if password is not None: + utils._check_bytes("password", password) + + m = _PEM_RC.search(data) + if not m: + raise ValueError("Not OpenSSH private key format") + p1 = m.start(1) + p2 = m.end(1) + data = binascii.a2b_base64(memoryview(data)[p1:p2]) + if not data.startswith(_SK_MAGIC): + raise ValueError("Not OpenSSH private key format") + data = memoryview(data)[len(_SK_MAGIC) :] + + # parse header + ciphername, data = _get_sshstr(data) + kdfname, data = _get_sshstr(data) + kdfoptions, data = _get_sshstr(data) + nkeys, data = _get_u32(data) + if nkeys != 1: + raise ValueError("Only one key supported") + + # load public key data + pubdata, data = _get_sshstr(data) + pub_key_type, pubdata = _get_sshstr(pubdata) + kformat = _lookup_kformat(pub_key_type) + pubfields, pubdata = kformat.get_public(pubdata) + _check_empty(pubdata) + + # load secret data + edata, data = _get_sshstr(data) + _check_empty(data) + + if (ciphername, kdfname) != (_NONE, _NONE): + ciphername_bytes = ciphername.tobytes() + if ciphername_bytes not in _SSH_CIPHERS: + raise UnsupportedAlgorithm( + f"Unsupported cipher: {ciphername_bytes!r}" + ) + if kdfname != _BCRYPT: + raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}") + blklen = _SSH_CIPHERS[ciphername_bytes][3] + _check_block_size(edata, blklen) + salt, kbuf = _get_sshstr(kdfoptions) + rounds, kbuf = _get_u32(kbuf) + _check_empty(kbuf) + ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds) + edata = memoryview(ciph.decryptor().update(edata)) + else: + blklen = 8 + _check_block_size(edata, blklen) + ck1, edata = _get_u32(edata) + ck2, edata = _get_u32(edata) + if ck1 != ck2: + raise ValueError("Corrupt data: broken checksum") + + # load per-key struct + key_type, edata = _get_sshstr(edata) + if key_type != pub_key_type: + raise ValueError("Corrupt data: key type mismatch") + private_key, edata = kformat.load_private(edata, pubfields) + comment, edata = _get_sshstr(edata) + + # yes, SSH does padding check *after* all other parsing is done. + # need to follow as it writes zero-byte padding too. + if edata != _PADDING[: len(edata)]: + raise ValueError("Corrupt data: invalid padding") + + return private_key + + +def _serialize_ssh_private_key( + private_key: _SSH_PRIVATE_KEY_TYPES, + password: bytes, + encryption_algorithm: KeySerializationEncryption, +) -> bytes: + """Serialize private key with OpenSSH custom encoding.""" + utils._check_bytes("password", password) + + if isinstance(private_key, ec.EllipticCurvePrivateKey): + key_type = _ecdsa_key_type(private_key.public_key()) + elif isinstance(private_key, rsa.RSAPrivateKey): + key_type = _SSH_RSA + elif isinstance(private_key, dsa.DSAPrivateKey): + key_type = _SSH_DSA + elif isinstance(private_key, ed25519.Ed25519PrivateKey): + key_type = _SSH_ED25519 + else: + raise ValueError("Unsupported key type") + kformat = _lookup_kformat(key_type) + + # setup parameters + f_kdfoptions = _FragList() + if password: + ciphername = _DEFAULT_CIPHER + blklen = _SSH_CIPHERS[ciphername][3] + kdfname = _BCRYPT + rounds = _DEFAULT_ROUNDS + if ( + isinstance(encryption_algorithm, _KeySerializationEncryption) + and encryption_algorithm._kdf_rounds is not None + ): + rounds = encryption_algorithm._kdf_rounds + salt = os.urandom(16) + f_kdfoptions.put_sshstr(salt) + f_kdfoptions.put_u32(rounds) + ciph = _init_cipher(ciphername, password, salt, rounds) + else: + ciphername = kdfname = _NONE + blklen = 8 + ciph = None + nkeys = 1 + checkval = os.urandom(4) + comment = b"" + + # encode public and private parts together + f_public_key = _FragList() + f_public_key.put_sshstr(key_type) + kformat.encode_public(private_key.public_key(), f_public_key) + + f_secrets = _FragList([checkval, checkval]) + f_secrets.put_sshstr(key_type) + kformat.encode_private(private_key, f_secrets) + f_secrets.put_sshstr(comment) + f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)]) + + # top-level structure + f_main = _FragList() + f_main.put_raw(_SK_MAGIC) + f_main.put_sshstr(ciphername) + f_main.put_sshstr(kdfname) + f_main.put_sshstr(f_kdfoptions) + f_main.put_u32(nkeys) + f_main.put_sshstr(f_public_key) + f_main.put_sshstr(f_secrets) + + # copy result info bytearray + slen = f_secrets.size() + mlen = f_main.size() + buf = memoryview(bytearray(mlen + blklen)) + f_main.render(buf) + ofs = mlen - slen + + # encrypt in-place + if ciph is not None: + ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:]) + + return _ssh_pem_encode(buf[:mlen]) + + +_SSH_PUBLIC_KEY_TYPES = typing.Union[ + ec.EllipticCurvePublicKey, + rsa.RSAPublicKey, + dsa.DSAPublicKey, + ed25519.Ed25519PublicKey, +] + + +def load_ssh_public_key( + data: bytes, backend: typing.Any = None +) -> _SSH_PUBLIC_KEY_TYPES: + """Load public key from OpenSSH one-line format.""" + utils._check_byteslike("data", data) + + m = _SSH_PUBKEY_RC.match(data) + if not m: + raise ValueError("Invalid line format") + key_type = orig_key_type = m.group(1) + key_body = m.group(2) + with_cert = False + if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]: + with_cert = True + key_type = key_type[: -len(_CERT_SUFFIX)] + kformat = _lookup_kformat(key_type) + + try: + rest = memoryview(binascii.a2b_base64(key_body)) + except (TypeError, binascii.Error): + raise ValueError("Invalid key format") + + inner_key_type, rest = _get_sshstr(rest) + if inner_key_type != orig_key_type: + raise ValueError("Invalid key format") + if with_cert: + nonce, rest = _get_sshstr(rest) + public_key, rest = kformat.load_public(rest) + if with_cert: + serial, rest = _get_u64(rest) + cctype, rest = _get_u32(rest) + key_id, rest = _get_sshstr(rest) + principals, rest = _get_sshstr(rest) + valid_after, rest = _get_u64(rest) + valid_before, rest = _get_u64(rest) + crit_options, rest = _get_sshstr(rest) + extensions, rest = _get_sshstr(rest) + reserved, rest = _get_sshstr(rest) + sig_key, rest = _get_sshstr(rest) + signature, rest = _get_sshstr(rest) + _check_empty(rest) + return public_key + + +def serialize_ssh_public_key(public_key: _SSH_PUBLIC_KEY_TYPES) -> bytes: + """One-line public key format for OpenSSH""" + if isinstance(public_key, ec.EllipticCurvePublicKey): + key_type = _ecdsa_key_type(public_key) + elif isinstance(public_key, rsa.RSAPublicKey): + key_type = _SSH_RSA + elif isinstance(public_key, dsa.DSAPublicKey): + key_type = _SSH_DSA + elif isinstance(public_key, ed25519.Ed25519PublicKey): + key_type = _SSH_ED25519 + else: + raise ValueError("Unsupported key type") + kformat = _lookup_kformat(key_type) + + f_pub = _FragList() + f_pub.put_sshstr(key_type) + kformat.encode_public(public_key, f_pub) + + pub = binascii.b2a_base64(f_pub.tobytes()).strip() + return b"".join([key_type, b" ", pub]) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py new file mode 100644 index 000000000..8a8b30f2a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py @@ -0,0 +1,7 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +class InvalidToken(Exception): + pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py new file mode 100644 index 000000000..cbb22704b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py @@ -0,0 +1,91 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import base64 +import typing +from urllib.parse import quote, urlencode + +from cryptography.hazmat.primitives import constant_time, hmac +from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 +from cryptography.hazmat.primitives.twofactor import InvalidToken + +_ALLOWED_HASH_TYPES = typing.Union[SHA1, SHA256, SHA512] + + +def _generate_uri( + hotp: "HOTP", + type_name: str, + account_name: str, + issuer: typing.Optional[str], + extra_parameters: typing.List[typing.Tuple[str, int]], +) -> str: + parameters = [ + ("digits", hotp._length), + ("secret", base64.b32encode(hotp._key)), + ("algorithm", hotp._algorithm.name.upper()), + ] + + if issuer is not None: + parameters.append(("issuer", issuer)) + + parameters.extend(extra_parameters) + + label = ( + f"{quote(issuer)}:{quote(account_name)}" + if issuer + else quote(account_name) + ) + return f"otpauth://{type_name}/{label}?{urlencode(parameters)}" + + +class HOTP: + def __init__( + self, + key: bytes, + length: int, + algorithm: _ALLOWED_HASH_TYPES, + backend: typing.Any = None, + enforce_key_length: bool = True, + ) -> None: + if len(key) < 16 and enforce_key_length is True: + raise ValueError("Key length has to be at least 128 bits.") + + if not isinstance(length, int): + raise TypeError("Length parameter must be an integer type.") + + if length < 6 or length > 8: + raise ValueError("Length of HOTP has to be between 6 and 8.") + + if not isinstance(algorithm, (SHA1, SHA256, SHA512)): + raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") + + self._key = key + self._length = length + self._algorithm = algorithm + + def generate(self, counter: int) -> bytes: + truncated_value = self._dynamic_truncate(counter) + hotp = truncated_value % (10**self._length) + return "{0:0{1}}".format(hotp, self._length).encode() + + def verify(self, hotp: bytes, counter: int) -> None: + if not constant_time.bytes_eq(self.generate(counter), hotp): + raise InvalidToken("Supplied HOTP value does not match.") + + def _dynamic_truncate(self, counter: int) -> int: + ctx = hmac.HMAC(self._key, self._algorithm) + ctx.update(counter.to_bytes(length=8, byteorder="big")) + hmac_value = ctx.finalize() + + offset = hmac_value[len(hmac_value) - 1] & 0b1111 + p = hmac_value[offset : offset + 4] + return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF + + def get_provisioning_uri( + self, account_name: str, counter: int, issuer: typing.Optional[str] + ) -> str: + return _generate_uri( + self, "hotp", account_name, issuer, [("counter", int(counter))] + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py new file mode 100644 index 000000000..314dbef71 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py @@ -0,0 +1,48 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import typing + +from cryptography.hazmat.primitives import constant_time +from cryptography.hazmat.primitives.twofactor import InvalidToken +from cryptography.hazmat.primitives.twofactor.hotp import ( + _ALLOWED_HASH_TYPES, + HOTP, + _generate_uri, +) + + +class TOTP: + def __init__( + self, + key: bytes, + length: int, + algorithm: _ALLOWED_HASH_TYPES, + time_step: int, + backend: typing.Any = None, + enforce_key_length: bool = True, + ): + self._time_step = time_step + self._hotp = HOTP( + key, length, algorithm, enforce_key_length=enforce_key_length + ) + + def generate(self, time: typing.Union[int, float]) -> bytes: + counter = int(time / self._time_step) + return self._hotp.generate(counter) + + def verify(self, totp: bytes, time: int) -> None: + if not constant_time.bytes_eq(self.generate(time), totp): + raise InvalidToken("Supplied TOTP value does not match.") + + def get_provisioning_uri( + self, account_name: str, issuer: typing.Optional[str] + ) -> str: + return _generate_uri( + self._hotp, + "totp", + account_name, + issuer, + [("period", int(self._time_step))], + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/py.typed b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py new file mode 100644 index 000000000..7f4a4799b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py @@ -0,0 +1,132 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import enum +import sys +import types +import typing +import warnings + + +# We use a UserWarning subclass, instead of DeprecationWarning, because CPython +# decided deprecation warnings should be invisble by default. +class CryptographyDeprecationWarning(UserWarning): + pass + + +# Several APIs were deprecated with no specific end-of-life date because of the +# ubiquity of their use. They should not be removed until we agree on when that +# cycle ends. +DeprecatedIn36 = CryptographyDeprecationWarning +DeprecatedIn37 = CryptographyDeprecationWarning +DeprecatedIn39 = CryptographyDeprecationWarning + + +def _check_bytes(name: str, value: bytes) -> None: + if not isinstance(value, bytes): + raise TypeError("{} must be bytes".format(name)) + + +def _check_byteslike(name: str, value: bytes) -> None: + try: + memoryview(value) + except TypeError: + raise TypeError("{} must be bytes-like".format(name)) + + +def int_to_bytes(integer: int, length: typing.Optional[int] = None) -> bytes: + return integer.to_bytes( + length or (integer.bit_length() + 7) // 8 or 1, "big" + ) + + +class InterfaceNotImplemented(Exception): + pass + + +# DeprecatedIn39 -- Our only known consumer is aws-encryption-sdk, but we've +# made this a no-op to avoid breaking old versions. +def verify_interface( + iface: abc.ABCMeta, klass: object, *, check_annotations: bool = False +): + # Exists exclusively for `aws-encryption-sdk` which relies on it existing, + # even though it was never a public API. + pass + + +class _DeprecatedValue: + def __init__(self, value: object, message: str, warning_class): + self.value = value + self.message = message + self.warning_class = warning_class + + +class _ModuleWithDeprecations(types.ModuleType): + def __init__(self, module: types.ModuleType): + super().__init__(module.__name__) + self.__dict__["_module"] = module + + def __getattr__(self, attr: str) -> object: + obj = getattr(self._module, attr) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + obj = obj.value + return obj + + def __setattr__(self, attr: str, value: object) -> None: + setattr(self._module, attr, value) + + def __delattr__(self, attr: str) -> None: + obj = getattr(self._module, attr) + if isinstance(obj, _DeprecatedValue): + warnings.warn(obj.message, obj.warning_class, stacklevel=2) + + delattr(self._module, attr) + + def __dir__(self) -> typing.Sequence[str]: + return ["_module"] + dir(self._module) + + +def deprecated( + value: object, + module_name: str, + message: str, + warning_class: typing.Type[Warning], + name: typing.Optional[str] = None, +) -> _DeprecatedValue: + module = sys.modules[module_name] + if not isinstance(module, _ModuleWithDeprecations): + sys.modules[module_name] = module = _ModuleWithDeprecations(module) + dv = _DeprecatedValue(value, message, warning_class) + # Maintain backwards compatibility with `name is None` for pyOpenSSL. + if name is not None: + setattr(module, name, dv) + return dv + + +def cached_property(func: typing.Callable) -> property: + cached_name = "_cached_{}".format(func) + sentinel = object() + + def inner(instance: object): + cache = getattr(instance, cached_name, sentinel) + if cache is not sentinel: + return cache + result = func(instance) + setattr(instance, cached_name, result) + return result + + return property(inner) + + +# Python 3.10 changed representation of enums. We use well-defined object +# representation and string representation from Python 3.9. +class Enum(enum.Enum): + def __repr__(self) -> str: + return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" + + def __str__(self) -> str: + return f"{self.__class__.__name__}.{self._name_}" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py new file mode 100644 index 000000000..ad924ad42 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py @@ -0,0 +1,250 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +from cryptography.x509 import certificate_transparency +from cryptography.x509.base import ( + Attribute, + AttributeNotFound, + Attributes, + Certificate, + CertificateBuilder, + CertificateRevocationList, + CertificateRevocationListBuilder, + CertificateSigningRequest, + CertificateSigningRequestBuilder, + InvalidVersion, + RevokedCertificate, + RevokedCertificateBuilder, + Version, + load_der_x509_certificate, + load_der_x509_crl, + load_der_x509_csr, + load_pem_x509_certificate, + load_pem_x509_certificates, + load_pem_x509_crl, + load_pem_x509_csr, + random_serial_number, +) +from cryptography.x509.extensions import ( + AccessDescription, + AuthorityInformationAccess, + AuthorityKeyIdentifier, + BasicConstraints, + CertificateIssuer, + CertificatePolicies, + CRLDistributionPoints, + CRLNumber, + CRLReason, + DeltaCRLIndicator, + DistributionPoint, + DuplicateExtension, + ExtendedKeyUsage, + Extension, + ExtensionNotFound, + Extensions, + ExtensionType, + FreshestCRL, + GeneralNames, + InhibitAnyPolicy, + InvalidityDate, + IssuerAlternativeName, + IssuingDistributionPoint, + KeyUsage, + NameConstraints, + NoticeReference, + OCSPNoCheck, + OCSPNonce, + PolicyConstraints, + PolicyInformation, + PrecertificateSignedCertificateTimestamps, + PrecertPoison, + ReasonFlags, + SignedCertificateTimestamps, + SubjectAlternativeName, + SubjectInformationAccess, + SubjectKeyIdentifier, + TLSFeature, + TLSFeatureType, + UnrecognizedExtension, + UserNotice, +) +from cryptography.x509.general_name import ( + DirectoryName, + DNSName, + GeneralName, + IPAddress, + OtherName, + RegisteredID, + RFC822Name, + UniformResourceIdentifier, + UnsupportedGeneralNameType, +) +from cryptography.x509.name import ( + Name, + NameAttribute, + RelativeDistinguishedName, +) +from cryptography.x509.oid import ( + AuthorityInformationAccessOID, + CertificatePoliciesOID, + CRLEntryExtensionOID, + ExtendedKeyUsageOID, + ExtensionOID, + NameOID, + ObjectIdentifier, + SignatureAlgorithmOID, +) + +OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS +OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER +OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS +OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES +OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS +OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE +OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL +OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY +OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME +OID_KEY_USAGE = ExtensionOID.KEY_USAGE +OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS +OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK +OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS +OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS +OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME +OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES +OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS +OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER + +OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 +OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 +OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 +OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 +OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 +OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 +OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 +OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 +OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 +OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 +OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 +OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 +OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 +OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 +OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS + +OID_COMMON_NAME = NameOID.COMMON_NAME +OID_COUNTRY_NAME = NameOID.COUNTRY_NAME +OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT +OID_DN_QUALIFIER = NameOID.DN_QUALIFIER +OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS +OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER +OID_GIVEN_NAME = NameOID.GIVEN_NAME +OID_LOCALITY_NAME = NameOID.LOCALITY_NAME +OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME +OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME +OID_PSEUDONYM = NameOID.PSEUDONYM +OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER +OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME +OID_SURNAME = NameOID.SURNAME +OID_TITLE = NameOID.TITLE + +OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH +OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING +OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION +OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING +OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH +OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING + +OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY +OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER +OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE + +OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER +OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON +OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE + +OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS +OID_OCSP = AuthorityInformationAccessOID.OCSP + +__all__ = [ + "certificate_transparency", + "load_pem_x509_certificate", + "load_pem_x509_certificates", + "load_der_x509_certificate", + "load_pem_x509_csr", + "load_der_x509_csr", + "load_pem_x509_crl", + "load_der_x509_crl", + "random_serial_number", + "Attribute", + "AttributeNotFound", + "Attributes", + "InvalidVersion", + "DeltaCRLIndicator", + "DuplicateExtension", + "ExtensionNotFound", + "UnsupportedGeneralNameType", + "NameAttribute", + "Name", + "RelativeDistinguishedName", + "ObjectIdentifier", + "ExtensionType", + "Extensions", + "Extension", + "ExtendedKeyUsage", + "FreshestCRL", + "IssuingDistributionPoint", + "TLSFeature", + "TLSFeatureType", + "OCSPNoCheck", + "BasicConstraints", + "CRLNumber", + "KeyUsage", + "AuthorityInformationAccess", + "SubjectInformationAccess", + "AccessDescription", + "CertificatePolicies", + "PolicyInformation", + "UserNotice", + "NoticeReference", + "SubjectKeyIdentifier", + "NameConstraints", + "CRLDistributionPoints", + "DistributionPoint", + "ReasonFlags", + "InhibitAnyPolicy", + "SubjectAlternativeName", + "IssuerAlternativeName", + "AuthorityKeyIdentifier", + "GeneralNames", + "GeneralName", + "RFC822Name", + "DNSName", + "UniformResourceIdentifier", + "RegisteredID", + "DirectoryName", + "IPAddress", + "OtherName", + "Certificate", + "CertificateRevocationList", + "CertificateRevocationListBuilder", + "CertificateSigningRequest", + "RevokedCertificate", + "RevokedCertificateBuilder", + "CertificateSigningRequestBuilder", + "CertificateBuilder", + "Version", + "OID_CA_ISSUERS", + "OID_OCSP", + "CertificateIssuer", + "CRLReason", + "InvalidityDate", + "UnrecognizedExtension", + "PolicyConstraints", + "PrecertificateSignedCertificateTimestamps", + "PrecertPoison", + "OCSPNonce", + "SignedCertificateTimestamps", + "SignatureAlgorithmOID", + "NameOID", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py new file mode 100644 index 000000000..6eae41cbe --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py @@ -0,0 +1,1131 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import datetime +import os +import typing + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import ( + dsa, + ec, + ed448, + ed25519, + rsa, + x448, + x25519, +) +from cryptography.hazmat.primitives.asymmetric.types import ( + CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, + CERTIFICATE_PRIVATE_KEY_TYPES, + CERTIFICATE_PUBLIC_KEY_TYPES, +) +from cryptography.x509.extensions import ( + Extension, + Extensions, + ExtensionType, + _make_sequence_methods, +) +from cryptography.x509.name import Name, _ASN1Type +from cryptography.x509.oid import ObjectIdentifier + +_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) + + +class AttributeNotFound(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super(AttributeNotFound, self).__init__(msg) + self.oid = oid + + +def _reject_duplicate_extension( + extension: Extension[ExtensionType], + extensions: typing.List[Extension[ExtensionType]], +) -> None: + # This is quadratic in the number of extensions + for e in extensions: + if e.oid == extension.oid: + raise ValueError("This extension has already been set.") + + +def _reject_duplicate_attribute( + oid: ObjectIdentifier, + attributes: typing.List[ + typing.Tuple[ObjectIdentifier, bytes, typing.Optional[int]] + ], +) -> None: + # This is quadratic in the number of attributes + for attr_oid, _, _ in attributes: + if attr_oid == oid: + raise ValueError("This attribute has already been set.") + + +def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime: + """Normalizes a datetime to a naive datetime in UTC. + + time -- datetime to normalize. Assumed to be in UTC if not timezone + aware. + """ + if time.tzinfo is not None: + offset = time.utcoffset() + offset = offset if offset else datetime.timedelta() + return time.replace(tzinfo=None) - offset + else: + return time + + +class Attribute: + def __init__( + self, + oid: ObjectIdentifier, + value: bytes, + _type: int = _ASN1Type.UTF8String.value, + ) -> None: + self._oid = oid + self._value = value + self._type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return "".format(self.oid, self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Attribute): + return NotImplemented + + return ( + self.oid == other.oid + and self.value == other.value + and self._type == other._type + ) + + def __hash__(self) -> int: + return hash((self.oid, self.value, self._type)) + + +class Attributes: + def __init__( + self, + attributes: typing.Iterable[Attribute], + ) -> None: + self._attributes = list(attributes) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes") + + def __repr__(self) -> str: + return "".format(self._attributes) + + def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute: + for attr in self: + if attr.oid == oid: + return attr + + raise AttributeNotFound("No {} attribute was found".format(oid), oid) + + +class Version(utils.Enum): + v1 = 0 + v3 = 2 + + +class InvalidVersion(Exception): + def __init__(self, msg: str, parsed_version: int) -> None: + super(InvalidVersion, self).__init__(msg) + self.parsed_version = parsed_version + + +class Certificate(metaclass=abc.ABCMeta): + @abc.abstractmethod + def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: + """ + Returns bytes using digest passed. + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + Returns certificate serial number + """ + + @property + @abc.abstractmethod + def version(self) -> Version: + """ + Returns the certificate version + """ + + @abc.abstractmethod + def public_key(self) -> CERTIFICATE_PUBLIC_KEY_TYPES: + """ + Returns the public key + """ + + @property + @abc.abstractmethod + def not_valid_before(self) -> datetime.datetime: + """ + Not before time (represented as UTC datetime) + """ + + @property + @abc.abstractmethod + def not_valid_after(self) -> datetime.datetime: + """ + Not after time (represented as UTC datetime) + """ + + @property + @abc.abstractmethod + def issuer(self) -> Name: + """ + Returns the issuer name object. + """ + + @property + @abc.abstractmethod + def subject(self) -> Name: + """ + Returns the subject name object. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> typing.Optional[hashes.HashAlgorithm]: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns an Extensions object. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature bytes. + """ + + @property + @abc.abstractmethod + def tbs_certificate_bytes(self) -> bytes: + """ + Returns the tbsCertificate payload bytes as defined in RFC 5280. + """ + + @property + @abc.abstractmethod + def tbs_precertificate_bytes(self) -> bytes: + """ + Returns the tbsCertificate payload bytes with the SCT list extension + stripped. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Computes a hash. + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the certificate to PEM or DER format. + """ + + +# Runtime isinstance checks need this since the rust class is not a subclass. +Certificate.register(rust_x509.Certificate) + + +class RevokedCertificate(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + Returns the serial number of the revoked certificate. + """ + + @property + @abc.abstractmethod + def revocation_date(self) -> datetime.datetime: + """ + Returns the date of when this certificate was revoked. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns an Extensions object containing a list of Revoked extensions. + """ + + +# Runtime isinstance checks need this since the rust class is not a subclass. +RevokedCertificate.register(rust_x509.RevokedCertificate) + + +class _RawRevokedCertificate(RevokedCertificate): + def __init__( + self, + serial_number: int, + revocation_date: datetime.datetime, + extensions: Extensions, + ): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + @property + def serial_number(self) -> int: + return self._serial_number + + @property + def revocation_date(self) -> datetime.datetime: + return self._revocation_date + + @property + def extensions(self) -> Extensions: + return self._extensions + + +class CertificateRevocationList(metaclass=abc.ABCMeta): + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the CRL to PEM or DER format. + """ + + @abc.abstractmethod + def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: + """ + Returns bytes using digest passed. + """ + + @abc.abstractmethod + def get_revoked_certificate_by_serial_number( + self, serial_number: int + ) -> typing.Optional[RevokedCertificate]: + """ + Returns an instance of RevokedCertificate or None if the serial_number + is not in the CRL. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> typing.Optional[hashes.HashAlgorithm]: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @property + @abc.abstractmethod + def issuer(self) -> Name: + """ + Returns the X509Name with the issuer of this CRL. + """ + + @property + @abc.abstractmethod + def next_update(self) -> typing.Optional[datetime.datetime]: + """ + Returns the date of next update for this CRL. + """ + + @property + @abc.abstractmethod + def last_update(self) -> datetime.datetime: + """ + Returns the date of last update for this CRL. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns an Extensions object containing a list of CRL extensions. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature bytes. + """ + + @property + @abc.abstractmethod + def tbs_certlist_bytes(self) -> bytes: + """ + Returns the tbsCertList payload bytes as defined in RFC 5280. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __len__(self) -> int: + """ + Number of revoked certificates in the CRL. + """ + + @typing.overload + def __getitem__(self, idx: int) -> RevokedCertificate: + ... + + @typing.overload + def __getitem__(self, idx: slice) -> typing.List[RevokedCertificate]: + ... + + @abc.abstractmethod + def __getitem__( + self, idx: typing.Union[int, slice] + ) -> typing.Union[RevokedCertificate, typing.List[RevokedCertificate]]: + """ + Returns a revoked certificate (or slice of revoked certificates). + """ + + @abc.abstractmethod + def __iter__(self) -> typing.Iterator[RevokedCertificate]: + """ + Iterator over the revoked certificates + """ + + @abc.abstractmethod + def is_signature_valid( + self, public_key: CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES + ) -> bool: + """ + Verifies signature of revocation list against given public key. + """ + + +CertificateRevocationList.register(rust_x509.CertificateRevocationList) + + +class CertificateSigningRequest(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Checks equality. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Computes a hash. + """ + + @abc.abstractmethod + def public_key(self) -> CERTIFICATE_PUBLIC_KEY_TYPES: + """ + Returns the public key + """ + + @property + @abc.abstractmethod + def subject(self) -> Name: + """ + Returns the subject name object. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> typing.Optional[hashes.HashAlgorithm]: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + in the certificate. + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> ObjectIdentifier: + """ + Returns the ObjectIdentifier of the signature algorithm. + """ + + @property + @abc.abstractmethod + def extensions(self) -> Extensions: + """ + Returns the extensions in the signing request. + """ + + @property + @abc.abstractmethod + def attributes(self) -> Attributes: + """ + Returns an Attributes object. + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Encodes the request to PEM or DER format. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature bytes. + """ + + @property + @abc.abstractmethod + def tbs_certrequest_bytes(self) -> bytes: + """ + Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC + 2986. + """ + + @property + @abc.abstractmethod + def is_signature_valid(self) -> bool: + """ + Verifies signature of signing request. + """ + + @abc.abstractmethod + def get_attribute_for_oid(self, oid: ObjectIdentifier) -> bytes: + """ + Get the attribute value for a given OID. + """ + + +# Runtime isinstance checks need this since the rust class is not a subclass. +CertificateSigningRequest.register(rust_x509.CertificateSigningRequest) + + +# Backend argument preserved for API compatibility, but ignored. +def load_pem_x509_certificate( + data: bytes, backend: typing.Any = None +) -> Certificate: + return rust_x509.load_pem_x509_certificate(data) + + +def load_pem_x509_certificates(data: bytes) -> typing.List[Certificate]: + return rust_x509.load_pem_x509_certificates(data) + + +# Backend argument preserved for API compatibility, but ignored. +def load_der_x509_certificate( + data: bytes, backend: typing.Any = None +) -> Certificate: + return rust_x509.load_der_x509_certificate(data) + + +# Backend argument preserved for API compatibility, but ignored. +def load_pem_x509_csr( + data: bytes, backend: typing.Any = None +) -> CertificateSigningRequest: + return rust_x509.load_pem_x509_csr(data) + + +# Backend argument preserved for API compatibility, but ignored. +def load_der_x509_csr( + data: bytes, backend: typing.Any = None +) -> CertificateSigningRequest: + return rust_x509.load_der_x509_csr(data) + + +# Backend argument preserved for API compatibility, but ignored. +def load_pem_x509_crl( + data: bytes, backend: typing.Any = None +) -> CertificateRevocationList: + return rust_x509.load_pem_x509_crl(data) + + +# Backend argument preserved for API compatibility, but ignored. +def load_der_x509_crl( + data: bytes, backend: typing.Any = None +) -> CertificateRevocationList: + return rust_x509.load_der_x509_crl(data) + + +class CertificateSigningRequestBuilder: + def __init__( + self, + subject_name: typing.Optional[Name] = None, + extensions: typing.List[Extension[ExtensionType]] = [], + attributes: typing.List[ + typing.Tuple[ObjectIdentifier, bytes, typing.Optional[int]] + ] = [], + ): + """ + Creates an empty X.509 certificate request (v1). + """ + self._subject_name = subject_name + self._extensions = extensions + self._attributes = attributes + + def subject_name(self, name: Name) -> "CertificateSigningRequestBuilder": + """ + Sets the certificate requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._subject_name is not None: + raise ValueError("The subject name may only be set once.") + return CertificateSigningRequestBuilder( + name, self._extensions, self._attributes + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> "CertificateSigningRequestBuilder": + """ + Adds an X.509 extension to the certificate request. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateSigningRequestBuilder( + self._subject_name, + self._extensions + [extension], + self._attributes, + ) + + def add_attribute( + self, + oid: ObjectIdentifier, + value: bytes, + *, + _tag: typing.Optional[_ASN1Type] = None, + ) -> "CertificateSigningRequestBuilder": + """ + Adds an X.509 attribute with an OID and associated value. + """ + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + + if not isinstance(value, bytes): + raise TypeError("value must be bytes") + + if _tag is not None and not isinstance(_tag, _ASN1Type): + raise TypeError("tag must be _ASN1Type") + + _reject_duplicate_attribute(oid, self._attributes) + + if _tag is not None: + tag = _tag.value + else: + tag = None + + return CertificateSigningRequestBuilder( + self._subject_name, + self._extensions, + self._attributes + [(oid, value, tag)], + ) + + def sign( + self, + private_key: CERTIFICATE_PRIVATE_KEY_TYPES, + algorithm: typing.Optional[hashes.HashAlgorithm], + backend: typing.Any = None, + ) -> CertificateSigningRequest: + """ + Signs the request using the requestor's private key. + """ + if self._subject_name is None: + raise ValueError("A CertificateSigningRequest must have a subject") + return rust_x509.create_x509_csr(self, private_key, algorithm) + + +class CertificateBuilder: + _extensions: typing.List[Extension[ExtensionType]] + + def __init__( + self, + issuer_name: typing.Optional[Name] = None, + subject_name: typing.Optional[Name] = None, + public_key: typing.Optional[CERTIFICATE_PUBLIC_KEY_TYPES] = None, + serial_number: typing.Optional[int] = None, + not_valid_before: typing.Optional[datetime.datetime] = None, + not_valid_after: typing.Optional[datetime.datetime] = None, + extensions: typing.List[Extension[ExtensionType]] = [], + ) -> None: + self._version = Version.v3 + self._issuer_name = issuer_name + self._subject_name = subject_name + self._public_key = public_key + self._serial_number = serial_number + self._not_valid_before = not_valid_before + self._not_valid_after = not_valid_after + self._extensions = extensions + + def issuer_name(self, name: Name) -> "CertificateBuilder": + """ + Sets the CA's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._issuer_name is not None: + raise ValueError("The issuer name may only be set once.") + return CertificateBuilder( + name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def subject_name(self, name: Name) -> "CertificateBuilder": + """ + Sets the requestor's distinguished name. + """ + if not isinstance(name, Name): + raise TypeError("Expecting x509.Name object.") + if self._subject_name is not None: + raise ValueError("The subject name may only be set once.") + return CertificateBuilder( + self._issuer_name, + name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def public_key( + self, + key: CERTIFICATE_PUBLIC_KEY_TYPES, + ) -> "CertificateBuilder": + """ + Sets the requestor's public key (as found in the signing request). + """ + if not isinstance( + key, + ( + dsa.DSAPublicKey, + rsa.RSAPublicKey, + ec.EllipticCurvePublicKey, + ed25519.Ed25519PublicKey, + ed448.Ed448PublicKey, + x25519.X25519PublicKey, + x448.X448PublicKey, + ), + ): + raise TypeError( + "Expecting one of DSAPublicKey, RSAPublicKey," + " EllipticCurvePublicKey, Ed25519PublicKey," + " Ed448PublicKey, X25519PublicKey, or " + "X448PublicKey." + ) + if self._public_key is not None: + raise ValueError("The public key may only be set once.") + return CertificateBuilder( + self._issuer_name, + self._subject_name, + key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def serial_number(self, number: int) -> "CertificateBuilder": + """ + Sets the certificate serial number. + """ + if not isinstance(number, int): + raise TypeError("Serial number must be of integral type.") + if self._serial_number is not None: + raise ValueError("The serial number may only be set once.") + if number <= 0: + raise ValueError("The serial number should be positive.") + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError( + "The serial number should not be more than 159 " "bits." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + number, + self._not_valid_before, + self._not_valid_after, + self._extensions, + ) + + def not_valid_before( + self, time: datetime.datetime + ) -> "CertificateBuilder": + """ + Sets the certificate activation time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._not_valid_before is not None: + raise ValueError("The not valid before may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The not valid before date must be on or after" + " 1950 January 1)." + ) + if self._not_valid_after is not None and time > self._not_valid_after: + raise ValueError( + "The not valid before date must be before the not valid after " + "date." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + time, + self._not_valid_after, + self._extensions, + ) + + def not_valid_after(self, time: datetime.datetime) -> "CertificateBuilder": + """ + Sets the certificate expiration time. + """ + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._not_valid_after is not None: + raise ValueError("The not valid after may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The not valid after date must be on or after" + " 1950 January 1." + ) + if ( + self._not_valid_before is not None + and time < self._not_valid_before + ): + raise ValueError( + "The not valid after date must be after the not valid before " + "date." + ) + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + time, + self._extensions, + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> "CertificateBuilder": + """ + Adds an X.509 extension to the certificate. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return CertificateBuilder( + self._issuer_name, + self._subject_name, + self._public_key, + self._serial_number, + self._not_valid_before, + self._not_valid_after, + self._extensions + [extension], + ) + + def sign( + self, + private_key: CERTIFICATE_PRIVATE_KEY_TYPES, + algorithm: typing.Optional[hashes.HashAlgorithm], + backend: typing.Any = None, + ) -> Certificate: + """ + Signs the certificate using the CA's private key. + """ + if self._subject_name is None: + raise ValueError("A certificate must have a subject name") + + if self._issuer_name is None: + raise ValueError("A certificate must have an issuer name") + + if self._serial_number is None: + raise ValueError("A certificate must have a serial number") + + if self._not_valid_before is None: + raise ValueError("A certificate must have a not valid before time") + + if self._not_valid_after is None: + raise ValueError("A certificate must have a not valid after time") + + if self._public_key is None: + raise ValueError("A certificate must have a public key") + + return rust_x509.create_x509_certificate(self, private_key, algorithm) + + +class CertificateRevocationListBuilder: + _extensions: typing.List[Extension[ExtensionType]] + _revoked_certificates: typing.List[RevokedCertificate] + + def __init__( + self, + issuer_name: typing.Optional[Name] = None, + last_update: typing.Optional[datetime.datetime] = None, + next_update: typing.Optional[datetime.datetime] = None, + extensions: typing.List[Extension[ExtensionType]] = [], + revoked_certificates: typing.List[RevokedCertificate] = [], + ): + self._issuer_name = issuer_name + self._last_update = last_update + self._next_update = next_update + self._extensions = extensions + self._revoked_certificates = revoked_certificates + + def issuer_name( + self, issuer_name: Name + ) -> "CertificateRevocationListBuilder": + if not isinstance(issuer_name, Name): + raise TypeError("Expecting x509.Name object.") + if self._issuer_name is not None: + raise ValueError("The issuer name may only be set once.") + return CertificateRevocationListBuilder( + issuer_name, + self._last_update, + self._next_update, + self._extensions, + self._revoked_certificates, + ) + + def last_update( + self, last_update: datetime.datetime + ) -> "CertificateRevocationListBuilder": + if not isinstance(last_update, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._last_update is not None: + raise ValueError("Last update may only be set once.") + last_update = _convert_to_naive_utc_time(last_update) + if last_update < _EARLIEST_UTC_TIME: + raise ValueError( + "The last update date must be on or after" " 1950 January 1." + ) + if self._next_update is not None and last_update > self._next_update: + raise ValueError( + "The last update date must be before the next update date." + ) + return CertificateRevocationListBuilder( + self._issuer_name, + last_update, + self._next_update, + self._extensions, + self._revoked_certificates, + ) + + def next_update( + self, next_update: datetime.datetime + ) -> "CertificateRevocationListBuilder": + if not isinstance(next_update, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._next_update is not None: + raise ValueError("Last update may only be set once.") + next_update = _convert_to_naive_utc_time(next_update) + if next_update < _EARLIEST_UTC_TIME: + raise ValueError( + "The last update date must be on or after" " 1950 January 1." + ) + if self._last_update is not None and next_update < self._last_update: + raise ValueError( + "The next update date must be after the last update date." + ) + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + next_update, + self._extensions, + self._revoked_certificates, + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> "CertificateRevocationListBuilder": + """ + Adds an X.509 extension to the certificate revocation list. + """ + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + self._next_update, + self._extensions + [extension], + self._revoked_certificates, + ) + + def add_revoked_certificate( + self, revoked_certificate: RevokedCertificate + ) -> "CertificateRevocationListBuilder": + """ + Adds a revoked certificate to the CRL. + """ + if not isinstance(revoked_certificate, RevokedCertificate): + raise TypeError("Must be an instance of RevokedCertificate") + + return CertificateRevocationListBuilder( + self._issuer_name, + self._last_update, + self._next_update, + self._extensions, + self._revoked_certificates + [revoked_certificate], + ) + + def sign( + self, + private_key: CERTIFICATE_PRIVATE_KEY_TYPES, + algorithm: typing.Optional[hashes.HashAlgorithm], + backend: typing.Any = None, + ) -> CertificateRevocationList: + if self._issuer_name is None: + raise ValueError("A CRL must have an issuer name") + + if self._last_update is None: + raise ValueError("A CRL must have a last update time") + + if self._next_update is None: + raise ValueError("A CRL must have a next update time") + + return rust_x509.create_x509_crl(self, private_key, algorithm) + + +class RevokedCertificateBuilder: + def __init__( + self, + serial_number: typing.Optional[int] = None, + revocation_date: typing.Optional[datetime.datetime] = None, + extensions: typing.List[Extension[ExtensionType]] = [], + ): + self._serial_number = serial_number + self._revocation_date = revocation_date + self._extensions = extensions + + def serial_number(self, number: int) -> "RevokedCertificateBuilder": + if not isinstance(number, int): + raise TypeError("Serial number must be of integral type.") + if self._serial_number is not None: + raise ValueError("The serial number may only be set once.") + if number <= 0: + raise ValueError("The serial number should be positive") + + # ASN.1 integers are always signed, so most significant bit must be + # zero. + if number.bit_length() >= 160: # As defined in RFC 5280 + raise ValueError( + "The serial number should not be more than 159 " "bits." + ) + return RevokedCertificateBuilder( + number, self._revocation_date, self._extensions + ) + + def revocation_date( + self, time: datetime.datetime + ) -> "RevokedCertificateBuilder": + if not isinstance(time, datetime.datetime): + raise TypeError("Expecting datetime object.") + if self._revocation_date is not None: + raise ValueError("The revocation date may only be set once.") + time = _convert_to_naive_utc_time(time) + if time < _EARLIEST_UTC_TIME: + raise ValueError( + "The revocation date must be on or after" " 1950 January 1." + ) + return RevokedCertificateBuilder( + self._serial_number, time, self._extensions + ) + + def add_extension( + self, extval: ExtensionType, critical: bool + ) -> "RevokedCertificateBuilder": + if not isinstance(extval, ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + return RevokedCertificateBuilder( + self._serial_number, + self._revocation_date, + self._extensions + [extension], + ) + + def build(self, backend: typing.Any = None) -> RevokedCertificate: + if self._serial_number is None: + raise ValueError("A revoked certificate must have a serial number") + if self._revocation_date is None: + raise ValueError( + "A revoked certificate must have a revocation date" + ) + return _RawRevokedCertificate( + self._serial_number, + self._revocation_date, + Extensions(self._extensions), + ) + + +def random_serial_number() -> int: + return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py new file mode 100644 index 000000000..a67709865 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py @@ -0,0 +1,96 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import datetime + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives.hashes import HashAlgorithm + + +class LogEntryType(utils.Enum): + X509_CERTIFICATE = 0 + PRE_CERTIFICATE = 1 + + +class Version(utils.Enum): + v1 = 0 + + +class SignatureAlgorithm(utils.Enum): + """ + Signature algorithms that are valid for SCTs. + + These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2). + + See: + """ + + ANONYMOUS = 0 + RSA = 1 + DSA = 2 + ECDSA = 3 + + +class SignedCertificateTimestamp(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def version(self) -> Version: + """ + Returns the SCT version. + """ + + @property + @abc.abstractmethod + def log_id(self) -> bytes: + """ + Returns an identifier indicating which log this SCT is for. + """ + + @property + @abc.abstractmethod + def timestamp(self) -> datetime.datetime: + """ + Returns the timestamp for this SCT. + """ + + @property + @abc.abstractmethod + def entry_type(self) -> LogEntryType: + """ + Returns whether this is an SCT for a certificate or pre-certificate. + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm(self) -> HashAlgorithm: + """ + Returns the hash algorithm used for the SCT's signature. + """ + + @property + @abc.abstractmethod + def signature_algorithm(self) -> SignatureAlgorithm: + """ + Returns the signing algorithm used for the SCT's signature. + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + Returns the signature for this SCT. + """ + + @property + @abc.abstractmethod + def extension_bytes(self) -> bytes: + """ + Returns the raw bytes of any extensions for this SCT. + """ + + +SignedCertificateTimestamp.register(rust_x509.Sct) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py new file mode 100644 index 000000000..2012515f2 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py @@ -0,0 +1,2113 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import datetime +import hashlib +import ipaddress +import typing + +from cryptography import utils +from cryptography.hazmat.bindings._rust import asn1 +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.hazmat.primitives import constant_time, serialization +from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey +from cryptography.hazmat.primitives.asymmetric.types import ( + CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, + CERTIFICATE_PUBLIC_KEY_TYPES, +) +from cryptography.x509.certificate_transparency import ( + SignedCertificateTimestamp, +) +from cryptography.x509.general_name import ( + _IPADDRESS_TYPES, + DirectoryName, + DNSName, + GeneralName, + IPAddress, + OtherName, + RegisteredID, + RFC822Name, + UniformResourceIdentifier, +) +from cryptography.x509.name import Name, RelativeDistinguishedName +from cryptography.x509.oid import ( + CRLEntryExtensionOID, + ExtensionOID, + ObjectIdentifier, + OCSPExtensionOID, +) + +ExtensionTypeVar = typing.TypeVar( + "ExtensionTypeVar", bound="ExtensionType", covariant=True +) + + +def _key_identifier_from_public_key( + public_key: CERTIFICATE_PUBLIC_KEY_TYPES, +) -> bytes: + if isinstance(public_key, RSAPublicKey): + data = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.PKCS1, + ) + elif isinstance(public_key, EllipticCurvePublicKey): + data = public_key.public_bytes( + serialization.Encoding.X962, + serialization.PublicFormat.UncompressedPoint, + ) + else: + # This is a very slow way to do this. + serialized = public_key.public_bytes( + serialization.Encoding.DER, + serialization.PublicFormat.SubjectPublicKeyInfo, + ) + data = asn1.parse_spki_for_data(serialized) + + return hashlib.sha1(data).digest() + + +def _make_sequence_methods(field_name: str): + def len_method(self) -> int: + return len(getattr(self, field_name)) + + def iter_method(self): + return iter(getattr(self, field_name)) + + def getitem_method(self, idx): + return getattr(self, field_name)[idx] + + return len_method, iter_method, getitem_method + + +class DuplicateExtension(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super(DuplicateExtension, self).__init__(msg) + self.oid = oid + + +class ExtensionNotFound(Exception): + def __init__(self, msg: str, oid: ObjectIdentifier) -> None: + super(ExtensionNotFound, self).__init__(msg) + self.oid = oid + + +class ExtensionType(metaclass=abc.ABCMeta): + oid: typing.ClassVar[ObjectIdentifier] + + def public_bytes(self) -> bytes: + """ + Serializes the extension type to DER. + """ + raise NotImplementedError( + "public_bytes is not implemented for extension type {0!r}".format( + self + ) + ) + + +class Extensions: + def __init__( + self, extensions: typing.Iterable["Extension[ExtensionType]"] + ) -> None: + self._extensions = list(extensions) + + def get_extension_for_oid( + self, oid: ObjectIdentifier + ) -> "Extension[ExtensionType]": + for ext in self: + if ext.oid == oid: + return ext + + raise ExtensionNotFound("No {} extension was found".format(oid), oid) + + def get_extension_for_class( + self, extclass: typing.Type[ExtensionTypeVar] + ) -> "Extension[ExtensionTypeVar]": + if extclass is UnrecognizedExtension: + raise TypeError( + "UnrecognizedExtension can't be used with " + "get_extension_for_class because more than one instance of the" + " class may be present." + ) + + for ext in self: + if isinstance(ext.value, extclass): + return ext + + raise ExtensionNotFound( + "No {} extension was found".format(extclass), extclass.oid + ) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") + + def __repr__(self) -> str: + return "".format(self._extensions) + + +class CRLNumber(ExtensionType): + oid = ExtensionOID.CRL_NUMBER + + def __init__(self, crl_number: int) -> None: + if not isinstance(crl_number, int): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLNumber): + return NotImplemented + + return self.crl_number == other.crl_number + + def __hash__(self) -> int: + return hash(self.crl_number) + + def __repr__(self) -> str: + return "".format(self.crl_number) + + @property + def crl_number(self) -> int: + return self._crl_number + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AuthorityKeyIdentifier(ExtensionType): + oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER + + def __init__( + self, + key_identifier: typing.Optional[bytes], + authority_cert_issuer: typing.Optional[typing.Iterable[GeneralName]], + authority_cert_serial_number: typing.Optional[int], + ) -> None: + if (authority_cert_issuer is None) != ( + authority_cert_serial_number is None + ): + raise ValueError( + "authority_cert_issuer and authority_cert_serial_number " + "must both be present or both None" + ) + + if authority_cert_issuer is not None: + authority_cert_issuer = list(authority_cert_issuer) + if not all( + isinstance(x, GeneralName) for x in authority_cert_issuer + ): + raise TypeError( + "authority_cert_issuer must be a list of GeneralName " + "objects" + ) + + if authority_cert_serial_number is not None and not isinstance( + authority_cert_serial_number, int + ): + raise TypeError("authority_cert_serial_number must be an integer") + + self._key_identifier = key_identifier + self._authority_cert_issuer = authority_cert_issuer + self._authority_cert_serial_number = authority_cert_serial_number + + # This takes a subset of CERTIFICATE_PUBLIC_KEY_TYPES because an issuer + # cannot have an X25519/X448 key. This introduces some unfortunate + # asymmetry that requires typing users to explicitly + # narrow their type, but we should make this accurate and not just + # convenient. + @classmethod + def from_issuer_public_key( + cls, public_key: CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES + ) -> "AuthorityKeyIdentifier": + digest = _key_identifier_from_public_key(public_key) + return cls( + key_identifier=digest, + authority_cert_issuer=None, + authority_cert_serial_number=None, + ) + + @classmethod + def from_issuer_subject_key_identifier( + cls, ski: "SubjectKeyIdentifier" + ) -> "AuthorityKeyIdentifier": + return cls( + key_identifier=ski.digest, + authority_cert_issuer=None, + authority_cert_serial_number=None, + ) + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AuthorityKeyIdentifier): + return NotImplemented + + return ( + self.key_identifier == other.key_identifier + and self.authority_cert_issuer == other.authority_cert_issuer + and self.authority_cert_serial_number + == other.authority_cert_serial_number + ) + + def __hash__(self) -> int: + if self.authority_cert_issuer is None: + aci = None + else: + aci = tuple(self.authority_cert_issuer) + return hash( + (self.key_identifier, aci, self.authority_cert_serial_number) + ) + + @property + def key_identifier(self) -> typing.Optional[bytes]: + return self._key_identifier + + @property + def authority_cert_issuer( + self, + ) -> typing.Optional[typing.List[GeneralName]]: + return self._authority_cert_issuer + + @property + def authority_cert_serial_number(self) -> typing.Optional[int]: + return self._authority_cert_serial_number + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SubjectKeyIdentifier(ExtensionType): + oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER + + def __init__(self, digest: bytes) -> None: + self._digest = digest + + @classmethod + def from_public_key( + cls, public_key: CERTIFICATE_PUBLIC_KEY_TYPES + ) -> "SubjectKeyIdentifier": + return cls(_key_identifier_from_public_key(public_key)) + + @property + def digest(self) -> bytes: + return self._digest + + @property + def key_identifier(self) -> bytes: + return self._digest + + def __repr__(self) -> str: + return "".format(self.digest) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectKeyIdentifier): + return NotImplemented + + return constant_time.bytes_eq(self.digest, other.digest) + + def __hash__(self) -> int: + return hash(self.digest) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AuthorityInformationAccess(ExtensionType): + oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS + + def __init__( + self, descriptions: typing.Iterable["AccessDescription"] + ) -> None: + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self) -> str: + return "".format(self._descriptions) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AuthorityInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __hash__(self) -> int: + return hash(tuple(self._descriptions)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SubjectInformationAccess(ExtensionType): + oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS + + def __init__( + self, descriptions: typing.Iterable["AccessDescription"] + ) -> None: + descriptions = list(descriptions) + if not all(isinstance(x, AccessDescription) for x in descriptions): + raise TypeError( + "Every item in the descriptions list must be an " + "AccessDescription" + ) + + self._descriptions = descriptions + + __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") + + def __repr__(self) -> str: + return "".format(self._descriptions) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectInformationAccess): + return NotImplemented + + return self._descriptions == other._descriptions + + def __hash__(self) -> int: + return hash(tuple(self._descriptions)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class AccessDescription: + def __init__( + self, access_method: ObjectIdentifier, access_location: GeneralName + ) -> None: + if not isinstance(access_method, ObjectIdentifier): + raise TypeError("access_method must be an ObjectIdentifier") + + if not isinstance(access_location, GeneralName): + raise TypeError("access_location must be a GeneralName") + + self._access_method = access_method + self._access_location = access_location + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, AccessDescription): + return NotImplemented + + return ( + self.access_method == other.access_method + and self.access_location == other.access_location + ) + + def __hash__(self) -> int: + return hash((self.access_method, self.access_location)) + + @property + def access_method(self) -> ObjectIdentifier: + return self._access_method + + @property + def access_location(self) -> GeneralName: + return self._access_location + + +class BasicConstraints(ExtensionType): + oid = ExtensionOID.BASIC_CONSTRAINTS + + def __init__(self, ca: bool, path_length: typing.Optional[int]) -> None: + if not isinstance(ca, bool): + raise TypeError("ca must be a boolean value") + + if path_length is not None and not ca: + raise ValueError("path_length must be None when ca is False") + + if path_length is not None and ( + not isinstance(path_length, int) or path_length < 0 + ): + raise TypeError( + "path_length must be a non-negative integer or None" + ) + + self._ca = ca + self._path_length = path_length + + @property + def ca(self) -> bool: + return self._ca + + @property + def path_length(self) -> typing.Optional[int]: + return self._path_length + + def __repr__(self) -> str: + return ( + "" + ).format(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, BasicConstraints): + return NotImplemented + + return self.ca == other.ca and self.path_length == other.path_length + + def __hash__(self) -> int: + return hash((self.ca, self.path_length)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class DeltaCRLIndicator(ExtensionType): + oid = ExtensionOID.DELTA_CRL_INDICATOR + + def __init__(self, crl_number: int) -> None: + if not isinstance(crl_number, int): + raise TypeError("crl_number must be an integer") + + self._crl_number = crl_number + + @property + def crl_number(self) -> int: + return self._crl_number + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DeltaCRLIndicator): + return NotImplemented + + return self.crl_number == other.crl_number + + def __hash__(self) -> int: + return hash(self.crl_number) + + def __repr__(self) -> str: + return "".format(self) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CRLDistributionPoints(ExtensionType): + oid = ExtensionOID.CRL_DISTRIBUTION_POINTS + + def __init__( + self, distribution_points: typing.Iterable["DistributionPoint"] + ) -> None: + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self) -> str: + return "".format(self._distribution_points) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLDistributionPoints): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __hash__(self) -> int: + return hash(tuple(self._distribution_points)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class FreshestCRL(ExtensionType): + oid = ExtensionOID.FRESHEST_CRL + + def __init__( + self, distribution_points: typing.Iterable["DistributionPoint"] + ) -> None: + distribution_points = list(distribution_points) + if not all( + isinstance(x, DistributionPoint) for x in distribution_points + ): + raise TypeError( + "distribution_points must be a list of DistributionPoint " + "objects" + ) + + self._distribution_points = distribution_points + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_distribution_points" + ) + + def __repr__(self) -> str: + return "".format(self._distribution_points) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, FreshestCRL): + return NotImplemented + + return self._distribution_points == other._distribution_points + + def __hash__(self) -> int: + return hash(tuple(self._distribution_points)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class DistributionPoint: + def __init__( + self, + full_name: typing.Optional[typing.Iterable[GeneralName]], + relative_name: typing.Optional[RelativeDistinguishedName], + reasons: typing.Optional[typing.FrozenSet["ReasonFlags"]], + crl_issuer: typing.Optional[typing.Iterable[GeneralName]], + ) -> None: + if full_name and relative_name: + raise ValueError( + "You cannot provide both full_name and relative_name, at " + "least one must be None." + ) + if not full_name and not relative_name and not crl_issuer: + raise ValueError( + "Either full_name, relative_name or crl_issuer must be " + "provided." + ) + + if full_name is not None: + full_name = list(full_name) + if not all(isinstance(x, GeneralName) for x in full_name): + raise TypeError( + "full_name must be a list of GeneralName objects" + ) + + if relative_name: + if not isinstance(relative_name, RelativeDistinguishedName): + raise TypeError( + "relative_name must be a RelativeDistinguishedName" + ) + + if crl_issuer is not None: + crl_issuer = list(crl_issuer) + if not all(isinstance(x, GeneralName) for x in crl_issuer): + raise TypeError( + "crl_issuer must be None or a list of general names" + ) + + if reasons and ( + not isinstance(reasons, frozenset) + or not all(isinstance(x, ReasonFlags) for x in reasons) + ): + raise TypeError("reasons must be None or frozenset of ReasonFlags") + + if reasons and ( + ReasonFlags.unspecified in reasons + or ReasonFlags.remove_from_crl in reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in a " + "DistributionPoint" + ) + + self._full_name = full_name + self._relative_name = relative_name + self._reasons = reasons + self._crl_issuer = crl_issuer + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name + and self.relative_name == other.relative_name + and self.reasons == other.reasons + and self.crl_issuer == other.crl_issuer + ) + + def __hash__(self) -> int: + if self.full_name is not None: + fn: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple( + self.full_name + ) + else: + fn = None + + if self.crl_issuer is not None: + crl_issuer: typing.Optional[ + typing.Tuple[GeneralName, ...] + ] = tuple(self.crl_issuer) + else: + crl_issuer = None + + return hash((fn, self.relative_name, self.reasons, crl_issuer)) + + @property + def full_name(self) -> typing.Optional[typing.List[GeneralName]]: + return self._full_name + + @property + def relative_name(self) -> typing.Optional[RelativeDistinguishedName]: + return self._relative_name + + @property + def reasons(self) -> typing.Optional[typing.FrozenSet["ReasonFlags"]]: + return self._reasons + + @property + def crl_issuer(self) -> typing.Optional[typing.List[GeneralName]]: + return self._crl_issuer + + +class ReasonFlags(utils.Enum): + unspecified = "unspecified" + key_compromise = "keyCompromise" + ca_compromise = "cACompromise" + affiliation_changed = "affiliationChanged" + superseded = "superseded" + cessation_of_operation = "cessationOfOperation" + certificate_hold = "certificateHold" + privilege_withdrawn = "privilegeWithdrawn" + aa_compromise = "aACompromise" + remove_from_crl = "removeFromCRL" + + +# These are distribution point bit string mappings. Not to be confused with +# CRLReason reason flags bit string mappings. +# ReasonFlags ::= BIT STRING { +# unused (0), +# keyCompromise (1), +# cACompromise (2), +# affiliationChanged (3), +# superseded (4), +# cessationOfOperation (5), +# certificateHold (6), +# privilegeWithdrawn (7), +# aACompromise (8) } +_REASON_BIT_MAPPING = { + 1: ReasonFlags.key_compromise, + 2: ReasonFlags.ca_compromise, + 3: ReasonFlags.affiliation_changed, + 4: ReasonFlags.superseded, + 5: ReasonFlags.cessation_of_operation, + 6: ReasonFlags.certificate_hold, + 7: ReasonFlags.privilege_withdrawn, + 8: ReasonFlags.aa_compromise, +} + +_CRLREASONFLAGS = { + ReasonFlags.key_compromise: 1, + ReasonFlags.ca_compromise: 2, + ReasonFlags.affiliation_changed: 3, + ReasonFlags.superseded: 4, + ReasonFlags.cessation_of_operation: 5, + ReasonFlags.certificate_hold: 6, + ReasonFlags.privilege_withdrawn: 7, + ReasonFlags.aa_compromise: 8, +} + + +class PolicyConstraints(ExtensionType): + oid = ExtensionOID.POLICY_CONSTRAINTS + + def __init__( + self, + require_explicit_policy: typing.Optional[int], + inhibit_policy_mapping: typing.Optional[int], + ) -> None: + if require_explicit_policy is not None and not isinstance( + require_explicit_policy, int + ): + raise TypeError( + "require_explicit_policy must be a non-negative integer or " + "None" + ) + + if inhibit_policy_mapping is not None and not isinstance( + inhibit_policy_mapping, int + ): + raise TypeError( + "inhibit_policy_mapping must be a non-negative integer or None" + ) + + if inhibit_policy_mapping is None and require_explicit_policy is None: + raise ValueError( + "At least one of require_explicit_policy and " + "inhibit_policy_mapping must not be None" + ) + + self._require_explicit_policy = require_explicit_policy + self._inhibit_policy_mapping = inhibit_policy_mapping + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PolicyConstraints): + return NotImplemented + + return ( + self.require_explicit_policy == other.require_explicit_policy + and self.inhibit_policy_mapping == other.inhibit_policy_mapping + ) + + def __hash__(self) -> int: + return hash( + (self.require_explicit_policy, self.inhibit_policy_mapping) + ) + + @property + def require_explicit_policy(self) -> typing.Optional[int]: + return self._require_explicit_policy + + @property + def inhibit_policy_mapping(self) -> typing.Optional[int]: + return self._inhibit_policy_mapping + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CertificatePolicies(ExtensionType): + oid = ExtensionOID.CERTIFICATE_POLICIES + + def __init__(self, policies: typing.Iterable["PolicyInformation"]) -> None: + policies = list(policies) + if not all(isinstance(x, PolicyInformation) for x in policies): + raise TypeError( + "Every item in the policies list must be a " + "PolicyInformation" + ) + + self._policies = policies + + __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") + + def __repr__(self) -> str: + return "".format(self._policies) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CertificatePolicies): + return NotImplemented + + return self._policies == other._policies + + def __hash__(self) -> int: + return hash(tuple(self._policies)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PolicyInformation: + def __init__( + self, + policy_identifier: ObjectIdentifier, + policy_qualifiers: typing.Optional[ + typing.Iterable[typing.Union[str, "UserNotice"]] + ], + ) -> None: + if not isinstance(policy_identifier, ObjectIdentifier): + raise TypeError("policy_identifier must be an ObjectIdentifier") + + self._policy_identifier = policy_identifier + + if policy_qualifiers is not None: + policy_qualifiers = list(policy_qualifiers) + if not all( + isinstance(x, (str, UserNotice)) for x in policy_qualifiers + ): + raise TypeError( + "policy_qualifiers must be a list of strings and/or " + "UserNotice objects or None" + ) + + self._policy_qualifiers = policy_qualifiers + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PolicyInformation): + return NotImplemented + + return ( + self.policy_identifier == other.policy_identifier + and self.policy_qualifiers == other.policy_qualifiers + ) + + def __hash__(self) -> int: + if self.policy_qualifiers is not None: + pq: typing.Optional[ + typing.Tuple[typing.Union[str, "UserNotice"], ...] + ] = tuple(self.policy_qualifiers) + else: + pq = None + + return hash((self.policy_identifier, pq)) + + @property + def policy_identifier(self) -> ObjectIdentifier: + return self._policy_identifier + + @property + def policy_qualifiers( + self, + ) -> typing.Optional[typing.List[typing.Union[str, "UserNotice"]]]: + return self._policy_qualifiers + + +class UserNotice: + def __init__( + self, + notice_reference: typing.Optional["NoticeReference"], + explicit_text: typing.Optional[str], + ) -> None: + if notice_reference and not isinstance( + notice_reference, NoticeReference + ): + raise TypeError( + "notice_reference must be None or a NoticeReference" + ) + + self._notice_reference = notice_reference + self._explicit_text = explicit_text + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UserNotice): + return NotImplemented + + return ( + self.notice_reference == other.notice_reference + and self.explicit_text == other.explicit_text + ) + + def __hash__(self) -> int: + return hash((self.notice_reference, self.explicit_text)) + + @property + def notice_reference(self) -> typing.Optional["NoticeReference"]: + return self._notice_reference + + @property + def explicit_text(self) -> typing.Optional[str]: + return self._explicit_text + + +class NoticeReference: + def __init__( + self, + organization: typing.Optional[str], + notice_numbers: typing.Iterable[int], + ) -> None: + self._organization = organization + notice_numbers = list(notice_numbers) + if not all(isinstance(x, int) for x in notice_numbers): + raise TypeError("notice_numbers must be a list of integers") + + self._notice_numbers = notice_numbers + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NoticeReference): + return NotImplemented + + return ( + self.organization == other.organization + and self.notice_numbers == other.notice_numbers + ) + + def __hash__(self) -> int: + return hash((self.organization, tuple(self.notice_numbers))) + + @property + def organization(self) -> typing.Optional[str]: + return self._organization + + @property + def notice_numbers(self) -> typing.List[int]: + return self._notice_numbers + + +class ExtendedKeyUsage(ExtensionType): + oid = ExtensionOID.EXTENDED_KEY_USAGE + + def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None: + usages = list(usages) + if not all(isinstance(x, ObjectIdentifier) for x in usages): + raise TypeError( + "Every item in the usages list must be an ObjectIdentifier" + ) + + self._usages = usages + + __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") + + def __repr__(self) -> str: + return "".format(self._usages) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ExtendedKeyUsage): + return NotImplemented + + return self._usages == other._usages + + def __hash__(self) -> int: + return hash(tuple(self._usages)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPNoCheck(ExtensionType): + oid = ExtensionOID.OCSP_NO_CHECK + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPNoCheck): + return NotImplemented + + return True + + def __hash__(self) -> int: + return hash(OCSPNoCheck) + + def __repr__(self) -> str: + return "" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrecertPoison(ExtensionType): + oid = ExtensionOID.PRECERT_POISON + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrecertPoison): + return NotImplemented + + return True + + def __hash__(self) -> int: + return hash(PrecertPoison) + + def __repr__(self) -> str: + return "" + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class TLSFeature(ExtensionType): + oid = ExtensionOID.TLS_FEATURE + + def __init__(self, features: typing.Iterable["TLSFeatureType"]) -> None: + features = list(features) + if ( + not all(isinstance(x, TLSFeatureType) for x in features) + or len(features) == 0 + ): + raise TypeError( + "features must be a list of elements from the TLSFeatureType " + "enum" + ) + + self._features = features + + __len__, __iter__, __getitem__ = _make_sequence_methods("_features") + + def __repr__(self) -> str: + return "".format(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, TLSFeature): + return NotImplemented + + return self._features == other._features + + def __hash__(self) -> int: + return hash(tuple(self._features)) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class TLSFeatureType(utils.Enum): + # status_request is defined in RFC 6066 and is used for what is commonly + # called OCSP Must-Staple when present in the TLS Feature extension in an + # X.509 certificate. + status_request = 5 + # status_request_v2 is defined in RFC 6961 and allows multiple OCSP + # responses to be provided. It is not currently in use by clients or + # servers. + status_request_v2 = 17 + + +_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} + + +class InhibitAnyPolicy(ExtensionType): + oid = ExtensionOID.INHIBIT_ANY_POLICY + + def __init__(self, skip_certs: int) -> None: + if not isinstance(skip_certs, int): + raise TypeError("skip_certs must be an integer") + + if skip_certs < 0: + raise ValueError("skip_certs must be a non-negative integer") + + self._skip_certs = skip_certs + + def __repr__(self) -> str: + return "".format(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InhibitAnyPolicy): + return NotImplemented + + return self.skip_certs == other.skip_certs + + def __hash__(self) -> int: + return hash(self.skip_certs) + + @property + def skip_certs(self) -> int: + return self._skip_certs + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class KeyUsage(ExtensionType): + oid = ExtensionOID.KEY_USAGE + + def __init__( + self, + digital_signature: bool, + content_commitment: bool, + key_encipherment: bool, + data_encipherment: bool, + key_agreement: bool, + key_cert_sign: bool, + crl_sign: bool, + encipher_only: bool, + decipher_only: bool, + ) -> None: + if not key_agreement and (encipher_only or decipher_only): + raise ValueError( + "encipher_only and decipher_only can only be true when " + "key_agreement is true" + ) + + self._digital_signature = digital_signature + self._content_commitment = content_commitment + self._key_encipherment = key_encipherment + self._data_encipherment = data_encipherment + self._key_agreement = key_agreement + self._key_cert_sign = key_cert_sign + self._crl_sign = crl_sign + self._encipher_only = encipher_only + self._decipher_only = decipher_only + + @property + def digital_signature(self) -> bool: + return self._digital_signature + + @property + def content_commitment(self) -> bool: + return self._content_commitment + + @property + def key_encipherment(self) -> bool: + return self._key_encipherment + + @property + def data_encipherment(self) -> bool: + return self._data_encipherment + + @property + def key_agreement(self) -> bool: + return self._key_agreement + + @property + def key_cert_sign(self) -> bool: + return self._key_cert_sign + + @property + def crl_sign(self) -> bool: + return self._crl_sign + + @property + def encipher_only(self) -> bool: + if not self.key_agreement: + raise ValueError( + "encipher_only is undefined unless key_agreement is true" + ) + else: + return self._encipher_only + + @property + def decipher_only(self) -> bool: + if not self.key_agreement: + raise ValueError( + "decipher_only is undefined unless key_agreement is true" + ) + else: + return self._decipher_only + + def __repr__(self) -> str: + try: + encipher_only = self.encipher_only + decipher_only = self.decipher_only + except ValueError: + # Users found None confusing because even though encipher/decipher + # have no meaning unless key_agreement is true, to construct an + # instance of the class you still need to pass False. + encipher_only = False + decipher_only = False + + return ( + "" + ).format(self, encipher_only, decipher_only) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, KeyUsage): + return NotImplemented + + return ( + self.digital_signature == other.digital_signature + and self.content_commitment == other.content_commitment + and self.key_encipherment == other.key_encipherment + and self.data_encipherment == other.data_encipherment + and self.key_agreement == other.key_agreement + and self.key_cert_sign == other.key_cert_sign + and self.crl_sign == other.crl_sign + and self._encipher_only == other._encipher_only + and self._decipher_only == other._decipher_only + ) + + def __hash__(self) -> int: + return hash( + ( + self.digital_signature, + self.content_commitment, + self.key_encipherment, + self.data_encipherment, + self.key_agreement, + self.key_cert_sign, + self.crl_sign, + self._encipher_only, + self._decipher_only, + ) + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class NameConstraints(ExtensionType): + oid = ExtensionOID.NAME_CONSTRAINTS + + def __init__( + self, + permitted_subtrees: typing.Optional[typing.Iterable[GeneralName]], + excluded_subtrees: typing.Optional[typing.Iterable[GeneralName]], + ) -> None: + if permitted_subtrees is not None: + permitted_subtrees = list(permitted_subtrees) + if not permitted_subtrees: + raise ValueError( + "permitted_subtrees must be a non-empty list or None" + ) + if not all(isinstance(x, GeneralName) for x in permitted_subtrees): + raise TypeError( + "permitted_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_ip_name(permitted_subtrees) + + if excluded_subtrees is not None: + excluded_subtrees = list(excluded_subtrees) + if not excluded_subtrees: + raise ValueError( + "excluded_subtrees must be a non-empty list or None" + ) + if not all(isinstance(x, GeneralName) for x in excluded_subtrees): + raise TypeError( + "excluded_subtrees must be a list of GeneralName objects " + "or None" + ) + + self._validate_ip_name(excluded_subtrees) + + if permitted_subtrees is None and excluded_subtrees is None: + raise ValueError( + "At least one of permitted_subtrees and excluded_subtrees " + "must not be None" + ) + + self._permitted_subtrees = permitted_subtrees + self._excluded_subtrees = excluded_subtrees + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NameConstraints): + return NotImplemented + + return ( + self.excluded_subtrees == other.excluded_subtrees + and self.permitted_subtrees == other.permitted_subtrees + ) + + def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None: + if any( + isinstance(name, IPAddress) + and not isinstance( + name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) + ) + for name in tree + ): + raise TypeError( + "IPAddress name constraints must be an IPv4Network or" + " IPv6Network object" + ) + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __hash__(self) -> int: + if self.permitted_subtrees is not None: + ps: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple( + self.permitted_subtrees + ) + else: + ps = None + + if self.excluded_subtrees is not None: + es: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple( + self.excluded_subtrees + ) + else: + es = None + + return hash((ps, es)) + + @property + def permitted_subtrees( + self, + ) -> typing.Optional[typing.List[GeneralName]]: + return self._permitted_subtrees + + @property + def excluded_subtrees( + self, + ) -> typing.Optional[typing.List[GeneralName]]: + return self._excluded_subtrees + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class Extension(typing.Generic[ExtensionTypeVar]): + def __init__( + self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar + ) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + + if not isinstance(critical, bool): + raise TypeError("critical must be a boolean value") + + self._oid = oid + self._critical = critical + self._value = value + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def critical(self) -> bool: + return self._critical + + @property + def value(self) -> ExtensionTypeVar: + return self._value + + def __repr__(self) -> str: + return ( + "" + ).format(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Extension): + return NotImplemented + + return ( + self.oid == other.oid + and self.critical == other.critical + and self.value == other.value + ) + + def __hash__(self) -> int: + return hash((self.oid, self.critical, self.value)) + + +class GeneralNames: + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + general_names = list(general_names) + if not all(isinstance(x, GeneralName) for x in general_names): + raise TypeError( + "Every item in the general_names list must be an " + "object conforming to the GeneralName interface" + ) + + self._general_names = general_names + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[UniformResourceIdentifier], + typing.Type[RFC822Name], + ], + ) -> typing.List[str]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[DirectoryName], + ) -> typing.List[Name]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[RegisteredID], + ) -> typing.List[ObjectIdentifier]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[IPAddress] + ) -> typing.List[_IPADDRESS_TYPES]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[OtherName] + ) -> typing.List[OtherName]: + ... + + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[DirectoryName], + typing.Type[IPAddress], + typing.Type[OtherName], + typing.Type[RFC822Name], + typing.Type[RegisteredID], + typing.Type[UniformResourceIdentifier], + ], + ) -> typing.Union[ + typing.List[_IPADDRESS_TYPES], + typing.List[str], + typing.List[OtherName], + typing.List[Name], + typing.List[ObjectIdentifier], + ]: + # Return the value of each GeneralName, except for OtherName instances + # which we return directly because it has two important properties not + # just one value. + objs = (i for i in self if isinstance(i, type)) + if type != OtherName: + return [i.value for i in objs] + return list(objs) + + def __repr__(self) -> str: + return "".format(self._general_names) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, GeneralNames): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(tuple(self._general_names)) + + +class SubjectAlternativeName(ExtensionType): + oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME + + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[UniformResourceIdentifier], + typing.Type[RFC822Name], + ], + ) -> typing.List[str]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[DirectoryName], + ) -> typing.List[Name]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[RegisteredID], + ) -> typing.List[ObjectIdentifier]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[IPAddress] + ) -> typing.List[_IPADDRESS_TYPES]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[OtherName] + ) -> typing.List[OtherName]: + ... + + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[DirectoryName], + typing.Type[IPAddress], + typing.Type[OtherName], + typing.Type[RFC822Name], + typing.Type[RegisteredID], + typing.Type[UniformResourceIdentifier], + ], + ) -> typing.Union[ + typing.List[_IPADDRESS_TYPES], + typing.List[str], + typing.List[OtherName], + typing.List[Name], + typing.List[ObjectIdentifier], + ]: + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return "".format(self._general_names) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SubjectAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class IssuerAlternativeName(ExtensionType): + oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME + + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[UniformResourceIdentifier], + typing.Type[RFC822Name], + ], + ) -> typing.List[str]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[DirectoryName], + ) -> typing.List[Name]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[RegisteredID], + ) -> typing.List[ObjectIdentifier]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[IPAddress] + ) -> typing.List[_IPADDRESS_TYPES]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[OtherName] + ) -> typing.List[OtherName]: + ... + + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[DirectoryName], + typing.Type[IPAddress], + typing.Type[OtherName], + typing.Type[RFC822Name], + typing.Type[RegisteredID], + typing.Type[UniformResourceIdentifier], + ], + ) -> typing.Union[ + typing.List[_IPADDRESS_TYPES], + typing.List[str], + typing.List[OtherName], + typing.List[Name], + typing.List[ObjectIdentifier], + ]: + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return "".format(self._general_names) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IssuerAlternativeName): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CertificateIssuer(ExtensionType): + oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER + + def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: + self._general_names = GeneralNames(general_names) + + __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") + + @typing.overload + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[UniformResourceIdentifier], + typing.Type[RFC822Name], + ], + ) -> typing.List[str]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[DirectoryName], + ) -> typing.List[Name]: + ... + + @typing.overload + def get_values_for_type( + self, + type: typing.Type[RegisteredID], + ) -> typing.List[ObjectIdentifier]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[IPAddress] + ) -> typing.List[_IPADDRESS_TYPES]: + ... + + @typing.overload + def get_values_for_type( + self, type: typing.Type[OtherName] + ) -> typing.List[OtherName]: + ... + + def get_values_for_type( + self, + type: typing.Union[ + typing.Type[DNSName], + typing.Type[DirectoryName], + typing.Type[IPAddress], + typing.Type[OtherName], + typing.Type[RFC822Name], + typing.Type[RegisteredID], + typing.Type[UniformResourceIdentifier], + ], + ) -> typing.Union[ + typing.List[_IPADDRESS_TYPES], + typing.List[str], + typing.List[OtherName], + typing.List[Name], + typing.List[ObjectIdentifier], + ]: + return self._general_names.get_values_for_type(type) + + def __repr__(self) -> str: + return "".format(self._general_names) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CertificateIssuer): + return NotImplemented + + return self._general_names == other._general_names + + def __hash__(self) -> int: + return hash(self._general_names) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class CRLReason(ExtensionType): + oid = CRLEntryExtensionOID.CRL_REASON + + def __init__(self, reason: ReasonFlags) -> None: + if not isinstance(reason, ReasonFlags): + raise TypeError("reason must be an element from ReasonFlags") + + self._reason = reason + + def __repr__(self) -> str: + return "".format(self._reason) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, CRLReason): + return NotImplemented + + return self.reason == other.reason + + def __hash__(self) -> int: + return hash(self.reason) + + @property + def reason(self) -> ReasonFlags: + return self._reason + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class InvalidityDate(ExtensionType): + oid = CRLEntryExtensionOID.INVALIDITY_DATE + + def __init__(self, invalidity_date: datetime.datetime) -> None: + if not isinstance(invalidity_date, datetime.datetime): + raise TypeError("invalidity_date must be a datetime.datetime") + + self._invalidity_date = invalidity_date + + def __repr__(self) -> str: + return "".format( + self._invalidity_date + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InvalidityDate): + return NotImplemented + + return self.invalidity_date == other.invalidity_date + + def __hash__(self) -> int: + return hash(self.invalidity_date) + + @property + def invalidity_date(self) -> datetime.datetime: + return self._invalidity_date + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class PrecertificateSignedCertificateTimestamps(ExtensionType): + oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__( + self, + signed_certificate_timestamps: typing.Iterable[ + SignedCertificateTimestamp + ], + ) -> None: + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self) -> str: + return "".format( + list(self) + ) + + def __hash__(self) -> int: + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, PrecertificateSignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps + == other._signed_certificate_timestamps + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class SignedCertificateTimestamps(ExtensionType): + oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS + + def __init__( + self, + signed_certificate_timestamps: typing.Iterable[ + SignedCertificateTimestamp + ], + ) -> None: + signed_certificate_timestamps = list(signed_certificate_timestamps) + if not all( + isinstance(sct, SignedCertificateTimestamp) + for sct in signed_certificate_timestamps + ): + raise TypeError( + "Every item in the signed_certificate_timestamps list must be " + "a SignedCertificateTimestamp" + ) + self._signed_certificate_timestamps = signed_certificate_timestamps + + __len__, __iter__, __getitem__ = _make_sequence_methods( + "_signed_certificate_timestamps" + ) + + def __repr__(self) -> str: + return "".format(list(self)) + + def __hash__(self) -> int: + return hash(tuple(self._signed_certificate_timestamps)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, SignedCertificateTimestamps): + return NotImplemented + + return ( + self._signed_certificate_timestamps + == other._signed_certificate_timestamps + ) + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class OCSPNonce(ExtensionType): + oid = OCSPExtensionOID.NONCE + + def __init__(self, nonce: bytes) -> None: + if not isinstance(nonce, bytes): + raise TypeError("nonce must be bytes") + + self._nonce = nonce + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OCSPNonce): + return NotImplemented + + return self.nonce == other.nonce + + def __hash__(self) -> int: + return hash(self.nonce) + + def __repr__(self) -> str: + return "".format(self) + + @property + def nonce(self) -> bytes: + return self._nonce + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class IssuingDistributionPoint(ExtensionType): + oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT + + def __init__( + self, + full_name: typing.Optional[typing.Iterable[GeneralName]], + relative_name: typing.Optional[RelativeDistinguishedName], + only_contains_user_certs: bool, + only_contains_ca_certs: bool, + only_some_reasons: typing.Optional[typing.FrozenSet[ReasonFlags]], + indirect_crl: bool, + only_contains_attribute_certs: bool, + ) -> None: + if full_name is not None: + full_name = list(full_name) + + if only_some_reasons and ( + not isinstance(only_some_reasons, frozenset) + or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) + ): + raise TypeError( + "only_some_reasons must be None or frozenset of ReasonFlags" + ) + + if only_some_reasons and ( + ReasonFlags.unspecified in only_some_reasons + or ReasonFlags.remove_from_crl in only_some_reasons + ): + raise ValueError( + "unspecified and remove_from_crl are not valid reasons in an " + "IssuingDistributionPoint" + ) + + if not ( + isinstance(only_contains_user_certs, bool) + and isinstance(only_contains_ca_certs, bool) + and isinstance(indirect_crl, bool) + and isinstance(only_contains_attribute_certs, bool) + ): + raise TypeError( + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl and only_contains_attribute_certs " + "must all be boolean." + ) + + crl_constraints = [ + only_contains_user_certs, + only_contains_ca_certs, + indirect_crl, + only_contains_attribute_certs, + ] + + if len([x for x in crl_constraints if x]) > 1: + raise ValueError( + "Only one of the following can be set to True: " + "only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, only_contains_attribute_certs" + ) + + if not any( + [ + only_contains_user_certs, + only_contains_ca_certs, + indirect_crl, + only_contains_attribute_certs, + full_name, + relative_name, + only_some_reasons, + ] + ): + raise ValueError( + "Cannot create empty extension: " + "if only_contains_user_certs, only_contains_ca_certs, " + "indirect_crl, and only_contains_attribute_certs are all False" + ", then either full_name, relative_name, or only_some_reasons " + "must have a value." + ) + + self._only_contains_user_certs = only_contains_user_certs + self._only_contains_ca_certs = only_contains_ca_certs + self._indirect_crl = indirect_crl + self._only_contains_attribute_certs = only_contains_attribute_certs + self._only_some_reasons = only_some_reasons + self._full_name = full_name + self._relative_name = relative_name + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IssuingDistributionPoint): + return NotImplemented + + return ( + self.full_name == other.full_name + and self.relative_name == other.relative_name + and self.only_contains_user_certs == other.only_contains_user_certs + and self.only_contains_ca_certs == other.only_contains_ca_certs + and self.only_some_reasons == other.only_some_reasons + and self.indirect_crl == other.indirect_crl + and self.only_contains_attribute_certs + == other.only_contains_attribute_certs + ) + + def __hash__(self) -> int: + return hash( + ( + self.full_name, + self.relative_name, + self.only_contains_user_certs, + self.only_contains_ca_certs, + self.only_some_reasons, + self.indirect_crl, + self.only_contains_attribute_certs, + ) + ) + + @property + def full_name(self) -> typing.Optional[typing.List[GeneralName]]: + return self._full_name + + @property + def relative_name(self) -> typing.Optional[RelativeDistinguishedName]: + return self._relative_name + + @property + def only_contains_user_certs(self) -> bool: + return self._only_contains_user_certs + + @property + def only_contains_ca_certs(self) -> bool: + return self._only_contains_ca_certs + + @property + def only_some_reasons( + self, + ) -> typing.Optional[typing.FrozenSet[ReasonFlags]]: + return self._only_some_reasons + + @property + def indirect_crl(self) -> bool: + return self._indirect_crl + + @property + def only_contains_attribute_certs(self) -> bool: + return self._only_contains_attribute_certs + + def public_bytes(self) -> bytes: + return rust_x509.encode_extension_value(self) + + +class UnrecognizedExtension(ExtensionType): + def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError("oid must be an ObjectIdentifier") + self._oid = oid + self._value = value + + @property + def oid(self) -> ObjectIdentifier: # type: ignore[override] + return self._oid + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return ( + "".format(self) + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UnrecognizedExtension): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __hash__(self) -> int: + return hash((self.oid, self.value)) + + def public_bytes(self) -> bytes: + return self.value diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py new file mode 100644 index 000000000..b8b91ed94 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py @@ -0,0 +1,284 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import ipaddress +import typing +from email.utils import parseaddr + +from cryptography.x509.name import Name +from cryptography.x509.oid import ObjectIdentifier + +_IPADDRESS_TYPES = typing.Union[ + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network, +] + + +class UnsupportedGeneralNameType(Exception): + pass + + +class GeneralName(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def value(self) -> typing.Any: + """ + Return the value of the object + """ + + +class RFC822Name(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "RFC822Name values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + name, address = parseaddr(value) + if name or not address: + # parseaddr has found a name (e.g. Name ) or the entire + # value is an empty string. + raise ValueError("Invalid rfc822name value") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> "RFC822Name": + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return "".format(self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RFC822Name): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class DNSName(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "DNSName values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation(cls, value: str) -> "DNSName": + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return "".format(self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DNSName): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class UniformResourceIdentifier(GeneralName): + def __init__(self, value: str) -> None: + if isinstance(value, str): + try: + value.encode("ascii") + except UnicodeEncodeError: + raise ValueError( + "URI values should be passed as an A-label string. " + "This means unicode characters should be encoded via " + "a library like idna." + ) + else: + raise TypeError("value must be string") + + self._value = value + + @property + def value(self) -> str: + return self._value + + @classmethod + def _init_without_validation( + cls, value: str + ) -> "UniformResourceIdentifier": + instance = cls.__new__(cls) + instance._value = value + return instance + + def __repr__(self) -> str: + return "".format(self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, UniformResourceIdentifier): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class DirectoryName(GeneralName): + def __init__(self, value: Name) -> None: + if not isinstance(value, Name): + raise TypeError("value must be a Name") + + self._value = value + + @property + def value(self) -> Name: + return self._value + + def __repr__(self) -> str: + return "".format(self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, DirectoryName): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class RegisteredID(GeneralName): + def __init__(self, value: ObjectIdentifier) -> None: + if not isinstance(value, ObjectIdentifier): + raise TypeError("value must be an ObjectIdentifier") + + self._value = value + + @property + def value(self) -> ObjectIdentifier: + return self._value + + def __repr__(self) -> str: + return "".format(self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RegisteredID): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class IPAddress(GeneralName): + def __init__(self, value: _IPADDRESS_TYPES) -> None: + if not isinstance( + value, + ( + ipaddress.IPv4Address, + ipaddress.IPv6Address, + ipaddress.IPv4Network, + ipaddress.IPv6Network, + ), + ): + raise TypeError( + "value must be an instance of ipaddress.IPv4Address, " + "ipaddress.IPv6Address, ipaddress.IPv4Network, or " + "ipaddress.IPv6Network" + ) + + self._value = value + + @property + def value(self) -> _IPADDRESS_TYPES: + return self._value + + def _packed(self) -> bytes: + if isinstance( + self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) + ): + return self.value.packed + else: + return ( + self.value.network_address.packed + self.value.netmask.packed + ) + + def __repr__(self) -> str: + return "".format(self.value) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, IPAddress): + return NotImplemented + + return self.value == other.value + + def __hash__(self) -> int: + return hash(self.value) + + +class OtherName(GeneralName): + def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: + if not isinstance(type_id, ObjectIdentifier): + raise TypeError("type_id must be an ObjectIdentifier") + if not isinstance(value, bytes): + raise TypeError("value must be a binary string") + + self._type_id = type_id + self._value = value + + @property + def type_id(self) -> ObjectIdentifier: + return self._type_id + + @property + def value(self) -> bytes: + return self._value + + def __repr__(self) -> str: + return "".format( + self.type_id, self.value + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, OtherName): + return NotImplemented + + return self.type_id == other.type_id and self.value == other.value + + def __hash__(self) -> int: + return hash((self.type_id, self.value)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py new file mode 100644 index 000000000..acd7c0f1e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py @@ -0,0 +1,460 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import binascii +import re +import sys +import typing +import warnings + +from cryptography import utils +from cryptography.hazmat.bindings._rust import x509 as rust_x509 +from cryptography.x509.oid import NameOID, ObjectIdentifier + + +class _ASN1Type(utils.Enum): + BitString = 3 + OctetString = 4 + UTF8String = 12 + NumericString = 18 + PrintableString = 19 + T61String = 20 + IA5String = 22 + UTCTime = 23 + GeneralizedTime = 24 + VisibleString = 26 + UniversalString = 28 + BMPString = 30 + + +_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} +_NAMEOID_DEFAULT_TYPE: typing.Dict[ObjectIdentifier, _ASN1Type] = { + NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, + NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, + NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, + NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, + NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, +} + +# Type alias +_OidNameMap = typing.Mapping[ObjectIdentifier, str] +_NameOidMap = typing.Mapping[str, ObjectIdentifier] + +#: Short attribute names from RFC 4514: +#: https://tools.ietf.org/html/rfc4514#page-7 +_NAMEOID_TO_NAME: _OidNameMap = { + NameOID.COMMON_NAME: "CN", + NameOID.LOCALITY_NAME: "L", + NameOID.STATE_OR_PROVINCE_NAME: "ST", + NameOID.ORGANIZATION_NAME: "O", + NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", + NameOID.COUNTRY_NAME: "C", + NameOID.STREET_ADDRESS: "STREET", + NameOID.DOMAIN_COMPONENT: "DC", + NameOID.USER_ID: "UID", +} +_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} + + +def _escape_dn_value(val: typing.Union[str, bytes]) -> str: + """Escape special characters in RFC4514 Distinguished Name value.""" + + if not val: + return "" + + # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character + # followed by the hexadecimal encoding of the octets. + if isinstance(val, bytes): + return "#" + binascii.hexlify(val).decode("utf8") + + # See https://tools.ietf.org/html/rfc4514#section-2.4 + val = val.replace("\\", "\\\\") + val = val.replace('"', '\\"') + val = val.replace("+", "\\+") + val = val.replace(",", "\\,") + val = val.replace(";", "\\;") + val = val.replace("<", "\\<") + val = val.replace(">", "\\>") + val = val.replace("\0", "\\00") + + if val[0] in ("#", " "): + val = "\\" + val + if val[-1] == " ": + val = val[:-1] + "\\ " + + return val + + +def _unescape_dn_value(val: str) -> str: + if not val: + return "" + + # See https://tools.ietf.org/html/rfc4514#section-3 + + # special = escaped / SPACE / SHARP / EQUALS + # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE + def sub(m): + val = m.group(1) + # Regular escape + if len(val) == 1: + return val + # Hex-value scape + return chr(int(val, 16)) + + return _RFC4514NameParser._PAIR_RE.sub(sub, val) + + +class NameAttribute: + def __init__( + self, + oid: ObjectIdentifier, + value: typing.Union[str, bytes], + _type: typing.Optional[_ASN1Type] = None, + *, + _validate: bool = True, + ) -> None: + if not isinstance(oid, ObjectIdentifier): + raise TypeError( + "oid argument must be an ObjectIdentifier instance." + ) + if _type == _ASN1Type.BitString: + if oid != NameOID.X500_UNIQUE_IDENTIFIER: + raise TypeError( + "oid must be X500_UNIQUE_IDENTIFIER for BitString type." + ) + if not isinstance(value, bytes): + raise TypeError("value must be bytes for BitString") + else: + if not isinstance(value, str): + raise TypeError("value argument must be a str") + + if ( + oid == NameOID.COUNTRY_NAME + or oid == NameOID.JURISDICTION_COUNTRY_NAME + ): + assert isinstance(value, str) + c_len = len(value.encode("utf8")) + if c_len != 2 and _validate is True: + raise ValueError( + "Country name must be a 2 character country code" + ) + elif c_len != 2: + warnings.warn( + "Country names should be two characters, but the " + "attribute is {} characters in length.".format(c_len), + stacklevel=2, + ) + + # The appropriate ASN1 string type varies by OID and is defined across + # multiple RFCs including 2459, 3280, and 5280. In general UTF8String + # is preferred (2459), but 3280 and 5280 specify several OIDs with + # alternate types. This means when we see the sentinel value we need + # to look up whether the OID has a non-UTF8 type. If it does, set it + # to that. Otherwise, UTF8! + if _type is None: + _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) + + if not isinstance(_type, _ASN1Type): + raise TypeError("_type must be from the _ASN1Type enum") + + self._oid = oid + self._value = value + self._type = _type + + @property + def oid(self) -> ObjectIdentifier: + return self._oid + + @property + def value(self) -> typing.Union[str, bytes]: + return self._value + + @property + def rfc4514_attribute_name(self) -> str: + """ + The short attribute name (for example "CN") if available, + otherwise the OID dotted string. + """ + return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) + + def rfc4514_string( + self, attr_name_overrides: typing.Optional[_OidNameMap] = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + + Use short attribute name if available, otherwise fall back to OID + dotted string. + """ + attr_name = ( + attr_name_overrides.get(self.oid) if attr_name_overrides else None + ) + if attr_name is None: + attr_name = self.rfc4514_attribute_name + + return f"{attr_name}={_escape_dn_value(self.value)}" + + def __eq__(self, other: object) -> bool: + if not isinstance(other, NameAttribute): + return NotImplemented + + return self.oid == other.oid and self.value == other.value + + def __hash__(self) -> int: + return hash((self.oid, self.value)) + + def __repr__(self) -> str: + return "".format(self) + + +class RelativeDistinguishedName: + def __init__(self, attributes: typing.Iterable[NameAttribute]): + attributes = list(attributes) + if not attributes: + raise ValueError("a relative distinguished name cannot be empty") + if not all(isinstance(x, NameAttribute) for x in attributes): + raise TypeError("attributes must be an iterable of NameAttribute") + + # Keep list and frozenset to preserve attribute order where it matters + self._attributes = attributes + self._attribute_set = frozenset(attributes) + + if len(self._attribute_set) != len(attributes): + raise ValueError("duplicate attributes are not allowed") + + def get_attributes_for_oid( + self, oid: ObjectIdentifier + ) -> typing.List[NameAttribute]: + return [i for i in self if i.oid == oid] + + def rfc4514_string( + self, attr_name_overrides: typing.Optional[_OidNameMap] = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + + Within each RDN, attributes are joined by '+', although that is rarely + used in certificates. + """ + return "+".join( + attr.rfc4514_string(attr_name_overrides) + for attr in self._attributes + ) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, RelativeDistinguishedName): + return NotImplemented + + return self._attribute_set == other._attribute_set + + def __hash__(self) -> int: + return hash(self._attribute_set) + + def __iter__(self) -> typing.Iterator[NameAttribute]: + return iter(self._attributes) + + def __len__(self) -> int: + return len(self._attributes) + + def __repr__(self) -> str: + return "".format(self.rfc4514_string()) + + +class Name: + @typing.overload + def __init__(self, attributes: typing.Iterable[NameAttribute]) -> None: + ... + + @typing.overload + def __init__( + self, attributes: typing.Iterable[RelativeDistinguishedName] + ) -> None: + ... + + def __init__( + self, + attributes: typing.Iterable[ + typing.Union[NameAttribute, RelativeDistinguishedName] + ], + ) -> None: + attributes = list(attributes) + if all(isinstance(x, NameAttribute) for x in attributes): + self._attributes = [ + RelativeDistinguishedName([typing.cast(NameAttribute, x)]) + for x in attributes + ] + elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): + self._attributes = typing.cast( + typing.List[RelativeDistinguishedName], attributes + ) + else: + raise TypeError( + "attributes must be a list of NameAttribute" + " or a list RelativeDistinguishedName" + ) + + @classmethod + def from_rfc4514_string( + cls, + data: str, + attr_name_overrides: typing.Optional[_NameOidMap] = None, + ) -> "Name": + return _RFC4514NameParser(data, attr_name_overrides or {}).parse() + + def rfc4514_string( + self, attr_name_overrides: typing.Optional[_OidNameMap] = None + ) -> str: + """ + Format as RFC4514 Distinguished Name string. + For example 'CN=foobar.com,O=Foo Corp,C=US' + + An X.509 name is a two-level structure: a list of sets of attributes. + Each list element is separated by ',' and within each list element, set + elements are separated by '+'. The latter is almost never used in + real world certificates. According to RFC4514 section 2.1 the + RDNSequence must be reversed when converting to string representation. + """ + return ",".join( + attr.rfc4514_string(attr_name_overrides) + for attr in reversed(self._attributes) + ) + + def get_attributes_for_oid( + self, oid: ObjectIdentifier + ) -> typing.List[NameAttribute]: + return [i for i in self if i.oid == oid] + + @property + def rdns(self) -> typing.List[RelativeDistinguishedName]: + return self._attributes + + def public_bytes(self, backend: typing.Any = None) -> bytes: + return rust_x509.encode_name_bytes(self) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Name): + return NotImplemented + + return self._attributes == other._attributes + + def __hash__(self) -> int: + # TODO: this is relatively expensive, if this looks like a bottleneck + # for you, consider optimizing! + return hash(tuple(self._attributes)) + + def __iter__(self) -> typing.Iterator[NameAttribute]: + for rdn in self._attributes: + for ava in rdn: + yield ava + + def __len__(self) -> int: + return sum(len(rdn) for rdn in self._attributes) + + def __repr__(self) -> str: + rdns = ",".join(attr.rfc4514_string() for attr in self._attributes) + return "".format(rdns) + + +class _RFC4514NameParser: + _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+") + _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*") + + _PAIR = r"\\([\\ #=\"\+,;<>]|[\da-zA-Z]{2})" + _PAIR_RE = re.compile(_PAIR) + _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" + _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]" + _LEADCHAR = rf"{_LUTF1}|{_UTFMB}" + _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}" + _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}" + _STRING_RE = re.compile( + rf""" + ( + ({_LEADCHAR}|{_PAIR}) + ( + ({_STRINGCHAR}|{_PAIR})* + ({_TRAILCHAR}|{_PAIR}) + )? + )? + """, + re.VERBOSE, + ) + _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+") + + def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None: + self._data = data + self._idx = 0 + + self._attr_name_overrides = attr_name_overrides + + def _has_data(self) -> bool: + return self._idx < len(self._data) + + def _peek(self) -> typing.Optional[str]: + if self._has_data(): + return self._data[self._idx] + return None + + def _read_char(self, ch: str) -> None: + if self._peek() != ch: + raise ValueError + self._idx += 1 + + def _read_re(self, pat) -> str: + match = pat.match(self._data, pos=self._idx) + if match is None: + raise ValueError + val = match.group() + self._idx += len(val) + return val + + def parse(self) -> Name: + """ + Parses the `data` string and converts it to a Name. + + According to RFC4514 section 2.1 the RDNSequence must be + reversed when converting to string representation. So, when + we parse it, we need to reverse again to get the RDNs on the + correct order. + """ + rdns = [self._parse_rdn()] + + while self._has_data(): + self._read_char(",") + rdns.append(self._parse_rdn()) + + return Name(reversed(rdns)) + + def _parse_rdn(self) -> RelativeDistinguishedName: + nas = [self._parse_na()] + while self._peek() == "+": + self._read_char("+") + nas.append(self._parse_na()) + + return RelativeDistinguishedName(nas) + + def _parse_na(self) -> NameAttribute: + try: + oid_value = self._read_re(self._OID_RE) + except ValueError: + name = self._read_re(self._DESCR_RE) + oid = self._attr_name_overrides.get( + name, _NAME_TO_NAMEOID.get(name) + ) + if oid is None: + raise ValueError + else: + oid = ObjectIdentifier(oid_value) + + self._read_char("=") + if self._peek() == "#": + value = self._read_re(self._HEXSTRING_RE) + value = binascii.unhexlify(value[1:]).decode() + else: + raw_value = self._read_re(self._STRING_RE) + value = _unescape_dn_value(raw_value) + + return NameAttribute(oid, value) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py new file mode 100644 index 000000000..70aa3b361 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py @@ -0,0 +1,621 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +import abc +import datetime +import typing + +from cryptography import utils, x509 +from cryptography.hazmat.bindings._rust import ocsp +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric.types import ( + CERTIFICATE_PRIVATE_KEY_TYPES, +) +from cryptography.x509.base import ( + _EARLIEST_UTC_TIME, + _convert_to_naive_utc_time, + _reject_duplicate_extension, +) + + +class OCSPResponderEncoding(utils.Enum): + HASH = "By Hash" + NAME = "By Name" + + +class OCSPResponseStatus(utils.Enum): + SUCCESSFUL = 0 + MALFORMED_REQUEST = 1 + INTERNAL_ERROR = 2 + TRY_LATER = 3 + SIG_REQUIRED = 5 + UNAUTHORIZED = 6 + + +_ALLOWED_HASHES = ( + hashes.SHA1, + hashes.SHA224, + hashes.SHA256, + hashes.SHA384, + hashes.SHA512, +) + + +def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None: + if not isinstance(algorithm, _ALLOWED_HASHES): + raise ValueError( + "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" + ) + + +class OCSPCertStatus(utils.Enum): + GOOD = 0 + REVOKED = 1 + UNKNOWN = 2 + + +class _SingleResponse: + def __init__( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: typing.Optional[datetime.datetime], + revocation_time: typing.Optional[datetime.datetime], + revocation_reason: typing.Optional[x509.ReasonFlags], + ): + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + _verify_algorithm(algorithm) + if not isinstance(this_update, datetime.datetime): + raise TypeError("this_update must be a datetime object") + if next_update is not None and not isinstance( + next_update, datetime.datetime + ): + raise TypeError("next_update must be a datetime object or None") + + self._cert = cert + self._issuer = issuer + self._algorithm = algorithm + self._this_update = this_update + self._next_update = next_update + + if not isinstance(cert_status, OCSPCertStatus): + raise TypeError( + "cert_status must be an item from the OCSPCertStatus enum" + ) + if cert_status is not OCSPCertStatus.REVOKED: + if revocation_time is not None: + raise ValueError( + "revocation_time can only be provided if the certificate " + "is revoked" + ) + if revocation_reason is not None: + raise ValueError( + "revocation_reason can only be provided if the certificate" + " is revoked" + ) + else: + if not isinstance(revocation_time, datetime.datetime): + raise TypeError("revocation_time must be a datetime object") + + revocation_time = _convert_to_naive_utc_time(revocation_time) + if revocation_time < _EARLIEST_UTC_TIME: + raise ValueError( + "The revocation_time must be on or after" + " 1950 January 1." + ) + + if revocation_reason is not None and not isinstance( + revocation_reason, x509.ReasonFlags + ): + raise TypeError( + "revocation_reason must be an item from the ReasonFlags " + "enum or None" + ) + + self._cert_status = cert_status + self._revocation_time = revocation_time + self._revocation_reason = revocation_reason + + +class OCSPRequest(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def issuer_key_hash(self) -> bytes: + """ + The hash of the issuer public key + """ + + @property + @abc.abstractmethod + def issuer_name_hash(self) -> bytes: + """ + The hash of the issuer name + """ + + @property + @abc.abstractmethod + def hash_algorithm(self) -> hashes.HashAlgorithm: + """ + The hash algorithm used in the issuer name and key hashes + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + The serial number of the cert whose status is being checked + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the request to DER + """ + + @property + @abc.abstractmethod + def extensions(self) -> x509.Extensions: + """ + The list of request extensions. Not single request extensions. + """ + + +class OCSPSingleResponse(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def certificate_status(self) -> OCSPCertStatus: + """ + The status of the certificate (an element from the OCSPCertStatus enum) + """ + + @property + @abc.abstractmethod + def revocation_time(self) -> typing.Optional[datetime.datetime]: + """ + The date of when the certificate was revoked or None if not + revoked. + """ + + @property + @abc.abstractmethod + def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]: + """ + The reason the certificate was revoked or None if not specified or + not revoked. + """ + + @property + @abc.abstractmethod + def this_update(self) -> datetime.datetime: + """ + The most recent time at which the status being indicated is known by + the responder to have been correct + """ + + @property + @abc.abstractmethod + def next_update(self) -> typing.Optional[datetime.datetime]: + """ + The time when newer information will be available + """ + + @property + @abc.abstractmethod + def issuer_key_hash(self) -> bytes: + """ + The hash of the issuer public key + """ + + @property + @abc.abstractmethod + def issuer_name_hash(self) -> bytes: + """ + The hash of the issuer name + """ + + @property + @abc.abstractmethod + def hash_algorithm(self) -> hashes.HashAlgorithm: + """ + The hash algorithm used in the issuer name and key hashes + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + The serial number of the cert whose status is being checked + """ + + +class OCSPResponse(metaclass=abc.ABCMeta): + @property + @abc.abstractmethod + def responses(self) -> typing.Iterator[OCSPSingleResponse]: + """ + An iterator over the individual SINGLERESP structures in the + response + """ + + @property + @abc.abstractmethod + def response_status(self) -> OCSPResponseStatus: + """ + The status of the response. This is a value from the OCSPResponseStatus + enumeration + """ + + @property + @abc.abstractmethod + def signature_algorithm_oid(self) -> x509.ObjectIdentifier: + """ + The ObjectIdentifier of the signature algorithm + """ + + @property + @abc.abstractmethod + def signature_hash_algorithm( + self, + ) -> typing.Optional[hashes.HashAlgorithm]: + """ + Returns a HashAlgorithm corresponding to the type of the digest signed + """ + + @property + @abc.abstractmethod + def signature(self) -> bytes: + """ + The signature bytes + """ + + @property + @abc.abstractmethod + def tbs_response_bytes(self) -> bytes: + """ + The tbsResponseData bytes + """ + + @property + @abc.abstractmethod + def certificates(self) -> typing.List[x509.Certificate]: + """ + A list of certificates used to help build a chain to verify the OCSP + response. This situation occurs when the OCSP responder uses a delegate + certificate. + """ + + @property + @abc.abstractmethod + def responder_key_hash(self) -> typing.Optional[bytes]: + """ + The responder's key hash or None + """ + + @property + @abc.abstractmethod + def responder_name(self) -> typing.Optional[x509.Name]: + """ + The responder's Name or None + """ + + @property + @abc.abstractmethod + def produced_at(self) -> datetime.datetime: + """ + The time the response was produced + """ + + @property + @abc.abstractmethod + def certificate_status(self) -> OCSPCertStatus: + """ + The status of the certificate (an element from the OCSPCertStatus enum) + """ + + @property + @abc.abstractmethod + def revocation_time(self) -> typing.Optional[datetime.datetime]: + """ + The date of when the certificate was revoked or None if not + revoked. + """ + + @property + @abc.abstractmethod + def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]: + """ + The reason the certificate was revoked or None if not specified or + not revoked. + """ + + @property + @abc.abstractmethod + def this_update(self) -> datetime.datetime: + """ + The most recent time at which the status being indicated is known by + the responder to have been correct + """ + + @property + @abc.abstractmethod + def next_update(self) -> typing.Optional[datetime.datetime]: + """ + The time when newer information will be available + """ + + @property + @abc.abstractmethod + def issuer_key_hash(self) -> bytes: + """ + The hash of the issuer public key + """ + + @property + @abc.abstractmethod + def issuer_name_hash(self) -> bytes: + """ + The hash of the issuer name + """ + + @property + @abc.abstractmethod + def hash_algorithm(self) -> hashes.HashAlgorithm: + """ + The hash algorithm used in the issuer name and key hashes + """ + + @property + @abc.abstractmethod + def serial_number(self) -> int: + """ + The serial number of the cert whose status is being checked + """ + + @property + @abc.abstractmethod + def extensions(self) -> x509.Extensions: + """ + The list of response extensions. Not single response extensions. + """ + + @property + @abc.abstractmethod + def single_extensions(self) -> x509.Extensions: + """ + The list of single response extensions. Not response extensions. + """ + + @abc.abstractmethod + def public_bytes(self, encoding: serialization.Encoding) -> bytes: + """ + Serializes the response to DER + """ + + +class OCSPRequestBuilder: + def __init__( + self, + request: typing.Optional[ + typing.Tuple[ + x509.Certificate, x509.Certificate, hashes.HashAlgorithm + ] + ] = None, + request_hash: typing.Optional[ + typing.Tuple[bytes, bytes, int, hashes.HashAlgorithm] + ] = None, + extensions: typing.List[x509.Extension[x509.ExtensionType]] = [], + ) -> None: + self._request = request + self._request_hash = request_hash + self._extensions = extensions + + def add_certificate( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + ) -> "OCSPRequestBuilder": + if self._request is not None or self._request_hash is not None: + raise ValueError("Only one certificate can be added to a request") + + _verify_algorithm(algorithm) + if not isinstance(cert, x509.Certificate) or not isinstance( + issuer, x509.Certificate + ): + raise TypeError("cert and issuer must be a Certificate") + + return OCSPRequestBuilder( + (cert, issuer, algorithm), self._request_hash, self._extensions + ) + + def add_certificate_by_hash( + self, + issuer_name_hash: bytes, + issuer_key_hash: bytes, + serial_number: int, + algorithm: hashes.HashAlgorithm, + ) -> "OCSPRequestBuilder": + if self._request is not None or self._request_hash is not None: + raise ValueError("Only one certificate can be added to a request") + + if not isinstance(serial_number, int): + raise TypeError("serial_number must be an integer") + + _verify_algorithm(algorithm) + utils._check_bytes("issuer_name_hash", issuer_name_hash) + utils._check_bytes("issuer_key_hash", issuer_key_hash) + if algorithm.digest_size != len( + issuer_name_hash + ) or algorithm.digest_size != len(issuer_key_hash): + raise ValueError( + "issuer_name_hash and issuer_key_hash must be the same length " + "as the digest size of the algorithm" + ) + + return OCSPRequestBuilder( + self._request, + (issuer_name_hash, issuer_key_hash, serial_number, algorithm), + self._extensions, + ) + + def add_extension( + self, extval: x509.ExtensionType, critical: bool + ) -> "OCSPRequestBuilder": + if not isinstance(extval, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPRequestBuilder( + self._request, self._request_hash, self._extensions + [extension] + ) + + def build(self) -> OCSPRequest: + if self._request is None and self._request_hash is None: + raise ValueError("You must add a certificate before building") + + return ocsp.create_ocsp_request(self) + + +class OCSPResponseBuilder: + def __init__( + self, + response: typing.Optional[_SingleResponse] = None, + responder_id: typing.Optional[ + typing.Tuple[x509.Certificate, OCSPResponderEncoding] + ] = None, + certs: typing.Optional[typing.List[x509.Certificate]] = None, + extensions: typing.List[x509.Extension[x509.ExtensionType]] = [], + ): + self._response = response + self._responder_id = responder_id + self._certs = certs + self._extensions = extensions + + def add_response( + self, + cert: x509.Certificate, + issuer: x509.Certificate, + algorithm: hashes.HashAlgorithm, + cert_status: OCSPCertStatus, + this_update: datetime.datetime, + next_update: typing.Optional[datetime.datetime], + revocation_time: typing.Optional[datetime.datetime], + revocation_reason: typing.Optional[x509.ReasonFlags], + ) -> "OCSPResponseBuilder": + if self._response is not None: + raise ValueError("Only one response per OCSPResponse.") + + singleresp = _SingleResponse( + cert, + issuer, + algorithm, + cert_status, + this_update, + next_update, + revocation_time, + revocation_reason, + ) + return OCSPResponseBuilder( + singleresp, + self._responder_id, + self._certs, + self._extensions, + ) + + def responder_id( + self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate + ) -> "OCSPResponseBuilder": + if self._responder_id is not None: + raise ValueError("responder_id can only be set once") + if not isinstance(responder_cert, x509.Certificate): + raise TypeError("responder_cert must be a Certificate") + if not isinstance(encoding, OCSPResponderEncoding): + raise TypeError( + "encoding must be an element from OCSPResponderEncoding" + ) + + return OCSPResponseBuilder( + self._response, + (responder_cert, encoding), + self._certs, + self._extensions, + ) + + def certificates( + self, certs: typing.Iterable[x509.Certificate] + ) -> "OCSPResponseBuilder": + if self._certs is not None: + raise ValueError("certificates may only be set once") + certs = list(certs) + if len(certs) == 0: + raise ValueError("certs must not be an empty list") + if not all(isinstance(x, x509.Certificate) for x in certs): + raise TypeError("certs must be a list of Certificates") + return OCSPResponseBuilder( + self._response, + self._responder_id, + certs, + self._extensions, + ) + + def add_extension( + self, extval: x509.ExtensionType, critical: bool + ) -> "OCSPResponseBuilder": + if not isinstance(extval, x509.ExtensionType): + raise TypeError("extension must be an ExtensionType") + + extension = x509.Extension(extval.oid, critical, extval) + _reject_duplicate_extension(extension, self._extensions) + + return OCSPResponseBuilder( + self._response, + self._responder_id, + self._certs, + self._extensions + [extension], + ) + + def sign( + self, + private_key: CERTIFICATE_PRIVATE_KEY_TYPES, + algorithm: typing.Optional[hashes.HashAlgorithm], + ) -> OCSPResponse: + if self._response is None: + raise ValueError("You must add a response before signing") + if self._responder_id is None: + raise ValueError("You must add a responder_id before signing") + + return ocsp.create_ocsp_response( + OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm + ) + + @classmethod + def build_unsuccessful( + cls, response_status: OCSPResponseStatus + ) -> OCSPResponse: + if not isinstance(response_status, OCSPResponseStatus): + raise TypeError( + "response_status must be an item from OCSPResponseStatus" + ) + if response_status is OCSPResponseStatus.SUCCESSFUL: + raise ValueError("response_status cannot be SUCCESSFUL") + + return ocsp.create_ocsp_response(response_status, None, None, None) + + +def load_der_ocsp_request(data: bytes) -> OCSPRequest: + return ocsp.load_der_ocsp_request(data) + + +def load_der_ocsp_response(data: bytes) -> OCSPResponse: + return ocsp.load_der_ocsp_response(data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py new file mode 100644 index 000000000..0d91a5469 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py @@ -0,0 +1,31 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from cryptography.hazmat._oid import ( + AttributeOID, + AuthorityInformationAccessOID, + CertificatePoliciesOID, + CRLEntryExtensionOID, + ExtendedKeyUsageOID, + ExtensionOID, + NameOID, + ObjectIdentifier, + OCSPExtensionOID, + SignatureAlgorithmOID, + SubjectInformationAccessOID, +) + +__all__ = [ + "AttributeOID", + "AuthorityInformationAccessOID", + "CRLEntryExtensionOID", + "CertificatePoliciesOID", + "ExtendedKeyUsageOID", + "ExtensionOID", + "NameOID", + "OCSPExtensionOID", + "ObjectIdentifier", + "SignatureAlgorithmOID", + "SubjectInformationAccessOID", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE new file mode 100644 index 000000000..474479a2c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE @@ -0,0 +1,24 @@ +"python-ecdsa" Copyright (c) 2010 Brian Warner + +Portions written in 2005 by Peter Pearson and placed in the public domain. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA new file mode 100644 index 000000000..fa1c5feef --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA @@ -0,0 +1,675 @@ +Metadata-Version: 2.1 +Name: ecdsa +Version: 0.18.0 +Summary: ECDSA cryptographic signature library (pure python) +Home-page: http://github.com/tlsfuzzer/python-ecdsa +Author: Brian Warner +Author-email: warner@lothar.com +License: MIT +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.* +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: six (>=1.9.0) +Provides-Extra: gmpy +Requires-Dist: gmpy ; extra == 'gmpy' +Provides-Extra: gmpy2 +Requires-Dist: gmpy2 ; extra == 'gmpy2' + +# Pure-Python ECDSA and ECDH + +[![Build Status](https://github.com/tlsfuzzer/python-ecdsa/workflows/GitHub%20CI/badge.svg?branch=master)](https://github.com/tlsfuzzer/python-ecdsa/actions?query=workflow%3A%22GitHub+CI%22+branch%3Amaster) +[![Documentation Status](https://readthedocs.org/projects/ecdsa/badge/?version=latest)](https://ecdsa.readthedocs.io/en/latest/?badge=latest) +[![Coverage Status](https://coveralls.io/repos/github/tlsfuzzer/python-ecdsa/badge.svg?branch=master)](https://coveralls.io/github/tlsfuzzer/python-ecdsa?branch=master) +![condition coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-condition-coverage.json) +[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/tlsfuzzer/python-ecdsa.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tlsfuzzer/python-ecdsa/context:python) +[![Total alerts](https://img.shields.io/lgtm/alerts/g/tlsfuzzer/python-ecdsa.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tlsfuzzer/python-ecdsa/alerts/) +[![Latest Version](https://img.shields.io/pypi/v/ecdsa.svg?style=flat)](https://pypi.python.org/pypi/ecdsa/) +![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat) + + +This is an easy-to-use implementation of ECC (Elliptic Curve Cryptography) +with support for ECDSA (Elliptic Curve Digital Signature Algorithm), +EdDSA (Edwards-curve Digital Signature Algorithm) and ECDH +(Elliptic Curve Diffie-Hellman), implemented purely in Python, released under +the MIT license. With this library, you can quickly create key pairs (signing +key and verifying key), sign messages, and verify the signatures. You can +also agree on a shared secret key based on exchanged public keys. +The keys and signatures are very short, making them easy to handle and +incorporate into other protocols. + +**NOTE: This library should not be used in production settings, see [Security](#Security) for more details.** + +## Features + +This library provides key generation, signing, verifying, and shared secret +derivation for five +popular NIST "Suite B" GF(p) (_prime field_) curves, with key lengths of 192, +224, 256, 384, and 521 bits. The "short names" for these curves, as known by +the OpenSSL tool (`openssl ecparam -list_curves`), are: `prime192v1`, +`secp224r1`, `prime256v1`, `secp384r1`, and `secp521r1`. It includes the +256-bit curve `secp256k1` used by Bitcoin. There is also support for the +regular (non-twisted) variants of Brainpool curves from 160 to 512 bits. The +"short names" of those curves are: `brainpoolP160r1`, `brainpoolP192r1`, +`brainpoolP224r1`, `brainpoolP256r1`, `brainpoolP320r1`, `brainpoolP384r1`, +`brainpoolP512r1`. Few of the small curves from SEC standard are also +included (mainly to speed-up testing of the library), those are: +`secp112r1`, `secp112r2`, `secp128r1`, and `secp160r1`. +Key generation, siging and verifying is also supported for Ed25519 and +Ed448 curves. +No other curves are included, but it is not too hard to add support for more +curves over prime fields. + +## Dependencies + +This library uses only Python and the 'six' package. It is compatible with +Python 2.6, 2.7, and 3.3+. It also supports execution on alternative +implementations like pypy and pypy3. + +If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic. +Either of them can be installed after this library is installed, +`python-ecdsa` will detect their presence on start-up and use them +automatically. +You should prefer `gmpy2` on Python3 for optimal performance. + +To run the OpenSSL compatibility tests, the 'openssl' tool must be in your +`PATH`. This release has been tested successfully against OpenSSL 0.9.8o, +1.0.0a, 1.0.2f, 1.1.1d and 3.0.1 (among others). + + +## Installation + +This library is available on PyPI, it's recommended to install it using `pip`: + +``` +pip install ecdsa +``` + +In case higher performance is wanted and using native code is not a problem, +it's possible to specify installation together with `gmpy2`: + +``` +pip install ecdsa[gmpy2] +``` + +or (slower, legacy option): +``` +pip install ecdsa[gmpy] +``` + +## Speed + +The following table shows how long this library takes to generate key pairs +(`keygen`), to sign data (`sign`), to verify those signatures (`verify`), +to derive a shared secret (`ecdh`), and +to verify the signatures with no key-specific precomputation (`no PC verify`). +All those values are in seconds. +For convenience, the inverses of those values are also provided: +how many keys per second can be generated (`keygen/s`), how many signatures +can be made per second (`sign/s`), how many signatures can be verified +per second (`verify/s`), how many shared secrets can be derived per second +(`ecdh/s`), and how many signatures with no key specific +precomputation can be verified per second (`no PC verify/s`). The size of raw +signature (generally the smallest +the way a signature can be encoded) is also provided in the `siglen` column. +Use `tox -e speed` to generate this table on your own computer. +On an Intel Core i7 4790K @ 4.0GHz I'm getting the following performance: + +``` + siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s + NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43 + NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66 + NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27 + NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00 + NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39 + SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01 + BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92 + BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29 + BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16 + BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25 + BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49 + BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20 + BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61 + SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20 + SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16 + SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16 + SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07 + Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50 + Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69 + + ecdh ecdh/s + NIST192p: 0.00104s 964.89 + NIST224p: 0.00134s 748.63 + NIST256p: 0.00170s 587.08 + NIST384p: 0.00352s 283.90 + NIST521p: 0.00717s 139.51 + SECP256k1: 0.00154s 648.40 + BRAINPOOLP160r1: 0.00082s 1220.70 + BRAINPOOLP192r1: 0.00105s 956.75 + BRAINPOOLP224r1: 0.00136s 734.52 + BRAINPOOLP256r1: 0.00178s 563.32 + BRAINPOOLP320r1: 0.00252s 397.23 + BRAINPOOLP384r1: 0.00376s 266.27 + BRAINPOOLP512r1: 0.00733s 136.35 + SECP112r1: 0.00046s 2180.40 + SECP112r2: 0.00045s 2229.14 + SECP128r1: 0.00054s 1868.15 + SECP160r1: 0.00080s 1243.98 +``` + +To test performance with `gmpy2` loaded, use `tox -e speedgmpy2`. +On the same machine I'm getting the following performance with `gmpy2`: +``` + siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s + NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41 + NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90 + NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82 + NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43 + NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40 + SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32 + BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81 + BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19 + BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87 + BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60 + BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34 + BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85 + BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83 + SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83 + SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28 + SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88 + SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35 + Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02 + Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62 + + ecdh ecdh/s + NIST192p: 0.00050s 1985.70 + NIST224p: 0.00066s 1524.16 + NIST256p: 0.00071s 1413.07 + NIST384p: 0.00127s 788.89 + NIST521p: 0.00230s 434.85 + SECP256k1: 0.00071s 1409.95 + BRAINPOOLP160r1: 0.00042s 2374.65 + BRAINPOOLP192r1: 0.00051s 1960.01 + BRAINPOOLP224r1: 0.00066s 1518.37 + BRAINPOOLP256r1: 0.00071s 1399.90 + BRAINPOOLP320r1: 0.00100s 997.21 + BRAINPOOLP384r1: 0.00129s 777.51 + BRAINPOOLP512r1: 0.00210s 475.99 + SECP112r1: 0.00022s 4457.70 + SECP112r2: 0.00024s 4252.33 + SECP128r1: 0.00028s 3589.31 + SECP160r1: 0.00043s 2305.02 +``` + +(there's also `gmpy` version, execute it using `tox -e speedgmpy`) + +For comparison, a highly optimised implementation (including curve-specific +assembly for some curves), like the one in OpenSSL 1.1.1d, provides the +following performance numbers on the same machine. +Run `openssl speed ecdsa` and `openssl speed ecdh` to reproduce it: +``` + sign verify sign/s verify/s + 192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7 + 224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0 + 256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6 + 384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1 + 521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5 + 256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2 + 384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1 + 512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1 + + sign verify sign/s verify/s + 253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7 + 456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7 + + op op/s + 192 bits ecdh (nistp192) 0.0002s 4853.4 + 224 bits ecdh (nistp224) 0.0001s 15252.1 + 256 bits ecdh (nistp256) 0.0001s 18436.3 + 384 bits ecdh (nistp384) 0.0008s 1292.7 + 521 bits ecdh (nistp521) 0.0003s 2884.7 + 256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5 + 384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0 + 512 bits ecdh (brainpoolP512r1) 0.0014s 694.8 +``` + +Keys and signature can be serialized in different ways (see Usage, below). +For a NIST192p key, the three basic representations require strings of the +following lengths (in bytes): + + to_string: signkey= 24, verifykey= 48, signature=48 + compressed: signkey=n/a, verifykey= 25, signature=n/a + DER: signkey=106, verifykey= 80, signature=55 + PEM: signkey=278, verifykey=162, (no support for PEM signatures) + +## History + +In 2006, Peter Pearson announced his pure-python implementation of ECDSA in a +[message to sci.crypt][1], available from his [download site][2]. In 2010, +Brian Warner wrote a wrapper around this code, to make it a bit easier and +safer to use. In 2020, Hubert Kario included an implementation of elliptic +curve cryptography that uses Jacobian coordinates internally, improving +performance about 20-fold. You are looking at the README for this wrapper. + +[1]: http://www.derkeiler.com/Newsgroups/sci.crypt/2006-01/msg00651.html +[2]: http://webpages.charter.net/curryfans/peter/downloads.html + +## Testing + +To run the full test suite, do this: + + tox -e coverage + +On an Intel Core i7 4790K @ 4.0GHz, the tests take about 18 seconds to execute. +The test suite uses +[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some +inherent variability in the test suite execution time. + +One part of `test_pyecdsa.py` and `test_ecdh.py` checks compatibility with +OpenSSL, by running the "openssl" CLI tool, make sure it's in your `PATH` if +you want to test compatibility with it (if OpenSSL is missing, too old, or +doesn't support all the curves supported in upstream releases you will see +skipped tests in the above `coverage` run). + +## Security + +This library was not designed with security in mind. If you are processing +data that needs to be protected we suggest you use a quality wrapper around +OpenSSL. [pyca/cryptography](https://cryptography.io) is one example of such +a wrapper. The primary use-case of this library is as a portable library for +interoperability testing and as a teaching tool. + +**This library does not protect against side-channel attacks.** + +Do not allow attackers to measure how long it takes you to generate a key pair +or sign a message. Do not allow attackers to run code on the same physical +machine when key pair generation or signing is taking place (this includes +virtual machines). Do not allow attackers to measure how much power your +computer uses while generating the key pair or signing a message. Do not allow +attackers to measure RF interference coming from your computer while generating +a key pair or signing a message. Note: just loading the private key will cause +key pair generation. Other operations or attack vectors may also be +vulnerable to attacks. **For a sophisticated attacker observing just one +operation with a private key will be sufficient to completely +reconstruct the private key**. + +Please also note that any Pure-python cryptographic library will be vulnerable +to the same side-channel attacks. This is because Python does not provide +side-channel secure primitives (with the exception of +[`hmac.compare_digest()`][3]), making side-channel secure programming +impossible. + +This library depends upon a strong source of random numbers. Do not use it on +a system where `os.urandom()` does not provide cryptographically secure +random numbers. + +[3]: https://docs.python.org/3/library/hmac.html#hmac.compare_digest + +## Usage + +You start by creating a `SigningKey`. You can use this to sign data, by passing +in data as a byte string and getting back the signature (also a byte string). +You can also ask a `SigningKey` to give you the corresponding `VerifyingKey`. +The `VerifyingKey` can be used to verify a signature, by passing it both the +data string and the signature byte string: it either returns True or raises +`BadSignatureError`. + +```python +from ecdsa import SigningKey +sk = SigningKey.generate() # uses NIST192p +vk = sk.verifying_key +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +Each `SigningKey`/`VerifyingKey` is associated with a specific curve, like +NIST192p (the default one). Longer curves are more secure, but take longer to +use, and result in longer keys and signatures. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +The `SigningKey` can be serialized into several different formats: the shortest +is to call `s=sk.to_string()`, and then re-create it with +`SigningKey.from_string(s, curve)` . This short form does not record the +curve, so you must be sure to pass to `from_string()` the same curve you used +for the original key. The short form of a NIST192p-based signing key is just 24 +bytes long. If a point encoding is invalid or it does not lie on the specified +curve, `from_string()` will raise `MalformedPointError`. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +sk_string = sk.to_string() +sk2 = SigningKey.from_string(sk_string, curve=NIST384p) +print(sk_string.hex()) +print(sk2.to_string().hex()) +``` + +Note: while the methods are called `to_string()` the type they return is +actually `bytes`, the "string" part is leftover from Python 2. + +`sk.to_pem()` and `sk.to_der()` will serialize the signing key into the same +formats that OpenSSL uses. The PEM file looks like the familiar ASCII-armored +`"-----BEGIN EC PRIVATE KEY-----"` base64-encoded format, and the DER format +is a shorter binary form of the same data. +`SigningKey.from_pem()/.from_der()` will undo this serialization. These +formats include the curve name, so you do not need to pass in a curve +identifier to the deserializer. In case the file is malformed `from_der()` +and `from_pem()` will raise `UnexpectedDER` or` MalformedPointError`. + +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +sk_pem = sk.to_pem() +sk2 = SigningKey.from_pem(sk_pem) +# sk and sk2 are the same key +``` + +Likewise, the `VerifyingKey` can be serialized in the same way: +`vk.to_string()/VerifyingKey.from_string()`, `to_pem()/from_pem()`, and +`to_der()/from_der()`. The same `curve=` argument is needed for +`VerifyingKey.from_string()`. + +```python +from ecdsa import SigningKey, VerifyingKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk_string = vk.to_string() +vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p) +# vk and vk2 are the same key + +from ecdsa import SigningKey, VerifyingKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk_pem = vk.to_pem() +vk2 = VerifyingKey.from_pem(vk_pem) +# vk and vk2 are the same key +``` + +There are a couple of different ways to compute a signature. Fundamentally, +ECDSA takes a number that represents the data being signed, and returns a +pair of numbers that represent the signature. The `hashfunc=` argument to +`sk.sign()` and `vk.verify()` is used to turn an arbitrary string into a +fixed-length digest, which is then turned into a number that ECDSA can sign, +and both sign and verify must use the same approach. The default value is +`hashlib.sha1`, but if you use NIST256p or a longer curve, you can use +`hashlib.sha256` instead. + +There are also multiple ways to represent a signature. The default +`sk.sign()` and `vk.verify()` methods present it as a short string, for +simplicity and minimal overhead. To use a different scheme, use the +`sk.sign(sigencode=)` and `vk.verify(sigdecode=)` arguments. There are helper +functions in the `ecdsa.util` module that can be useful here. + +It is also possible to create a `SigningKey` from a "seed", which is +deterministic. This can be used in protocols where you want to derive +consistent signing keys from some other secret, for example when you want +three separate keys and only want to store a single master secret. You should +start with a uniformly-distributed unguessable seed with about `curve.baselen` +bytes of entropy, and then use one of the helper functions in `ecdsa.util` to +convert it into an integer in the correct range, and then finally pass it +into `SigningKey.from_secret_exponent()`, like this: + +```python +import os +from ecdsa import NIST384p, SigningKey +from ecdsa.util import randrange_from_seed__trytryagain + +def make_key(seed): + secexp = randrange_from_seed__trytryagain(seed, NIST384p.order) + return SigningKey.from_secret_exponent(secexp, curve=NIST384p) + +seed = os.urandom(NIST384p.baselen) # or other starting point +sk1a = make_key(seed) +sk1b = make_key(seed) +# note: sk1a and sk1b are the same key +assert sk1a.to_string() == sk1b.to_string() +sk2 = make_key(b"2-"+seed) # different key +assert sk1a.to_string() != sk2.to_string() +``` + +In case the application will verify a lot of signatures made with a single +key, it's possible to precompute some of the internal values to make +signature verification significantly faster. The break-even point occurs at +about 100 signatures verified. + +To perform precomputation, you can call the `precompute()` method +on `VerifyingKey` instance: +```python +from ecdsa import SigningKey, NIST384p +sk = SigningKey.generate(curve=NIST384p) +vk = sk.verifying_key +vk.precompute() +signature = sk.sign(b"message") +assert vk.verify(signature, b"message") +``` + +Once `precompute()` was called, all signature verifications with this key will +be faster to execute. + +## OpenSSL Compatibility + +To produce signatures that can be verified by OpenSSL tools, or to verify +signatures that were produced by those tools, use: + +```python +# openssl ecparam -name prime256v1 -genkey -out sk.pem +# openssl ec -in sk.pem -pubout -out vk.pem +# echo "data for signing" > data +# openssl dgst -sha256 -sign sk.pem -out data.sig data +# openssl dgst -sha256 -verify vk.pem -signature data.sig data +# openssl dgst -sha256 -prverify sk.pem -signature data.sig data + +import hashlib +from ecdsa import SigningKey, VerifyingKey +from ecdsa.util import sigencode_der, sigdecode_der + +with open("vk.pem") as f: + vk = VerifyingKey.from_pem(f.read()) + +with open("data", "rb") as f: + data = f.read() + +with open("data.sig", "rb") as f: + signature = f.read() + +assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der) + +with open("sk.pem") as f: + sk = SigningKey.from_pem(f.read(), hashlib.sha256) + +new_signature = sk.sign_deterministic(data, sigencode=sigencode_der) + +with open("data.sig2", "wb") as f: + f.write(new_signature) + +# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data +``` + +Note: if compatibility with OpenSSL 1.0.0 or earlier is necessary, the +`sigencode_string` and `sigdecode_string` from `ecdsa.util` can be used for +respectively writing and reading the signatures. + +The keys also can be written in format that openssl can handle: + +```python +from ecdsa import SigningKey, VerifyingKey + +with open("sk.pem") as f: + sk = SigningKey.from_pem(f.read()) +with open("sk.pem", "wb") as f: + f.write(sk.to_pem()) + +with open("vk.pem") as f: + vk = VerifyingKey.from_pem(f.read()) +with open("vk.pem", "wb") as f: + f.write(vk.to_pem()) +``` + +## Entropy + +Creating a signing key with `SigningKey.generate()` requires some form of +entropy (as opposed to +`from_secret_exponent`/`from_string`/`from_der`/`from_pem`, +which are deterministic and do not require an entropy source). The default +source is `os.urandom()`, but you can pass any other function that behaves +like `os.urandom` as the `entropy=` argument to do something different. This +may be useful in unit tests, where you want to achieve repeatable results. The +`ecdsa.util.PRNG` utility is handy here: it takes a seed and produces a strong +pseudo-random stream from it: + +```python +from ecdsa.util import PRNG +from ecdsa import SigningKey +rng1 = PRNG(b"seed") +sk1 = SigningKey.generate(entropy=rng1) +rng2 = PRNG(b"seed") +sk2 = SigningKey.generate(entropy=rng2) +# sk1 and sk2 are the same key +``` + +Likewise, ECDSA signature generation requires a random number, and each +signature must use a different one (using the same number twice will +immediately reveal the private signing key). The `sk.sign()` method takes an +`entropy=` argument which behaves the same as `SigningKey.generate(entropy=)`. + +## Deterministic Signatures + +If you call `SigningKey.sign_deterministic(data)` instead of `.sign(data)`, +the code will generate a deterministic signature instead of a random one. +This uses the algorithm from RFC6979 to safely generate a unique `k` value, +derived from the private key and the message being signed. Each time you sign +the same message with the same key, you will get the same signature (using +the same `k`). + +This may become the default in a future version, as it is not vulnerable to +failures of the entropy source. + +## Examples + +Create a NIST192p key pair and immediately save both to disk: + +```python +from ecdsa import SigningKey +sk = SigningKey.generate() +vk = sk.verifying_key +with open("private.pem", "wb") as f: + f.write(sk.to_pem()) +with open("public.pem", "wb") as f: + f.write(vk.to_pem()) +``` + +Load a signing key from disk, use it to sign a message (using SHA-1), and write +the signature to disk: + +```python +from ecdsa import SigningKey +with open("private.pem") as f: + sk = SigningKey.from_pem(f.read()) +with open("message", "rb") as f: + message = f.read() +sig = sk.sign(message) +with open("signature", "wb") as f: + f.write(sig) +``` + +Load the verifying key, message, and signature from disk, and verify the +signature (assume SHA-1 hash): + +```python +from ecdsa import VerifyingKey, BadSignatureError +vk = VerifyingKey.from_pem(open("public.pem").read()) +with open("message", "rb") as f: + message = f.read() +with open("signature", "rb") as f: + sig = f.read() +try: + vk.verify(sig, message) + print "good signature" +except BadSignatureError: + print "BAD SIGNATURE" +``` + +Create a NIST521p key pair: + +```python +from ecdsa import SigningKey, NIST521p +sk = SigningKey.generate(curve=NIST521p) +vk = sk.verifying_key +``` + +Create three independent signing keys from a master seed: + +```python +from ecdsa import NIST192p, SigningKey +from ecdsa.util import randrange_from_seed__trytryagain + +def make_key_from_seed(seed, curve=NIST192p): + secexp = randrange_from_seed__trytryagain(seed, curve.order) + return SigningKey.from_secret_exponent(secexp, curve) + +sk1 = make_key_from_seed("1:%s" % seed) +sk2 = make_key_from_seed("2:%s" % seed) +sk3 = make_key_from_seed("3:%s" % seed) +``` + +Load a verifying key from disk and print it using hex encoding in +uncompressed and compressed format (defined in X9.62 and SEC1 standards): + +```python +from ecdsa import VerifyingKey + +with open("public.pem") as f: + vk = VerifyingKey.from_pem(f.read()) + +print("uncompressed: {0}".format(vk.to_string("uncompressed").hex())) +print("compressed: {0}".format(vk.to_string("compressed").hex())) +``` + +Load a verifying key from a hex string from compressed format, output +uncompressed: + +```python +from ecdsa import VerifyingKey, NIST256p + +comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' +vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) +print(vk.to_string("uncompressed").hex()) +``` + +ECDH key exchange with remote party: + +```python +from ecdsa import ECDH, NIST256p + +ecdh = ECDH(curve=NIST256p) +ecdh.generate_private_key() +local_public_key = ecdh.get_public_key() +#send `local_public_key` to remote party and receive `remote_public_key` from remote party +with open("remote_public_key.pem") as e: + remote_public_key = e.read() +ecdh.load_received_public_key_pem(remote_public_key) +secret = ecdh.generate_sharedsecret_bytes() +``` + + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD new file mode 100644 index 000000000..f4130d1c5 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD @@ -0,0 +1,64 @@ +ecdsa-0.18.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ecdsa-0.18.0.dist-info/LICENSE,sha256=PsqYRXc9LluMydjBGdNF8ApIBuS9Zg1KPWzfnA6di7I,1147 +ecdsa-0.18.0.dist-info/METADATA,sha256=vesFVMWT6uSeOuNwGGxtBm8nm6GROqNNRO28jr8wWqM,29750 +ecdsa-0.18.0.dist-info/RECORD,, +ecdsa-0.18.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +ecdsa-0.18.0.dist-info/top_level.txt,sha256=7ovPHfAPyTou19f8gOSbHm6B9dGjTibWolcCB7Zjovs,6 +ecdsa/__init__.py,sha256=m8jWFcZ9E-iiDqVaTy7ABtvEyTJlF58tZ45UAKj3UWo,1637 +ecdsa/__pycache__/__init__.cpython-310.pyc,, +ecdsa/__pycache__/_compat.cpython-310.pyc,, +ecdsa/__pycache__/_rwlock.cpython-310.pyc,, +ecdsa/__pycache__/_sha3.cpython-310.pyc,, +ecdsa/__pycache__/_version.cpython-310.pyc,, +ecdsa/__pycache__/curves.cpython-310.pyc,, +ecdsa/__pycache__/der.cpython-310.pyc,, +ecdsa/__pycache__/ecdh.cpython-310.pyc,, +ecdsa/__pycache__/ecdsa.cpython-310.pyc,, +ecdsa/__pycache__/eddsa.cpython-310.pyc,, +ecdsa/__pycache__/ellipticcurve.cpython-310.pyc,, +ecdsa/__pycache__/errors.cpython-310.pyc,, +ecdsa/__pycache__/keys.cpython-310.pyc,, +ecdsa/__pycache__/numbertheory.cpython-310.pyc,, +ecdsa/__pycache__/rfc6979.cpython-310.pyc,, +ecdsa/__pycache__/test_curves.cpython-310.pyc,, +ecdsa/__pycache__/test_der.cpython-310.pyc,, +ecdsa/__pycache__/test_ecdh.cpython-310.pyc,, +ecdsa/__pycache__/test_ecdsa.cpython-310.pyc,, +ecdsa/__pycache__/test_eddsa.cpython-310.pyc,, +ecdsa/__pycache__/test_ellipticcurve.cpython-310.pyc,, +ecdsa/__pycache__/test_jacobi.cpython-310.pyc,, +ecdsa/__pycache__/test_keys.cpython-310.pyc,, +ecdsa/__pycache__/test_malformed_sigs.cpython-310.pyc,, +ecdsa/__pycache__/test_numbertheory.cpython-310.pyc,, +ecdsa/__pycache__/test_pyecdsa.cpython-310.pyc,, +ecdsa/__pycache__/test_rw_lock.cpython-310.pyc,, +ecdsa/__pycache__/test_sha3.cpython-310.pyc,, +ecdsa/__pycache__/util.cpython-310.pyc,, +ecdsa/_compat.py,sha256=EhUF8-sFu1dKKGDibkmItbYm_nKoklSIBgkIburUoAg,4619 +ecdsa/_rwlock.py,sha256=CAwHp2V65ksI8B1UqY7EccK9LaUToiv6pDLVzm44eag,2849 +ecdsa/_sha3.py,sha256=DJs7QLmdkQMU35llyD8HQeAXNvf5sMcujO6oFdScIqI,4747 +ecdsa/_version.py,sha256=YZ3BGOHr1Ltse4LfX7F80J6qmKFA-NS-G2eYUuw2WnU,498 +ecdsa/curves.py,sha256=Na5rpnuADNvWkCTlUGbs9xwVogY6Vl2_3uNzpVGgxtE,14390 +ecdsa/der.py,sha256=OmfH8fojeqfJnasAt7I1P8j_qfwcwl-W4gDx1-cO8M0,14109 +ecdsa/ecdh.py,sha256=Tiirawt5xegVDrY9eS-ATvvfmTIznUyv5fy2k7VnzTk,11011 +ecdsa/ecdsa.py,sha256=LPRHHXNvGyZ67lyM6cWqUuhQceCwoktfPij20UTsdJo,24955 +ecdsa/eddsa.py,sha256=IzsGzoGAefcoYjF7DVjFkX5ZJqiK2LTOmAMe6wyf4UU,7170 +ecdsa/ellipticcurve.py,sha256=HwlFqrihf7Q2GQTLYQ0PeCwsgMhk_GlkZz3I2Xj4-eI,53625 +ecdsa/errors.py,sha256=b4mhnmIpRnEdHzbectHAA5F7O9MtSaI-fYoc13_vBxQ,130 +ecdsa/keys.py,sha256=plsomRHYrN3Z3iigUqKM5An8_6TDk1nJNpFv_cPGAvM,65124 +ecdsa/numbertheory.py,sha256=XWugBG59BxrpvjZm7Ytsnmkv770vK8IkClObThpbvAM,17479 +ecdsa/rfc6979.py,sha256=zwzo33lsZJA9r2dSf7HCliI_yIbw5cJ0Ek9tLdRRO40,2850 +ecdsa/test_curves.py,sha256=l5N-m4Yo5IAy4a8aJMsBamaSlLAfSoYjYqCj1HDEVpU,13081 +ecdsa/test_der.py,sha256=q2mr4HS_JyUxojWTSLJu-MQZiTwaCE7W_VG3rwwPEas,14956 +ecdsa/test_ecdh.py,sha256=hUJXTo_Cr9ji9-EpPvpQf7-TgB97WUj2tWcwU-LqCXc,15238 +ecdsa/test_ecdsa.py,sha256=1clBfDtA0zdF-13BoKXsJffL5K-iFutlMgov0kmGro0,23923 +ecdsa/test_eddsa.py,sha256=Vlv5J0C4zNJu5zzr756qpm0AJmARpzBKCWrhHaF_bR4,32615 +ecdsa/test_ellipticcurve.py,sha256=K6W_EQunOfE-RVSux6d1O7LXzSuAsVk9F1elwyY-rYA,6085 +ecdsa/test_jacobi.py,sha256=JDQeM_JKwPfwWBd4IgqtOp1rboeQNUIPPA334b-nmLQ,18388 +ecdsa/test_keys.py,sha256=n_IYLxG4JwD84dYLDmRjV2A-NqsSrrR1P0XBJOCZsEI,32833 +ecdsa/test_malformed_sigs.py,sha256=hiV2vwzFrIdNIC-inYUJIKboyAAw2TKAIVXtFadyojg,10857 +ecdsa/test_numbertheory.py,sha256=g6hi7NZFKuMSAxJSAYW5sWM7ivSCiw8g5-PeoDyowgY,11619 +ecdsa/test_pyecdsa.py,sha256=WeRujEKpkZzHvEeXNnnhM1QdMH0Lxou7Bl4u8RXY-jM,82757 +ecdsa/test_rw_lock.py,sha256=byv0_FTM90cbuHPCI6__LeQJkHL_zYEeVYIBO8e2LLc,7021 +ecdsa/test_sha3.py,sha256=oKULy5KOTaXjpLXSyuHrB1wjPiQDxB6INp7Tf1EU8Ko,3022 +ecdsa/util.py,sha256=cOEN3_c8p79Dc8a-LcUQP2ctIsYky35jhSWc9hLP1qc,14618 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL new file mode 100644 index 000000000..01b8fc7d4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt new file mode 100644 index 000000000..aa5efdb54 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt @@ -0,0 +1 @@ +ecdsa diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py new file mode 100644 index 000000000..ce8749aa0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py @@ -0,0 +1,90 @@ +# while we don't use six in this file, we did bundle it for a long time, so +# keep as part of module in a virtual way (through __all__) +import six +from .keys import ( + SigningKey, + VerifyingKey, + BadSignatureError, + BadDigestError, + MalformedPointError, +) +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, +) +from .ecdh import ( + ECDH, + NoKeyError, + NoCurveError, + InvalidCurveError, + InvalidSharedSecretError, +) +from .der import UnexpectedDER +from . import _version + +# This code comes from http://github.com/tlsfuzzer/python-ecdsa +__all__ = [ + "curves", + "der", + "ecdsa", + "ellipticcurve", + "keys", + "numbertheory", + "test_pyecdsa", + "util", + "six", +] + +_hush_pyflakes = [ + SigningKey, + VerifyingKey, + BadSignatureError, + BadDigestError, + MalformedPointError, + UnexpectedDER, + InvalidCurveError, + NoKeyError, + InvalidSharedSecretError, + ECDH, + NoCurveError, + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, + six.b(""), +] +del _hush_pyflakes + +__version__ = _version.get_versions()["version"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py new file mode 100644 index 000000000..83d41a5f7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py @@ -0,0 +1,153 @@ +""" +Common functions for providing cross-python version compatibility. +""" +import sys +import re +import binascii +from six import integer_types + + +def str_idx_as_int(string, index): + """Take index'th byte from string, return as integer""" + val = string[index] + if isinstance(val, integer_types): + return val + return ord(val) + + +if sys.version_info < (3, 0): # pragma: no branch + import platform + + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + # flake8 runs on py3 where `buffer` indeed doesn't exist... + return buffer(buffer_object) # noqa: F821 + + def hmac_compat(ret): + return ret + + if ( + sys.version_info < (2, 7) + or sys.version_info < (2, 7, 4) + or platform.system() == "Java" + ): # pragma: no branch + + def remove_whitespace(text): + """Removes all whitespace from passed in string""" + return re.sub(r"\s+", "", text) + + def compat26_str(val): + return str(val) + + def bit_length(val): + if val == 0: + return 0 + return len(bin(val)) - 2 + + else: + + def remove_whitespace(text): + """Removes all whitespace from passed in string""" + return re.sub(r"\s+", "", text, flags=re.UNICODE) + + def compat26_str(val): + return val + + def bit_length(val): + """Return number of bits necessary to represent an integer.""" + return val.bit_length() + + def b2a_hex(val): + return binascii.b2a_hex(compat26_str(val)) + + def a2b_hex(val): + try: + return bytearray(binascii.a2b_hex(val)) + except Exception as e: + raise ValueError("base16 error: %s" % e) + + def bytes_to_int(val, byteorder): + """Convert bytes to an int.""" + if not val: + return 0 + if byteorder == "big": + return int(b2a_hex(val), 16) + if byteorder == "little": + return int(b2a_hex(val[::-1]), 16) + raise ValueError("Only 'big' and 'little' endian supported") + + def int_to_bytes(val, length=None, byteorder="big"): + """Return number converted to bytes""" + if length is None: + length = byte_length(val) + if byteorder == "big": + return bytearray( + (val >> i) & 0xFF for i in reversed(range(0, length * 8, 8)) + ) + if byteorder == "little": + return bytearray( + (val >> i) & 0xFF for i in range(0, length * 8, 8) + ) + raise ValueError("Only 'big' or 'little' endian supported") + +else: + if sys.version_info < (3, 4): # pragma: no branch + # on python 3.3 hmac.hmac.update() accepts only bytes, on newer + # versions it does accept memoryview() also + def hmac_compat(data): + if not isinstance(data, bytes): # pragma: no branch + return bytes(data) + return data + + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + if not buffer_object: + return b"" + return memoryview(buffer_object).cast("B") + + else: + + def hmac_compat(data): + return data + + def normalise_bytes(buffer_object): + """Cast the input into array of bytes.""" + return memoryview(buffer_object).cast("B") + + def compat26_str(val): + return val + + def remove_whitespace(text): + """Removes all whitespace from passed in string""" + return re.sub(r"\s+", "", text, flags=re.UNICODE) + + def a2b_hex(val): + try: + return bytearray(binascii.a2b_hex(bytearray(val, "ascii"))) + except Exception as e: + raise ValueError("base16 error: %s" % e) + + # pylint: disable=invalid-name + # pylint is stupid here and doesn't notice it's a function, not + # constant + bytes_to_int = int.from_bytes + # pylint: enable=invalid-name + + def bit_length(val): + """Return number of bits necessary to represent an integer.""" + return val.bit_length() + + def int_to_bytes(val, length=None, byteorder="big"): + """Convert integer to bytes.""" + if length is None: + length = byte_length(val) + # for gmpy we need to convert back to native int + if type(val) != int: + val = int(val) + return bytearray(val.to_bytes(length=length, byteorder=byteorder)) + + +def byte_length(val): + """Return number of bytes necessary to represent an integer.""" + length = bit_length(val) + return (length + 7) // 8 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py new file mode 100644 index 000000000..010e4981b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py @@ -0,0 +1,86 @@ +# Copyright Mateusz Kobos, (c) 2011 +# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ +# released under the MIT licence + +import threading + + +__author__ = "Mateusz Kobos" + + +class RWLock: + """ + Read-Write locking primitive + + Synchronization object used in a solution of so-called second + readers-writers problem. In this problem, many readers can simultaneously + access a share, and a writer has an exclusive access to this share. + Additionally, the following constraints should be met: + 1) no reader should be kept waiting if the share is currently opened for + reading unless a writer is also waiting for the share, + 2) no writer should be kept waiting for the share longer than absolutely + necessary. + + The implementation is based on [1, secs. 4.2.2, 4.2.6, 4.2.7] + with a modification -- adding an additional lock (C{self.__readers_queue}) + -- in accordance with [2]. + + Sources: + [1] A.B. Downey: "The little book of semaphores", Version 2.1.5, 2008 + [2] P.J. Courtois, F. Heymans, D.L. Parnas: + "Concurrent Control with 'Readers' and 'Writers'", + Communications of the ACM, 1971 (via [3]) + [3] http://en.wikipedia.org/wiki/Readers-writers_problem + """ + + def __init__(self): + """ + A lock giving an even higher priority to the writer in certain + cases (see [2] for a discussion). + """ + self.__read_switch = _LightSwitch() + self.__write_switch = _LightSwitch() + self.__no_readers = threading.Lock() + self.__no_writers = threading.Lock() + self.__readers_queue = threading.Lock() + + def reader_acquire(self): + self.__readers_queue.acquire() + self.__no_readers.acquire() + self.__read_switch.acquire(self.__no_writers) + self.__no_readers.release() + self.__readers_queue.release() + + def reader_release(self): + self.__read_switch.release(self.__no_writers) + + def writer_acquire(self): + self.__write_switch.acquire(self.__no_readers) + self.__no_writers.acquire() + + def writer_release(self): + self.__no_writers.release() + self.__write_switch.release(self.__no_readers) + + +class _LightSwitch: + """An auxiliary "light switch"-like object. The first thread turns on the + "switch", the last one turns it off (see [1, sec. 4.2.2] for details).""" + + def __init__(self): + self.__counter = 0 + self.__mutex = threading.Lock() + + def acquire(self, lock): + self.__mutex.acquire() + self.__counter += 1 + if self.__counter == 1: + lock.acquire() + self.__mutex.release() + + def release(self, lock): + self.__mutex.acquire() + self.__counter -= 1 + if self.__counter == 0: + lock.release() + self.__mutex.release() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py new file mode 100644 index 000000000..2db005869 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py @@ -0,0 +1,181 @@ +""" +Implementation of the SHAKE-256 algorithm for Ed448 +""" + +try: + import hashlib + + hashlib.new("shake256").digest(64) + + def shake_256(msg, outlen): + return hashlib.new("shake256", msg).digest(outlen) + +except (TypeError, ValueError): + + from ._compat import bytes_to_int, int_to_bytes + + # From little endian. + def _from_le(s): + return bytes_to_int(s, byteorder="little") + + # Rotate a word x by b places to the left. + def _rol(x, b): + return ((x << b) | (x >> (64 - b))) & (2**64 - 1) + + # Do the SHA-3 state transform on state s. + def _sha3_transform(s): + ROTATIONS = [ + 0, + 1, + 62, + 28, + 27, + 36, + 44, + 6, + 55, + 20, + 3, + 10, + 43, + 25, + 39, + 41, + 45, + 15, + 21, + 8, + 18, + 2, + 61, + 56, + 14, + ] + PERMUTATION = [ + 1, + 6, + 9, + 22, + 14, + 20, + 2, + 12, + 13, + 19, + 23, + 15, + 4, + 24, + 21, + 8, + 16, + 5, + 3, + 18, + 17, + 11, + 7, + 10, + ] + RC = [ + 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008, + ] + + for rnd in range(0, 24): + # AddColumnParity (Theta) + c = [0] * 5 + d = [0] * 5 + for i in range(0, 25): + c[i % 5] ^= s[i] + for i in range(0, 5): + d[i] = c[(i + 4) % 5] ^ _rol(c[(i + 1) % 5], 1) + for i in range(0, 25): + s[i] ^= d[i % 5] + # RotateWords (Rho) + for i in range(0, 25): + s[i] = _rol(s[i], ROTATIONS[i]) + # PermuteWords (Pi) + t = s[PERMUTATION[0]] + for i in range(0, len(PERMUTATION) - 1): + s[PERMUTATION[i]] = s[PERMUTATION[i + 1]] + s[PERMUTATION[-1]] = t + # NonlinearMixRows (Chi) + for i in range(0, 25, 5): + t = [ + s[i], + s[i + 1], + s[i + 2], + s[i + 3], + s[i + 4], + s[i], + s[i + 1], + ] + for j in range(0, 5): + s[i + j] = t[j] ^ ((~t[j + 1]) & (t[j + 2])) + # AddRoundConstant (Iota) + s[0] ^= RC[rnd] + + # Reinterpret octet array b to word array and XOR it to state s. + def _reinterpret_to_words_and_xor(s, b): + for j in range(0, len(b) // 8): + s[j] ^= _from_le(b[8 * j : 8 * j + 8]) + + # Reinterpret word array w to octet array and return it. + def _reinterpret_to_octets(w): + mp = bytearray() + for j in range(0, len(w)): + mp += int_to_bytes(w[j], 8, byteorder="little") + return mp + + def _sha3_raw(msg, r_w, o_p, e_b): + """Semi-generic SHA-3 implementation""" + r_b = 8 * r_w + s = [0] * 25 + # Handle whole blocks. + idx = 0 + blocks = len(msg) // r_b + for i in range(0, blocks): + _reinterpret_to_words_and_xor(s, msg[idx : idx + r_b]) + idx += r_b + _sha3_transform(s) + # Handle last block padding. + m = bytearray(msg[idx:]) + m.append(o_p) + while len(m) < r_b: + m.append(0) + m[len(m) - 1] |= 128 + # Handle padded last block. + _reinterpret_to_words_and_xor(s, m) + _sha3_transform(s) + # Output. + out = bytearray() + while len(out) < e_b: + out += _reinterpret_to_octets(s[:r_w]) + _sha3_transform(s) + return out[:e_b] + + def shake_256(msg, outlen): + return _sha3_raw(msg, 17, 31, outlen) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py new file mode 100644 index 000000000..96aae17bb --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py @@ -0,0 +1,21 @@ + +# This file was generated by 'versioneer.py' (0.21) from +# revision-control system data, or from the parent directory name of an +# unpacked source archive. Distribution tarballs contain a pre-generated copy +# of this file. + +import json + +version_json = ''' +{ + "date": "2022-07-09T14:49:17+0200", + "dirty": false, + "error": null, + "full-revisionid": "341e0d8be9fedf66fbc9a95630b4ed2138343380", + "version": "0.18.0" +} +''' # END VERSION_JSON + + +def get_versions(): + return json.loads(version_json) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py new file mode 100644 index 000000000..1119ee5a3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py @@ -0,0 +1,513 @@ +from __future__ import division + +from six import PY2 +from . import der, ecdsa, ellipticcurve, eddsa +from .util import orderlen, number_to_string, string_to_number +from ._compat import normalise_bytes, bit_length + + +# orderlen was defined in this module previously, so keep it in __all__, +# will need to mark it as deprecated later +__all__ = [ + "UnknownCurveError", + "orderlen", + "Curve", + "SECP112r1", + "SECP112r2", + "SECP128r1", + "SECP160r1", + "NIST192p", + "NIST224p", + "NIST256p", + "NIST384p", + "NIST521p", + "curves", + "find_curve", + "curve_by_name", + "SECP256k1", + "BRAINPOOLP160r1", + "BRAINPOOLP192r1", + "BRAINPOOLP224r1", + "BRAINPOOLP256r1", + "BRAINPOOLP320r1", + "BRAINPOOLP384r1", + "BRAINPOOLP512r1", + "PRIME_FIELD_OID", + "CHARACTERISTIC_TWO_FIELD_OID", + "Ed25519", + "Ed448", +] + + +PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1) +CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2) + + +class UnknownCurveError(Exception): + pass + + +class Curve: + def __init__(self, name, curve, generator, oid, openssl_name=None): + self.name = name + self.openssl_name = openssl_name # maybe None + self.curve = curve + self.generator = generator + self.order = generator.order() + if isinstance(curve, ellipticcurve.CurveEdTw): + # EdDSA keys are special in that both private and public + # are the same size (as it's defined only with compressed points) + + # +1 for the sign bit and then round up + self.baselen = (bit_length(curve.p()) + 1 + 7) // 8 + self.verifying_key_length = self.baselen + else: + self.baselen = orderlen(self.order) + self.verifying_key_length = 2 * orderlen(curve.p()) + self.signature_length = 2 * self.baselen + self.oid = oid + if oid: + self.encoded_oid = der.encode_oid(*oid) + + def __eq__(self, other): + if isinstance(other, Curve): + return ( + self.curve == other.curve and self.generator == other.generator + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def __repr__(self): + return self.name + + def to_der(self, encoding=None, point_encoding="uncompressed"): + """Serialise the curve parameters to binary string. + + :param str encoding: the format to save the curve parameters in. + Default is ``named_curve``, with fallback being the ``explicit`` + if the OID is not set for the curve. + :param str point_encoding: the point encoding of the generator when + explicit curve encoding is used. Ignored for ``named_curve`` + format. + + :return: DER encoded ECParameters structure + :rtype: bytes + """ + if encoding is None: + if self.oid: + encoding = "named_curve" + else: + encoding = "explicit" + + if encoding not in ("named_curve", "explicit"): + raise ValueError( + "Only 'named_curve' and 'explicit' encodings supported" + ) + + if encoding == "named_curve": + if not self.oid: + raise UnknownCurveError( + "Can't encode curve using named_curve encoding without " + "associated curve OID" + ) + return der.encode_oid(*self.oid) + elif isinstance(self.curve, ellipticcurve.CurveEdTw): + assert encoding == "explicit" + raise UnknownCurveError( + "Twisted Edwards curves don't support explicit encoding" + ) + + # encode the ECParameters sequence + curve_p = self.curve.p() + version = der.encode_integer(1) + field_id = der.encode_sequence( + der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p) + ) + curve = der.encode_sequence( + der.encode_octet_string( + number_to_string(self.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(self.curve.b() % curve_p, curve_p) + ), + ) + base = der.encode_octet_string(self.generator.to_bytes(point_encoding)) + order = der.encode_integer(self.generator.order()) + seq_elements = [version, field_id, curve, base, order] + if self.curve.cofactor(): + cofactor = der.encode_integer(self.curve.cofactor()) + seq_elements.append(cofactor) + + return der.encode_sequence(*seq_elements) + + def to_pem(self, encoding=None, point_encoding="uncompressed"): + """ + Serialise the curve parameters to the :term:`PEM` format. + + :param str encoding: the format to save the curve parameters in. + Default is ``named_curve``, with fallback being the ``explicit`` + if the OID is not set for the curve. + :param str point_encoding: the point encoding of the generator when + explicit curve encoding is used. Ignored for ``named_curve`` + format. + + :return: PEM encoded ECParameters structure + :rtype: str + """ + return der.topem( + self.to_der(encoding, point_encoding), "EC PARAMETERS" + ) + + @staticmethod + def from_der(data, valid_encodings=None): + """Decode the curve parameters from DER file. + + :param data: the binary string to decode the parameters from + :type data: :term:`bytes-like object` + :param valid_encodings: set of names of allowed encodings, by default + all (set by passing ``None``), supported ones are ``named_curve`` + and ``explicit`` + :type valid_encodings: :term:`set-like object` + """ + if not valid_encodings: + valid_encodings = set(("named_curve", "explicit")) + if not all(i in ["named_curve", "explicit"] for i in valid_encodings): + raise ValueError( + "Only named_curve and explicit encodings supported" + ) + data = normalise_bytes(data) + if not der.is_sequence(data): + if "named_curve" not in valid_encodings: + raise der.UnexpectedDER( + "named_curve curve parameters not allowed" + ) + oid, empty = der.remove_object(data) + if empty: + raise der.UnexpectedDER("Unexpected data after OID") + return find_curve(oid) + + if "explicit" not in valid_encodings: + raise der.UnexpectedDER("explicit curve parameters not allowed") + + seq, empty = der.remove_sequence(data) + if empty: + raise der.UnexpectedDER( + "Unexpected data after ECParameters structure" + ) + # decode the ECParameters sequence + version, rest = der.remove_integer(seq) + if version != 1: + raise der.UnexpectedDER("Unknown parameter encoding format") + field_id, rest = der.remove_sequence(rest) + curve, rest = der.remove_sequence(rest) + base_bytes, rest = der.remove_octet_string(rest) + order, rest = der.remove_integer(rest) + cofactor = None + if rest: + # the ASN.1 specification of ECParameters allows for future + # extensions of the sequence, so ignore the remaining bytes + cofactor, _ = der.remove_integer(rest) + + # decode the ECParameters.fieldID sequence + field_type, rest = der.remove_object(field_id) + if field_type == CHARACTERISTIC_TWO_FIELD_OID: + raise UnknownCurveError("Characteristic 2 curves unsupported") + if field_type != PRIME_FIELD_OID: + raise UnknownCurveError( + "Unknown field type: {0}".format(field_type) + ) + prime, empty = der.remove_integer(rest) + if empty: + raise der.UnexpectedDER( + "Unexpected data after ECParameters.fieldID.Prime-p element" + ) + + # decode the ECParameters.curve sequence + curve_a_bytes, rest = der.remove_octet_string(curve) + curve_b_bytes, rest = der.remove_octet_string(rest) + # seed can be defined here, but we don't parse it, so ignore `rest` + + curve_a = string_to_number(curve_a_bytes) + curve_b = string_to_number(curve_b_bytes) + + curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor) + + # decode the ECParameters.base point + + base = ellipticcurve.PointJacobi.from_bytes( + curve_fp, + base_bytes, + valid_encodings=("uncompressed", "compressed", "hybrid"), + order=order, + generator=True, + ) + tmp_curve = Curve("unknown", curve_fp, base, None) + + # if the curve matches one of the well-known ones, use the well-known + # one in preference, as it will have the OID and name associated + for i in curves: + if tmp_curve == i: + return i + return tmp_curve + + @classmethod + def from_pem(cls, string, valid_encodings=None): + """Decode the curve parameters from PEM file. + + :param str string: the text string to decode the parameters from + :param valid_encodings: set of names of allowed encodings, by default + all (set by passing ``None``), supported ones are ``named_curve`` + and ``explicit`` + :type valid_encodings: :term:`set-like object` + """ + if not PY2 and isinstance(string, str): # pragma: no branch + string = string.encode() + + ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----") + if ec_param_index == -1: + raise der.UnexpectedDER("EC PARAMETERS PEM header not found") + + return cls.from_der( + der.unpem(string[ec_param_index:]), valid_encodings + ) + + +# the SEC curves +SECP112r1 = Curve( + "SECP112r1", + ecdsa.curve_112r1, + ecdsa.generator_112r1, + (1, 3, 132, 0, 6), + "secp112r1", +) + + +SECP112r2 = Curve( + "SECP112r2", + ecdsa.curve_112r2, + ecdsa.generator_112r2, + (1, 3, 132, 0, 7), + "secp112r2", +) + + +SECP128r1 = Curve( + "SECP128r1", + ecdsa.curve_128r1, + ecdsa.generator_128r1, + (1, 3, 132, 0, 28), + "secp128r1", +) + + +SECP160r1 = Curve( + "SECP160r1", + ecdsa.curve_160r1, + ecdsa.generator_160r1, + (1, 3, 132, 0, 8), + "secp160r1", +) + + +# the NIST curves +NIST192p = Curve( + "NIST192p", + ecdsa.curve_192, + ecdsa.generator_192, + (1, 2, 840, 10045, 3, 1, 1), + "prime192v1", +) + + +NIST224p = Curve( + "NIST224p", + ecdsa.curve_224, + ecdsa.generator_224, + (1, 3, 132, 0, 33), + "secp224r1", +) + + +NIST256p = Curve( + "NIST256p", + ecdsa.curve_256, + ecdsa.generator_256, + (1, 2, 840, 10045, 3, 1, 7), + "prime256v1", +) + + +NIST384p = Curve( + "NIST384p", + ecdsa.curve_384, + ecdsa.generator_384, + (1, 3, 132, 0, 34), + "secp384r1", +) + + +NIST521p = Curve( + "NIST521p", + ecdsa.curve_521, + ecdsa.generator_521, + (1, 3, 132, 0, 35), + "secp521r1", +) + + +SECP256k1 = Curve( + "SECP256k1", + ecdsa.curve_secp256k1, + ecdsa.generator_secp256k1, + (1, 3, 132, 0, 10), + "secp256k1", +) + + +BRAINPOOLP160r1 = Curve( + "BRAINPOOLP160r1", + ecdsa.curve_brainpoolp160r1, + ecdsa.generator_brainpoolp160r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), + "brainpoolP160r1", +) + + +BRAINPOOLP192r1 = Curve( + "BRAINPOOLP192r1", + ecdsa.curve_brainpoolp192r1, + ecdsa.generator_brainpoolp192r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), + "brainpoolP192r1", +) + + +BRAINPOOLP224r1 = Curve( + "BRAINPOOLP224r1", + ecdsa.curve_brainpoolp224r1, + ecdsa.generator_brainpoolp224r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), + "brainpoolP224r1", +) + + +BRAINPOOLP256r1 = Curve( + "BRAINPOOLP256r1", + ecdsa.curve_brainpoolp256r1, + ecdsa.generator_brainpoolp256r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), + "brainpoolP256r1", +) + + +BRAINPOOLP320r1 = Curve( + "BRAINPOOLP320r1", + ecdsa.curve_brainpoolp320r1, + ecdsa.generator_brainpoolp320r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), + "brainpoolP320r1", +) + + +BRAINPOOLP384r1 = Curve( + "BRAINPOOLP384r1", + ecdsa.curve_brainpoolp384r1, + ecdsa.generator_brainpoolp384r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), + "brainpoolP384r1", +) + + +BRAINPOOLP512r1 = Curve( + "BRAINPOOLP512r1", + ecdsa.curve_brainpoolp512r1, + ecdsa.generator_brainpoolp512r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), + "brainpoolP512r1", +) + + +Ed25519 = Curve( + "Ed25519", + eddsa.curve_ed25519, + eddsa.generator_ed25519, + (1, 3, 101, 112), +) + + +Ed448 = Curve( + "Ed448", + eddsa.curve_ed448, + eddsa.generator_ed448, + (1, 3, 101, 113), +) + + +# no order in particular, but keep previously added curves first +curves = [ + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + Ed25519, + Ed448, +] + + +def find_curve(oid_curve): + """Select a curve based on its OID + + :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the + curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``. + + :raises UnknownCurveError: When the oid doesn't match any of the supported + curves + + :rtype: ~ecdsa.curves.Curve + """ + for c in curves: + if c.oid == oid_curve: + return c + raise UnknownCurveError( + "I don't know about the curve with oid %s." + "I only know about these: %s" % (oid_curve, [c.name for c in curves]) + ) + + +def curve_by_name(name): + """Select a curve based on its name. + + Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name. + Note that ``name`` is case-sensitve. + + :param str name: Name of the curve to return, like ``NIST256p`` or + ``prime256v1`` + + :raises UnknownCurveError: When the name doesn't match any of the supported + curves + + :rtype: ~ecdsa.curves.Curve + """ + for c in curves: + if name == c.name or (c.openssl_name and name == c.openssl_name): + return c + raise UnknownCurveError( + "Curve with name {0!r} unknown, only curves supported: {1}".format( + name, [c.name for c in curves] + ) + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py new file mode 100644 index 000000000..8b27941c4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py @@ -0,0 +1,409 @@ +from __future__ import division + +import binascii +import base64 +import warnings +from itertools import chain +from six import int2byte, b, text_type +from ._compat import str_idx_as_int + + +class UnexpectedDER(Exception): + pass + + +def encode_constructed(tag, value): + return int2byte(0xA0 + tag) + encode_length(len(value)) + value + + +def encode_integer(r): + assert r >= 0 # can't support negative numbers yet + h = ("%x" % r).encode() + if len(h) % 2: + h = b("0") + h + s = binascii.unhexlify(h) + num = str_idx_as_int(s, 0) + if num <= 0x7F: + return b("\x02") + encode_length(len(s)) + s + else: + # DER integers are two's complement, so if the first byte is + # 0x80-0xff then we need an extra 0x00 byte to prevent it from + # looking negative. + return b("\x02") + encode_length(len(s) + 1) + b("\x00") + s + + +# sentry object to check if an argument was specified (used to detect +# deprecated calling convention) +_sentry = object() + + +def encode_bitstring(s, unused=_sentry): + """ + Encode a binary string as a BIT STRING using :term:`DER` encoding. + + Note, because there is no native Python object that can encode an actual + bit string, this function only accepts byte strings as the `s` argument. + The byte string is the actual bit string that will be encoded, padded + on the right (least significant bits, looking from big endian perspective) + to the first full byte. If the bit string has a bit length that is multiple + of 8, then the padding should not be included. For correct DER encoding + the padding bits MUST be set to 0. + + Number of bits of padding need to be provided as the `unused` parameter. + In case they are specified as None, it means the number of unused bits + is already encoded in the string as the first byte. + + The deprecated call convention specifies just the `s` parameters and + encodes the number of unused bits as first parameter (same convention + as with None). + + Empty string must be encoded with `unused` specified as 0. + + Future version of python-ecdsa will make specifying the `unused` argument + mandatory. + + :param s: bytes to encode + :type s: bytes like object + :param unused: number of bits at the end of `s` that are unused, must be + between 0 and 7 (inclusive) + :type unused: int or None + + :raises ValueError: when `unused` is too large or too small + + :return: `s` encoded using DER + :rtype: bytes + """ + encoded_unused = b"" + len_extra = 0 + if unused is _sentry: + warnings.warn( + "Legacy call convention used, unused= needs to be specified", + DeprecationWarning, + ) + elif unused is not None: + if not 0 <= unused <= 7: + raise ValueError("unused must be integer between 0 and 7") + if unused: + if not s: + raise ValueError("unused is non-zero but s is empty") + last = str_idx_as_int(s, -1) + if last & (2**unused - 1): + raise ValueError("unused bits must be zeros in DER") + encoded_unused = int2byte(unused) + len_extra = 1 + return b("\x03") + encode_length(len(s) + len_extra) + encoded_unused + s + + +def encode_octet_string(s): + return b("\x04") + encode_length(len(s)) + s + + +def encode_oid(first, second, *pieces): + assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second + body = b"".join( + chain( + [encode_number(40 * first + second)], + (encode_number(p) for p in pieces), + ) + ) + return b"\x06" + encode_length(len(body)) + body + + +def encode_sequence(*encoded_pieces): + total_len = sum([len(p) for p in encoded_pieces]) + return b("\x30") + encode_length(total_len) + b("").join(encoded_pieces) + + +def encode_number(n): + b128_digits = [] + while n: + b128_digits.insert(0, (n & 0x7F) | 0x80) + n = n >> 7 + if not b128_digits: + b128_digits.append(0) + b128_digits[-1] &= 0x7F + return b("").join([int2byte(d) for d in b128_digits]) + + +def is_sequence(string): + return string and string[:1] == b"\x30" + + +def remove_constructed(string): + s0 = str_idx_as_int(string, 0) + if (s0 & 0xE0) != 0xA0: + raise UnexpectedDER( + "wanted type 'constructed tag' (0xa0-0xbf), got 0x%02x" % s0 + ) + tag = s0 & 0x1F + length, llen = read_length(string[1:]) + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + return tag, body, rest + + +def remove_sequence(string): + if not string: + raise UnexpectedDER("Empty string does not encode a sequence") + if string[:1] != b"\x30": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + if length > len(string) - 1 - lengthlength: + raise UnexpectedDER("Length longer than the provided buffer") + endseq = 1 + lengthlength + length + return string[1 + lengthlength : endseq], string[endseq:] + + +def remove_octet_string(string): + if string[:1] != b"\x04": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n) + length, llen = read_length(string[1:]) + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + return body, rest + + +def remove_object(string): + if not string: + raise UnexpectedDER( + "Empty string does not encode an object identifier" + ) + if string[:1] != b"\x06": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n) + length, lengthlength = read_length(string[1:]) + body = string[1 + lengthlength : 1 + lengthlength + length] + rest = string[1 + lengthlength + length :] + if not body: + raise UnexpectedDER("Empty object identifier") + if len(body) != length: + raise UnexpectedDER( + "Length of object identifier longer than the provided buffer" + ) + numbers = [] + while body: + n, ll = read_number(body) + numbers.append(n) + body = body[ll:] + n0 = numbers.pop(0) + if n0 < 80: + first = n0 // 40 + else: + first = 2 + second = n0 - (40 * first) + numbers.insert(0, first) + numbers.insert(1, second) + return tuple(numbers), rest + + +def remove_integer(string): + if not string: + raise UnexpectedDER( + "Empty string is an invalid encoding of an integer" + ) + if string[:1] != b"\x02": + n = str_idx_as_int(string, 0) + raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n) + length, llen = read_length(string[1:]) + if length > len(string) - 1 - llen: + raise UnexpectedDER("Length longer than provided buffer") + if length == 0: + raise UnexpectedDER("0-byte long encoding of integer") + numberbytes = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + msb = str_idx_as_int(numberbytes, 0) + if not msb < 0x80: + raise UnexpectedDER("Negative integers are not supported") + # check if the encoding is the minimal one (DER requirement) + if length > 1 and not msb: + # leading zero byte is allowed if the integer would have been + # considered a negative number otherwise + smsb = str_idx_as_int(numberbytes, 1) + if smsb < 0x80: + raise UnexpectedDER( + "Invalid encoding of integer, unnecessary " + "zero padding bytes" + ) + return int(binascii.hexlify(numberbytes), 16), rest + + +def read_number(string): + number = 0 + llen = 0 + if str_idx_as_int(string, 0) == 0x80: + raise UnexpectedDER("Non minimal encoding of OID subidentifier") + # base-128 big endian, with most significant bit set in all but the last + # byte + while True: + if llen >= len(string): + raise UnexpectedDER("ran out of length bytes") + number = number << 7 + d = str_idx_as_int(string, llen) + number += d & 0x7F + llen += 1 + if not d & 0x80: + break + return number, llen + + +def encode_length(l): + assert l >= 0 + if l < 0x80: + return int2byte(l) + s = ("%x" % l).encode() + if len(s) % 2: + s = b("0") + s + s = binascii.unhexlify(s) + llen = len(s) + return int2byte(0x80 | llen) + s + + +def read_length(string): + if not string: + raise UnexpectedDER("Empty string can't encode valid length value") + num = str_idx_as_int(string, 0) + if not (num & 0x80): + # short form + return (num & 0x7F), 1 + # else long-form: b0&0x7f is number of additional base256 length bytes, + # big-endian + llen = num & 0x7F + if not llen: + raise UnexpectedDER("Invalid length encoding, length of length is 0") + if llen > len(string) - 1: + raise UnexpectedDER("Length of length longer than provided buffer") + # verify that the encoding is minimal possible (DER requirement) + msb = str_idx_as_int(string, 1) + if not msb or llen == 1 and msb < 0x80: + raise UnexpectedDER("Not minimal encoding of length") + return int(binascii.hexlify(string[1 : 1 + llen]), 16), 1 + llen + + +def remove_bitstring(string, expect_unused=_sentry): + """ + Remove a BIT STRING object from `string` following :term:`DER`. + + The `expect_unused` can be used to specify if the bit string should + have the amount of unused bits decoded or not. If it's an integer, any + read BIT STRING that has number of unused bits different from specified + value will cause UnexpectedDER exception to be raised (this is especially + useful when decoding BIT STRINGS that have DER encoded object in them; + DER encoding is byte oriented, so the unused bits will always equal 0). + + If the `expect_unused` is specified as None, the first element returned + will be a tuple, with the first value being the extracted bit string + while the second value will be the decoded number of unused bits. + + If the `expect_unused` is unspecified, the decoding of byte with + number of unused bits will not be attempted and the bit string will be + returned as-is, the callee will be required to decode it and verify its + correctness. + + Future version of python will require the `expected_unused` parameter + to be specified. + + :param string: string of bytes to extract the BIT STRING from + :type string: bytes like object + :param expect_unused: number of bits that should be unused in the BIT + STRING, or None, to return it to caller + :type expect_unused: int or None + + :raises UnexpectedDER: when the encoding does not follow DER. + + :return: a tuple with first element being the extracted bit string and + the second being the remaining bytes in the string (if any); if the + `expect_unused` is specified as None, the first element of the returned + tuple will be a tuple itself, with first element being the bit string + as bytes and the second element being the number of unused bits at the + end of the byte array as an integer + :rtype: tuple + """ + if not string: + raise UnexpectedDER("Empty string does not encode a bitstring") + if expect_unused is _sentry: + warnings.warn( + "Legacy call convention used, expect_unused= needs to be" + " specified", + DeprecationWarning, + ) + num = str_idx_as_int(string, 0) + if string[:1] != b"\x03": + raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) + length, llen = read_length(string[1:]) + if not length: + raise UnexpectedDER("Invalid length of bit string, can't be 0") + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] + if expect_unused is not _sentry: + unused = str_idx_as_int(body, 0) + if not 0 <= unused <= 7: + raise UnexpectedDER("Invalid encoding of unused bits") + if expect_unused is not None and expect_unused != unused: + raise UnexpectedDER("Unexpected number of unused bits") + body = body[1:] + if unused: + if not body: + raise UnexpectedDER("Invalid encoding of empty bit string") + last = str_idx_as_int(body, -1) + # verify that all the unused bits are set to zero (DER requirement) + if last & (2**unused - 1): + raise UnexpectedDER("Non zero padding bits in bit string") + if expect_unused is None: + body = (body, unused) + return body, rest + + +# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) + + +# signatures: (from RFC3279) +# ansi-X9-62 OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) 10045 } +# +# id-ecSigType OBJECT IDENTIFIER ::= { +# ansi-X9-62 signatures(4) } +# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { +# id-ecSigType 1 } +# so 1,2,840,10045,4,1 +# so 0x42, .. .. + +# Ecdsa-Sig-Value ::= SEQUENCE { +# r INTEGER, +# s INTEGER } + +# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } +# +# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } + +# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) +# secp224r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 33 } +# and the secp384r1 is (t=06,l=05,v=2b81040022) +# secp384r1 OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) curve(0) 34 } + + +def unpem(pem): + if isinstance(pem, text_type): # pragma: no branch + pem = pem.encode() + + d = b("").join( + [ + l.strip() + for l in pem.split(b("\n")) + if l and not l.startswith(b("-----")) + ] + ) + return base64.b64decode(d) + + +def topem(der, name): + b64 = base64.b64encode(der) + lines = [("-----BEGIN %s-----\n" % name).encode()] + lines.extend( + [b64[start : start + 64] + b("\n") for start in range(0, len(b64), 64)] + ) + lines.append(("-----END %s-----\n" % name).encode()) + return b("").join(lines) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py new file mode 100644 index 000000000..7f697d9a3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py @@ -0,0 +1,336 @@ +""" +Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations. +""" + +from .util import number_to_string +from .ellipticcurve import INFINITY +from .keys import SigningKey, VerifyingKey + + +__all__ = [ + "ECDH", + "NoKeyError", + "NoCurveError", + "InvalidCurveError", + "InvalidSharedSecretError", +] + + +class NoKeyError(Exception): + """ECDH. Key not found but it is needed for operation.""" + + pass + + +class NoCurveError(Exception): + """ECDH. Curve not set but it is needed for operation.""" + + pass + + +class InvalidCurveError(Exception): + """ + ECDH. Raised in case the public and private keys use different curves. + """ + + pass + + +class InvalidSharedSecretError(Exception): + """ECDH. Raised in case the shared secret we obtained is an INFINITY.""" + + pass + + +class ECDH(object): + """ + Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol. + + Allows two parties, each having an elliptic-curve public-private key + pair, to establish a shared secret over an insecure channel + """ + + def __init__(self, curve=None, private_key=None, public_key=None): + """ + ECDH init. + + Call can be initialised without parameters, then the first operation + (loading either key) will set the used curve. + All parameters must be ultimately set before shared secret + calculation will be allowed. + + :param curve: curve for operations + :type curve: Curve + :param private_key: `my` private key for ECDH + :type private_key: SigningKey + :param public_key: `their` public key for ECDH + :type public_key: VerifyingKey + """ + self.curve = curve + self.private_key = None + self.public_key = None + if private_key: + self.load_private_key(private_key) + if public_key: + self.load_received_public_key(public_key) + + def _get_shared_secret(self, remote_public_key): + if not self.private_key: + raise NoKeyError( + "Private key needs to be set to create shared secret" + ) + if not self.public_key: + raise NoKeyError( + "Public key needs to be set to create shared secret" + ) + if not ( + self.private_key.curve == self.curve == remote_public_key.curve + ): + raise InvalidCurveError( + "Curves for public key and private key is not equal." + ) + + # shared secret = PUBKEYtheirs * PRIVATEKEYours + result = ( + remote_public_key.pubkey.point + * self.private_key.privkey.secret_multiplier + ) + if result == INFINITY: + raise InvalidSharedSecretError("Invalid shared secret (INFINITY).") + + return result.x() + + def set_curve(self, key_curve): + """ + Set the working curve for ecdh operations. + + :param key_curve: curve from `curves` module + :type key_curve: Curve + """ + self.curve = key_curve + + def generate_private_key(self): + """ + Generate local private key for ecdh operation with curve that was set. + + :raises NoCurveError: Curve must be set before key generation. + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + if not self.curve: + raise NoCurveError("Curve must be set prior to key generation.") + return self.load_private_key(SigningKey.generate(curve=self.curve)) + + def load_private_key(self, private_key): + """ + Load private key from SigningKey (keys.py) object. + + Needs to have the same curve as was set with set_curve method. + If curve is not set - it sets from this SigningKey + + :param private_key: Initialised SigningKey class + :type private_key: SigningKey + + :raises InvalidCurveError: private_key curve not the same as self.curve + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + if not self.curve: + self.curve = private_key.curve + if self.curve != private_key.curve: + raise InvalidCurveError("Curve mismatch.") + self.private_key = private_key + return self.private_key.get_verifying_key() + + def load_private_key_bytes(self, private_key): + """ + Load private key from byte string. + + Uses current curve and checks if the provided key matches + the curve of ECDH key agreement. + Key loads via from_string method of SigningKey class + + :param private_key: private key in bytes string format + :type private_key: :term:`bytes-like object` + + :raises NoCurveError: Curve must be set before loading. + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + if not self.curve: + raise NoCurveError("Curve must be set prior to key load.") + return self.load_private_key( + SigningKey.from_string(private_key, curve=self.curve) + ) + + def load_private_key_der(self, private_key_der): + """ + Load private key from DER byte string. + + Compares the curve of the DER-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only DER format supported is the RFC5915 + Look at keys.py:SigningKey.from_der() + + :param private_key_der: string with the DER encoding of private ECDSA + key + :type private_key_der: string + + :raises InvalidCurveError: private_key curve not the same as self.curve + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + return self.load_private_key(SigningKey.from_der(private_key_der)) + + def load_private_key_pem(self, private_key_pem): + """ + Load private key from PEM string. + + Compares the curve of the DER-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only PEM format supported is the RFC5915 + Look at keys.py:SigningKey.from_pem() + it needs to have `EC PRIVATE KEY` section + + :param private_key_pem: string with PEM-encoded private ECDSA key + :type private_key_pem: string + + :raises InvalidCurveError: private_key curve not the same as self.curve + + :return: public (verifying) key from this private key. + :rtype: VerifyingKey + """ + return self.load_private_key(SigningKey.from_pem(private_key_pem)) + + def get_public_key(self): + """ + Provides a public key that matches the local private key. + + Needs to be sent to the remote party. + + :return: public (verifying) key from local private key. + :rtype: VerifyingKey + """ + return self.private_key.get_verifying_key() + + def load_received_public_key(self, public_key): + """ + Load public key from VerifyingKey (keys.py) object. + + Needs to have the same curve as set as current for ecdh operation. + If curve is not set - it sets it from VerifyingKey. + + :param public_key: Initialised VerifyingKey class + :type public_key: VerifyingKey + + :raises InvalidCurveError: public_key curve not the same as self.curve + """ + if not self.curve: + self.curve = public_key.curve + if self.curve != public_key.curve: + raise InvalidCurveError("Curve mismatch.") + self.public_key = public_key + + def load_received_public_key_bytes( + self, public_key_str, valid_encodings=None + ): + """ + Load public key from byte string. + + Uses current curve and checks if key length corresponds to + the current curve. + Key loads via from_string method of VerifyingKey class + + :param public_key_str: public key in bytes string format + :type public_key_str: :term:`bytes-like object` + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + """ + return self.load_received_public_key( + VerifyingKey.from_string( + public_key_str, self.curve, valid_encodings + ) + ) + + def load_received_public_key_der(self, public_key_der): + """ + Load public key from DER byte string. + + Compares the curve of the DER-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only DER format supported is the RFC5912 + Look at keys.py:VerifyingKey.from_der() + + :param public_key_der: string with the DER encoding of public ECDSA key + :type public_key_der: string + + :raises InvalidCurveError: public_key curve not the same as self.curve + """ + return self.load_received_public_key( + VerifyingKey.from_der(public_key_der) + ) + + def load_received_public_key_pem(self, public_key_pem): + """ + Load public key from PEM string. + + Compares the curve of the PEM-encoded key with the ECDH set curve, + uses the former if unset. + + Note, the only PEM format supported is the RFC5912 + Look at keys.py:VerifyingKey.from_pem() + + :param public_key_pem: string with PEM-encoded public ECDSA key + :type public_key_pem: string + + :raises InvalidCurveError: public_key curve not the same as self.curve + """ + return self.load_received_public_key( + VerifyingKey.from_pem(public_key_pem) + ) + + def generate_sharedsecret_bytes(self): + """ + Generate shared secret from local private key and remote public key. + + The objects needs to have both private key and received public key + before generation is allowed. + + :raises InvalidCurveError: public_key curve not the same as self.curve + :raises NoKeyError: public_key or private_key is not set + + :return: shared secret + :rtype: bytes + """ + return number_to_string( + self.generate_sharedsecret(), self.private_key.curve.curve.p() + ) + + def generate_sharedsecret(self): + """ + Generate shared secret from local private key and remote public key. + + The objects needs to have both private key and received public key + before generation is allowed. + + It's the same for local and remote party, + shared secret(local private key, remote public key) == + shared secret(local public key, remote private key) + + :raises InvalidCurveError: public_key curve not the same as self.curve + :raises NoKeyError: public_key or private_key is not set + + :return: shared secret + :rtype: int + """ + return self._get_shared_secret(self.public_key) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py new file mode 100644 index 000000000..332828168 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py @@ -0,0 +1,859 @@ +#! /usr/bin/env python + +""" +Low level implementation of Elliptic-Curve Digital Signatures. + +.. note :: + You're most likely looking for the :py:class:`~ecdsa.keys` module. + This is a low-level implementation of the ECDSA that operates on + integers, not byte strings. + +NOTE: This a low level implementation of ECDSA, for normal applications +you should be looking at the keys.py module. + +Classes and methods for elliptic-curve signatures: +private keys, public keys, signatures, +and definitions of prime-modulus curves. + +Example: + +.. code-block:: python + + # (In real-life applications, you would probably want to + # protect against defects in SystemRandom.) + from random import SystemRandom + randrange = SystemRandom().randrange + + # Generate a public/private key pair using the NIST Curve P-192: + + g = generator_192 + n = g.order() + secret = randrange( 1, n ) + pubkey = Public_key( g, g * secret ) + privkey = Private_key( pubkey, secret ) + + # Signing a hash value: + + hash = randrange( 1, n ) + signature = privkey.sign( hash, randrange( 1, n ) ) + + # Verifying a signature for a hash value: + + if pubkey.verifies( hash, signature ): + print_("Demo verification succeeded.") + else: + print_("*** Demo verification failed.") + + # Verification fails if the hash value is modified: + + if pubkey.verifies( hash-1, signature ): + print_("**** Demo verification failed to reject tampered hash.") + else: + print_("Demo verification correctly rejected tampered hash.") + +Revision history: + 2005.12.31 - Initial version. + + 2008.11.25 - Substantial revisions introducing new classes. + + 2009.05.16 - Warn against using random.randrange in real applications. + + 2009.05.17 - Use random.SystemRandom by default. + +Originally written in 2005 by Peter Pearson and placed in the public domain, +modified as part of the python-ecdsa package. +""" + +from six import int2byte, b +from . import ellipticcurve +from . import numbertheory +from .util import bit_length +from ._compat import remove_whitespace + + +class RSZeroError(RuntimeError): + pass + + +class InvalidPointError(RuntimeError): + pass + + +class Signature(object): + """ + ECDSA signature. + + :ivar int r: the ``r`` element of the ECDSA signature + :ivar int s: the ``s`` element of the ECDSA signature + """ + + def __init__(self, r, s): + self.r = r + self.s = s + + def recover_public_keys(self, hash, generator): + """ + Returns two public keys for which the signature is valid + + :param int hash: signed hash + :param AbstractPoint generator: is the generator used in creation + of the signature + :rtype: tuple(Public_key, Public_key) + :return: a pair of public keys that can validate the signature + """ + curve = generator.curve() + n = generator.order() + r = self.r + s = self.s + e = hash + x = r + + # Compute the curve point with x as x-coordinate + alpha = ( + pow(x, 3, curve.p()) + (curve.a() * x) + curve.b() + ) % curve.p() + beta = numbertheory.square_root_mod_prime(alpha, curve.p()) + y = beta if beta % 2 == 0 else curve.p() - beta + + # Compute the public key + R1 = ellipticcurve.PointJacobi(curve, x, y, 1, n) + Q1 = numbertheory.inverse_mod(r, n) * (s * R1 + (-e % n) * generator) + Pk1 = Public_key(generator, Q1) + + # And the second solution + R2 = ellipticcurve.PointJacobi(curve, x, -y, 1, n) + Q2 = numbertheory.inverse_mod(r, n) * (s * R2 + (-e % n) * generator) + Pk2 = Public_key(generator, Q2) + + return [Pk1, Pk2] + + +class Public_key(object): + """Public key for ECDSA.""" + + def __init__(self, generator, point, verify=True): + """Low level ECDSA public key object. + + :param generator: the Point that generates the group (the base point) + :param point: the Point that defines the public key + :param bool verify: if True check if point is valid point on curve + + :raises InvalidPointError: if the point parameters are invalid or + point does not lay on the curve + """ + + self.curve = generator.curve() + self.generator = generator + self.point = point + n = generator.order() + p = self.curve.p() + if not (0 <= point.x() < p) or not (0 <= point.y() < p): + raise InvalidPointError( + "The public point has x or y out of range." + ) + if verify and not self.curve.contains_point(point.x(), point.y()): + raise InvalidPointError("Point does not lay on the curve") + if not n: + raise InvalidPointError("Generator point must have order.") + # for curve parameters with base point with cofactor 1, all points + # that are on the curve are scalar multiples of the base point, so + # verifying that is not necessary. See Section 3.2.2.1 of SEC 1 v2 + if ( + verify + and self.curve.cofactor() != 1 + and not n * point == ellipticcurve.INFINITY + ): + raise InvalidPointError("Generator point order is bad.") + + def __eq__(self, other): + """Return True if the keys are identical, False otherwise. + + Note: for comparison, only placement on the same curve and point + equality is considered, use of the same generator point is not + considered. + """ + if isinstance(other, Public_key): + return self.curve == other.curve and self.point == other.point + return NotImplemented + + def __ne__(self, other): + """Return False if the keys are identical, True otherwise.""" + return not self == other + + def verifies(self, hash, signature): + """Verify that signature is a valid signature of hash. + Return True if the signature is valid. + """ + + # From X9.62 J.3.1. + + G = self.generator + n = G.order() + r = signature.r + s = signature.s + if r < 1 or r > n - 1: + return False + if s < 1 or s > n - 1: + return False + c = numbertheory.inverse_mod(s, n) + u1 = (hash * c) % n + u2 = (r * c) % n + if hasattr(G, "mul_add"): + xy = G.mul_add(u1, self.point, u2) + else: + xy = u1 * G + u2 * self.point + v = xy.x() % n + return v == r + + +class Private_key(object): + """Private key for ECDSA.""" + + def __init__(self, public_key, secret_multiplier): + """public_key is of class Public_key; + secret_multiplier is a large integer. + """ + + self.public_key = public_key + self.secret_multiplier = secret_multiplier + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, Private_key): + return ( + self.public_key == other.public_key + and self.secret_multiplier == other.secret_multiplier + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + def sign(self, hash, random_k): + """Return a signature for the provided hash, using the provided + random nonce. It is absolutely vital that random_k be an unpredictable + number in the range [1, self.public_key.point.order()-1]. If + an attacker can guess random_k, he can compute our private key from a + single signature. Also, if an attacker knows a few high-order + bits (or a few low-order bits) of random_k, he can compute our private + key from many signatures. The generation of nonces with adequate + cryptographic strength is very difficult and far beyond the scope + of this comment. + + May raise RuntimeError, in which case retrying with a new + random value k is in order. + """ + + G = self.public_key.generator + n = G.order() + k = random_k % n + # Fix the bit-length of the random nonce, + # so that it doesn't leak via timing. + # This does not change that ks = k mod n + ks = k + n + kt = ks + n + if bit_length(ks) == bit_length(n): + p1 = kt * G + else: + p1 = ks * G + r = p1.x() % n + if r == 0: + raise RSZeroError("amazingly unlucky random number r") + s = ( + numbertheory.inverse_mod(k, n) + * (hash + (self.secret_multiplier * r) % n) + ) % n + if s == 0: + raise RSZeroError("amazingly unlucky random number s") + return Signature(r, s) + + +def int_to_string(x): + """Convert integer x into a string of bytes, as per X9.62.""" + assert x >= 0 + if x == 0: + return b("\0") + result = [] + while x: + ordinal = x & 0xFF + result.append(int2byte(ordinal)) + x >>= 8 + + result.reverse() + return b("").join(result) + + +def string_to_int(s): + """Convert a string of bytes into an integer, as per X9.62.""" + result = 0 + for c in s: + if not isinstance(c, int): + c = ord(c) + result = 256 * result + c + return result + + +def digest_integer(m): + """Convert an integer into a string of bytes, compute + its SHA-1 hash, and convert the result to an integer.""" + # + # I don't expect this function to be used much. I wrote + # it in order to be able to duplicate the examples + # in ECDSAVS. + # + from hashlib import sha1 + + return string_to_int(sha1(int_to_string(m)).digest()) + + +def point_is_valid(generator, x, y): + """Is (x,y) a valid public key based on the specified generator?""" + + # These are the tests specified in X9.62. + + n = generator.order() + curve = generator.curve() + p = curve.p() + if not (0 <= x < p) or not (0 <= y < p): + return False + if not curve.contains_point(x, y): + return False + if ( + curve.cofactor() != 1 + and not n * ellipticcurve.PointJacobi(curve, x, y, 1) + == ellipticcurve.INFINITY + ): + return False + return True + + +# secp112r1 curve +_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) +# s = 00F50B02 8E4D696E 67687561 51752904 72783FB1 +_a = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD2088"), 16) +_b = int(remove_whitespace("659E F8BA0439 16EEDE89 11702B22"), 16) +_Gx = int(remove_whitespace("09487239 995A5EE7 6B55F9C2 F098"), 16) +_Gy = int(remove_whitespace("A89C E5AF8724 C0A23E0E 0FF77500"), 16) +_r = int(remove_whitespace("DB7C 2ABF62E3 5E7628DF AC6561C5"), 16) +_h = 1 +curve_112r1 = ellipticcurve.CurveFp(_p, _a, _b, _h) +generator_112r1 = ellipticcurve.PointJacobi( + curve_112r1, _Gx, _Gy, 1, _r, generator=True +) + + +# secp112r2 curve +_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) +# s = 022757A1 114D69E 67687561 51755316 C05E0BD4 +_a = int(remove_whitespace("6127 C24C05F3 8A0AAAF6 5C0EF02C"), 16) +_b = int(remove_whitespace("51DE F1815DB5 ED74FCC3 4C85D709"), 16) +_Gx = int(remove_whitespace("4BA30AB5 E892B4E1 649DD092 8643"), 16) +_Gy = int(remove_whitespace("ADCD 46F5882E 3747DEF3 6E956E97"), 16) +_r = int(remove_whitespace("36DF 0AAFD8B8 D7597CA1 0520D04B"), 16) +_h = 4 +curve_112r2 = ellipticcurve.CurveFp(_p, _a, _b, _h) +generator_112r2 = ellipticcurve.PointJacobi( + curve_112r2, _Gx, _Gy, 1, _r, generator=True +) + + +# secp128r1 curve +_p = int(remove_whitespace("FFFFFFFD FFFFFFFF FFFFFFFF FFFFFFFF"), 16) +# S = 000E0D4D 69E6768 75615175 0CC03A44 73D03679 +# a and b are mod p, so a is equal to p-3, or simply -3 +# _a = -3 +_b = int(remove_whitespace("E87579C1 1079F43D D824993C 2CEE5ED3"), 16) +_Gx = int(remove_whitespace("161FF752 8B899B2D 0C28607C A52C5B86"), 16) +_Gy = int(remove_whitespace("CF5AC839 5BAFEB13 C02DA292 DDED7A83"), 16) +_r = int(remove_whitespace("FFFFFFFE 00000000 75A30D1B 9038A115"), 16) +_h = 1 +curve_128r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) +generator_128r1 = ellipticcurve.PointJacobi( + curve_128r1, _Gx, _Gy, 1, _r, generator=True +) + + +# secp160r1 +_p = int(remove_whitespace("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7FFFFFFF"), 16) +# S = 1053CDE4 2C14D696 E6768756 1517533B F3F83345 +# a and b are mod p, so a is equal to p-3, or simply -3 +# _a = -3 +_b = int(remove_whitespace("1C97BEFC 54BD7A8B 65ACF89F 81D4D4AD C565FA45"), 16) +_Gx = int( + remove_whitespace("4A96B568 8EF57328 46646989 68C38BB9 13CBFC82"), + 16, +) +_Gy = int( + remove_whitespace("23A62855 3168947D 59DCC912 04235137 7AC5FB32"), + 16, +) +_r = int( + remove_whitespace("01 00000000 00000000 0001F4C8 F927AED3 CA752257"), + 16, +) +_h = 1 +curve_160r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) +generator_160r1 = ellipticcurve.PointJacobi( + curve_160r1, _Gx, _Gy, 1, _r, generator=True +) + + +# NIST Curve P-192: +_p = 6277101735386680763835789423207666416083908700390324961279 +_r = 6277101735386680763835789423176059013767194773182842284081 +# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L +# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L +_b = int( + remove_whitespace( + """ + 64210519 E59C80E7 0FA7E9AB 72243049 FEB8DEEC C146B9B1""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 188DA80E B03090F6 7CBF20EB 43A18800 F4FF0AFD 82FF1012""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 07192B95 FFC8DA78 631011ED 6B24CDD5 73F977A1 1E794811""" + ), + 16, +) + +curve_192 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_192 = ellipticcurve.PointJacobi( + curve_192, _Gx, _Gy, 1, _r, generator=True +) + + +# NIST Curve P-224: +_p = int( + remove_whitespace( + """ + 2695994666715063979466701508701963067355791626002630814351 + 0066298881""" + ) +) +_r = int( + remove_whitespace( + """ + 2695994666715063979466701508701962594045780771442439172168 + 2722368061""" + ) +) +# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L +# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL +_b = int( + remove_whitespace( + """ + B4050A85 0C04B3AB F5413256 5044B0B7 D7BFD8BA 270B3943 + 2355FFB4""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + B70E0CBD 6BB4BF7F 321390B9 4A03C1D3 56C21122 343280D6 + 115C1D21""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + BD376388 B5F723FB 4C22DFE6 CD4375A0 5A074764 44D58199 + 85007E34""" + ), + 16, +) + +curve_224 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_224 = ellipticcurve.PointJacobi( + curve_224, _Gx, _Gy, 1, _r, generator=True +) + +# NIST Curve P-256: +_p = int( + remove_whitespace( + """ + 1157920892103562487626974469494075735300861434152903141955 + 33631308867097853951""" + ) +) +_r = int( + remove_whitespace( + """ + 115792089210356248762697446949407573529996955224135760342 + 422259061068512044369""" + ) +) +# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L +# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL +_b = int( + remove_whitespace( + """ + 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 + 3BCE3C3E 27D2604B""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 + F4A13945 D898C296""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE + CBB64068 37BF51F5""" + ), + 16, +) + +curve_256 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_256 = ellipticcurve.PointJacobi( + curve_256, _Gx, _Gy, 1, _r, generator=True +) + +# NIST Curve P-384: +_p = int( + remove_whitespace( + """ + 3940200619639447921227904010014361380507973927046544666794 + 8293404245721771496870329047266088258938001861606973112319""" + ) +) +_r = int( + remove_whitespace( + """ + 3940200619639447921227904010014361380507973927046544666794 + 6905279627659399113263569398956308152294913554433653942643""" + ) +) +# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L +# c = int(remove_whitespace( +# """ +# 79d1e655 f868f02f ff48dcde e14151dd b80643c1 406d0ca1 +# 0dfe6fc5 2009540a 495e8042 ea5f744f 6e184667 cc722483""" +# ), 16) +_b = int( + remove_whitespace( + """ + B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 + 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 + 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C + E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F""" + ), + 16, +) + +curve_384 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_384 = ellipticcurve.PointJacobi( + curve_384, _Gx, _Gy, 1, _r, generator=True +) + +# NIST Curve P-521: +_p = int( + "686479766013060971498190079908139321726943530014330540939" + "446345918554318339765605212255964066145455497729631139148" + "0858037121987999716643812574028291115057151" +) +_r = int( + "686479766013060971498190079908139321726943530014330540939" + "446345918554318339765539424505774633321719753296399637136" + "3321113864768612440380340372808892707005449" +) +# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL +# c = int(remove_whitespace( +# """ +# 0b4 8bfa5f42 0a349495 39d2bdfc 264eeeeb 077688e4 +# 4fbf0ad8 f6d0edb3 7bd6b533 28100051 8e19f1b9 ffbe0fe9 +# ed8a3c22 00b8f875 e523868c 70c1e5bf 55bad637""" +# ), 16) +_b = int( + remove_whitespace( + """ + 051 953EB961 8E1C9A1F 929A21A0 B68540EE A2DA725B + 99B315F3 B8B48991 8EF109E1 56193951 EC7E937B 1652C0BD + 3BB1BF07 3573DF88 3D2C34F1 EF451FD4 6B503F00""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + C6 858E06B7 0404E9CD 9E3ECB66 2395B442 9C648139 + 053FB521 F828AF60 6B4D3DBA A14B5E77 EFE75928 FE1DC127 + A2FFA8DE 3348B3C1 856A429B F97E7E31 C2E5BD66""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 118 39296A78 9A3BC004 5C8A5FB4 2C7D1BD9 98F54449 + 579B4468 17AFBD17 273E662C 97EE7299 5EF42640 C550B901 + 3FAD0761 353C7086 A272C240 88BE9476 9FD16650""" + ), + 16, +) + +curve_521 = ellipticcurve.CurveFp(_p, -3, _b, 1) +generator_521 = ellipticcurve.PointJacobi( + curve_521, _Gx, _Gy, 1, _r, generator=True +) + +# Certicom secp256-k1 +_a = 0x0000000000000000000000000000000000000000000000000000000000000000 +_b = 0x0000000000000000000000000000000000000000000000000000000000000007 +_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 +_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 +_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 + +curve_secp256k1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_secp256k1 = ellipticcurve.PointJacobi( + curve_secp256k1, _Gx, _Gy, 1, _r, generator=True +) + +# Brainpool P-160-r1 +_a = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 +_b = 0x1E589A8595423412134FAA2DBDEC95C8D8675E58 +_p = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F +_Gx = 0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3 +_Gy = 0x1667CB477A1A8EC338F94741669C976316DA6321 +_q = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 + +curve_brainpoolp160r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp160r1 = ellipticcurve.PointJacobi( + curve_brainpoolp160r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-192-r1 +_a = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF +_b = 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9 +_p = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297 +_Gx = 0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6 +_Gy = 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F +_q = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 + +curve_brainpoolp192r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp192r1 = ellipticcurve.PointJacobi( + curve_brainpoolp192r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-224-r1 +_a = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 +_b = 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B +_p = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF +_Gx = 0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D +_Gy = 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD +_q = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F + +curve_brainpoolp224r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp224r1 = ellipticcurve.PointJacobi( + curve_brainpoolp224r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-256-r1 +_a = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 +_b = 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 +_p = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 +_Gx = 0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 +_Gy = 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 +_q = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 + +curve_brainpoolp256r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp256r1 = ellipticcurve.PointJacobi( + curve_brainpoolp256r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-320-r1 +_a = int( + remove_whitespace( + """ + 3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9 + F492F375A97D860EB4""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539 + 816F5EB4AC8FB1F1A6""" + ), + 16, +) +_p = int( + remove_whitespace( + """ + D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC + 28FCD412B1F1B32E27""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599 + C710AF8D0D39E20611""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6A + C7D35245D1692E8EE1""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658 + E98691555B44C59311""" + ), + 16, +) + +curve_brainpoolp320r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp320r1 = ellipticcurve.PointJacobi( + curve_brainpoolp320r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-384-r1 +_a = int( + remove_whitespace( + """ + 7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F9 + 0F8AA5814A503AD4EB04A8C7DD22CE2826""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62 + D57CB4390295DBC9943AB78696FA504C11""" + ), + 16, +) +_p = int( + remove_whitespace( + """ + 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB711 + 23ACD3A729901D1A71874700133107EC53""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10 + E8E826E03436D646AAEF87B2E247D4AF1E""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF991292 + 80E4646217791811142820341263C5315""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425 + A7CF3AB6AF6B7FC3103B883202E9046565""" + ), + 16, +) + +curve_brainpoolp384r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp384r1 = ellipticcurve.PointJacobi( + curve_brainpoolp384r1, _Gx, _Gy, 1, _q, generator=True +) + +# Brainpool P-512-r1 +_a = int( + remove_whitespace( + """ + 7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863 + BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA""" + ), + 16, +) +_b = int( + remove_whitespace( + """ + 3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117 + A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723""" + ), + 16, +) +_p = int( + remove_whitespace( + """ + AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 + 717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3""" + ), + 16, +) +_Gx = int( + remove_whitespace( + """ + 81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D009 + 8EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822""" + ), + 16, +) +_Gy = int( + remove_whitespace( + """ + 7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F81 + 11B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892""" + ), + 16, +) +_q = int( + remove_whitespace( + """ + AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 + 70553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069""" + ), + 16, +) + +curve_brainpoolp512r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) +generator_brainpoolp512r1 = ellipticcurve.PointJacobi( + curve_brainpoolp512r1, _Gx, _Gy, 1, _q, generator=True +) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py new file mode 100644 index 000000000..9769cfd8e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py @@ -0,0 +1,252 @@ +"""Implementation of Edwards Digital Signature Algorithm.""" + +import hashlib +from ._sha3 import shake_256 +from . import ellipticcurve +from ._compat import ( + remove_whitespace, + bit_length, + bytes_to_int, + int_to_bytes, + compat26_str, +) + +# edwards25519, defined in RFC7748 +_p = 2**255 - 19 +_a = -1 +_d = int( + remove_whitespace( + "370957059346694393431380835087545651895421138798432190163887855330" + "85940283555" + ) +) +_h = 8 + +_Gx = int( + remove_whitespace( + "151122213495354007725011514095885315114540126930418572060461132" + "83949847762202" + ) +) +_Gy = int( + remove_whitespace( + "463168356949264781694283940034751631413079938662562256157830336" + "03165251855960" + ) +) +_r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED + + +def _sha512(data): + return hashlib.new("sha512", compat26_str(data)).digest() + + +curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) +generator_ed25519 = ellipticcurve.PointEdwards( + curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True +) + + +# edwards448, defined in RFC7748 +_p = 2**448 - 2**224 - 1 +_a = 1 +_d = -39081 % _p +_h = 4 + +_Gx = int( + remove_whitespace( + "224580040295924300187604334099896036246789641632564134246125461" + "686950415467406032909029192869357953282578032075146446173674602635" + "247710" + ) +) +_Gy = int( + remove_whitespace( + "298819210078481492676017930443930673437544040154080242095928241" + "372331506189835876003536878655418784733982303233503462500531545062" + "832660" + ) +) +_r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D + + +def _shake256(data): + return shake_256(data, 114) + + +curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) +generator_ed448 = ellipticcurve.PointEdwards( + curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True +) + + +class PublicKey(object): + """Public key for the Edwards Digital Signature Algorithm.""" + + def __init__(self, generator, public_key, public_point=None): + self.generator = generator + self.curve = generator.curve() + self.__encoded = public_key + # plus one for the sign bit and round up + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 + if len(public_key) != self.baselen: + raise ValueError( + "Incorrect size of the public key, expected: {0} bytes".format( + self.baselen + ) + ) + if public_point: + self.__point = public_point + else: + self.__point = ellipticcurve.PointEdwards.from_bytes( + self.curve, public_key + ) + + def __eq__(self, other): + if isinstance(other, PublicKey): + return ( + self.curve == other.curve and self.__encoded == other.__encoded + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + @property + def point(self): + return self.__point + + @point.setter + def point(self, other): + if self.__point != other: + raise ValueError("Can't change the coordinates of the point") + self.__point = other + + def public_point(self): + return self.__point + + def public_key(self): + return self.__encoded + + def verify(self, data, signature): + """Verify a Pure EdDSA signature over data.""" + data = compat26_str(data) + if len(signature) != 2 * self.baselen: + raise ValueError( + "Invalid signature length, expected: {0} bytes".format( + 2 * self.baselen + ) + ) + R = ellipticcurve.PointEdwards.from_bytes( + self.curve, signature[: self.baselen] + ) + S = bytes_to_int(signature[self.baselen :], "little") + if S >= self.generator.order(): + raise ValueError("Invalid signature") + + dom = bytearray() + if self.curve == curve_ed448: + dom = bytearray(b"SigEd448" + b"\x00\x00") + + k = bytes_to_int( + self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), + "little", + ) + + if self.generator * S != self.__point * k + R: + raise ValueError("Invalid signature") + + return True + + +class PrivateKey(object): + """Private key for the Edwards Digital Signature Algorithm.""" + + def __init__(self, generator, private_key): + self.generator = generator + self.curve = generator.curve() + # plus one for the sign bit and round up + self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 + if len(private_key) != self.baselen: + raise ValueError( + "Incorrect size of private key, expected: {0} bytes".format( + self.baselen + ) + ) + self.__private_key = bytes(private_key) + self.__h = bytearray(self.curve.hash_func(private_key)) + self.__public_key = None + + a = self.__h[: self.baselen] + a = self._key_prune(a) + scalar = bytes_to_int(a, "little") + self.__s = scalar + + @property + def private_key(self): + return self.__private_key + + def __eq__(self, other): + if isinstance(other, PrivateKey): + return ( + self.curve == other.curve + and self.__private_key == other.__private_key + ) + return NotImplemented + + def __ne__(self, other): + return not self == other + + def _key_prune(self, key): + # make sure the key is not in a small subgroup + h = self.curve.cofactor() + if h == 4: + h_log = 2 + elif h == 8: + h_log = 3 + else: + raise ValueError("Only cofactor 4 and 8 curves supported") + key[0] &= ~((1 << h_log) - 1) + + # ensure the highest bit is set but no higher + l = bit_length(self.curve.p()) + if l % 8 == 0: + key[-1] = 0 + key[-2] |= 0x80 + else: + key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 + return key + + def public_key(self): + """Generate the public key based on the included private key""" + if self.__public_key: + return self.__public_key + + public_point = self.generator * self.__s + + self.__public_key = PublicKey( + self.generator, public_point.to_bytes(), public_point + ) + + return self.__public_key + + def sign(self, data): + """Perform a Pure EdDSA signature over data.""" + data = compat26_str(data) + A = self.public_key().public_key() + + prefix = self.__h[self.baselen :] + + dom = bytearray() + if self.curve == curve_ed448: + dom = bytearray(b"SigEd448" + b"\x00\x00") + + r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") + R = (self.generator * r).to_bytes() + + k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") + k %= self.generator.order() + + S = (r + k * self.__s) % self.generator.order() + + return R + int_to_bytes(S, self.baselen, "little") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py new file mode 100644 index 000000000..d6f714630 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py @@ -0,0 +1,1584 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# +# Implementation of elliptic curves, for cryptographic applications. +# +# This module doesn't provide any way to choose a random elliptic +# curve, nor to verify that an elliptic curve was chosen randomly, +# because one can simply use NIST's standard curves. +# +# Notes from X9.62-1998 (draft): +# Nomenclature: +# - Q is a public key. +# The "Elliptic Curve Domain Parameters" include: +# - q is the "field size", which in our case equals p. +# - p is a big prime. +# - G is a point of prime order (5.1.1.1). +# - n is the order of G (5.1.1.1). +# Public-key validation (5.2.2): +# - Verify that Q is not the point at infinity. +# - Verify that X_Q and Y_Q are in [0,p-1]. +# - Verify that Q is on the curve. +# - Verify that nQ is the point at infinity. +# Signature generation (5.3): +# - Pick random k from [1,n-1]. +# Signature checking (5.4.2): +# - Verify that r and s are in [1,n-1]. +# +# Revision history: +# 2005.12.31 - Initial version. +# 2008.11.25 - Change CurveFp.is_on to contains_point. +# +# Written in 2005 by Peter Pearson and placed in the public domain. +# Modified extensively as part of python-ecdsa. + +from __future__ import division + +try: + from gmpy2 import mpz + + GMPY = True +except ImportError: # pragma: no branch + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + + +from six import python_2_unicode_compatible +from . import numbertheory +from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int +from .errors import MalformedPointError +from .util import orderlen, string_to_number, number_to_string + + +@python_2_unicode_compatible +class CurveFp(object): + """ + :term:`Short Weierstrass Elliptic Curve ` over a + prime field. + """ + + if GMPY: # pragma: no branch + + def __init__(self, p, a, b, h=None): + """ + The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + + h is an integer that is the cofactor of the elliptic curve domain + parameters; it is the number of points satisfying the elliptic + curve equation divided by the order of the base point. It is used + for selection of efficient algorithm for public point verification. + """ + self.__p = mpz(p) + self.__a = mpz(a) + self.__b = mpz(b) + # h is not used in calculations and it can be None, so don't use + # gmpy with it + self.__h = h + + else: # pragma: no branch + + def __init__(self, p, a, b, h=None): + """ + The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + + h is an integer that is the cofactor of the elliptic curve domain + parameters; it is the number of points satisfying the elliptic + curve equation divided by the order of the base point. It is used + for selection of efficient algorithm for public point verification. + """ + self.__p = p + self.__a = a + self.__b = b + self.__h = h + + def __eq__(self, other): + """Return True if other is an identical curve, False otherwise. + + Note: the value of the cofactor of the curve is not taken into account + when comparing curves, as it's derived from the base point and + intrinsic curve characteristic (but it's complex to compute), + only the prime and curve parameters are considered. + """ + if isinstance(other, CurveFp): + p = self.__p + return ( + self.__p == other.__p + and self.__a % p == other.__a % p + and self.__b % p == other.__b % p + ) + return NotImplemented + + def __ne__(self, other): + """Return False if other is an identical curve, True otherwise.""" + return not self == other + + def __hash__(self): + return hash((self.__p, self.__a, self.__b)) + + def p(self): + return self.__p + + def a(self): + return self.__a + + def b(self): + return self.__b + + def cofactor(self): + return self.__h + + def contains_point(self, x, y): + """Is the point (x,y) on this curve?""" + return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 + + def __str__(self): + return "CurveFp(p=%d, a=%d, b=%d, h=%d)" % ( + self.__p, + self.__a, + self.__b, + self.__h, + ) + + +class CurveEdTw(object): + """Parameters for a Twisted Edwards Elliptic Curve""" + + if GMPY: # pragma: no branch + + def __init__(self, p, a, d, h=None, hash_func=None): + """ + The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). + + h is the cofactor of the curve. + hash_func is the hash function associated with the curve + (like SHA-512 for Ed25519) + """ + self.__p = mpz(p) + self.__a = mpz(a) + self.__d = mpz(d) + self.__h = h + self.__hash_func = hash_func + + else: + + def __init__(self, p, a, d, h=None, hash_func=None): + """ + The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). + + h is the cofactor of the curve. + hash_func is the hash function associated with the curve + (like SHA-512 for Ed25519) + """ + self.__p = p + self.__a = a + self.__d = d + self.__h = h + self.__hash_func = hash_func + + def __eq__(self, other): + """Returns True if other is an identical curve.""" + if isinstance(other, CurveEdTw): + p = self.__p + return ( + self.__p == other.__p + and self.__a % p == other.__a % p + and self.__d % p == other.__d % p + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the other is an identical curve, True otherwise.""" + return not self == other + + def __hash__(self): + return hash((self.__p, self.__a, self.__d)) + + def contains_point(self, x, y): + """Is the point (x, y) on this curve?""" + return ( + self.__a * x * x + y * y - 1 - self.__d * x * x * y * y + ) % self.__p == 0 + + def p(self): + return self.__p + + def a(self): + return self.__a + + def d(self): + return self.__d + + def hash_func(self, data): + return self.__hash_func(data) + + def cofactor(self): + return self.__h + + def __str__(self): + return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( + self.__p, + self.__a, + self.__d, + self.__h, + ) + + +class AbstractPoint(object): + """Class for common methods of elliptic curve points.""" + + @staticmethod + def _from_raw_encoding(data, raw_encoding_length): + """ + Decode public point from :term:`raw encoding`. + + :term:`raw encoding` is the same as the :term:`uncompressed` encoding, + but without the 0x04 byte at the beginning. + """ + # real assert, from_bytes() should not call us with different length + assert len(data) == raw_encoding_length + xs = data[: raw_encoding_length // 2] + ys = data[raw_encoding_length // 2 :] + # real assert, raw_encoding_length is calculated by multiplying an + # integer by two so it will always be even + assert len(xs) == raw_encoding_length // 2 + assert len(ys) == raw_encoding_length // 2 + coord_x = string_to_number(xs) + coord_y = string_to_number(ys) + + return coord_x, coord_y + + @staticmethod + def _from_compressed(data, curve): + """Decode public point from compressed encoding.""" + if data[:1] not in (b"\x02", b"\x03"): + raise MalformedPointError("Malformed compressed point encoding") + + is_even = data[:1] == b"\x02" + x = string_to_number(data[1:]) + p = curve.p() + alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p + try: + beta = numbertheory.square_root_mod_prime(alpha, p) + except numbertheory.Error as e: + raise MalformedPointError( + "Encoding does not correspond to a point on curve", e + ) + if is_even == bool(beta & 1): + y = p - beta + else: + y = beta + return x, y + + @classmethod + def _from_hybrid(cls, data, raw_encoding_length, validate_encoding): + """Decode public point from hybrid encoding.""" + # real assert, from_bytes() should not call us with different types + assert data[:1] in (b"\x06", b"\x07") + + # primarily use the uncompressed as it's easiest to handle + x, y = cls._from_raw_encoding(data[1:], raw_encoding_length) + + # but validate if it's self-consistent if we're asked to do that + if validate_encoding and ( + y & 1 + and data[:1] != b"\x07" + or (not y & 1) + and data[:1] != b"\x06" + ): + raise MalformedPointError("Inconsistent hybrid point encoding") + + return x, y + + @classmethod + def _from_edwards(cls, curve, data): + """Decode a point on an Edwards curve.""" + data = bytearray(data) + p = curve.p() + # add 1 for the sign bit and then round up + exp_len = (bit_length(p) + 1 + 7) // 8 + if len(data) != exp_len: + raise MalformedPointError("Point length doesn't match the curve.") + x_0 = (data[-1] & 0x80) >> 7 + + data[-1] &= 0x80 - 1 + + y = bytes_to_int(data, "little") + if GMPY: + y = mpz(y) + + x2 = ( + (y * y - 1) + * numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p) + % p + ) + + try: + x = numbertheory.square_root_mod_prime(x2, p) + except numbertheory.Error as e: + raise MalformedPointError( + "Encoding does not correspond to a point on curve", e + ) + + if x % 2 != x_0: + x = -x % p + + return x, y + + @classmethod + def from_bytes( + cls, curve, data, validate_encoding=True, valid_encodings=None + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + Note: generally you will want to call the ``from_bytes()`` method of + either a child class, PointJacobi or Point. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: x and y coordinates of the encoded point + :rtype: tuple(int, int) + """ + if not valid_encodings: + valid_encodings = set( + ["uncompressed", "compressed", "hybrid", "raw"] + ) + if not all( + i in set(("uncompressed", "compressed", "hybrid", "raw")) + for i in valid_encodings + ): + raise ValueError( + "Only uncompressed, compressed, hybrid or raw encoding " + "supported." + ) + data = normalise_bytes(data) + + if isinstance(curve, CurveEdTw): + return cls._from_edwards(curve, data) + + key_len = len(data) + raw_encoding_length = 2 * orderlen(curve.p()) + if key_len == raw_encoding_length and "raw" in valid_encodings: + coord_x, coord_y = cls._from_raw_encoding( + data, raw_encoding_length + ) + elif key_len == raw_encoding_length + 1 and ( + "hybrid" in valid_encodings or "uncompressed" in valid_encodings + ): + if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings: + coord_x, coord_y = cls._from_hybrid( + data, raw_encoding_length, validate_encoding + ) + elif data[:1] == b"\x04" and "uncompressed" in valid_encodings: + coord_x, coord_y = cls._from_raw_encoding( + data[1:], raw_encoding_length + ) + else: + raise MalformedPointError( + "Invalid X9.62 encoding of the public point" + ) + elif ( + key_len == raw_encoding_length // 2 + 1 + and "compressed" in valid_encodings + ): + coord_x, coord_y = cls._from_compressed(data, curve) + else: + raise MalformedPointError( + "Length of string does not match lengths of " + "any of the enabled ({0}) encodings of the " + "curve.".format(", ".join(valid_encodings)) + ) + return coord_x, coord_y + + def _raw_encode(self): + """Convert the point to the :term:`raw encoding`.""" + prime = self.curve().p() + x_str = number_to_string(self.x(), prime) + y_str = number_to_string(self.y(), prime) + return x_str + y_str + + def _compressed_encode(self): + """Encode the point into the compressed form.""" + prime = self.curve().p() + x_str = number_to_string(self.x(), prime) + if self.y() & 1: + return b"\x03" + x_str + return b"\x02" + x_str + + def _hybrid_encode(self): + """Encode the point into the hybrid form.""" + raw_enc = self._raw_encode() + if self.y() & 1: + return b"\x07" + raw_enc + return b"\x06" + raw_enc + + def _edwards_encode(self): + """Encode the point according to RFC8032 encoding.""" + self.scale() + x, y, p = self.x(), self.y(), self.curve().p() + + # add 1 for the sign bit and then round up + enc_len = (bit_length(p) + 1 + 7) // 8 + y_str = int_to_bytes(y, enc_len, "little") + if x % 2: + y_str[-1] |= 0x80 + return y_str + + def to_bytes(self, encoding="raw"): + """ + Convert the point to a byte string. + + The method by default uses the :term:`raw encoding` (specified + by `encoding="raw"`. It can also output points in :term:`uncompressed`, + :term:`compressed`, and :term:`hybrid` formats. + + For points on Edwards curves `encoding` is ignored and only the + encoding defined in RFC 8032 is supported. + + :return: :term:`raw encoding` of a public on the curve + :rtype: bytes + """ + assert encoding in ("raw", "uncompressed", "compressed", "hybrid") + curve = self.curve() + if isinstance(curve, CurveEdTw): + return self._edwards_encode() + elif encoding == "raw": + return self._raw_encode() + elif encoding == "uncompressed": + return b"\x04" + self._raw_encode() + elif encoding == "hybrid": + return self._hybrid_encode() + else: + return self._compressed_encode() + + @staticmethod + def _naf(mult): + """Calculate non-adjacent form of number.""" + ret = [] + while mult: + if mult % 2: + nd = mult % 4 + if nd >= 2: + nd -= 4 + ret.append(nd) + mult -= nd + else: + ret.append(0) + mult //= 2 + return ret + + +class PointJacobi(AbstractPoint): + """ + Point on a short Weierstrass elliptic curve. Uses Jacobi coordinates. + + In Jacobian coordinates, there are three parameters, X, Y and Z. + They correspond to affine parameters 'x' and 'y' like so: + + x = X / Z² + y = Y / Z³ + """ + + def __init__(self, curve, x, y, z, order=None, generator=False): + """ + Initialise a point that uses Jacobi representation internally. + + :param CurveFp curve: curve on which the point resides + :param int x: the X parameter of Jacobi representation (equal to x when + converting from affine coordinates + :param int y: the Y parameter of Jacobi representation (equal to y when + converting from affine coordinates + :param int z: the Z parameter of Jacobi representation (equal to 1 when + converting from affine coordinates + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: the point provided is a curve generator, as + such, it will be commonly used with scalar multiplication. This will + cause to precompute multiplication table generation for it + """ + super(PointJacobi, self).__init__() + self.__curve = curve + if GMPY: # pragma: no branch + self.__coords = (mpz(x), mpz(y), mpz(z)) + self.__order = order and mpz(order) + else: # pragma: no branch + self.__coords = (x, y, z) + self.__order = order + self.__generator = generator + self.__precompute = [] + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=True, + valid_encodings=None, + order=None, + generator=False, + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: the point provided is a curve generator, as + such, it will be commonly used with scalar multiplication. This + will cause to precompute multiplication table generation for it + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Point on curve + :rtype: PointJacobi + """ + coord_x, coord_y = super(PointJacobi, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return PointJacobi(curve, coord_x, coord_y, 1, order, generator) + + def _maybe_precompute(self): + if not self.__generator or self.__precompute: + return + + # since this code will execute just once, and it's fully deterministic, + # depend on atomicity of the last assignment to switch from empty + # self.__precompute to filled one and just ignore the unlikely + # situation when two threads execute it at the same time (as it won't + # lead to inconsistent __precompute) + order = self.__order + assert order + precompute = [] + i = 1 + order *= 2 + coord_x, coord_y, coord_z = self.__coords + doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order) + order *= 2 + precompute.append((doubler.x(), doubler.y())) + + while i < order: + i *= 2 + doubler = doubler.double().scale() + precompute.append((doubler.x(), doubler.y())) + + self.__precompute = precompute + + def __getstate__(self): + # while this code can execute at the same time as _maybe_precompute() + # is updating the __precompute or scale() is updating the __coords, + # there is no requirement for consistency between __coords and + # __precompute + state = self.__dict__.copy() + return state + + def __setstate__(self, state): + self.__dict__.update(state) + + def __eq__(self, other): + """Compare for equality two points with each-other. + + Note: only points that lay on the same curve can be equal. + """ + x1, y1, z1 = self.__coords + if other is INFINITY: + return not y1 or not z1 + if isinstance(other, Point): + x2, y2, z2 = other.x(), other.y(), 1 + elif isinstance(other, PointJacobi): + x2, y2, z2 = other.__coords + else: + return NotImplemented + if self.__curve != other.curve(): + return False + p = self.__curve.p() + + zz1 = z1 * z1 % p + zz2 = z2 * z2 % p + + # compare the fractions by bringing them to the same denominator + # depend on short-circuit to save 4 multiplications in case of + # inequality + return (x1 * zz2 - x2 * zz1) % p == 0 and ( + y1 * zz2 * z2 - y2 * zz1 * z1 + ) % p == 0 + + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + + def order(self): + """Return the order of the point. + + None if it is undefined. + """ + return self.__order + + def curve(self): + """Return curve over which the point is defined.""" + return self.__curve + + def x(self): + """ + Return affine x coordinate. + + This method should be used only when the 'y' coordinate is not needed. + It's computationally more efficient to use `to_affine()` and then + call x() and y() on the returned instance. Or call `scale()` + and then x() and y() on the returned instance. + """ + x, _, z = self.__coords + if z == 1: + return x + p = self.__curve.p() + z = numbertheory.inverse_mod(z, p) + return x * z**2 % p + + def y(self): + """ + Return affine y coordinate. + + This method should be used only when the 'x' coordinate is not needed. + It's computationally more efficient to use `to_affine()` and then + call x() and y() on the returned instance. Or call `scale()` + and then x() and y() on the returned instance. + """ + _, y, z = self.__coords + if z == 1: + return y + p = self.__curve.p() + z = numbertheory.inverse_mod(z, p) + return y * z**3 % p + + def scale(self): + """ + Return point scaled so that z == 1. + + Modifies point in place, returns self. + """ + x, y, z = self.__coords + if z == 1: + return self + + # scaling is deterministic, so even if two threads execute the below + # code at the same time, they will set __coords to the same value + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(z, p) + zz_inv = z_inv * z_inv % p + x = x * zz_inv % p + y = y * zz_inv * z_inv % p + self.__coords = (x, y, 1) + return self + + def to_affine(self): + """Return point in affine form.""" + _, y, z = self.__coords + if not y or not z: + return INFINITY + self.scale() + x, y, z = self.__coords + return Point(self.__curve, x, y, self.__order) + + @staticmethod + def from_affine(point, generator=False): + """Create from an affine point. + + :param bool generator: set to True to make the point to precalculate + multiplication table - useful for public point when verifying many + signatures (around 100 or so) or for generator points of a curve. + """ + return PointJacobi( + point.curve(), point.x(), point.y(), 1, point.order(), generator + ) + + # please note that all the methods that use the equations from + # hyperelliptic + # are formatted in a way to maximise performance. + # Things that make code faster: multiplying instead of taking to the power + # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and + # `pow(x, 4, p)`), + # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is + # faster than `x1 = self.x1; x2 = self.x2`), + # similarly, sometimes the `% p` is skipped if it makes the calculation + # faster and the result of calculation is later reduced modulo `p` + + def _double_with_z_1(self, X1, Y1, p, a): + """Add a point to itself with z == 1.""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl + XX, YY = X1 * X1 % p, Y1 * Y1 % p + if not YY: + return 0, 0, 1 + YYYY = YY * YY % p + S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p + M = 3 * XX + a + T = (M * M - 2 * S) % p + # X3 = T + Y3 = (M * (S - T) - 8 * YYYY) % p + Z3 = 2 * Y1 % p + return T, Y3, Z3 + + def _double(self, X1, Y1, Z1, p, a): + """Add a point to itself, arbitrary z.""" + if Z1 == 1: + return self._double_with_z_1(X1, Y1, p, a) + if not Y1 or not Z1: + return 0, 0, 1 + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl + XX, YY = X1 * X1 % p, Y1 * Y1 % p + if not YY: + return 0, 0, 1 + YYYY = YY * YY % p + ZZ = Z1 * Z1 % p + S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p + M = (3 * XX + a * ZZ * ZZ) % p + T = (M * M - 2 * S) % p + # X3 = T + Y3 = (M * (S - T) - 8 * YYYY) % p + Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p + + return T, Y3, Z3 + + def double(self): + """Add a point to itself.""" + X1, Y1, Z1 = self.__coords + + if not Y1: + return INFINITY + + p, a = self.__curve.p(), self.__curve.a() + + X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def _add_with_z_1(self, X1, Y1, X2, Y2, p): + """add points when both Z1 and Z2 equal 1""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl + H = X2 - X1 + HH = H * H + I = 4 * HH % p + J = H * I + r = 2 * (Y2 - Y1) + if not H and not r: + return self._double_with_z_1(X1, Y1, p, self.__curve.a()) + V = X1 * I + X3 = (r**2 - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * Y1 * J) % p + Z3 = 2 * H % p + return X3, Y3, Z3 + + def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p): + """add points when Z1 == Z2""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m + A = (X2 - X1) ** 2 % p + B = X1 * A % p + C = X2 * A + D = (Y2 - Y1) ** 2 % p + if not A and not D: + return self._double(X1, Y1, Z1, p, self.__curve.a()) + X3 = (D - B - C) % p + Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p + Z3 = Z1 * (X2 - X1) % p + return X3, Y3, Z3 + + def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p): + """add points when Z2 == 1""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl + Z1Z1 = Z1 * Z1 % p + U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p + H = (U2 - X1) % p + HH = H * H % p + I = 4 * HH % p + J = H * I + r = 2 * (S2 - Y1) % p + if not r and not H: + return self._double_with_z_1(X2, Y2, p, self.__curve.a()) + V = X1 * I + X3 = (r * r - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * Y1 * J) % p + Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p + return X3, Y3, Z3 + + def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p): + """add points with arbitrary z""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl + Z1Z1 = Z1 * Z1 % p + Z2Z2 = Z2 * Z2 % p + U1 = X1 * Z2Z2 % p + U2 = X2 * Z1Z1 % p + S1 = Y1 * Z2 * Z2Z2 % p + S2 = Y2 * Z1 * Z1Z1 % p + H = U2 - U1 + I = 4 * H * H % p + J = H * I % p + r = 2 * (S2 - S1) % p + if not H and not r: + return self._double(X1, Y1, Z1, p, self.__curve.a()) + V = U1 * I + X3 = (r * r - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * S1 * J) % p + Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p + + return X3, Y3, Z3 + + def __radd__(self, other): + """Add other to self.""" + return self + other + + def _add(self, X1, Y1, Z1, X2, Y2, Z2, p): + """add two points, select fastest method.""" + if not Y1 or not Z1: + return X2, Y2, Z2 + if not Y2 or not Z2: + return X1, Y1, Z1 + if Z1 == Z2: + if Z1 == 1: + return self._add_with_z_1(X1, Y1, X2, Y2, p) + return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p) + if Z1 == 1: + return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p) + if Z2 == 1: + return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p) + return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p) + + def __add__(self, other): + """Add two points on elliptic curve.""" + if self == INFINITY: + return other + if other == INFINITY: + return self + if isinstance(other, Point): + other = PointJacobi.from_affine(other) + if self.__curve != other.__curve: + raise ValueError("The other point is on different curve") + + p = self.__curve.p() + X1, Y1, Z1 = self.__coords + X2, Y2, Z2 = other.__coords + + X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __rmul__(self, other): + """Multiply point by an integer.""" + return self * other + + def _mul_precompute(self, other): + """Multiply point by integer with precomputation table.""" + X3, Y3, Z3, p = 0, 0, 1, self.__curve.p() + _add = self._add + for X2, Y2 in self.__precompute: + if other % 2: + if other % 4 >= 2: + other = (other + 1) // 2 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) + else: + other = (other - 1) // 2 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + else: + other //= 2 + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __mul__(self, other): + """Multiply point by an integer.""" + if not self.__coords[1] or not other: + return INFINITY + if other == 1: + return self + if self.__order: + # order*2 as a protection for Minerva + other = other % (self.__order * 2) + self._maybe_precompute() + if self.__precompute: + return self._mul_precompute(other) + + self = self.scale() + X2, Y2, _ = self.__coords + X3, Y3, Z3 = 0, 0, 1 + p, a = self.__curve.p(), self.__curve.a() + _double = self._double + _add = self._add + # since adding points when at least one of them is scaled + # is quicker, reverse the NAF order + for i in reversed(self._naf(other)): + X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) + if i < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) + elif i > 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + + if not Y3 or not Z3: + return INFINITY + + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def mul_add(self, self_mul, other, other_mul): + """ + Do two multiplications at the same time, add results. + + calculates self*self_mul + other*other_mul + """ + if other == INFINITY or other_mul == 0: + return self * self_mul + if self_mul == 0: + return other * other_mul + if not isinstance(other, PointJacobi): + other = PointJacobi.from_affine(other) + # when the points have precomputed answers, then multiplying them alone + # is faster (as it uses NAF and no point doublings) + self._maybe_precompute() + other._maybe_precompute() + if self.__precompute and other.__precompute: + return self * self_mul + other * other_mul + + if self.__order: + self_mul = self_mul % self.__order + other_mul = other_mul % self.__order + + # (X3, Y3, Z3) is the accumulator + X3, Y3, Z3 = 0, 0, 1 + p, a = self.__curve.p(), self.__curve.a() + + # as we have 6 unique points to work with, we can't scale all of them, + # but do scale the ones that are used most often + self.scale() + X1, Y1, Z1 = self.__coords + other.scale() + X2, Y2, Z2 = other.__coords + + _double = self._double + _add = self._add + + # with NAF we have 3 options: no add, subtract, add + # so with 2 points, we have 9 combinations: + # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B + # so we need 4 combined points: + mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p) + pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p) + mApB_X, mApB_Y, mApB_Z = _add(X1, -Y1, Z1, X2, Y2, Z2, p) + pApB_X, pApB_Y, pApB_Z = _add(X1, Y1, Z1, X2, Y2, Z2, p) + # when the self and other sum to infinity, we need to add them + # one by one to get correct result but as that's very unlikely to + # happen in regular operation, we don't need to optimise this case + if not pApB_Y or not pApB_Z: + return self * self_mul + other * other_mul + + # gmp object creation has cumulatively higher overhead than the + # speedup we get from calculating the NAF using gmp so ensure use + # of int() + self_naf = list(reversed(self._naf(int(self_mul)))) + other_naf = list(reversed(self._naf(int(other_mul)))) + # ensure that the lists are the same length (zip() will truncate + # longer one otherwise) + if len(self_naf) < len(other_naf): + self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf + elif len(self_naf) > len(other_naf): + other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf + + for A, B in zip(self_naf, other_naf): + X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) + + # conditions ordered from most to least likely + if A == 0: + if B == 0: + pass + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p) + elif A < 0: + if B == 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p) + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p) + else: + assert A > 0 + if B == 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p) + elif B < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p) + else: + assert B > 0 + X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p) + + if not Y3 or not Z3: + return INFINITY + + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __neg__(self): + """Return negated point.""" + x, y, z = self.__coords + return PointJacobi(self.__curve, x, -y, z, self.__order) + + +class Point(AbstractPoint): + """A point on a short Weierstrass elliptic curve. Altering x and y is + forbidden, but they can be read by the x() and y() methods.""" + + def __init__(self, curve, x, y, order=None): + """curve, x, y, order; order (optional) is the order of this point.""" + super(Point, self).__init__() + self.__curve = curve + if GMPY: + self.__x = x and mpz(x) + self.__y = y and mpz(y) + self.__order = order and mpz(order) + else: + self.__x = x + self.__y = y + self.__order = order + # self.curve is allowed to be None only for INFINITY: + if self.__curve: + assert self.__curve.contains_point(x, y) + # for curves with cofactor 1, all points that are on the curve are + # scalar multiples of the base point, so performing multiplication is + # not necessary to verify that. See Section 3.2.2.1 of SEC 1 v2 + if curve and curve.cofactor() != 1 and order: + assert self * order == INFINITY + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=True, + valid_encodings=None, + order=None, + ): + """ + Initialise the object from byte encoding of a point. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.ellipticcurve.CurveFp + :param validate_encoding: whether to verify that the encoding of the + point is self-consistent, defaults to True, has effect only + on ``hybrid`` encoding + :type validate_encoding: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + :type valid_encodings: :term:`set-like object` + :param int order: the point order, must be non zero when using + generator=True + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Point on curve + :rtype: Point + """ + coord_x, coord_y = super(Point, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return Point(curve, coord_x, coord_y, order) + + def __eq__(self, other): + """Return True if the points are identical, False otherwise. + + Note: only points that lay on the same curve can be equal. + """ + if isinstance(other, Point): + return ( + self.__curve == other.__curve + and self.__x == other.__x + and self.__y == other.__y + ) + return NotImplemented + + def __ne__(self, other): + """Returns False if points are identical, True otherwise.""" + return not self == other + + def __neg__(self): + return Point(self.__curve, self.__x, self.__curve.p() - self.__y) + + def __add__(self, other): + """Add one point to another point.""" + + # X9.62 B.3: + + if not isinstance(other, Point): + return NotImplemented + if other == INFINITY: + return self + if self == INFINITY: + return other + assert self.__curve == other.__curve + if self.__x == other.__x: + if (self.__y + other.__y) % self.__curve.p() == 0: + return INFINITY + else: + return self.double() + + p = self.__curve.p() + + l = ( + (other.__y - self.__y) + * numbertheory.inverse_mod(other.__x - self.__x, p) + ) % p + + x3 = (l * l - self.__x - other.__x) % p + y3 = (l * (self.__x - x3) - self.__y) % p + + return Point(self.__curve, x3, y3) + + def __mul__(self, other): + """Multiply a point by an integer.""" + + def leftmost_bit(x): + assert x > 0 + result = 1 + while result <= x: + result = 2 * result + return result // 2 + + e = other + if e == 0 or (self.__order and e % self.__order == 0): + return INFINITY + if self == INFINITY: + return INFINITY + if e < 0: + return (-self) * (-e) + + # From X9.62 D.3.2: + + e3 = 3 * e + negative_self = Point(self.__curve, self.__x, -self.__y, self.__order) + i = leftmost_bit(e3) // 2 + result = self + # print_("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) + while i > 1: + result = result.double() + if (e3 & i) != 0 and (e & i) == 0: + result = result + self + if (e3 & i) == 0 and (e & i) != 0: + result = result + negative_self + # print_(". . . i = %d, result = %s" % ( i, result )) + i = i // 2 + + return result + + def __rmul__(self, other): + """Multiply a point by an integer.""" + + return self * other + + def __str__(self): + if self == INFINITY: + return "infinity" + return "(%d,%d)" % (self.__x, self.__y) + + def double(self): + """Return a new point that is twice the old.""" + + if self == INFINITY: + return INFINITY + + # X9.62 B.3: + + p = self.__curve.p() + a = self.__curve.a() + + l = ( + (3 * self.__x * self.__x + a) + * numbertheory.inverse_mod(2 * self.__y, p) + ) % p + + x3 = (l * l - 2 * self.__x) % p + y3 = (l * (self.__x - x3) - self.__y) % p + + return Point(self.__curve, x3, y3) + + def x(self): + return self.__x + + def y(self): + return self.__y + + def curve(self): + return self.__curve + + def order(self): + return self.__order + + +class PointEdwards(AbstractPoint): + """Point on Twisted Edwards curve. + + Internally represents the coordinates on the curve using four parameters, + X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so: + + x = X / Z + y = Y / Z + x*y = T / Z + """ + + def __init__(self, curve, x, y, z, t, order=None, generator=False): + """ + Initialise a point that uses the extended coordinates internally. + """ + super(PointEdwards, self).__init__() + self.__curve = curve + if GMPY: # pragma: no branch + self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t)) + self.__order = order and mpz(order) + else: # pragma: no branch + self.__coords = (x, y, z, t) + self.__order = order + self.__generator = generator + self.__precompute = [] + + @classmethod + def from_bytes( + cls, + curve, + data, + validate_encoding=None, + valid_encodings=None, + order=None, + generator=False, + ): + """ + Initialise the object from byte encoding of a point. + + `validate_encoding` and `valid_encodings` are provided for + compatibility with Weierstrass curves, they are ignored for Edwards + points. + + :param data: single point encoding of the public key + :type data: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ecdsa.ellipticcurve.CurveEdTw + :param None validate_encoding: Ignored, encoding is always validated + :param None valid_encodings: Ignored, there is just one encoding + supported + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: Flag to mark the point as a curve generator, + this will cause the library to pre-compute some values to + make repeated usages of the point much faster + + :raises `~ecdsa.errors.MalformedPointError`: if the public point does + not lay on the curve or the encoding is invalid + + :return: Initialised point on an Edwards curve + :rtype: PointEdwards + """ + coord_x, coord_y = super(PointEdwards, cls).from_bytes( + curve, data, validate_encoding, valid_encodings + ) + return PointEdwards( + curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator + ) + + def _maybe_precompute(self): + if not self.__generator or self.__precompute: + return self.__precompute + + # since this code will execute just once, and it's fully deterministic, + # depend on atomicity of the last assignment to switch from empty + # self.__precompute to filled one and just ignore the unlikely + # situation when two threads execute it at the same time (as it won't + # lead to inconsistent __precompute) + order = self.__order + assert order + precompute = [] + i = 1 + order *= 2 + coord_x, coord_y, coord_z, coord_t = self.__coords + prime = self.__curve.p() + + doubler = PointEdwards( + self.__curve, coord_x, coord_y, coord_z, coord_t, order + ) + # for "protection" against Minerva we need 1 or 2 more bits depending + # on order bit size, but it's easier to just calculate one + # point more always + order *= 4 + + while i < order: + doubler = doubler.scale() + coord_x, coord_y = doubler.x(), doubler.y() + coord_t = coord_x * coord_y % prime + precompute.append((coord_x, coord_y, coord_t)) + + i *= 2 + doubler = doubler.double() + + self.__precompute = precompute + return self.__precompute + + def x(self): + """Return affine x coordinate.""" + X1, _, Z1, _ = self.__coords + if Z1 == 1: + return X1 + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + return X1 * z_inv % p + + def y(self): + """Return affine y coordinate.""" + _, Y1, Z1, _ = self.__coords + if Z1 == 1: + return Y1 + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + return Y1 * z_inv % p + + def curve(self): + """Return the curve of the point.""" + return self.__curve + + def order(self): + return self.__order + + def scale(self): + """ + Return point scaled so that z == 1. + + Modifies point in place, returns self. + """ + X1, Y1, Z1, _ = self.__coords + if Z1 == 1: + return self + + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(Z1, p) + x = X1 * z_inv % p + y = Y1 * z_inv % p + t = x * y % p + self.__coords = (x, y, 1, t) + return self + + def __eq__(self, other): + """Compare for equality two points with each-other. + + Note: only points on the same curve can be equal. + """ + x1, y1, z1, t1 = self.__coords + if other is INFINITY: + return not x1 or not t1 + if isinstance(other, PointEdwards): + x2, y2, z2, t2 = other.__coords + else: + return NotImplemented + if self.__curve != other.curve(): + return False + p = self.__curve.p() + + # cross multiply to eliminate divisions + xn1 = x1 * z2 % p + xn2 = x2 * z1 % p + yn1 = y1 * z2 % p + yn2 = y2 * z1 % p + return xn1 == xn2 and yn1 == yn2 + + def __ne__(self, other): + """Compare for inequality two points with each-other.""" + return not self == other + + def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a): + """add two points, assume sane parameters.""" + # after add-2008-hwcd-2 + # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html + # NOTE: there are more efficient formulas for Z1 or Z2 == 1 + A = X1 * X2 % p + B = Y1 * Y2 % p + C = Z1 * T2 % p + D = T1 * Z2 % p + E = D + C + F = ((X1 - Y1) * (X2 + Y2) + B - A) % p + G = B + a * A + H = D - C + if not H: + return self._double(X1, Y1, Z1, T1, p, a) + X3 = E * F % p + Y3 = G * H % p + T3 = E * H % p + Z3 = F * G % p + + return X3, Y3, Z3, T3 + + def __add__(self, other): + """Add point to another.""" + if other == INFINITY: + return self + if ( + not isinstance(other, PointEdwards) + or self.__curve != other.__curve + ): + raise ValueError("The other point is on a different curve.") + + p, a = self.__curve.p(), self.__curve.a() + X1, Y1, Z1, T1 = self.__coords + X2, Y2, Z2, T2 = other.__coords + + X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a) + + if not X3 or not T3: + return INFINITY + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __radd__(self, other): + """Add other to self.""" + return self + other + + def _double(self, X1, Y1, Z1, T1, p, a): + """Double the point, assume sane parameters.""" + # after "dbl-2008-hwcd" + # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html + # NOTE: there are more efficient formulas for Z1 == 1 + A = X1 * X1 % p + B = Y1 * Y1 % p + C = 2 * Z1 * Z1 % p + D = a * A % p + E = ((X1 + Y1) * (X1 + Y1) - A - B) % p + G = D + B + F = G - C + H = D - B + X3 = E * F % p + Y3 = G * H % p + T3 = E * H % p + Z3 = F * G % p + + return X3, Y3, Z3, T3 + + def double(self): + """Return point added to itself.""" + X1, Y1, Z1, T1 = self.__coords + + if not X1 or not T1: + return INFINITY + + p, a = self.__curve.p(), self.__curve.a() + + X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a) + + if not X3 or not T3: + return INFINITY + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __rmul__(self, other): + """Multiply point by an integer.""" + return self * other + + def _mul_precompute(self, other): + """Multiply point by integer with precomputation table.""" + X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a() + _add = self._add + for X2, Y2, T2 in self.__precompute: + rem = other % 4 + if rem == 0 or rem == 2: + other //= 2 + elif rem == 3: + other = (other + 1) // 2 + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a) + else: + assert rem == 1 + other = (other - 1) // 2 + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a) + + if not X3 or not T3: + return INFINITY + + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + def __mul__(self, other): + """Multiply point by an integer.""" + X2, Y2, Z2, T2 = self.__coords + if not X2 or not T2 or not other: + return INFINITY + if other == 1: + return self + if self.__order: + # order*2 as a "protection" for Minerva + other = other % (self.__order * 2) + if self._maybe_precompute(): + return self._mul_precompute(other) + + X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates + p, a = self.__curve.p(), self.__curve.a() + _double = self._double + _add = self._add + + for i in reversed(self._naf(other)): + X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a) + if i < 0: + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a) + elif i > 0: + X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a) + + if not X3 or not T3: + return INFINITY + + return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) + + +# This one point is the Point At Infinity for all purposes: +INFINITY = Point(None, None, None) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py new file mode 100644 index 000000000..0184c05b1 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py @@ -0,0 +1,4 @@ +class MalformedPointError(AssertionError): + """Raised in case the encoding of private or public key is malformed.""" + + pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py new file mode 100644 index 000000000..2b7d3168f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py @@ -0,0 +1,1612 @@ +""" +Primary classes for performing signing and verification operations. +""" + +import binascii +from hashlib import sha1 +import os +from six import PY2, b +from . import ecdsa, eddsa +from . import der +from . import rfc6979 +from . import ellipticcurve +from .curves import NIST192p, Curve, Ed25519, Ed448 +from .ecdsa import RSZeroError +from .util import string_to_number, number_to_string, randrange +from .util import sigencode_string, sigdecode_string, bit_length +from .util import ( + oid_ecPublicKey, + encoded_oid_ecPublicKey, + oid_ecDH, + oid_ecMQV, + MalformedSignature, +) +from ._compat import normalise_bytes +from .errors import MalformedPointError +from .ellipticcurve import PointJacobi, CurveEdTw + + +__all__ = [ + "BadSignatureError", + "BadDigestError", + "VerifyingKey", + "SigningKey", + "MalformedPointError", +] + + +class BadSignatureError(Exception): + """ + Raised when verification of signature failed. + + Will be raised irrespective of reason of the failure: + + * the calculated or provided hash does not match the signature + * the signature does not match the curve/public key + * the encoding of the signature is malformed + * the size of the signature does not match the curve of the VerifyingKey + """ + + pass + + +class BadDigestError(Exception): + """Raised in case the selected hash is too large for the curve.""" + + pass + + +def _truncate_and_convert_digest(digest, curve, allow_truncate): + """Truncates and converts digest to an integer.""" + if not allow_truncate: + if len(digest) > curve.baselen: + raise BadDigestError( + "this curve ({0}) is too short " + "for the length of your digest ({1})".format( + curve.name, 8 * len(digest) + ) + ) + else: + digest = digest[: curve.baselen] + number = string_to_number(digest) + if allow_truncate: + max_length = bit_length(curve.order) + # we don't use bit_length(number) as that truncates leading zeros + length = len(digest) * 8 + + # See NIST FIPS 186-4: + # + # When the length of the output of the hash function is greater + # than N (i.e., the bit length of q), then the leftmost N bits of + # the hash function output block shall be used in any calculation + # using the hash function output during the generation or + # verification of a digital signature. + # + # as such, we need to shift-out the low-order bits: + number >>= max(0, length - max_length) + + return number + + +class VerifyingKey(object): + """ + Class for handling keys that can verify signatures (public keys). + + :ivar `~ecdsa.curves.Curve` ~.curve: The Curve over which all the + cryptographic operations will take place + :ivar default_hashfunc: the function that will be used for hashing the + data. Should implement the same API as hashlib.sha1 + :vartype default_hashfunc: callable + :ivar pubkey: the actual public key + :vartype pubkey: ~ecdsa.ecdsa.Public_key + """ + + def __init__(self, _error__please_use_generate=None): + """Unsupported, please use one of the classmethods to initialise.""" + if not _error__please_use_generate: + raise TypeError( + "Please use VerifyingKey.generate() to construct me" + ) + self.curve = None + self.default_hashfunc = None + self.pubkey = None + + def __repr__(self): + pub_key = self.to_string("compressed") + if self.default_hashfunc: + hash_name = self.default_hashfunc().name + else: + hash_name = "None" + return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( + pub_key, self.curve, hash_name + ) + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, VerifyingKey): + return self.curve == other.curve and self.pubkey == other.pubkey + return NotImplemented + + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + @classmethod + def from_public_point( + cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True + ): + """ + Initialise the object from a Point object. + + This is a low-level method, generally you will not want to use it. + + :param point: The point to wrap around, the actual public key + :type point: ~ecdsa.ellipticcurve.AbstractPoint + :param curve: The curve on which the point needs to reside, defaults + to NIST192p + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface + as :py:class:`hashlib.sha1` + :type hashfunc: callable + :type bool validate_point: whether to check if the point lays on curve + should always be used if the public point is not a result + of our own calculation + + :raises MalformedPointError: if the public point does not lay on the + curve + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + self = cls(_error__please_use_generate=True) + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method incompatible with Edwards curves") + if not isinstance(point, ellipticcurve.PointJacobi): + point = ellipticcurve.PointJacobi.from_affine(point) + self.curve = curve + self.default_hashfunc = hashfunc + try: + self.pubkey = ecdsa.Public_key( + curve.generator, point, validate_point + ) + except ecdsa.InvalidPointError: + raise MalformedPointError("Point does not lay on the curve") + self.pubkey.order = curve.order + return self + + def precompute(self, lazy=False): + """ + Precompute multiplication tables for faster signature verification. + + Calling this method will cause the library to precompute the + scalar multiplication tables, used in signature verification. + While it's an expensive operation (comparable to performing + as many signatures as the bit size of the curve, i.e. 256 for NIST256p) + it speeds up verification 2 times. You should call this method + if you expect to verify hundreds of signatures (or more) using the same + VerifyingKey object. + + Note: You should call this method only once, this method generates a + new precomputation table every time it's called. + + :param bool lazy: whether to calculate the precomputation table now + (if set to False) or if it should be delayed to the time of first + use (when set to True) + """ + if isinstance(self.curve.curve, CurveEdTw): + pt = self.pubkey.point + self.pubkey.point = ellipticcurve.PointEdwards( + pt.curve(), + pt.x(), + pt.y(), + 1, + pt.x() * pt.y(), + self.curve.order, + generator=True, + ) + else: + self.pubkey.point = ellipticcurve.PointJacobi.from_affine( + self.pubkey.point, True + ) + # as precomputation in now delayed to the time of first use of the + # point and we were asked specifically to precompute now, make + # sure the precomputation is performed now to preserve the behaviour + if not lazy: + self.pubkey.point * 2 + + @classmethod + def from_string( + cls, + string, + curve=NIST192p, + hashfunc=sha1, + validate_point=True, + valid_encodings=None, + ): + """ + Initialise the object from byte encoding of public key. + + The method does accept and automatically detect the type of point + encoding used. It supports the :term:`raw encoding`, + :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. + It also works with the native encoding of Ed25519 and Ed448 public + keys (technically those are compressed, but encoded differently than + in other signature systems). + + Note, while the method is named "from_string" it's a misnomer from + Python 2 days when there were no binary strings. In Python 3 the + input needs to be a bytes-like object. + + :param string: single point encoding of the public key + :type string: :term:`bytes-like object` + :param curve: the curve on which the public key is expected to lay + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as + hashlib.sha1. Ignored for EdDSA. + :type hashfunc: callable + :param validate_point: whether to verify that the point lays on the + provided curve or not, defaults to True. Ignored for EdDSA. + :type validate_point: bool + :param valid_encodings: list of acceptable point encoding formats, + supported ones are: :term:`uncompressed`, :term:`compressed`, + :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` + name). All formats by default (specified with ``None``). + Ignored for EdDSA. + :type valid_encodings: :term:`set-like object` + + :raises MalformedPointError: if the public point does not lay on the + curve or the encoding is invalid + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + if isinstance(curve.curve, CurveEdTw): + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None # ignored for EdDSA + try: + self.pubkey = eddsa.PublicKey(curve.generator, string) + except ValueError: + raise MalformedPointError("Malformed point for the curve") + return self + + point = PointJacobi.from_bytes( + curve.curve, + string, + validate_encoding=validate_point, + valid_encodings=valid_encodings, + ) + return cls.from_public_point(point, curve, hashfunc, validate_point) + + @classmethod + def from_pem( + cls, + string, + hashfunc=sha1, + valid_encodings=None, + valid_curve_encodings=None, + ): + """ + Initialise from public key stored in :term:`PEM` format. + + The PEM header of the key should be ``BEGIN PUBLIC KEY``. + + See the :func:`~VerifyingKey.from_der()` method for details of the + format supported. + + Note: only a single PEM object decoding is supported in provided + string. + + :param string: text with PEM-encoded public ECDSA key + :type string: str + :param valid_encodings: list of allowed point encodings. + By default :term:`uncompressed`, :term:`compressed`, and + :term:`hybrid`. To read malformed files, include + :term:`raw encoding` with ``raw`` in the list. + :type valid_encodings: :term:`set-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + return cls.from_der( + der.unpem(string), + hashfunc=hashfunc, + valid_encodings=valid_encodings, + valid_curve_encodings=valid_curve_encodings, + ) + + @classmethod + def from_der( + cls, + string, + hashfunc=sha1, + valid_encodings=None, + valid_curve_encodings=None, + ): + """ + Initialise the key stored in :term:`DER` format. + + The expected format of the key is the SubjectPublicKeyInfo structure + from RFC5912 (for RSA keys, it's known as the PKCS#1 format):: + + SubjectPublicKeyInfo {PUBLIC-KEY: IOSet} ::= SEQUENCE { + algorithm AlgorithmIdentifier {PUBLIC-KEY, {IOSet}}, + subjectPublicKey BIT STRING + } + + Note: only public EC keys are supported by this method. The + SubjectPublicKeyInfo.algorithm.algorithm field must specify + id-ecPublicKey (see RFC3279). + + Only the named curve encoding is supported, thus the + SubjectPublicKeyInfo.algorithm.parameters field needs to be an + object identifier. A sequence in that field indicates an explicit + parameter curve encoding, this format is not supported. A NULL object + in that field indicates an "implicitlyCA" encoding, where the curve + parameters come from CA certificate, those, again, are not supported. + + :param string: binary string with the DER encoding of public ECDSA key + :type string: bytes-like object + :param valid_encodings: list of allowed point encodings. + By default :term:`uncompressed`, :term:`compressed`, and + :term:`hybrid`. To read malformed files, include + :term:`raw encoding` with ``raw`` in the list. + :type valid_encodings: :term:`set-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + if valid_encodings is None: + valid_encodings = set(["uncompressed", "compressed", "hybrid"]) + string = normalise_bytes(string) + # [[oid_ecPublicKey,oid_curve], point_str_bitstring] + s1, empty = der.remove_sequence(string) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER pubkey: %s" % binascii.hexlify(empty) + ) + s2, point_str_bitstring = der.remove_sequence(s1) + # s2 = oid_ecPublicKey,oid_curve + oid_pk, rest = der.remove_object(s2) + if oid_pk in (Ed25519.oid, Ed448.oid): + if oid_pk == Ed25519.oid: + curve = Ed25519 + else: + assert oid_pk == Ed448.oid + curve = Ed448 + point_str, empty = der.remove_bitstring(point_str_bitstring, 0) + if empty: + raise der.UnexpectedDER("trailing junk after public key") + return cls.from_string(point_str, curve, None) + if not oid_pk == oid_ecPublicKey: + raise der.UnexpectedDER( + "Unexpected object identifier in DER " + "encoding: {0!r}".format(oid_pk) + ) + curve = Curve.from_der(rest, valid_curve_encodings) + point_str, empty = der.remove_bitstring(point_str_bitstring, 0) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after pubkey pointstring: %s" + % binascii.hexlify(empty) + ) + # raw encoding of point is invalid in DER files + if len(point_str) == curve.verifying_key_length: + raise der.UnexpectedDER("Malformed encoding of public point") + return cls.from_string( + point_str, + curve, + hashfunc=hashfunc, + valid_encodings=valid_encodings, + ) + + @classmethod + def from_public_key_recovery( + cls, + signature, + data, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + allow_truncate=True, + ): + """ + Return keys that can be used as verifiers of the provided signature. + + Tries to recover the public key that can be used to verify the + signature, usually returns two keys like that. + + :param signature: the byte string with the encoded signature + :type signature: bytes-like object + :param data: the data to be hashed for signature verification + :type data: bytes-like object + :param curve: the curve over which the signature was performed + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as hashlib.sha1 + :type hashfunc: callable + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve, the + extra bits (at the end of the digest) will be truncated. + :type sigdecode: callable + + :return: Initialised VerifyingKey objects + :rtype: list of VerifyingKey + """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + data = normalise_bytes(data) + digest = hashfunc(data).digest() + return cls.from_public_key_recovery_with_digest( + signature, + digest, + curve, + hashfunc=hashfunc, + sigdecode=sigdecode, + allow_truncate=allow_truncate, + ) + + @classmethod + def from_public_key_recovery_with_digest( + cls, + signature, + digest, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + allow_truncate=False, + ): + """ + Return keys that can be used as verifiers of the provided signature. + + Tries to recover the public key that can be used to verify the + signature, usually returns two keys like that. + + :param signature: the byte string with the encoded signature + :type signature: bytes-like object + :param digest: the hash value of the message signed by the signature + :type digest: bytes-like object + :param curve: the curve over which the signature was performed + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as hashlib.sha1 + :type hashfunc: callable + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :type sigdecode: callable + :param bool allow_truncate: if True, the provided hashfunc can generate + values larger than the bit size of the order of the curve (and + the length of provided `digest`), the extra bits (at the end of the + digest) will be truncated. + + :return: Initialised VerifyingKey object + :rtype: VerifyingKey + """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + generator = curve.generator + r, s = sigdecode(signature, generator.order()) + sig = ecdsa.Signature(r, s) + + digest = normalise_bytes(digest) + digest_as_number = _truncate_and_convert_digest( + digest, curve, allow_truncate + ) + pks = sig.recover_public_keys(digest_as_number, generator) + + # Transforms the ecdsa.Public_key object into a VerifyingKey + verifying_keys = [ + cls.from_public_point(pk.point, curve, hashfunc) for pk in pks + ] + return verifying_keys + + def to_string(self, encoding="raw"): + """ + Convert the public key to a byte string. + + The method by default uses the :term:`raw encoding` (specified + by `encoding="raw"`. It can also output keys in :term:`uncompressed`, + :term:`compressed` and :term:`hybrid` formats. + + Remember that the curve identification is not part of the encoding + so to decode the point using :func:`~VerifyingKey.from_string`, curve + needs to be specified. + + Note: while the method is called "to_string", it's a misnomer from + Python 2 days when character strings and byte strings shared type. + On Python 3 the returned type will be `bytes`. + + :return: :term:`raw encoding` of the public key (public point) on the + curve + :rtype: bytes + """ + assert encoding in ("raw", "uncompressed", "compressed", "hybrid") + return self.pubkey.point.to_bytes(encoding) + + def to_pem( + self, point_encoding="uncompressed", curve_parameters_encoding=None + ): + """ + Convert the public key to the :term:`PEM` format. + + The PEM header of the key will be ``BEGIN PUBLIC KEY``. + + The format of the key is described in the + :func:`~VerifyingKey.from_der()` method. + This method supports only "named curve" encoding of keys. + + :param str point_encoding: specification of the encoding format + of public keys. "uncompressed" is most portable, "compressed" is + smallest. "hybrid" is uncommon and unsupported by most + implementations, it is as big as "uncompressed". + :param str curve_parameters_encoding: the encoding for curve parameters + to use, by default tries to use ``named_curve`` encoding, + if that is not possible, falls back to ``explicit`` encoding. + + :return: portable encoding of the public key + :rtype: bytes + + .. warning:: The PEM is encoded to US-ASCII, it needs to be + re-encoded if the system is incompatible (e.g. uses UTF-16) + """ + return der.topem( + self.to_der(point_encoding, curve_parameters_encoding), + "PUBLIC KEY", + ) + + def to_der( + self, point_encoding="uncompressed", curve_parameters_encoding=None + ): + """ + Convert the public key to the :term:`DER` format. + + The format of the key is described in the + :func:`~VerifyingKey.from_der()` method. + This method supports only "named curve" encoding of keys. + + :param str point_encoding: specification of the encoding format + of public keys. "uncompressed" is most portable, "compressed" is + smallest. "hybrid" is uncommon and unsupported by most + implementations, it is as big as "uncompressed". + :param str curve_parameters_encoding: the encoding for curve parameters + to use, by default tries to use ``named_curve`` encoding, + if that is not possible, falls back to ``explicit`` encoding. + + :return: DER encoding of the public key + :rtype: bytes + """ + if point_encoding == "raw": + raise ValueError("raw point_encoding not allowed in DER") + point_str = self.to_string(point_encoding) + if isinstance(self.curve.curve, CurveEdTw): + return der.encode_sequence( + der.encode_sequence(der.encode_oid(*self.curve.oid)), + der.encode_bitstring(bytes(point_str), 0), + ) + return der.encode_sequence( + der.encode_sequence( + encoded_oid_ecPublicKey, + self.curve.to_der(curve_parameters_encoding, point_encoding), + ), + # 0 is the number of unused bits in the + # bit string + der.encode_bitstring(point_str, 0), + ) + + def verify( + self, + signature, + data, + hashfunc=None, + sigdecode=sigdecode_string, + allow_truncate=True, + ): + """ + Verify a signature made over provided data. + + Will hash `data` to verify the signature. + + By default expects signature in :term:`raw encoding`. Can also be used + to verify signatures in ASN.1 DER encoding by using + :func:`ecdsa.util.sigdecode_der` + as the `sigdecode` parameter. + + :param signature: encoding of the signature + :type signature: sigdecode method dependent + :param data: data signed by the `signature`, will be hashed using + `hashfunc`, if specified, or default hash function + :type data: :term:`bytes-like object` + :param hashfunc: The default hash function that will be used for + verification, needs to implement the same interface as hashlib.sha1 + :type hashfunc: callable + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :type sigdecode: callable + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when verifying + SHA-384 output using NIST256p or in similar situations. Defaults to + True. + + :raises BadSignatureError: if the signature is invalid or malformed + + :return: True if the verification was successful + :rtype: bool + """ + # signature doesn't have to be a bytes-like-object so don't normalise + # it, the decoders will do that + data = normalise_bytes(data) + if isinstance(self.curve.curve, CurveEdTw): + signature = normalise_bytes(signature) + try: + return self.pubkey.verify(data, signature) + except (ValueError, MalformedPointError) as e: + raise BadSignatureError("Signature verification failed", e) + + hashfunc = hashfunc or self.default_hashfunc + digest = hashfunc(data).digest() + return self.verify_digest(signature, digest, sigdecode, allow_truncate) + + def verify_digest( + self, + signature, + digest, + sigdecode=sigdecode_string, + allow_truncate=False, + ): + """ + Verify a signature made over provided hash value. + + By default expects signature in :term:`raw encoding`. Can also be used + to verify signatures in ASN.1 DER encoding by using + :func:`ecdsa.util.sigdecode_der` + as the `sigdecode` parameter. + + :param signature: encoding of the signature + :type signature: sigdecode method dependent + :param digest: raw hash value that the signature authenticates. + :type digest: :term:`bytes-like object` + :param sigdecode: Callable to define the way the signature needs to + be decoded to an object, needs to handle `signature` as the + first parameter, the curve order (an int) as the second and return + a tuple with two integers, "r" as the first one and "s" as the + second one. See :func:`ecdsa.util.sigdecode_string` and + :func:`ecdsa.util.sigdecode_der` for examples. + :type sigdecode: callable + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when verifying + SHA-384 output using NIST256p or in similar situations. + + :raises BadSignatureError: if the signature is invalid or malformed + :raises BadDigestError: if the provided digest is too big for the curve + associated with this VerifyingKey and allow_truncate was not set + + :return: True if the verification was successful + :rtype: bool + """ + # signature doesn't have to be a bytes-like-object so don't normalise + # it, the decoders will do that + digest = normalise_bytes(digest) + number = _truncate_and_convert_digest( + digest, + self.curve, + allow_truncate, + ) + + try: + r, s = sigdecode(signature, self.pubkey.order) + except (der.UnexpectedDER, MalformedSignature) as e: + raise BadSignatureError("Malformed formatting of signature", e) + sig = ecdsa.Signature(r, s) + if self.pubkey.verifies(number, sig): + return True + raise BadSignatureError("Signature verification failed") + + +class SigningKey(object): + """ + Class for handling keys that can create signatures (private keys). + + :ivar `~ecdsa.curves.Curve` curve: The Curve over which all the + cryptographic operations will take place + :ivar default_hashfunc: the function that will be used for hashing the + data. Should implement the same API as :py:class:`hashlib.sha1` + :ivar int baselen: the length of a :term:`raw encoding` of private key + :ivar `~ecdsa.keys.VerifyingKey` verifying_key: the public key + associated with this private key + :ivar `~ecdsa.ecdsa.Private_key` privkey: the actual private key + """ + + def __init__(self, _error__please_use_generate=None): + """Unsupported, please use one of the classmethods to initialise.""" + if not _error__please_use_generate: + raise TypeError("Please use SigningKey.generate() to construct me") + self.curve = None + self.default_hashfunc = None + self.baselen = None + self.verifying_key = None + self.privkey = None + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, SigningKey): + return ( + self.curve == other.curve + and self.verifying_key == other.verifying_key + and self.privkey == other.privkey + ) + return NotImplemented + + def __ne__(self, other): + """Return False if the points are identical, True otherwise.""" + return not self == other + + @classmethod + def _twisted_edwards_keygen(cls, curve, entropy): + """Generate a private key on a Twisted Edwards curve.""" + if not entropy: + entropy = os.urandom + random = entropy(curve.baselen) + private_key = eddsa.PrivateKey(curve.generator, random) + public_key = private_key.public_key() + + verifying_key = VerifyingKey.from_string( + public_key.public_key(), curve + ) + + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None + self.baselen = curve.baselen + self.privkey = private_key + self.verifying_key = verifying_key + return self + + @classmethod + def _weierstrass_keygen(cls, curve, entropy, hashfunc): + """Generate a private key on a Weierstrass curve.""" + secexp = randrange(curve.order, entropy) + return cls.from_secret_exponent(secexp, curve, hashfunc) + + @classmethod + def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): + """ + Generate a random private key. + + :param curve: The curve on which the point needs to reside, defaults + to NIST192p + :type curve: ~ecdsa.curves.Curve + :param entropy: Source of randomness for generating the private keys, + should provide cryptographically secure random numbers if the keys + need to be secure. Uses os.urandom() by default. + :type entropy: callable + :param hashfunc: The default hash function that will be used for + signing, needs to implement the same interface + as hashlib.sha1 + :type hashfunc: callable + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + if isinstance(curve.curve, CurveEdTw): + return cls._twisted_edwards_keygen(curve, entropy) + return cls._weierstrass_keygen(curve, entropy, hashfunc) + + @classmethod + def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1): + """ + Create a private key from a random integer. + + Note: it's a low level method, it's recommended to use the + :func:`~SigningKey.generate` method to create private keys. + + :param int secexp: secret multiplier (the actual private key in ECDSA). + Needs to be an integer between 1 and the curve order. + :param curve: The curve on which the point needs to reside + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + signing, needs to implement the same interface + as hashlib.sha1 + :type hashfunc: callable + + :raises MalformedPointError: when the provided secexp is too large + or too small for the curve selected + :raises RuntimeError: if the generation of public key from private + key failed + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + if isinstance(curve.curve, CurveEdTw): + raise ValueError( + "Edwards keys don't support setting the secret scalar " + "(exponent) directly" + ) + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = hashfunc + self.baselen = curve.baselen + n = curve.order + if not 1 <= secexp < n: + raise MalformedPointError( + "Invalid value for secexp, expected integer " + "between 1 and {0}".format(n) + ) + pubkey_point = curve.generator * secexp + if hasattr(pubkey_point, "scale"): + pubkey_point = pubkey_point.scale() + self.verifying_key = VerifyingKey.from_public_point( + pubkey_point, curve, hashfunc, False + ) + pubkey = self.verifying_key.pubkey + self.privkey = ecdsa.Private_key(pubkey, secexp) + self.privkey.order = n + return self + + @classmethod + def from_string(cls, string, curve=NIST192p, hashfunc=sha1): + """ + Decode the private key from :term:`raw encoding`. + + Note: the name of this method is a misnomer coming from days of + Python 2, when binary strings and character strings shared a type. + In Python 3, the expected type is `bytes`. + + :param string: the raw encoding of the private key + :type string: :term:`bytes-like object` + :param curve: The curve on which the point needs to reside + :type curve: ~ecdsa.curves.Curve + :param hashfunc: The default hash function that will be used for + signing, needs to implement the same interface + as hashlib.sha1 + :type hashfunc: callable + + :raises MalformedPointError: if the length of encoding doesn't match + the provided curve or the encoded values is too large + :raises RuntimeError: if the generation of public key from private + key failed + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + string = normalise_bytes(string) + + if len(string) != curve.baselen: + raise MalformedPointError( + "Invalid length of private key, received {0}, " + "expected {1}".format(len(string), curve.baselen) + ) + if isinstance(curve.curve, CurveEdTw): + self = cls(_error__please_use_generate=True) + self.curve = curve + self.default_hashfunc = None # Ignored for EdDSA + self.baselen = curve.baselen + self.privkey = eddsa.PrivateKey(curve.generator, string) + self.verifying_key = VerifyingKey.from_string( + self.privkey.public_key().public_key(), curve + ) + return self + secexp = string_to_number(string) + return cls.from_secret_exponent(secexp, curve, hashfunc) + + @classmethod + def from_pem(cls, string, hashfunc=sha1, valid_curve_encodings=None): + """ + Initialise from key stored in :term:`PEM` format. + + The PEM formats supported are the un-encrypted RFC5915 + (the ssleay format) supported by OpenSSL, and the more common + un-encrypted RFC5958 (the PKCS #8 format). + + The legacy format files have the header with the string + ``BEGIN EC PRIVATE KEY``. + PKCS#8 files have the header ``BEGIN PRIVATE KEY``. + Encrypted files (ones that include the string + ``Proc-Type: 4,ENCRYPTED`` + right after the PEM header) are not supported. + + See :func:`~SigningKey.from_der` for ASN.1 syntax of the objects in + this files. + + :param string: text with PEM-encoded private ECDSA key + :type string: str + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + :type valid_curve_encodings: :term:`set-like object` + + + :raises MalformedPointError: if the length of encoding doesn't match + the provided curve or the encoded values is too large + :raises RuntimeError: if the generation of public key from private + key failed + :raises UnexpectedDER: if the encoding of the PEM file is incorrect + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + if not PY2 and isinstance(string, str): # pragma: no branch + string = string.encode() + + # The privkey pem may have multiple sections, commonly it also has + # "EC PARAMETERS", we need just "EC PRIVATE KEY". PKCS#8 should not + # have the "EC PARAMETERS" section; it's just "PRIVATE KEY". + private_key_index = string.find(b"-----BEGIN EC PRIVATE KEY-----") + if private_key_index == -1: + private_key_index = string.index(b"-----BEGIN PRIVATE KEY-----") + + return cls.from_der( + der.unpem(string[private_key_index:]), + hashfunc, + valid_curve_encodings, + ) + + @classmethod + def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None): + """ + Initialise from key stored in :term:`DER` format. + + The DER formats supported are the un-encrypted RFC5915 + (the ssleay format) supported by OpenSSL, and the more common + un-encrypted RFC5958 (the PKCS #8 format). + + Both formats contain an ASN.1 object following the syntax specified + in RFC5915:: + + ECPrivateKey ::= SEQUENCE { + version INTEGER { ecPrivkeyVer1(1) }} (ecPrivkeyVer1), + privateKey OCTET STRING, + parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + publicKey [1] BIT STRING OPTIONAL + } + + `publicKey` field is ignored completely (errors, if any, in it will + be undetected). + + Two formats are supported for the `parameters` field: the named + curve and the explicit encoding of curve parameters. + In the legacy ssleay format, this implementation requires the optional + `parameters` field to get the curve name. In PKCS #8 format, the curve + is part of the PrivateKeyAlgorithmIdentifier. + + The PKCS #8 format includes an ECPrivateKey object as the `privateKey` + field within a larger structure:: + + OneAsymmetricKey ::= SEQUENCE { + version Version, + privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + privateKey PrivateKey, + attributes [0] Attributes OPTIONAL, + ..., + [[2: publicKey [1] PublicKey OPTIONAL ]], + ... + } + + The `attributes` and `publicKey` fields are completely ignored; errors + in them will not be detected. + + :param string: binary string with DER-encoded private ECDSA key + :type string: :term:`bytes-like object` + :param valid_curve_encodings: list of allowed encoding formats + for curve parameters. By default (``None``) all are supported: + ``named_curve`` and ``explicit``. + Ignored for EdDSA. + :type valid_curve_encodings: :term:`set-like object` + + :raises MalformedPointError: if the length of encoding doesn't match + the provided curve or the encoded values is too large + :raises RuntimeError: if the generation of public key from private + key failed + :raises UnexpectedDER: if the encoding of the DER file is incorrect + + :return: Initialised SigningKey object + :rtype: SigningKey + """ + s = normalise_bytes(string) + curve = None + + s, empty = der.remove_sequence(s) + if empty != b(""): + raise der.UnexpectedDER( + "trailing junk after DER privkey: %s" % binascii.hexlify(empty) + ) + + version, s = der.remove_integer(s) + + # At this point, PKCS #8 has a sequence containing the algorithm + # identifier and the curve identifier. The ssleay format instead has + # an octet string containing the key data, so this is how we can + # distinguish the two formats. + if der.is_sequence(s): + if version not in (0, 1): + raise der.UnexpectedDER( + "expected version '0' or '1' at start of privkey, got %d" + % version + ) + + sequence, s = der.remove_sequence(s) + algorithm_oid, algorithm_identifier = der.remove_object(sequence) + + if algorithm_oid in (Ed25519.oid, Ed448.oid): + if algorithm_identifier: + raise der.UnexpectedDER( + "Non NULL parameters for a EdDSA key" + ) + key_str_der, s = der.remove_octet_string(s) + + # As RFC5958 describe, there are may be optional Attributes + # and Publickey. Don't raise error if something after + # Privatekey + + # TODO parse attributes or validate publickey + # if s: + # raise der.UnexpectedDER( + # "trailing junk inside the privateKey" + # ) + key_str, s = der.remove_octet_string(key_str_der) + if s: + raise der.UnexpectedDER( + "trailing junk after the encoded private key" + ) + + if algorithm_oid == Ed25519.oid: + curve = Ed25519 + else: + assert algorithm_oid == Ed448.oid + curve = Ed448 + + return cls.from_string(key_str, curve, None) + + if algorithm_oid not in (oid_ecPublicKey, oid_ecDH, oid_ecMQV): + raise der.UnexpectedDER( + "unexpected algorithm identifier '%s'" % (algorithm_oid,) + ) + + curve = Curve.from_der(algorithm_identifier, valid_curve_encodings) + + if empty != b"": + raise der.UnexpectedDER( + "unexpected data after algorithm identifier: %s" + % binascii.hexlify(empty) + ) + + # Up next is an octet string containing an ECPrivateKey. Ignore + # the optional "attributes" and "publicKey" fields that come after. + s, _ = der.remove_octet_string(s) + + # Unpack the ECPrivateKey to get to the key data octet string, + # and rejoin the ssleay parsing path. + s, empty = der.remove_sequence(s) + if empty != b(""): + raise der.UnexpectedDER( + "trailing junk after DER privkey: %s" + % binascii.hexlify(empty) + ) + + version, s = der.remove_integer(s) + + # The version of the ECPrivateKey must be 1. + if version != 1: + raise der.UnexpectedDER( + "expected version '1' at start of DER privkey, got %d" + % version + ) + + privkey_str, s = der.remove_octet_string(s) + + if not curve: + tag, curve_oid_str, s = der.remove_constructed(s) + if tag != 0: + raise der.UnexpectedDER( + "expected tag 0 in DER privkey, got %d" % tag + ) + curve = Curve.from_der(curve_oid_str, valid_curve_encodings) + + # we don't actually care about the following fields + # + # tag, pubkey_bitstring, s = der.remove_constructed(s) + # if tag != 1: + # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" + # % tag) + # pubkey_str = der.remove_bitstring(pubkey_bitstring, 0) + # if empty != "": + # raise der.UnexpectedDER("trailing junk after DER privkey " + # "pubkeystr: %s" + # % binascii.hexlify(empty)) + + # our from_string method likes fixed-length privkey strings + if len(privkey_str) < curve.baselen: + privkey_str = ( + b("\x00") * (curve.baselen - len(privkey_str)) + privkey_str + ) + return cls.from_string(privkey_str, curve, hashfunc) + + def to_string(self): + """ + Convert the private key to :term:`raw encoding`. + + Note: while the method is named "to_string", its name comes from + Python 2 days, when binary and character strings used the same type. + The type used in Python 3 is `bytes`. + + :return: raw encoding of private key + :rtype: bytes + """ + if isinstance(self.curve.curve, CurveEdTw): + return bytes(self.privkey.private_key) + secexp = self.privkey.secret_multiplier + s = number_to_string(secexp, self.privkey.order) + return s + + def to_pem( + self, + point_encoding="uncompressed", + format="ssleay", + curve_parameters_encoding=None, + ): + """ + Convert the private key to the :term:`PEM` format. + + See :func:`~SigningKey.from_pem` method for format description. + + Only the named curve format is supported. + The public key will be included in generated string. + + The PEM header will specify ``BEGIN EC PRIVATE KEY`` or + ``BEGIN PRIVATE KEY``, depending on the desired format. + + :param str point_encoding: format to use for encoding public point + :param str format: either ``ssleay`` (default) or ``pkcs8`` + :param str curve_parameters_encoding: format of encoded curve + parameters, default depends on the curve, if the curve has + an associated OID, ``named_curve`` format will be used, + if no OID is associated with the curve, the fallback of + ``explicit`` parameters will be used. + + :return: PEM encoded private key + :rtype: bytes + + .. warning:: The PEM is encoded to US-ASCII, it needs to be + re-encoded if the system is incompatible (e.g. uses UTF-16) + """ + # TODO: "BEGIN ECPARAMETERS" + assert format in ("ssleay", "pkcs8") + header = "EC PRIVATE KEY" if format == "ssleay" else "PRIVATE KEY" + return der.topem( + self.to_der(point_encoding, format, curve_parameters_encoding), + header, + ) + + def _encode_eddsa(self): + """Create a PKCS#8 encoding of EdDSA keys.""" + ec_private_key = der.encode_octet_string(self.to_string()) + return der.encode_sequence( + der.encode_integer(0), + der.encode_sequence(der.encode_oid(*self.curve.oid)), + der.encode_octet_string(ec_private_key), + ) + + def to_der( + self, + point_encoding="uncompressed", + format="ssleay", + curve_parameters_encoding=None, + ): + """ + Convert the private key to the :term:`DER` format. + + See :func:`~SigningKey.from_der` method for format specification. + + Only the named curve format is supported. + The public key will be included in the generated string. + + :param str point_encoding: format to use for encoding public point + Ignored for EdDSA + :param str format: either ``ssleay`` (default) or ``pkcs8``. + EdDSA keys require ``pkcs8``. + :param str curve_parameters_encoding: format of encoded curve + parameters, default depends on the curve, if the curve has + an associated OID, ``named_curve`` format will be used, + if no OID is associated with the curve, the fallback of + ``explicit`` parameters will be used. + Ignored for EdDSA. + + :return: DER encoded private key + :rtype: bytes + """ + # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), + # cont[1],bitstring]) + if point_encoding == "raw": + raise ValueError("raw encoding not allowed in DER") + assert format in ("ssleay", "pkcs8") + if isinstance(self.curve.curve, CurveEdTw): + if format != "pkcs8": + raise ValueError("Only PKCS#8 format supported for EdDSA keys") + return self._encode_eddsa() + encoded_vk = self.get_verifying_key().to_string(point_encoding) + priv_key_elems = [ + der.encode_integer(1), + der.encode_octet_string(self.to_string()), + ] + if format == "ssleay": + priv_key_elems.append( + der.encode_constructed( + 0, self.curve.to_der(curve_parameters_encoding) + ) + ) + # the 0 in encode_bitstring specifies the number of unused bits + # in the `encoded_vk` string + priv_key_elems.append( + der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)) + ) + ec_private_key = der.encode_sequence(*priv_key_elems) + + if format == "ssleay": + return ec_private_key + else: + return der.encode_sequence( + # version = 1 means the public key is not present in the + # top-level structure. + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(*oid_ecPublicKey), + self.curve.to_der(curve_parameters_encoding), + ), + der.encode_octet_string(ec_private_key), + ) + + def get_verifying_key(self): + """ + Return the VerifyingKey associated with this private key. + + Equivalent to reading the `verifying_key` field of an instance. + + :return: a public key that can be used to verify the signatures made + with this SigningKey + :rtype: VerifyingKey + """ + return self.verifying_key + + def sign_deterministic( + self, + data, + hashfunc=None, + sigencode=sigencode_string, + extra_entropy=b"", + ): + """ + Create signature over data. + + For Weierstrass curves it uses the deterministic RFC6979 algorithm. + For Edwards curves it uses the standard EdDSA algorithm. + + For ECDSA the data will be hashed using the `hashfunc` function before + signing. + For EdDSA the data will be hashed with the hash associated with the + curve (SHA-512 for Ed25519 and SHAKE-256 for Ed448). + + This is the recommended method for performing signatures when hashing + of data is necessary. + + :param data: data to be hashed and computed signature over + :type data: :term:`bytes-like object` + :param hashfunc: hash function to use for computing the signature, + if unspecified, the default hash function selected during + object initialisation will be used (see + `VerifyingKey.default_hashfunc`). The object needs to implement + the same interface as hashlib.sha1. + Ignored with EdDSA. + :type hashfunc: callable + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` + as examples of such functions. + Ignored with EdDSA. + :type sigencode: callable + :param extra_entropy: additional data that will be fed into the random + number generator used in the RFC6979 process. Entirely optional. + Ignored with EdDSA. + :type extra_entropy: :term:`bytes-like object` + + :return: encoded signature over `data` + :rtype: bytes or sigencode function dependent type + """ + hashfunc = hashfunc or self.default_hashfunc + data = normalise_bytes(data) + + if isinstance(self.curve.curve, CurveEdTw): + return self.privkey.sign(data) + + extra_entropy = normalise_bytes(extra_entropy) + digest = hashfunc(data).digest() + + return self.sign_digest_deterministic( + digest, + hashfunc=hashfunc, + sigencode=sigencode, + extra_entropy=extra_entropy, + allow_truncate=True, + ) + + def sign_digest_deterministic( + self, + digest, + hashfunc=None, + sigencode=sigencode_string, + extra_entropy=b"", + allow_truncate=False, + ): + """ + Create signature for digest using the deterministic RFC6979 algorithm. + + `digest` should be the output of cryptographically secure hash function + like SHA256 or SHA-3-256. + + This is the recommended method for performing signatures when no + hashing of data is necessary. + + :param digest: hash of data that will be signed + :type digest: :term:`bytes-like object` + :param hashfunc: hash function to use for computing the random "k" + value from RFC6979 process, + if unspecified, the default hash function selected during + object initialisation will be used (see + :attr:`.VerifyingKey.default_hashfunc`). The object needs to + implement + the same interface as :func:`~hashlib.sha1` from :py:mod:`hashlib`. + :type hashfunc: callable + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See :func:`~ecdsa.util.sigencode_string` and + :func:`~ecdsa.util.sigencode_der` + as examples of such functions. + :type sigencode: callable + :param extra_entropy: additional data that will be fed into the random + number generator used in the RFC6979 process. Entirely optional. + :type extra_entropy: :term:`bytes-like object` + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. + + :return: encoded signature for the `digest` hash + :rtype: bytes or sigencode function dependent type + """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + secexp = self.privkey.secret_multiplier + hashfunc = hashfunc or self.default_hashfunc + digest = normalise_bytes(digest) + extra_entropy = normalise_bytes(extra_entropy) + + def simple_r_s(r, s, order): + return r, s, order + + retry_gen = 0 + while True: + k = rfc6979.generate_k( + self.curve.generator.order(), + secexp, + hashfunc, + digest, + retry_gen=retry_gen, + extra_entropy=extra_entropy, + ) + try: + r, s, order = self.sign_digest( + digest, + sigencode=simple_r_s, + k=k, + allow_truncate=allow_truncate, + ) + break + except RSZeroError: + retry_gen += 1 + + return sigencode(r, s, order) + + def sign( + self, + data, + entropy=None, + hashfunc=None, + sigencode=sigencode_string, + k=None, + allow_truncate=True, + ): + """ + Create signature over data. + + Uses the probabilistic ECDSA algorithm for Weierstrass curves + (NIST256p, etc.) and the deterministic EdDSA algorithm for the + Edwards curves (Ed25519, Ed448). + + This method uses the standard ECDSA algorithm that requires a + cryptographically secure random number generator. + + It's recommended to use the :func:`~SigningKey.sign_deterministic` + method instead of this one. + + :param data: data that will be hashed for signing + :type data: :term:`bytes-like object` + :param callable entropy: randomness source, :func:`os.urandom` by + default. Ignored with EdDSA. + :param hashfunc: hash function to use for hashing the provided + ``data``. + If unspecified the default hash function selected during + object initialisation will be used (see + :attr:`.VerifyingKey.default_hashfunc`). + Should behave like :func:`~hashlib.sha1` from :py:mod:`hashlib`. + The output length of the + hash (in bytes) must not be longer than the length of the curve + order (rounded up to the nearest byte), so using SHA256 with + NIST256p is ok, but SHA256 with NIST192p is not. (In the 2**-96ish + unlikely event of a hash output larger than the curve order, the + hash will effectively be wrapped mod n). + If you want to explicitly allow use of large hashes with small + curves set the ``allow_truncate`` to ``True``. + Use ``hashfunc=hashlib.sha1`` to match openssl's + ``-ecdsa-with-SHA1`` mode, + or ``hashfunc=hashlib.sha256`` for openssl-1.0.0's + ``-ecdsa-with-SHA256``. + Ignored for EdDSA + :type hashfunc: callable + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See :func:`~ecdsa.util.sigencode_string` and + :func:`~ecdsa.util.sigencode_der` + as examples of such functions. + Ignored for EdDSA + :type sigencode: callable + :param int k: a pre-selected nonce for calculating the signature. + In typical use cases, it should be set to None (the default) to + allow its generation from an entropy source. + Ignored for EdDSA. + :param bool allow_truncate: if ``True``, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. True by + default. + Ignored for EdDSA. + + :raises RSZeroError: in the unlikely event when *r* parameter or + *s* parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different ``k``, or use the + :func:`~SigningKey.sign_deterministic` in such case. + + :return: encoded signature of the hash of `data` + :rtype: bytes or sigencode function dependent type + """ + hashfunc = hashfunc or self.default_hashfunc + data = normalise_bytes(data) + if isinstance(self.curve.curve, CurveEdTw): + return self.sign_deterministic(data) + h = hashfunc(data).digest() + return self.sign_digest(h, entropy, sigencode, k, allow_truncate) + + def sign_digest( + self, + digest, + entropy=None, + sigencode=sigencode_string, + k=None, + allow_truncate=False, + ): + """ + Create signature over digest using the probabilistic ECDSA algorithm. + + This method uses the standard ECDSA algorithm that requires a + cryptographically secure random number generator. + + This method does not hash the input. + + It's recommended to use the + :func:`~SigningKey.sign_digest_deterministic` method + instead of this one. + + :param digest: hash value that will be signed + :type digest: :term:`bytes-like object` + :param callable entropy: randomness source, os.urandom by default + :param sigencode: function used to encode the signature. + The function needs to accept three parameters: the two integers + that are the signature and the order of the curve over which the + signature was computed. It needs to return an encoded signature. + See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` + as examples of such functions. + :type sigencode: callable + :param int k: a pre-selected nonce for calculating the signature. + In typical use cases, it should be set to None (the default) to + allow its generation from an entropy source. + :param bool allow_truncate: if True, the provided digest can have + bigger bit-size than the order of the curve, the extra bits (at + the end of the digest) will be truncated. Use it when signing + SHA-384 output using NIST256p or in similar situations. + + :raises RSZeroError: in the unlikely event when "r" parameter or + "s" parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different 'k', or use the + :func:`~SigningKey.sign_digest_deterministic` in such case. + + :return: encoded signature for the `digest` hash + :rtype: bytes or sigencode function dependent type + """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + digest = normalise_bytes(digest) + number = _truncate_and_convert_digest( + digest, + self.curve, + allow_truncate, + ) + r, s = self.sign_number(number, entropy, k) + return sigencode(r, s, self.privkey.order) + + def sign_number(self, number, entropy=None, k=None): + """ + Sign an integer directly. + + Note, this is a low level method, usually you will want to use + :func:`~SigningKey.sign_deterministic` or + :func:`~SigningKey.sign_digest_deterministic`. + + :param int number: number to sign using the probabilistic ECDSA + algorithm. + :param callable entropy: entropy source, os.urandom by default + :param int k: pre-selected nonce for signature operation. If unset + it will be selected at random using the entropy source. + + :raises RSZeroError: in the unlikely event when "r" parameter or + "s" parameter of the created signature is equal 0, as that would + leak the key. Caller should try a better entropy source, retry with + different 'k', or use the + :func:`~SigningKey.sign_digest_deterministic` in such case. + + :return: the "r" and "s" parameters of the signature + :rtype: tuple of ints + """ + if isinstance(self.curve.curve, CurveEdTw): + raise ValueError("Method unsupported for Edwards curves") + order = self.privkey.order + + if k is not None: + _k = k + else: + _k = randrange(order, entropy) + + assert 1 <= _k < order + sig = self.privkey.sign(number, _k) + return sig.r, sig.s diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py new file mode 100644 index 000000000..d3500c709 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py @@ -0,0 +1,825 @@ +#! /usr/bin/env python +# +# Provide some simple capabilities from number theory. +# +# Version of 2008.11.14. +# +# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. +# Revision history: +# 2008.11.14: Use pow(base, exponent, modulus) for modular_exp. +# Make gcd and lcm accept arbitrarily many arguments. + +from __future__ import division + +import sys +from six import integer_types, PY2 +from six.moves import reduce + +try: + xrange +except NameError: + xrange = range +try: + from gmpy2 import powmod + + GMPY2 = True + GMPY = False +except ImportError: + GMPY2 = False + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + +import math +import warnings + + +class Error(Exception): + """Base class for exceptions in this module.""" + + pass + + +class JacobiError(Error): + pass + + +class SquareRootError(Error): + pass + + +class NegativeExponentError(Error): + pass + + +def modular_exp(base, exponent, modulus): # pragma: no cover + """Raise base to exponent, reducing by modulus""" + # deprecated in 0.14 + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to pow() builtin.", + DeprecationWarning, + ) + if exponent < 0: + raise NegativeExponentError( + "Negative exponents (%d) not allowed" % exponent + ) + return pow(base, exponent, modulus) + + +def polynomial_reduce_mod(poly, polymod, p): + """Reduce poly by polymod, integer arithmetic modulo p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Just to make this easy, require a monic polynomial: + assert polymod[-1] == 1 + + assert len(polymod) > 1 + + while len(poly) >= len(polymod): + if poly[-1] != 0: + for i in xrange(2, len(polymod) + 1): + poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p + poly = poly[0:-1] + + return poly + + +def polynomial_multiply_mod(m1, m2, polymod, p): + """Polynomial multiplication modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # This is just a seat-of-the-pants implementation. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + # Initialize the product to zero: + + prod = (len(m1) + len(m2) - 1) * [0] + + # Add together all the cross-terms: + + for i in xrange(len(m1)): + for j in xrange(len(m2)): + prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p + + return polynomial_reduce_mod(prod, polymod, p) + + +def polynomial_exp_mod(base, exponent, polymod, p): + """Polynomial exponentiation modulo a polynomial over ints mod p. + + Polynomials are represented as lists of coefficients + of increasing powers of x.""" + + # Based on the Handbook of Applied Cryptography, algorithm 2.227. + + # This module has been tested only by extensive use + # in calculating modular square roots. + + assert exponent < p + + if exponent == 0: + return [1] + + G = base + k = exponent + if k % 2 == 1: + s = G + else: + s = [1] + + while k > 1: + k = k // 2 + G = polynomial_multiply_mod(G, G, polymod, p) + if k % 2 == 1: + s = polynomial_multiply_mod(G, s, polymod, p) + + return s + + +def jacobi(a, n): + """Jacobi symbol""" + + # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. + + # This function has been tested by comparison with a small + # table printed in HAC, and by extensive use in calculating + # modular square roots. + + if not n >= 3: + raise JacobiError("n must be larger than 2") + if not n % 2 == 1: + raise JacobiError("n must be odd") + a = a % n + if a == 0: + return 0 + if a == 1: + return 1 + a1, e = a, 0 + while a1 % 2 == 0: + a1, e = a1 // 2, e + 1 + if e % 2 == 0 or n % 8 == 1 or n % 8 == 7: + s = 1 + else: + s = -1 + if a1 == 1: + return s + if n % 4 == 3 and a1 % 4 == 3: + s = -s + return s * jacobi(n % a1, a1) + + +def square_root_mod_prime(a, p): + """Modular square root of a, mod p, p prime.""" + + # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. + + # This module has been tested for all values in [0,p-1] for + # every prime p from 3 to 1229. + + assert 0 <= a < p + assert 1 < p + + if a == 0: + return 0 + if p == 2: + return a + + jac = jacobi(a, p) + if jac == -1: + raise SquareRootError("%d has no square root modulo %d" % (a, p)) + + if p % 4 == 3: + return pow(a, (p + 1) // 4, p) + + if p % 8 == 5: + d = pow(a, (p - 1) // 4, p) + if d == 1: + return pow(a, (p + 3) // 8, p) + assert d == p - 1 + return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p + + if PY2: + # xrange on python2 can take integers representable as C long only + range_top = min(0x7FFFFFFF, p) + else: + range_top = p + for b in xrange(2, range_top): + if jacobi(b * b - 4 * a, p) == -1: + f = (a, -b, 1) + ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) + if ff[1]: + raise SquareRootError("p is not prime") + return ff[0] + raise RuntimeError("No b found.") + + +# because all the inverse_mod code is arch/environment specific, and coveralls +# expects it to execute equal number of times, we need to waive it by +# adding the "no branch" pragma to all branches +if GMPY2: # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + if a == 0: # pragma: no branch + return 0 + return powmod(a, -1, m) + +elif GMPY: # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + # while libgmp does support inverses modulo, it is accessible + # only using the native `pow()` function, and `pow()` in gmpy sanity + # checks the parameters before passing them on to underlying + # implementation + if a == 0: # pragma: no branch + return 0 + a = mpz(a) + m = mpz(m) + + lm, hm = mpz(1), mpz(0) + low, high = a % m, m + while low > 1: # pragma: no branch + r = high // low + lm, low, hm, high = hm - lm * r, high - low * r, lm, low + + return lm % m + +elif sys.version_info >= (3, 8): # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + if a == 0: # pragma: no branch + return 0 + return pow(a, -1, m) + +else: # pragma: no branch + + def inverse_mod(a, m): + """Inverse of a mod m.""" + + if a == 0: # pragma: no branch + return 0 + + lm, hm = 1, 0 + low, high = a % m, m + while low > 1: # pragma: no branch + r = high // low + lm, low, hm, high = hm - lm * r, high - low * r, lm, low + + return lm % m + + +try: + gcd2 = math.gcd +except AttributeError: + + def gcd2(a, b): + """Greatest common divisor using Euclid's algorithm.""" + while a: + a, b = b % a, a + return b + + +def gcd(*a): + """Greatest common divisor. + + Usage: gcd([ 2, 4, 6 ]) + or: gcd(2, 4, 6) + """ + + if len(a) > 1: + return reduce(gcd2, a) + if hasattr(a[0], "__iter__"): + return reduce(gcd2, a[0]) + return a[0] + + +def lcm2(a, b): + """Least common multiple of two integers.""" + + return (a * b) // gcd(a, b) + + +def lcm(*a): + """Least common multiple. + + Usage: lcm([ 3, 4, 5 ]) + or: lcm(3, 4, 5) + """ + + if len(a) > 1: + return reduce(lcm2, a) + if hasattr(a[0], "__iter__"): + return reduce(lcm2, a[0]) + return a[0] + + +def factorization(n): + """Decompose n into a list of (prime,exponent) pairs.""" + + assert isinstance(n, integer_types) + + if n < 2: + return [] + + result = [] + + # Test the small primes: + + for d in smallprimes: + if d > n: + break + q, r = divmod(n, d) + if r == 0: + count = 1 + while d <= n: + n = q + q, r = divmod(n, d) + if r != 0: + break + count = count + 1 + result.append((d, count)) + + # If n is still greater than the last of our small primes, + # it may require further work: + + if n > smallprimes[-1]: + if is_prime(n): # If what's left is prime, it's easy: + result.append((n, 1)) + else: # Ugh. Search stupidly for a divisor: + d = smallprimes[-1] + while 1: + d = d + 2 # Try the next divisor. + q, r = divmod(n, d) + if q < d: # n < d*d means we're done, n = 1 or prime. + break + if r == 0: # d divides n. How many times? + count = 1 + n = q + while d <= n: # As long as d might still divide n, + q, r = divmod(n, d) # see if it does. + if r != 0: + break + n = q # It does. Reduce n, increase count. + count = count + 1 + result.append((d, count)) + if n > 1: + result.append((n, 1)) + + return result + + +def phi(n): # pragma: no cover + """Return the Euler totient function of n.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + assert isinstance(n, integer_types) + + if n < 3: + return 1 + + result = 1 + ff = factorization(n) + for f in ff: + e = f[1] + if e > 1: + result = result * f[0] ** (e - 1) * (f[0] - 1) + else: + result = result * (f[0] - 1) + return result + + +def carmichael(n): # pragma: no cover + """Return Carmichael function of n. + + Carmichael(n) is the smallest integer x such that + m**x = 1 mod n for all m relatively prime to n. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + return carmichael_of_factorized(factorization(n)) + + +def carmichael_of_factorized(f_list): # pragma: no cover + """Return the Carmichael function of a number that is + represented as a list of (prime,exponent) pairs. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + if len(f_list) < 1: + return 1 + + result = carmichael_of_ppower(f_list[0]) + for i in xrange(1, len(f_list)): + result = lcm(result, carmichael_of_ppower(f_list[i])) + + return result + + +def carmichael_of_ppower(pp): # pragma: no cover + """Carmichael function of the given power of the given prime.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + p, a = pp + if p == 2 and a > 2: + return 2 ** (a - 2) + else: + return (p - 1) * p ** (a - 1) + + +def order_mod(x, m): # pragma: no cover + """Return the order of x in the multiplicative group mod m.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + # Warning: this implementation is not very clever, and will + # take a long time if m is very large. + + if m <= 1: + return 0 + + assert gcd(x, m) == 1 + + z = x + result = 1 + while z != 1: + z = (z * x) % m + result = result + 1 + return result + + +def largest_factor_relatively_prime(a, b): # pragma: no cover + """Return the largest factor of a relatively prime to b.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + while 1: + d = gcd(a, b) + if d <= 1: + break + b = d + while 1: + q, r = divmod(a, d) + if r > 0: + break + a = q + return a + + +def kinda_order_mod(x, m): # pragma: no cover + """Return the order of x in the multiplicative group mod m', + where m' is the largest factor of m relatively prime to x. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/tlsfuzzer/python-ecdsa", + DeprecationWarning, + ) + + return order_mod(x, largest_factor_relatively_prime(m, x)) + + +def is_prime(n): + """Return True if x is prime, False otherwise. + + We use the Miller-Rabin test, as given in Menezes et al. p. 138. + This test is not exact: there are composite values n for which + it returns True. + + In testing the odd numbers from 10000001 to 19999999, + about 66 composites got past the first test, + 5 got past the second test, and none got past the third. + Since factors of 2, 3, 5, 7, and 11 were detected during + preliminary screening, the number of numbers tested by + Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) + = 4.57 million. + """ + + # (This is used to study the risk of false positives:) + global miller_rabin_test_count + + miller_rabin_test_count = 0 + + if n <= smallprimes[-1]: + if n in smallprimes: + return True + else: + return False + + if gcd(n, 2 * 3 * 5 * 7 * 11) != 1: + return False + + # Choose a number of iterations sufficient to reduce the + # probability of accepting a composite below 2**-80 + # (from Menezes et al. Table 4.4): + + t = 40 + n_bits = 1 + int(math.log(n, 2)) + for k, tt in ( + (100, 27), + (150, 18), + (200, 15), + (250, 12), + (300, 9), + (350, 8), + (400, 7), + (450, 6), + (550, 5), + (650, 4), + (850, 3), + (1300, 2), + ): + if n_bits < k: + break + t = tt + + # Run the test t times: + + s = 0 + r = n - 1 + while (r % 2) == 0: + s = s + 1 + r = r // 2 + for i in xrange(t): + a = smallprimes[i] + y = pow(a, r, n) + if y != 1 and y != n - 1: + j = 1 + while j <= s - 1 and y != n - 1: + y = pow(y, 2, n) + if y == 1: + miller_rabin_test_count = i + 1 + return False + j = j + 1 + if y != n - 1: + miller_rabin_test_count = i + 1 + return False + return True + + +def next_prime(starting_value): + """Return the smallest prime larger than the starting value.""" + + if starting_value < 2: + return 2 + result = (starting_value + 1) | 1 + while not is_prime(result): + result = result + 2 + return result + + +smallprimes = [ + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + 131, + 137, + 139, + 149, + 151, + 157, + 163, + 167, + 173, + 179, + 181, + 191, + 193, + 197, + 199, + 211, + 223, + 227, + 229, + 233, + 239, + 241, + 251, + 257, + 263, + 269, + 271, + 277, + 281, + 283, + 293, + 307, + 311, + 313, + 317, + 331, + 337, + 347, + 349, + 353, + 359, + 367, + 373, + 379, + 383, + 389, + 397, + 401, + 409, + 419, + 421, + 431, + 433, + 439, + 443, + 449, + 457, + 461, + 463, + 467, + 479, + 487, + 491, + 499, + 503, + 509, + 521, + 523, + 541, + 547, + 557, + 563, + 569, + 571, + 577, + 587, + 593, + 599, + 601, + 607, + 613, + 617, + 619, + 631, + 641, + 643, + 647, + 653, + 659, + 661, + 673, + 677, + 683, + 691, + 701, + 709, + 719, + 727, + 733, + 739, + 743, + 751, + 757, + 761, + 769, + 773, + 787, + 797, + 809, + 811, + 821, + 823, + 827, + 829, + 839, + 853, + 857, + 859, + 863, + 877, + 881, + 883, + 887, + 907, + 911, + 919, + 929, + 937, + 941, + 947, + 953, + 967, + 971, + 977, + 983, + 991, + 997, + 1009, + 1013, + 1019, + 1021, + 1031, + 1033, + 1039, + 1049, + 1051, + 1061, + 1063, + 1069, + 1087, + 1091, + 1093, + 1097, + 1103, + 1109, + 1117, + 1123, + 1129, + 1151, + 1153, + 1163, + 1171, + 1181, + 1187, + 1193, + 1201, + 1213, + 1217, + 1223, + 1229, +] + +miller_rabin_test_count = 0 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py new file mode 100644 index 000000000..0728b5a41 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py @@ -0,0 +1,113 @@ +""" +RFC 6979: + Deterministic Usage of the Digital Signature Algorithm (DSA) and + Elliptic Curve Digital Signature Algorithm (ECDSA) + + http://tools.ietf.org/html/rfc6979 + +Many thanks to Coda Hale for his implementation in Go language: + https://github.com/codahale/rfc6979 +""" + +import hmac +from binascii import hexlify +from .util import number_to_string, number_to_string_crop, bit_length +from ._compat import hmac_compat + + +# bit_length was defined in this module previously so keep it for backwards +# compatibility, will need to deprecate and remove it later +__all__ = ["bit_length", "bits2int", "bits2octets", "generate_k"] + + +def bits2int(data, qlen): + x = int(hexlify(data), 16) + l = len(data) * 8 + + if l > qlen: + return x >> (l - qlen) + return x + + +def bits2octets(data, order): + z1 = bits2int(data, bit_length(order)) + z2 = z1 - order + + if z2 < 0: + z2 = z1 + + return number_to_string_crop(z2, order) + + +# https://tools.ietf.org/html/rfc6979#section-3.2 +def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""): + """ + Generate the ``k`` value - the nonce for DSA. + + :param int order: order of the DSA generator used in the signature + :param int secexp: secure exponent (private key) in numeric form + :param hash_func: reference to the same hash function used for generating + hash, like :py:class:`hashlib.sha1` + :param bytes data: hash in binary form of the signing data + :param int retry_gen: how many good 'k' values to skip before returning + :param bytes extra_entropy: additional added data in binary form as per + section-3.6 of rfc6979 + :rtype: int + """ + + qlen = bit_length(order) + holen = hash_func().digest_size + rolen = (qlen + 7) // 8 + bx = ( + hmac_compat(number_to_string(secexp, order)), + hmac_compat(bits2octets(data, order)), + hmac_compat(extra_entropy), + ) + + # Step B + v = b"\x01" * holen + + # Step C + k = b"\x00" * holen + + # Step D + + k = hmac.new(k, digestmod=hash_func) + k.update(v + b"\x00") + for i in bx: + k.update(i) + k = k.digest() + + # Step E + v = hmac.new(k, v, hash_func).digest() + + # Step F + k = hmac.new(k, digestmod=hash_func) + k.update(v + b"\x01") + for i in bx: + k.update(i) + k = k.digest() + + # Step G + v = hmac.new(k, v, hash_func).digest() + + # Step H + while True: + # Step H1 + t = b"" + + # Step H2 + while len(t) < rolen: + v = hmac.new(k, v, hash_func).digest() + t += v + + # Step H3 + secret = bits2int(t, qlen) + + if 1 <= secret < order: + if retry_gen <= 0: + return secret + retry_gen -= 1 + + k = hmac.new(k, v + b"\x00", hash_func).digest() + v = hmac.new(k, v, hash_func).digest() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py new file mode 100644 index 000000000..93b6c9bde --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py @@ -0,0 +1,361 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest + +import base64 +import pytest +from .curves import ( + Curve, + NIST256p, + curves, + UnknownCurveError, + PRIME_FIELD_OID, + curve_by_name, +) +from .ellipticcurve import CurveFp, PointJacobi, CurveEdTw +from . import der +from .util import number_to_string + + +class TestParameterEncoding(unittest.TestCase): + @classmethod + def setUpClass(cls): + # minimal, but with cofactor (excludes seed when compared to + # OpenSSL output) + cls.base64_params = ( + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=" + ) + + def test_from_pem(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END EC PARAMETERS-----\n" + ) + curve = Curve.from_pem(pem_params) + + self.assertIs(curve, NIST256p) + + def test_from_pem_with_explicit_when_explicit_disabled(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END EC PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params, ["named_curve"]) + + self.assertIn("explicit curve parameters not", str(e.exception)) + + def test_from_pem_with_named_curve_with_named_curve_disabled(self): + pem_params = ( + "-----BEGIN EC PARAMETERS-----\n" + "BggqhkjOPQMBBw==\n" + "-----END EC PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params, ["explicit"]) + + self.assertIn("named_curve curve parameters not", str(e.exception)) + + def test_from_pem_with_wrong_header(self): + pem_params = ( + "-----BEGIN PARAMETERS-----\n" + "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" + "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" + "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" + "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" + "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" + "-----END PARAMETERS-----\n" + ) + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_pem(pem_params) + + self.assertIn("PARAMETERS PEM header", str(e.exception)) + + def test_to_pem(self): + pem_params = ( + b"-----BEGIN EC PARAMETERS-----\n" + b"BggqhkjOPQMBBw==\n" + b"-----END EC PARAMETERS-----\n" + ) + encoding = NIST256p.to_pem() + + self.assertEqual(pem_params, encoding) + + def test_compare_with_different_object(self): + self.assertNotEqual(NIST256p, 256) + + def test_named_curve_params_der(self): + encoded = NIST256p.to_der() + + # just the encoding of the NIST256p OID (prime256v1) + self.assertEqual(b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07", encoded) + + def test_verify_that_default_is_named_curve_der(self): + encoded_default = NIST256p.to_der() + encoded_named = NIST256p.to_der("named_curve") + + self.assertEqual(encoded_default, encoded_named) + + def test_encoding_to_explicit_params(self): + encoded = NIST256p.to_der("explicit") + + self.assertEqual(encoded, bytes(base64.b64decode(self.base64_params))) + + def test_encoding_to_unsupported_type(self): + with self.assertRaises(ValueError) as e: + NIST256p.to_der("unsupported") + + self.assertIn("Only 'named_curve'", str(e.exception)) + + def test_encoding_to_explicit_compressed_params(self): + encoded = NIST256p.to_der("explicit", "compressed") + + compressed_base_point = ( + "MIHAAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" + "/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" + "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEIQNrF9Hy4SxCR/i85uVjpEDydwN9" + "gS3rM6D0oTlF2JjClgIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8YyVR" + "AgEB" + ) + + self.assertEqual( + encoded, bytes(base64.b64decode(compressed_base_point)) + ) + + def test_decoding_explicit_from_openssl(self): + # generated with openssl 1.1.1k using + # openssl ecparam -name P-256 -param_enc explicit -out /tmp/file.pem + p256_explicit = ( + "MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" + "/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" + "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+" + "kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK" + "fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz" + "ucrC/GMlUQIBAQ==" + ) + + decoded = Curve.from_der(bytes(base64.b64decode(p256_explicit))) + + self.assertEqual(NIST256p, decoded) + + def test_decoding_well_known_from_explicit_params(self): + curve = Curve.from_der(bytes(base64.b64decode(self.base64_params))) + + self.assertIs(curve, NIST256p) + + def test_decoding_with_incorrect_valid_encodings(self): + with self.assertRaises(ValueError) as e: + Curve.from_der(b"", ["explicitCA"]) + + self.assertIn("Only named_curve", str(e.exception)) + + def test_compare_curves_with_different_generators(self): + curve_fp = CurveFp(23, 1, 7) + base_a = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + base_b = PointJacobi(curve_fp, 1, 20, 1, 9, generator=True) + + curve_a = Curve("unknown", curve_fp, base_a, None) + curve_b = Curve("unknown", curve_fp, base_b, None) + + self.assertNotEqual(curve_a, curve_b) + + def test_default_encode_for_custom_curve(self): + curve_fp = CurveFp(23, 1, 7) + base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + + curve = Curve("unknown", curve_fp, base_point, None) + + encoded = curve.to_der() + + decoded = Curve.from_der(encoded) + + self.assertEqual(curve, decoded) + + expected = "MCECAQEwDAYHKoZIzj0BAQIBFzAGBAEBBAEHBAMEDQMCAQk=" + + self.assertEqual(encoded, bytes(base64.b64decode(expected))) + + def test_named_curve_encode_for_custom_curve(self): + curve_fp = CurveFp(23, 1, 7) + base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) + + curve = Curve("unknown", curve_fp, base_point, None) + + with self.assertRaises(UnknownCurveError) as e: + curve.to_der("named_curve") + + self.assertIn("Can't encode curve", str(e.exception)) + + def test_try_decoding_binary_explicit(self): + sect113r1_explicit = ( + "MIGRAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0BAgMCAgEJMDkEDwAwiCUMpufH" + "/mSc6Fgg9wQPAOi+5NPiJgdEGIvg6ccjAxUAEOcjqxTWluZ2h1YVF1b+v4/LSakE" + "HwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY7oTRMV7TGIYCDwEAAAAAAAAA2czsijnl" + "bwIBAg==" + ) + + with self.assertRaises(UnknownCurveError) as e: + Curve.from_der(base64.b64decode(sect113r1_explicit)) + + self.assertIn("Characteristic 2 curves unsupported", str(e.exception)) + + def test_decode_malformed_named_curve(self): + bad_der = der.encode_oid(*NIST256p.oid) + der.encode_integer(1) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unexpected data after OID", str(e.exception)) + + def test_decode_malformed_explicit_garbage_after_ECParam(self): + bad_der = bytes( + base64.b64decode(self.base64_params) + ) + der.encode_integer(1) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unexpected data after ECParameters", str(e.exception)) + + def test_decode_malformed_unknown_version_number(self): + bad_der = der.encode_sequence(der.encode_integer(2)) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Unknown parameter encoding format", str(e.exception)) + + def test_decode_malformed_unknown_field_type(self): + curve_p = NIST256p.curve.p() + bad_der = der.encode_sequence( + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(1, 2, 3), der.encode_integer(curve_p) + ), + der.encode_sequence( + der.encode_octet_string( + number_to_string(NIST256p.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(NIST256p.curve.b(), curve_p) + ), + ), + der.encode_octet_string( + NIST256p.generator.to_bytes("uncompressed") + ), + der.encode_integer(NIST256p.generator.order()), + ) + + with self.assertRaises(UnknownCurveError) as e: + Curve.from_der(bad_der) + + self.assertIn("Unknown field type: (1, 2, 3)", str(e.exception)) + + def test_decode_malformed_garbage_after_prime(self): + curve_p = NIST256p.curve.p() + bad_der = der.encode_sequence( + der.encode_integer(1), + der.encode_sequence( + der.encode_oid(*PRIME_FIELD_OID), + der.encode_integer(curve_p), + der.encode_integer(1), + ), + der.encode_sequence( + der.encode_octet_string( + number_to_string(NIST256p.curve.a() % curve_p, curve_p) + ), + der.encode_octet_string( + number_to_string(NIST256p.curve.b(), curve_p) + ), + ), + der.encode_octet_string( + NIST256p.generator.to_bytes("uncompressed") + ), + der.encode_integer(NIST256p.generator.order()), + ) + + with self.assertRaises(der.UnexpectedDER) as e: + Curve.from_der(bad_der) + + self.assertIn("Prime-p element", str(e.exception)) + + +class TestCurveSearching(unittest.TestCase): + def test_correct_name(self): + c = curve_by_name("NIST256p") + self.assertIs(c, NIST256p) + + def test_openssl_name(self): + c = curve_by_name("prime256v1") + self.assertIs(c, NIST256p) + + def test_unknown_curve(self): + with self.assertRaises(UnknownCurveError) as e: + curve_by_name("foo bar") + + self.assertIn( + "name 'foo bar' unknown, only curves supported: " + "['NIST192p', 'NIST224p'", + str(e.exception), + ) + + def test_with_None_as_parameter(self): + with self.assertRaises(UnknownCurveError) as e: + curve_by_name(None) + + self.assertIn( + "name None unknown, only curves supported: " + "['NIST192p', 'NIST224p'", + str(e.exception), + ) + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_named(curve): + ret = Curve.from_der(curve.to_der("named_curve")) + + assert curve == ret + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_explicit(curve): + if isinstance(curve.curve, CurveEdTw): + with pytest.raises(UnknownCurveError): + curve.to_der("explicit") + else: + ret = Curve.from_der(curve.to_der("explicit")) + + assert curve == ret + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_default(curve): + ret = Curve.from_der(curve.to_der()) + + assert curve == ret + + +@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) +def test_curve_params_encode_decode_explicit_compressed(curve): + if isinstance(curve.curve, CurveEdTw): + with pytest.raises(UnknownCurveError): + curve.to_der("explicit", "compressed") + else: + ret = Curve.from_der(curve.to_der("explicit", "compressed")) + + assert curve == ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py new file mode 100644 index 000000000..0ca5bd7fd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py @@ -0,0 +1,476 @@ +# compatibility with Python 2.6, for that we need unittest2 package, +# which is not available on 3.3 or 3.4 +import warnings +from binascii import hexlify + +try: + import unittest2 as unittest +except ImportError: + import unittest +from six import b +import hypothesis.strategies as st +from hypothesis import given +import pytest +from ._compat import str_idx_as_int +from .curves import NIST256p, NIST224p +from .der import ( + remove_integer, + UnexpectedDER, + read_length, + encode_bitstring, + remove_bitstring, + remove_object, + encode_oid, + remove_constructed, + remove_octet_string, + remove_sequence, +) + + +class TestRemoveInteger(unittest.TestCase): + # DER requires the integers to be 0-padded only if they would be + # interpreted as negative, check if those errors are detected + def test_non_minimal_encoding(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b("\x02\x02\x00\x01")) + + def test_negative_with_high_bit_set(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b("\x02\x01\x80")) + + def test_minimal_with_high_bit_set(self): + val, rem = remove_integer(b("\x02\x02\x00\x80")) + + self.assertEqual(val, 0x80) + self.assertEqual(rem, b"") + + def test_two_zero_bytes_with_high_bit_set(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b("\x02\x03\x00\x00\xff")) + + def test_zero_length_integer(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b("\x02\x00")) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_integer(b("")) + + def test_encoding_of_zero(self): + val, rem = remove_integer(b("\x02\x01\x00")) + + self.assertEqual(val, 0) + self.assertEqual(rem, b"") + + def test_encoding_of_127(self): + val, rem = remove_integer(b("\x02\x01\x7f")) + + self.assertEqual(val, 127) + self.assertEqual(rem, b"") + + def test_encoding_of_128(self): + val, rem = remove_integer(b("\x02\x02\x00\x80")) + + self.assertEqual(val, 128) + self.assertEqual(rem, b"") + + def test_wrong_tag(self): + with self.assertRaises(UnexpectedDER) as e: + remove_integer(b"\x01\x02\x00\x80") + + self.assertIn("wanted type 'integer'", str(e.exception)) + + def test_wrong_length(self): + with self.assertRaises(UnexpectedDER) as e: + remove_integer(b"\x02\x03\x00\x80") + + self.assertIn("Length longer", str(e.exception)) + + +class TestReadLength(unittest.TestCase): + # DER requires the lengths between 0 and 127 to be encoded using the short + # form and lengths above that encoded with minimal number of bytes + # necessary + def test_zero_length(self): + self.assertEqual((0, 1), read_length(b("\x00"))) + + def test_two_byte_zero_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b("\x81\x00")) + + def test_two_byte_small_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b("\x81\x7f")) + + def test_long_form_with_zero_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b("\x80")) + + def test_smallest_two_byte_length(self): + self.assertEqual((128, 2), read_length(b("\x81\x80"))) + + def test_zero_padded_length(self): + with self.assertRaises(UnexpectedDER): + read_length(b("\x82\x00\x80")) + + def test_two_three_byte_length(self): + self.assertEqual((256, 3), read_length(b"\x82\x01\x00")) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + read_length(b("")) + + def test_length_overflow(self): + with self.assertRaises(UnexpectedDER): + read_length(b("\x83\x01\x00")) + + +class TestEncodeBitstring(unittest.TestCase): + # DER requires BIT STRINGS to include a number of padding bits in the + # encoded byte string, that padding must be between 0 and 7 + + def test_old_call_convention(self): + """This is the old way to use the function.""" + warnings.simplefilter("always") + with pytest.warns(DeprecationWarning) as warns: + der = encode_bitstring(b"\x00\xff") + + self.assertEqual(len(warns), 1) + self.assertIn( + "unused= needs to be specified", warns[0].message.args[0] + ) + + self.assertEqual(der, b"\x03\x02\x00\xff") + + def test_new_call_convention(self): + """This is how it should be called now.""" + warnings.simplefilter("always") + with pytest.warns(None) as warns: + der = encode_bitstring(b"\xff", 0) + + # verify that new call convention doesn't raise Warnings + self.assertEqual(len(warns), 0) + + self.assertEqual(der, b"\x03\x02\x00\xff") + + def test_implicit_unused_bits(self): + """ + Writing bit string with already included the number of unused bits. + """ + warnings.simplefilter("always") + with pytest.warns(None) as warns: + der = encode_bitstring(b"\x00\xff", None) + + # verify that new call convention doesn't raise Warnings + self.assertEqual(len(warns), 0) + + self.assertEqual(der, b"\x03\x02\x00\xff") + + def test_explicit_unused_bits(self): + der = encode_bitstring(b"\xff\xf0", 4) + + self.assertEqual(der, b"\x03\x03\x04\xff\xf0") + + def test_empty_string(self): + self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00") + + def test_invalid_unused_count(self): + with self.assertRaises(ValueError): + encode_bitstring(b"\xff\x00", 8) + + def test_invalid_unused_with_empty_string(self): + with self.assertRaises(ValueError): + encode_bitstring(b"", 1) + + def test_non_zero_padding_bits(self): + with self.assertRaises(ValueError): + encode_bitstring(b"\xff", 2) + + +class TestRemoveBitstring(unittest.TestCase): + def test_old_call_convention(self): + """This is the old way to call the function.""" + warnings.simplefilter("always") + with pytest.warns(DeprecationWarning) as warns: + bits, rest = remove_bitstring(b"\x03\x02\x00\xff") + + self.assertEqual(len(warns), 1) + self.assertIn( + "expect_unused= needs to be specified", warns[0].message.args[0] + ) + + self.assertEqual(bits, b"\x00\xff") + self.assertEqual(rest, b"") + + def test_new_call_convention(self): + warnings.simplefilter("always") + with pytest.warns(None) as warns: + bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0) + + self.assertEqual(len(warns), 0) + + self.assertEqual(bits, b"\xff") + self.assertEqual(rest, b"") + + def test_implicit_unexpected_unused(self): + warnings.simplefilter("always") + with pytest.warns(None) as warns: + bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None) + + self.assertEqual(len(warns), 0) + + self.assertEqual(bits, (b"\xff", 0)) + self.assertEqual(rest, b"") + + def test_with_padding(self): + ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None) + + self.assertEqual(ret, (b"\xf0", 4)) + self.assertEqual(rest, b"") + + def test_not_a_bitstring(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x02\x02\x00\xff", None) + + def test_empty_encoding(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x00", None) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"", None) + + def test_no_length(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03", None) + + def test_unexpected_number_of_unused_bits(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x02\x00\xff", 1) + + def test_invalid_encoding_of_unused_bits(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x03\x08\xff\x00", None) + + def test_invalid_encoding_of_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x01\x01", None) + + def test_invalid_padding_bits(self): + with self.assertRaises(UnexpectedDER): + remove_bitstring(b"\x03\x02\x01\xff", None) + + +class TestStrIdxAsInt(unittest.TestCase): + def test_str(self): + self.assertEqual(115, str_idx_as_int("str", 0)) + + def test_bytes(self): + self.assertEqual(115, str_idx_as_int(b"str", 0)) + + def test_bytearray(self): + self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0)) + + +class TestEncodeOid(unittest.TestCase): + def test_pub_key_oid(self): + oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) + self.assertEqual(hexlify(oid_ecPublicKey), b("06072a8648ce3d0201")) + + def test_nist224p_oid(self): + self.assertEqual(hexlify(NIST224p.encoded_oid), b("06052b81040021")) + + def test_nist256p_oid(self): + self.assertEqual( + hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107" + ) + + def test_large_second_subid(self): + # from X.690, section 8.19.5 + oid = encode_oid(2, 999, 3) + self.assertEqual(oid, b"\x06\x03\x88\x37\x03") + + def test_with_two_subids(self): + oid = encode_oid(2, 999) + self.assertEqual(oid, b"\x06\x02\x88\x37") + + def test_zero_zero(self): + oid = encode_oid(0, 0) + self.assertEqual(oid, b"\x06\x01\x00") + + def test_with_wrong_types(self): + with self.assertRaises((TypeError, AssertionError)): + encode_oid(0, None) + + def test_with_small_first_large_second(self): + with self.assertRaises(AssertionError): + encode_oid(1, 40) + + def test_small_first_max_second(self): + oid = encode_oid(1, 39) + self.assertEqual(oid, b"\x06\x01\x4f") + + def test_with_invalid_first(self): + with self.assertRaises(AssertionError): + encode_oid(3, 39) + + +class TestRemoveObject(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) + + def test_pub_key_oid(self): + oid, rest = remove_object(self.oid_ecPublicKey) + self.assertEqual(rest, b"") + self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) + + def test_with_extra_bytes(self): + oid, rest = remove_object(self.oid_ecPublicKey + b"more") + self.assertEqual(rest, b"more") + self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) + + def test_with_large_second_subid(self): + # from X.690, section 8.19.5 + oid, rest = remove_object(b"\x06\x03\x88\x37\x03") + self.assertEqual(rest, b"") + self.assertEqual(oid, (2, 999, 3)) + + def test_with_padded_first_subid(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x02\x80\x00") + + def test_with_padded_second_subid(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x04\x88\x37\x80\x01") + + def test_with_missing_last_byte_of_multi_byte(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x03\x88\x37\x83") + + def test_with_two_subids(self): + oid, rest = remove_object(b"\x06\x02\x88\x37") + self.assertEqual(rest, b"") + self.assertEqual(oid, (2, 999)) + + def test_zero_zero(self): + oid, rest = remove_object(b"\x06\x01\x00") + self.assertEqual(rest, b"") + self.assertEqual(oid, (0, 0)) + + def test_empty_string(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"") + + def test_missing_length(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06") + + def test_empty_oid(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x00") + + def test_empty_oid_overflow(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x01") + + def test_with_wrong_type(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x04\x02\x88\x37") + + def test_with_too_long_length(self): + with self.assertRaises(UnexpectedDER): + remove_object(b"\x06\x03\x88\x37") + + +class TestRemoveConstructed(unittest.TestCase): + def test_simple(self): + data = b"\xa1\x02\xff\xaa" + + tag, body, rest = remove_constructed(data) + + self.assertEqual(tag, 0x01) + self.assertEqual(body, b"\xff\xaa") + self.assertEqual(rest, b"") + + def test_with_malformed_tag(self): + data = b"\x01\x02\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_constructed(data) + + self.assertIn("constructed tag", str(e.exception)) + + +class TestRemoveOctetString(unittest.TestCase): + def test_simple(self): + data = b"\x04\x03\xaa\xbb\xcc" + body, rest = remove_octet_string(data) + self.assertEqual(body, b"\xaa\xbb\xcc") + self.assertEqual(rest, b"") + + def test_with_malformed_tag(self): + data = b"\x03\x03\xaa\xbb\xcc" + with self.assertRaises(UnexpectedDER) as e: + remove_octet_string(data) + + self.assertIn("octetstring", str(e.exception)) + + +class TestRemoveSequence(unittest.TestCase): + def test_simple(self): + data = b"\x30\x02\xff\xaa" + body, rest = remove_sequence(data) + self.assertEqual(body, b"\xff\xaa") + self.assertEqual(rest, b"") + + def test_with_empty_string(self): + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(b"") + + self.assertIn("Empty string", str(e.exception)) + + def test_with_wrong_tag(self): + data = b"\x20\x02\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(data) + + self.assertIn("wanted type 'sequence'", str(e.exception)) + + def test_with_wrong_length(self): + data = b"\x30\x03\xff\xaa" + + with self.assertRaises(UnexpectedDER) as e: + remove_sequence(data) + + self.assertIn("Length longer", str(e.exception)) + + +@st.composite +def st_oid(draw, max_value=2**512, max_size=50): + """ + Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples + + :param max_value: maximum value of any single sub-identifier + :param max_size: maximum length of the generated OID + """ + first = draw(st.integers(min_value=0, max_value=2)) + if first < 2: + second = draw(st.integers(min_value=0, max_value=39)) + else: + second = draw(st.integers(min_value=0, max_value=max_value)) + rest = draw( + st.lists( + st.integers(min_value=0, max_value=max_value), max_size=max_size + ) + ) + return (first, second) + tuple(rest) + + +@given(st_oid()) +def test_oids(ids): + encoded_oid = encode_oid(*ids) + decoded_oid, rest = remove_object(encoded_oid) + assert rest == b"" + assert decoded_oid == ids diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py new file mode 100644 index 000000000..872d4d14c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py @@ -0,0 +1,441 @@ +import os +import shutil +import subprocess +import pytest +from binascii import unhexlify + +try: + import unittest2 as unittest +except ImportError: + import unittest + +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + BRAINPOOLP160r1, +) +from .curves import curves +from .ecdh import ( + ECDH, + InvalidCurveError, + InvalidSharedSecretError, + NoKeyError, + NoCurveError, +) +from .keys import SigningKey, VerifyingKey +from .ellipticcurve import CurveEdTw + + +@pytest.mark.parametrize( + "vcurve", + curves, + ids=[curve.name for curve in curves], +) +def test_ecdh_each(vcurve): + if isinstance(vcurve.curve, CurveEdTw): + pytest.skip("ECDH is not supported for Edwards curves") + ecdh1 = ECDH(curve=vcurve) + ecdh2 = ECDH(curve=vcurve) + + ecdh2.generate_private_key() + ecdh1.load_received_public_key(ecdh2.get_public_key()) + ecdh2.load_received_public_key(ecdh1.generate_private_key()) + + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + assert secret1 == secret2 + + +def test_ecdh_both_keys_present(): + key1 = SigningKey.generate(BRAINPOOLP160r1) + key2 = SigningKey.generate(BRAINPOOLP160r1) + + ecdh1 = ECDH(BRAINPOOLP160r1, key1, key2.verifying_key) + ecdh2 = ECDH(private_key=key2, public_key=key1.verifying_key) + + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + + assert secret1 == secret2 + + +def test_ecdh_no_public_key(): + ecdh1 = ECDH(curve=NIST192p) + + with pytest.raises(NoKeyError): + ecdh1.generate_sharedsecret_bytes() + + ecdh1.generate_private_key() + + with pytest.raises(NoKeyError): + ecdh1.generate_sharedsecret_bytes() + + +class TestECDH(unittest.TestCase): + def test_load_key_from_wrong_curve(self): + ecdh1 = ECDH() + ecdh1.set_curve(NIST192p) + + key1 = SigningKey.generate(BRAINPOOLP160r1) + + with self.assertRaises(InvalidCurveError) as e: + ecdh1.load_private_key(key1) + + self.assertIn("Curve mismatch", str(e.exception)) + + def test_generate_without_curve(self): + ecdh1 = ECDH() + + with self.assertRaises(NoCurveError) as e: + ecdh1.generate_private_key() + + self.assertIn("Curve must be set", str(e.exception)) + + def test_load_bytes_without_curve_set(self): + ecdh1 = ECDH() + + with self.assertRaises(NoCurveError) as e: + ecdh1.load_private_key_bytes(b"\x01" * 32) + + self.assertIn("Curve must be set", str(e.exception)) + + def test_set_curve_from_received_public_key(self): + ecdh1 = ECDH() + + key1 = SigningKey.generate(BRAINPOOLP160r1) + + ecdh1.load_received_public_key(key1.verifying_key) + + self.assertEqual(ecdh1.curve, BRAINPOOLP160r1) + + +def test_ecdh_wrong_public_key_curve(): + ecdh1 = ECDH(curve=NIST192p) + ecdh1.generate_private_key() + ecdh2 = ECDH(curve=NIST256p) + ecdh2.generate_private_key() + + with pytest.raises(InvalidCurveError): + ecdh1.load_received_public_key(ecdh2.get_public_key()) + + with pytest.raises(InvalidCurveError): + ecdh2.load_received_public_key(ecdh1.get_public_key()) + + ecdh1.public_key = ecdh2.get_public_key() + ecdh2.public_key = ecdh1.get_public_key() + + with pytest.raises(InvalidCurveError): + ecdh1.generate_sharedsecret_bytes() + + with pytest.raises(InvalidCurveError): + ecdh2.generate_sharedsecret_bytes() + + +def test_ecdh_invalid_shared_secret_curve(): + ecdh1 = ECDH(curve=NIST256p) + ecdh1.generate_private_key() + + ecdh1.load_received_public_key( + SigningKey.generate(NIST256p).get_verifying_key() + ) + + ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order + + with pytest.raises(InvalidSharedSecretError): + ecdh1.generate_sharedsecret_bytes() + + +# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp192r1.txt +# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp256r1.txt +# https://github.com/coruus/nist-testvectors/blob/master/csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors/KAS_ECC_CDH_PrimitiveTest.txt +@pytest.mark.parametrize( + "curve,privatekey,pubkey,secret", + [ + pytest.param( + NIST192p, + "f17d3fea367b74d340851ca4270dcb24c271f445bed9d527", + "42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0" + "dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523", + "803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0", + id="NIST192p-1", + ), + pytest.param( + NIST192p, + "56e853349d96fe4c442448dacb7cf92bb7a95dcf574a9bd5", + "deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7" + "7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125", + "c208847568b98835d7312cef1f97f7aa298283152313c29d", + id="NIST192p-2", + ), + pytest.param( + NIST192p, + "c6ef61fe12e80bf56f2d3f7d0bb757394519906d55500949", + "4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f" + "0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279", + "87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd", + id="NIST192p-3", + ), + pytest.param( + NIST192p, + "e6747b9c23ba7044f38ff7e62c35e4038920f5a0163d3cda", + "8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88" + "04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5", + "eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c", + id="NIST192p-4", + ), + pytest.param( + NIST192p, + "beabedd0154a1afcfc85d52181c10f5eb47adc51f655047d", + "0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594" + "542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73", + "716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4", + id="NIST192p-5", + ), + pytest.param( + NIST192p, + "cf70354226667321d6e2baf40999e2fd74c7a0f793fa8699", + "fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516" + "368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7", + "f67053b934459985a315cb017bf0302891798d45d0e19508", + id="NIST192p-6", + ), + pytest.param( + NIST224p, + "8346a60fc6f293ca5a0d2af68ba71d1dd389e5e40837942df3e43cbd", + "af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280" + "882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7", + "7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8", + id="NIST224p", + ), + pytest.param( + NIST256p, + "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", + "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", + "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", + id="NIST256p-1", + ), + pytest.param( + NIST256p, + "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5", + "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae" + "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", + "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", + id="NIST256p-2", + ), + pytest.param( + NIST256p, + "1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8", + "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3" + "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536", + "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec", + id="NIST256p-3", + ), + pytest.param( + NIST256p, + "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d", + "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed" + "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", + "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", + id="NIST256p-4", + ), + pytest.param( + NIST256p, + "59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf", + "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922" + "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328", + "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62", + id="NIST256p-5", + ), + pytest.param( + NIST256p, + "f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef", + "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792" + "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f", + "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3", + id="NIST256p-6", + ), + pytest.param( + NIST384p, + "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b1" + "5618b6818a661774ad463b205da88cf699ab4d43c9cf98a1", + "a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e76459" + "2efda27fe7513272734466b400091adbf2d68c58e0c50066" + "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b66" + "1efedf243451915ed0905a32b060992b468c64766fc8437a", + "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4" + "0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1", + id="NIST384p", + ), + pytest.param( + NIST521p, + "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4ea" + "c6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47", + "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433" + "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d" + "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83" + "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676", + "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366" + "72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831", + id="NIST521p", + ), + ], +) +def test_ecdh_NIST(curve, privatekey, pubkey, secret): + ecdh = ECDH(curve=curve) + ecdh.load_private_key_bytes(unhexlify(privatekey)) + ecdh.load_received_public_key_bytes(unhexlify(pubkey)) + + sharedsecret = ecdh.generate_sharedsecret_bytes() + + assert sharedsecret == unhexlify(secret) + + +pem_local_private_key = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" +) +der_local_private_key = ( + "305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864" + "8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06" + "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c" +) +pem_remote_public_key = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" + "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" + "-----END PUBLIC KEY-----\n" +) +der_remote_public_key = ( + "3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563" + "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c" +) +gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea" + + +def test_ecdh_pem(): + ecdh = ECDH() + ecdh.load_private_key_pem(pem_local_private_key) + ecdh.load_received_public_key_pem(pem_remote_public_key) + + sharedsecret = ecdh.generate_sharedsecret_bytes() + + assert sharedsecret == unhexlify(gshared_secret) + + +def test_ecdh_der(): + ecdh = ECDH() + ecdh.load_private_key_der(unhexlify(der_local_private_key)) + ecdh.load_received_public_key_der(unhexlify(der_remote_public_key)) + + sharedsecret = ecdh.generate_sharedsecret_bytes() + + assert sharedsecret == unhexlify(gshared_secret) + + +# Exception classes used by run_openssl. +class RunOpenSslError(Exception): + pass + + +def run_openssl(cmd): + OPENSSL = "openssl" + p = subprocess.Popen( + [OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout, ignored = p.communicate() + if p.returncode != 0: + raise RunOpenSslError( + "cmd '%s %s' failed: rc=%s, stdout/err was %s" + % (OPENSSL, cmd, p.returncode, stdout) + ) + return stdout.decode() + + +OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") +) + + +@pytest.mark.parametrize( + "vcurve", + curves, + ids=[curve.name for curve in curves], +) +def test_ecdh_with_openssl(vcurve): + if isinstance(vcurve.curve, CurveEdTw): + pytest.skip("Edwards curves are not supported for ECDH") + + assert vcurve.openssl_name + + if vcurve.openssl_name not in OPENSSL_SUPPORTED_CURVES: + pytest.skip("system openssl does not support " + vcurve.openssl_name) + + try: + hlp = run_openssl("pkeyutl -help") + if hlp.find("-derive") == 0: # pragma: no cover + pytest.skip("system openssl does not support `pkeyutl -derive`") + except RunOpenSslError: # pragma: no cover + pytest.skip("system openssl could not be executed") + + if os.path.isdir("t"): # pragma: no branch + shutil.rmtree("t") + os.mkdir("t") + run_openssl( + "ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name + ) + run_openssl( + "ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name + ) + run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem") + + ecdh1 = ECDH(curve=vcurve) + ecdh2 = ECDH(curve=vcurve) + with open("t/privkey1.pem") as e: + key = e.read() + ecdh1.load_private_key_pem(key) + with open("t/privkey2.pem") as e: + key = e.read() + ecdh2.load_private_key_pem(key) + + with open("t/pubkey1.pem") as e: + key = e.read() + vk1 = VerifyingKey.from_pem(key) + assert vk1.to_string() == ecdh1.get_public_key().to_string() + vk2 = ecdh2.get_public_key() + with open("t/pubkey2.pem", "wb") as e: + e.write(vk2.to_pem()) + + ecdh1.load_received_public_key(vk2) + ecdh2.load_received_public_key(vk1) + secret1 = ecdh1.generate_sharedsecret_bytes() + secret2 = ecdh2.generate_sharedsecret_bytes() + + assert secret1 == secret2 + + run_openssl( + "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" + ) + run_openssl( + "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" + ) + + with open("t/secret1", "rb") as e: + ssl_secret1 = e.read() + with open("t/secret1", "rb") as e: + ssl_secret2 = e.read() + + assert len(ssl_secret1) == vk1.curve.verifying_key_length // 2 + assert len(secret1) == vk1.curve.verifying_key_length // 2 + + assert ssl_secret1 == ssl_secret2 + assert secret1 == ssl_secret1 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py new file mode 100644 index 000000000..dbc4a6eb5 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py @@ -0,0 +1,661 @@ +from __future__ import print_function +import sys +import hypothesis.strategies as st +from hypothesis import given, settings, note, example + +try: + import unittest2 as unittest +except ImportError: + import unittest +import pytest +from .ecdsa import ( + Private_key, + Public_key, + Signature, + generator_192, + digest_integer, + ellipticcurve, + point_is_valid, + generator_224, + generator_256, + generator_384, + generator_521, + generator_secp256k1, + curve_192, + InvalidPointError, + curve_112r2, + generator_112r2, + int_to_string, +) + + +HYP_SETTINGS = {} +# old hypothesis doesn't have the "deadline" setting +if sys.version_info > (2, 7): # pragma: no branch + # SEC521p is slow, allow long execution for it + HYP_SETTINGS["deadline"] = 5000 + + +class TestP192FromX9_62(unittest.TestCase): + """Check test vectors from X9.62""" + + @classmethod + def setUpClass(cls): + cls.d = 651056770906015076056810763456358567190100156695615665659 + cls.Q = cls.d * generator_192 + cls.k = 6140507067065001063065065565667405560006161556565665656654 + cls.R = cls.k * generator_192 + + cls.msg = 968236873715988614170569073515315707566766479517 + cls.pubk = Public_key(generator_192, generator_192 * cls.d) + cls.privk = Private_key(cls.pubk, cls.d) + cls.sig = cls.privk.sign(cls.msg, cls.k) + + def test_point_multiplication(self): + assert self.Q.x() == 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 + + def test_point_multiplication_2(self): + assert self.R.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + assert self.R.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + + def test_mult_and_addition(self): + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * generator_192 + u2 * self.Q + assert temp.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + assert temp.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + + def test_signature(self): + r, s = self.sig.r, self.sig.s + assert r == 3342403536405981729393488334694600415596881826869351677613 + assert s == 5735822328888155254683894997897571951568553642892029982342 + + def test_verification(self): + assert self.pubk.verifies(self.msg, self.sig) + + def test_rejection(self): + assert not self.pubk.verifies(self.msg - 1, self.sig) + + +class TestPublicKey(unittest.TestCase): + def test_equality_public_keys(self): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + pub_key1 = Public_key(gen, point) + pub_key2 = Public_key(gen, point) + self.assertEqual(pub_key1, pub_key2) + + def test_inequality_public_key(self): + gen = generator_192 + x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point1 = ellipticcurve.Point(gen.curve(), x1, y1) + + x2 = 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15 + y2 = 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF + point2 = ellipticcurve.Point(gen.curve(), x2, y2) + + pub_key1 = Public_key(gen, point1) + pub_key2 = Public_key(gen, point2) + self.assertNotEqual(pub_key1, pub_key2) + + def test_inequality_different_curves(self): + gen = generator_192 + x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point1 = ellipticcurve.Point(gen.curve(), x1, y1) + + x2 = 0x722BA0FB6B8FC8898A4C6AB49E66 + y2 = 0x2B7344BB57A7ABC8CA0F1A398C7D + point2 = ellipticcurve.Point(generator_112r2.curve(), x2, y2) + + pub_key1 = Public_key(gen, point1) + pub_key2 = Public_key(generator_112r2, point2) + self.assertNotEqual(pub_key1, pub_key2) + + def test_inequality_public_key_not_implemented(self): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + pub_key = Public_key(gen, point) + self.assertNotEqual(pub_key, None) + + def test_public_key_with_generator_without_order(self): + gen = ellipticcurve.PointJacobi( + generator_192.curve(), generator_192.x(), generator_192.y(), 1 + ) + + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + + with self.assertRaises(InvalidPointError) as e: + Public_key(gen, point) + + self.assertIn("Generator point must have order", str(e.exception)) + + def test_public_point_on_curve_not_scalar_multiple_of_base_point(self): + x = 2 + y = 0xBE6AA4938EF7CFE6FE29595B6B00 + # we need a curve with cofactor != 1 + point = ellipticcurve.PointJacobi(curve_112r2, x, y, 1) + + self.assertTrue(curve_112r2.contains_point(x, y)) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_112r2, point) + + self.assertIn("Generator point order", str(e.exception)) + + def test_point_is_valid_with_not_scalar_multiple_of_base_point(self): + x = 2 + y = 0xBE6AA4938EF7CFE6FE29595B6B00 + + self.assertFalse(point_is_valid(generator_112r2, x, y)) + + # the tests to verify the extensiveness of tests in ecdsa.ecdsa + # if PointJacobi gets modified to calculate the x and y mod p the tests + # below will need to use a fake/mock object + def test_invalid_point_x_negative(self): + pt = ellipticcurve.PointJacobi(curve_192, -1, 0, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_x_equal_p(self): + pt = ellipticcurve.PointJacobi(curve_192, curve_192.p(), 0, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_y_negative(self): + pt = ellipticcurve.PointJacobi(curve_192, 0, -1, 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + def test_invalid_point_y_equal_p(self): + pt = ellipticcurve.PointJacobi(curve_192, 0, curve_192.p(), 1) + + with self.assertRaises(InvalidPointError) as e: + Public_key(generator_192, pt) + + self.assertIn("The public point has x or y", str(e.exception)) + + +class TestPublicKeyVerifies(unittest.TestCase): + # test all the different ways that a signature can be publicly invalid + @classmethod + def setUpClass(cls): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + + cls.pub_key = Public_key(gen, point) + + def test_sig_with_r_zero(self): + sig = Signature(0, 1) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_r_order(self): + sig = Signature(generator_192.order(), 1) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_s_zero(self): + sig = Signature(1, 0) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + def test_sig_with_s_order(self): + sig = Signature(1, generator_192.order()) + + self.assertFalse(self.pub_key.verifies(1, sig)) + + +class TestPrivateKey(unittest.TestCase): + @classmethod + def setUpClass(cls): + gen = generator_192 + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F + point = ellipticcurve.Point(gen.curve(), x, y) + cls.pub_key = Public_key(gen, point) + + def test_equality_private_keys(self): + pr_key1 = Private_key(self.pub_key, 100) + pr_key2 = Private_key(self.pub_key, 100) + self.assertEqual(pr_key1, pr_key2) + + def test_inequality_private_keys(self): + pr_key1 = Private_key(self.pub_key, 100) + pr_key2 = Private_key(self.pub_key, 200) + self.assertNotEqual(pr_key1, pr_key2) + + def test_inequality_private_keys_not_implemented(self): + pr_key = Private_key(self.pub_key, 100) + self.assertNotEqual(pr_key, None) + + +# Testing point validity, as per ECDSAVS.pdf B.2.2: +P192_POINTS = [ + ( + generator_192, + 0xCD6D0F029A023E9AACA429615B8F577ABEE685D8257CC83A, + 0x00019C410987680E9FB6C0B6ECC01D9A2647C8BAE27721BACDFC, + False, + ), + ( + generator_192, + 0x00017F2FCE203639E9EAF9FB50B81FC32776B30E3B02AF16C73B, + 0x95DA95C5E72DD48E229D4748D4EEE658A9A54111B23B2ADB, + False, + ), + ( + generator_192, + 0x4F77F8BC7FCCBADD5760F4938746D5F253EE2168C1CF2792, + 0x000147156FF824D131629739817EDB197717C41AAB5C2A70F0F6, + False, + ), + ( + generator_192, + 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6, + 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F, + True, + ), + ( + generator_192, + 0xCDF56C1AA3D8AFC53C521ADF3FFB96734A6A630A4A5B5A70, + 0x97C1C44A5FB229007B5EC5D25F7413D170068FFD023CAA4E, + True, + ), + ( + generator_192, + 0x89009C0DC361C81E99280C8E91DF578DF88CDF4B0CDEDCED, + 0x27BE44A529B7513E727251F128B34262A0FD4D8EC82377B9, + True, + ), + ( + generator_192, + 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15, + 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF, + True, + ), + ( + generator_192, + 0x6DCCBDE75C0948C98DAB32EA0BC59FE125CF0FB1A3798EDA, + 0x0001171A3E0FA60CF3096F4E116B556198DE430E1FBD330C8835, + False, + ), + ( + generator_192, + 0xD266B39E1F491FC4ACBBBC7D098430931CFA66D55015AF12, + 0x193782EB909E391A3148B7764E6B234AA94E48D30A16DBB2, + False, + ), + ( + generator_192, + 0x9D6DDBCD439BAA0C6B80A654091680E462A7D1D3F1FFEB43, + 0x6AD8EFC4D133CCF167C44EB4691C80ABFFB9F82B932B8CAA, + False, + ), + ( + generator_192, + 0x146479D944E6BDA87E5B35818AA666A4C998A71F4E95EDBC, + 0xA86D6FE62BC8FBD88139693F842635F687F132255858E7F6, + False, + ), + ( + generator_192, + 0xE594D4A598046F3598243F50FD2C7BD7D380EDB055802253, + 0x509014C0C4D6B536E3CA750EC09066AF39B4C8616A53A923, + False, + ), +] + + +@pytest.mark.parametrize("generator,x,y,expected", P192_POINTS) +def test_point_validity(generator, x, y, expected): + """ + `generator` defines the curve; is `(x, y)` a point on + this curve? `expected` is True if the right answer is Yes. + """ + assert point_is_valid(generator, x, y) == expected + + +# Trying signature-verification tests from ECDSAVS.pdf B.2.4: +CURVE_192_KATS = [ + ( + generator_192, + int( + "0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee" + "425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30" + "d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79" + "8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1" + "58", + 16, + ), + 0xD9DBFB332AA8E5FF091E8CE535857C37C73F6250FFB2E7AC, + 0x282102E364FEDED3AD15DDF968F88D8321AA268DD483EBC4, + 0x64DCA58A20787C488D11D6DD96313F1B766F2D8EFE122916, + 0x1ECBA28141E84AB4ECAD92F56720E2CC83EB3D22DEC72479, + True, + ), + ( + generator_192, + int( + "0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1" + "2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a" + "91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3" + "26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63" + "f4", + 16, + ), + 0x3E53EF8D3112AF3285C0E74842090712CD324832D4277AE7, + 0xCC75F8952D30AEC2CBB719FC6AA9934590B5D0FF5A83ADB7, + 0x8285261607283BA18F335026130BAB31840DCFD9C3E555AF, + 0x356D89E1B04541AFC9704A45E9C535CE4A50929E33D7E06C, + True, + ), + ( + generator_192, + int( + "0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911" + "b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd" + "d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30" + "3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42" + "dd", + 16, + ), + 0x16335DBE95F8E8254A4E04575D736BEFB258B8657F773CB7, + 0x421B13379C59BC9DCE38A1099CA79BBD06D647C7F6242336, + 0x4141BD5D64EA36C5B0BD21EF28C02DA216ED9D04522B1E91, + 0x159A6AA852BCC579E821B7BB0994C0861FB08280C38DAA09, + False, + ), + ( + generator_192, + int( + "0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309" + "7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8" + "bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447" + "bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd" + "8a", + 16, + ), + 0xFD14CDF1607F5EFB7B1793037B15BDF4BAA6F7C16341AB0B, + 0x83FA0795CC6C4795B9016DAC928FD6BAC32F3229A96312C4, + 0x8DFDB832951E0167C5D762A473C0416C5C15BC1195667DC1, + 0x1720288A2DC13FA1EC78F763F8FE2FF7354A7E6FDDE44520, + False, + ), + ( + generator_192, + int( + "0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919" + "2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196" + "683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc" + "eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072" + "fb", + 16, + ), + 0x674F941DC1A1F8B763C9334D726172D527B90CA324DB8828, + 0x65ADFA32E8B236CB33A3E84CF59BFB9417AE7E8EDE57A7FF, + 0x9508B9FDD7DAF0D8126F9E2BC5A35E4C6D800B5B804D7796, + 0x36F2BF6B21B987C77B53BB801B3435A577E3D493744BFAB0, + False, + ), + ( + generator_192, + int( + "0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c" + "e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa" + "e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc" + "55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca" + "6d", + 16, + ), + 0x10ECCA1AAD7220B56A62008B35170BFD5E35885C4014A19F, + 0x04EB61984C6C12ADE3BC47F3C629ECE7AA0A033B9948D686, + 0x82BFA4E82C0DFE9274169B86694E76CE993FD83B5C60F325, + 0xA97685676C59A65DBDE002FE9D613431FB183E8006D05633, + False, + ), + ( + generator_192, + int( + "0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f" + "698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98" + "f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2" + "78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76" + "e1", + 16, + ), + 0x6636653CB5B894CA65C448277B29DA3AD101C4C2300F7C04, + 0xFDF1CBB3FC3FD6A4F890B59E554544175FA77DBDBEB656C1, + 0xEAC2DDECDDFB79931A9C3D49C08DE0645C783A24CB365E1C, + 0x3549FEE3CFA7E5F93BC47D92D8BA100E881A2A93C22F8D50, + False, + ), + ( + generator_192, + int( + "0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6" + "c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7" + "a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b" + "9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b" + "a2", + 16, + ), + 0xA82BD718D01D354001148CD5F69B9EBF38FF6F21898F8AAA, + 0xE67CEEDE07FC2EBFAFD62462A51E4B6C6B3D5B537B7CAF3E, + 0x4D292486C620C3DE20856E57D3BB72FCDE4A73AD26376955, + 0xA85289591A6081D5728825520E62FF1C64F94235C04C7F95, + False, + ), + ( + generator_192, + int( + "0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a" + "961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91" + "0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53" + "808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6" + "58", + 16, + ), + 0x7D3B016B57758B160C4FCA73D48DF07AE3B6B30225126C2F, + 0x4AF3790D9775742BDE46F8DA876711BE1B65244B2B39E7EC, + 0x95F778F5F656511A5AB49A5D69DDD0929563C29CBC3A9E62, + 0x75C87FC358C251B4C83D2DD979FAAD496B539F9F2EE7A289, + False, + ), + ( + generator_192, + int( + "0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102" + "88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9" + "0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e" + "a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9" + "7a", + 16, + ), + 0x9362F28C4EF96453D8A2F849F21E881CD7566887DA8BEB4A, + 0xE64D26D8D74C48A024AE85D982EE74CD16046F4EE5333905, + 0xF3923476A296C88287E8DE914B0B324AD5A963319A4FE73B, + 0xF0BAEED7624ED00D15244D8BA2AEDE085517DBDEC8AC65F5, + True, + ), + ( + generator_192, + int( + "0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645" + "0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90" + "64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c" + "e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045" + "6d", + 16, + ), + 0xCC6FC032A846AAAC25533EB033522824F94E670FA997ECEF, + 0xE25463EF77A029ECCDA8B294FD63DD694E38D223D30862F1, + 0x066B1D07F3A40E679B620EDA7F550842A35C18B80C5EBE06, + 0xA0B0FB201E8F2DF65E2C4508EF303BDC90D934016F16B2DC, + False, + ), + ( + generator_192, + int( + "0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae" + "5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e" + "ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4" + "40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839" + "d7", + 16, + ), + 0x955C908FE900A996F7E2089BEE2F6376830F76A19135E753, + 0xBA0C42A91D3847DE4A592A46DC3FDAF45A7CC709B90DE520, + 0x1F58AD77FC04C782815A1405B0925E72095D906CBF52A668, + 0xF2E93758B3AF75EDF784F05A6761C9B9A6043C66B845B599, + False, + ), + ( + generator_192, + int( + "0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866" + "70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412" + "69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52" + "e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce" + "f3", + 16, + ), + 0x31F7FA05576D78A949B24812D4383107A9A45BB5FCCDD835, + 0x8DC0EB65994A90F02B5E19BD18B32D61150746C09107E76B, + 0xBE26D59E4E883DDE7C286614A767B31E49AD88789D3A78FF, + 0x8762CA831C1CE42DF77893C9B03119428E7A9B819B619068, + False, + ), + ( + generator_192, + int( + "0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f" + "387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357" + "2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670" + "716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1", + 16, + ), + 0x66AA8EDBBDB5CF8E28CEB51B5BDA891CAE2DF84819FE25C0, + 0x0C6BC2F69030A7CE58D4A00E3B3349844784A13B8936F8DA, + 0xA4661E69B1734F4A71B788410A464B71E7FFE42334484F23, + 0x738421CF5E049159D69C57A915143E226CAC8355E149AFE9, + False, + ), + ( + generator_192, + int( + "0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af" + "a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461" + "184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d" + "b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb", + 16, + ), + 0xBCFACF45139B6F5F690A4C35A5FFFA498794136A2353FC77, + 0x6F4A6C906316A6AFC6D98FE1F0399D056F128FE0270B0F22, + 0x9DB679A3DAFE48F7CCAD122933ACFE9DA0970B71C94C21C1, + 0x984C2DB99827576C0A41A5DA41E07D8CC768BC82F18C9DA9, + False, + ), +] + + +@pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", CURVE_192_KATS) +def test_signature_validity(gen, msg, qx, qy, r, s, expected): + """ + `msg` = message, `qx` and `qy` represent the base point on + elliptic curve of `gen`, `r` and `s` are the signature, and + `expected` is True iff the signature is expected to be valid.""" + pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy)) + assert expected == pubk.verifies(digest_integer(msg), Signature(r, s)) + + +@pytest.mark.parametrize( + "gen,msg,qx,qy,r,s,expected", [x for x in CURVE_192_KATS if x[6]] +) +def test_pk_recovery(gen, msg, r, s, qx, qy, expected): + del expected + sign = Signature(r, s) + pks = sign.recover_public_keys(digest_integer(msg), gen) + + assert pks + + # Test if the signature is valid for all found public keys + for pk in pks: + q = pk.point + test_signature_validity(gen, msg, q.x(), q.y(), r, s, True) + + # Test if the original public key is in the set of found keys + original_q = ellipticcurve.Point(gen.curve(), qx, qy) + points = [pk.point for pk in pks] + assert original_q in points + + +@st.composite +def st_random_gen_key_msg_nonce(draw): + """Hypothesis strategy for test_sig_verify().""" + name_gen = { + "generator_192": generator_192, + "generator_224": generator_224, + "generator_256": generator_256, + "generator_secp256k1": generator_secp256k1, + "generator_384": generator_384, + "generator_521": generator_521, + } + name = draw(st.sampled_from(sorted(name_gen.keys()))) + note("Generator used: {0}".format(name)) + generator = name_gen[name] + order = int(generator.order()) + + key = draw(st.integers(min_value=1, max_value=order)) + msg = draw(st.integers(min_value=1, max_value=order)) + nonce = draw( + st.integers(min_value=1, max_value=order + 1) + | st.integers(min_value=order >> 1, max_value=order) + ) + return generator, key, msg, nonce + + +SIG_VER_SETTINGS = dict(HYP_SETTINGS) +SIG_VER_SETTINGS["max_examples"] = 10 + + +@settings(**SIG_VER_SETTINGS) +@example((generator_224, 4, 1, 1)) +@given(st_random_gen_key_msg_nonce()) +def test_sig_verify(args): + """ + Check if signing and verification works for arbitrary messages and + that signatures for other messages are rejected. + """ + generator, sec_mult, msg, nonce = args + + pubkey = Public_key(generator, generator * sec_mult) + privkey = Private_key(pubkey, sec_mult) + + signature = privkey.sign(msg, nonce) + + assert pubkey.verifies(msg, signature) + + assert not pubkey.verifies(msg - 1, signature) + + +def test_int_to_string_with_zero(): + assert int_to_string(0) == b"\x00" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py new file mode 100644 index 000000000..7a09ad7f3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py @@ -0,0 +1,1079 @@ +import pickle +import hashlib +import pytest + +try: + import unittest2 as unittest +except ImportError: + import unittest +from hypothesis import given, settings, example +import hypothesis.strategies as st +from .ellipticcurve import PointEdwards, INFINITY, CurveEdTw +from .eddsa import ( + generator_ed25519, + curve_ed25519, + generator_ed448, + curve_ed448, + PrivateKey, + PublicKey, +) +from .ecdsa import generator_256, curve_256 +from .errors import MalformedPointError +from ._compat import a2b_hex, compat26_str + + +class TestA2B_Hex(unittest.TestCase): + def test_invalid_input(self): + with self.assertRaises(ValueError): + a2b_hex("abcdefghi") + + +def test_ed25519_curve_compare(): + assert curve_ed25519 != curve_256 + + +def test_ed25519_and_ed448_compare(): + assert curve_ed448 != curve_ed25519 + + +def test_ed25519_and_custom_curve_compare(): + a = CurveEdTw(curve_ed25519.p(), -curve_ed25519.a(), 1) + + assert curve_ed25519 != a + + +def test_ed25519_and_almost_exact_curve_compare(): + a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), 1) + + assert curve_ed25519 != a + + +def test_ed25519_and_same_curve_params(): + a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), curve_ed25519.d()) + + assert curve_ed25519 == a + assert not (curve_ed25519 != a) + + +def test_ed25519_contains_point(): + g = generator_ed25519 + assert curve_ed25519.contains_point(g.x(), g.y()) + + +def test_ed25519_contains_point_bad(): + assert not curve_ed25519.contains_point(1, 1) + + +def test_ed25519_double(): + a = generator_ed25519 + + z = a.double() + + assert isinstance(z, PointEdwards) + + x2 = int( + "24727413235106541002554574571675588834622768167397638456726423" + "682521233608206" + ) + y2 = int( + "15549675580280190176352668710449542251549572066445060580507079" + "593062643049417" + ) + + b = PointEdwards(curve_ed25519, x2, y2, 1, x2 * y2) + + assert z == b + assert a != b + + +def test_ed25519_add_as_double(): + a = generator_ed25519 + + z = a + a + + assert isinstance(z, PointEdwards) + + b = generator_ed25519.double() + + assert z == b + + +def test_ed25519_double_infinity(): + a = PointEdwards(curve_ed25519, 0, 1, 1, 0) + + z = a.double() + + assert z is INFINITY + + +def test_ed25519_double_badly_encoded_infinity(): + # invalid point, mostly to make instrumental happy + a = PointEdwards(curve_ed25519, 1, 1, 1, 0) + + z = a.double() + + assert z is INFINITY + + +def test_ed25519_eq_with_different_z(): + x = generator_ed25519.x() + y = generator_ed25519.y() + p = curve_ed25519.p() + + a = PointEdwards(curve_ed25519, x * 2 % p, y * 2 % p, 2, x * y * 2 % p) + b = PointEdwards(curve_ed25519, x * 3 % p, y * 3 % p, 3, x * y * 3 % p) + + assert a == b + + assert not (a != b) + + +def test_ed25519_eq_against_infinity(): + assert generator_ed25519 != INFINITY + + +def test_ed25519_eq_encoded_infinity_against_infinity(): + a = PointEdwards(curve_ed25519, 0, 1, 1, 0) + assert a == INFINITY + + +def test_ed25519_eq_bad_encode_of_infinity_against_infinity(): + # technically incorrect encoding of the point at infinity, but we check + # both X and T, so verify that just T==0 works + a = PointEdwards(curve_ed25519, 1, 1, 1, 0) + assert a == INFINITY + + +def test_ed25519_eq_against_non_Edwards_point(): + assert generator_ed25519 != generator_256 + + +def test_ed25519_eq_against_negated_point(): + g = generator_ed25519 + neg = PointEdwards(curve_ed25519, -g.x(), g.y(), 1, -g.x() * g.y()) + assert g != neg + + +def test_ed25519_eq_x_different_y(): + # not points on the curve, but __eq__ doesn't care + a = PointEdwards(curve_ed25519, 1, 1, 1, 1) + b = PointEdwards(curve_ed25519, 1, 2, 1, 2) + + assert a != b + + +def test_ed25519_test_normalisation_and_scaling(): + x = generator_ed25519.x() + y = generator_ed25519.y() + p = curve_ed25519.p() + + a = PointEdwards(curve_ed25519, x * 11 % p, y * 11 % p, 11, x * y * 11 % p) + + assert a.x() == x + assert a.y() == y + + a.scale() + + assert a.x() == x + assert a.y() == y + + a.scale() # second execution should be a noop + + assert a.x() == x + assert a.y() == y + + +def test_ed25519_add_three_times(): + a = generator_ed25519 + + z = a + a + a + + x3 = int( + "468967334644549386571235445953867877890461982801326656862413" + "21779790909858396" + ) + y3 = int( + "832484377853344397649037712036920113830141722629755531674120" + "2210403726505172" + ) + + b = PointEdwards(curve_ed25519, x3, y3, 1, x3 * y3) + + assert z == b + + +def test_ed25519_add_to_infinity(): + # generator * (order-1) + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + inf = inf_m_1 + generator_ed25519 + + assert inf is INFINITY + + +def test_ed25519_add_and_mul_equivalence(): + g = generator_ed25519 + + assert g + g == g * 2 + assert g + g + g == g * 3 + + +def test_ed25519_add_literal_infinity(): + g = generator_ed25519 + z = g + INFINITY + + assert z == g + + +def test_ed25519_add_infinity(): + inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) + g = generator_ed25519 + z = g + inf + + assert z == g + + z = inf + g + + assert z == g + + +class TestEd25519(unittest.TestCase): + def test_add_wrong_curves(self): + with self.assertRaises(ValueError) as e: + generator_ed25519 + generator_ed448 + + self.assertIn("different curve", str(e.exception)) + + def test_add_wrong_point_type(self): + with self.assertRaises(ValueError) as e: + generator_ed25519 + generator_256 + + self.assertIn("different curve", str(e.exception)) + + +def test_ed25519_mul_to_order_min_1(): + x1 = int( + "427838232691226969392843410947554224151809796397784248136826" + "78720006717057747" + ) + y1 = int( + "463168356949264781694283940034751631413079938662562256157830" + "33603165251855960" + ) + inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) + + assert generator_ed25519 * (generator_ed25519.order() - 1) == inf_m_1 + + +def test_ed25519_mul_to_infinity(): + assert generator_ed25519 * generator_ed25519.order() == INFINITY + + +def test_ed25519_mul_to_infinity_plus_1(): + g = generator_ed25519 + assert g * (g.order() + 1) == g + + +def test_ed25519_mul_and_add(): + g = generator_ed25519 + a = g * 128 + b = g * 64 + g * 64 + + assert a == b + + +def test_ed25519_mul_and_add_2(): + g = generator_ed25519 + + a = g * 123 + b = g * 120 + g * 3 + + assert a == b + + +def test_ed25519_mul_infinity(): + inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) + + z = inf * 11 + + assert z == INFINITY + + +def test_ed25519_mul_by_zero(): + z = generator_ed25519 * 0 + + assert z == INFINITY + + +def test_ed25519_mul_by_one(): + z = generator_ed25519 * 1 + + assert z == generator_ed25519 + + +def test_ed25519_mul_custom_point(): + # verify that multiplication without order set works + + g = generator_ed25519 + + a = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + z = a * 11 + + assert z == g * 11 + + +def test_ed25519_pickle(): + g = generator_ed25519 + assert pickle.loads(pickle.dumps(g)) == g + + +def test_ed448_eq_against_different_curve(): + assert generator_ed25519 != generator_ed448 + + +def test_ed448_double(): + g = generator_ed448 + z = g.double() + + assert isinstance(z, PointEdwards) + + x2 = int( + "4845591495304045936995492052586696895690942404582120401876" + "6013278705691214670908136440114445572635086627683154494739" + "7859048262938744149" + ) + y2 = int( + "4940887598674337276743026725267350893505445523037277237461" + "2648447308771911703729389009346215770388834286503647778745" + "3078312060500281069" + ) + + b = PointEdwards(curve_ed448, x2, y2, 1, x2 * y2) + + assert z == b + assert g != b + + +def test_ed448_add_as_double(): + g = generator_ed448 + z = g + g + + b = g.double() + + assert z == b + + +def test_ed448_mul_as_double(): + g = generator_ed448 + z = g * 2 + b = g.double() + + assert z == b + + +def test_ed448_add_to_infinity(): + # generator * (order - 1) + x1 = int( + "5022586839996825903617194737881084981068517190547539260353" + "6473749366191269932473977736719082931859264751085238669719" + "1187378895383117729" + ) + y1 = int( + "2988192100784814926760179304439306734375440401540802420959" + "2824137233150618983587600353687865541878473398230323350346" + "2500531545062832660" + ) + inf_m_1 = PointEdwards(curve_ed448, x1, y1, 1, x1 * y1) + + inf = inf_m_1 + generator_ed448 + + assert inf is INFINITY + + +def test_ed448_mul_to_infinity(): + g = generator_ed448 + inf = g * g.order() + + assert inf is INFINITY + + +def test_ed448_mul_to_infinity_plus_1(): + g = generator_ed448 + + z = g * (g.order() + 1) + + assert z == g + + +def test_ed448_add_and_mul_equivalence(): + g = generator_ed448 + + assert g + g == g * 2 + assert g + g + g == g * 3 + + +def test_ed25519_encode(): + g = generator_ed25519 + g_bytes = g.to_bytes() + assert len(g_bytes) == 32 + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + assert g_bytes == exp_bytes + + +def test_ed25519_decode(): + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + a = PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + assert a == generator_ed25519 + + +class TestEdwardsMalformed(unittest.TestCase): + def test_invalid_point(self): + exp_bytes = ( + b"\x78\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + ) + with self.assertRaises(MalformedPointError): + PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + def test_invalid_length(self): + exp_bytes = ( + b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" + b"\x66" + ) + with self.assertRaises(MalformedPointError) as e: + PointEdwards.from_bytes(curve_ed25519, exp_bytes) + + self.assertIn("length", str(e.exception)) + + def test_ed448_invalid(self): + exp_bytes = b"\xff" * 57 + with self.assertRaises(MalformedPointError): + PointEdwards.from_bytes(curve_ed448, exp_bytes) + + +def test_ed448_encode(): + g = generator_ed448 + g_bytes = g.to_bytes() + assert len(g_bytes) == 57 + exp_bytes = ( + b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" + b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" + b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" + b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" + ) + assert g_bytes == exp_bytes + + +def test_ed448_decode(): + exp_bytes = ( + b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" + b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" + b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" + b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" + ) + + a = PointEdwards.from_bytes(curve_ed448, exp_bytes) + + assert a == generator_ed448 + + +class TestEdDSAEquality(unittest.TestCase): + def test_equal_public_points(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed25519, b"\x01" * 32) + + self.assertEqual(key1, key2) + self.assertFalse(key1 != key2) + + def test_unequal_public_points(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed25519, b"\x03" * 32) + + self.assertNotEqual(key1, key2) + + def test_unequal_to_string(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = b"\x01" * 32 + + self.assertNotEqual(key1, key2) + + def test_unequal_publickey_curves(self): + key1 = PublicKey(generator_ed25519, b"\x01" * 32) + key2 = PublicKey(generator_ed448, b"\x03" * 56 + b"\x00") + + self.assertNotEqual(key1, key2) + self.assertTrue(key1 != key2) + + def test_equal_private_keys(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed25519, b"\x01" * 32) + + self.assertEqual(key1, key2) + self.assertFalse(key1 != key2) + + def test_unequal_private_keys(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed25519, b"\x02" * 32) + + self.assertNotEqual(key1, key2) + self.assertTrue(key1 != key2) + + def test_unequal_privatekey_to_string(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = b"\x01" * 32 + + self.assertNotEqual(key1, key2) + + def test_unequal_privatekey_curves(self): + key1 = PrivateKey(generator_ed25519, b"\x01" * 32) + key2 = PrivateKey(generator_ed448, b"\x01" * 57) + + self.assertNotEqual(key1, key2) + + +class TestInvalidEdDSAInputs(unittest.TestCase): + def test_wrong_length_of_private_key(self): + with self.assertRaises(ValueError): + PrivateKey(generator_ed25519, b"\x01" * 31) + + def test_wrong_length_of_public_key(self): + with self.assertRaises(ValueError): + PublicKey(generator_ed25519, b"\x01" * 33) + + def test_wrong_cofactor_curve(self): + ed_c = curve_ed25519 + + def _hash(data): + return hashlib.new("sha512", compat26_str(data)).digest() + + curve = CurveEdTw(ed_c.p(), ed_c.a(), ed_c.d(), 1, _hash) + g = generator_ed25519 + fake_gen = PointEdwards(curve, g.x(), g.y(), 1, g.x() * g.y()) + + with self.assertRaises(ValueError) as e: + PrivateKey(fake_gen, g.to_bytes()) + + self.assertIn("cofactor", str(e.exception)) + + def test_invalid_signature_length(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + with self.assertRaises(ValueError) as e: + key.verify(b"", b"\x01" * 65) + + self.assertIn("length", str(e.exception)) + + def test_changing_public_key(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + g = key.point + + new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + key.point = new_g + + self.assertEqual(g, key.point) + + def test_changing_public_key_to_different_point(self): + key = PublicKey(generator_ed25519, b"\x01" * 32) + + with self.assertRaises(ValueError) as e: + key.point = generator_ed25519 + + self.assertIn("coordinates", str(e.exception)) + + def test_invalid_s_value(self): + key = PublicKey( + generator_ed25519, + b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" + b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", + ) + sig_valid = bytearray( + b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" + b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" + b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" + b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" + ) + + self.assertTrue(key.verify(b"", sig_valid)) + + sig_invalid = bytearray(sig_valid) + sig_invalid[-1] = 0xFF + + with self.assertRaises(ValueError): + key.verify(b"", sig_invalid) + + def test_invalid_r_value(self): + key = PublicKey( + generator_ed25519, + b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" + b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", + ) + sig_valid = bytearray( + b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" + b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" + b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" + b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" + ) + + self.assertTrue(key.verify(b"", sig_valid)) + + sig_invalid = bytearray(sig_valid) + sig_invalid[0] = 0xE0 + + with self.assertRaises(ValueError): + key.verify(b"", sig_invalid) + + +HYP_SETTINGS = dict() +HYP_SETTINGS["max_examples"] = 10 + + +@settings(**HYP_SETTINGS) +@example(1) +@example(5) # smallest multiple that requires changing sign of x +@given(st.integers(min_value=1, max_value=int(generator_ed25519.order() - 1))) +def test_ed25519_encode_decode(multiple): + a = generator_ed25519 * multiple + + b = PointEdwards.from_bytes(curve_ed25519, a.to_bytes()) + + assert a == b + + +@settings(**HYP_SETTINGS) +@example(1) +@example(2) # smallest multiple that requires changing the sign of x +@given(st.integers(min_value=1, max_value=int(generator_ed448.order() - 1))) +def test_ed448_encode_decode(multiple): + a = generator_ed448 * multiple + + b = PointEdwards.from_bytes(curve_ed448, a.to_bytes()) + + assert a == b + + +@settings(**HYP_SETTINGS) +@example(1) +@example(2) +@given(st.integers(min_value=1, max_value=int(generator_ed25519.order()) - 1)) +def test_ed25519_mul_precompute_vs_naf(multiple): + """Compare multiplication with and without precomputation.""" + g = generator_ed25519 + new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) + + assert g * multiple == multiple * new_g + + +# Test vectors from RFC 8032 +TEST_VECTORS = [ + # TEST 1 + ( + generator_ed25519, + "9d61b19deffd5a60ba844af492ec2cc4" "4449c5697b326919703bac031cae7f60", + "d75a980182b10ab7d54bfed3c964073a" "0ee172f3daa62325af021a68f707511a", + "", + "e5564300c360ac729086e2cc806e828a" + "84877f1eb8e5d974d873e06522490155" + "5fb8821590a33bacc61e39701cf9b46b" + "d25bf5f0595bbe24655141438e7a100b", + ), + # TEST 2 + ( + generator_ed25519, + "4ccd089b28ff96da9db6c346ec114e0f" "5b8a319f35aba624da8cf6ed4fb8a6fb", + "3d4017c3e843895a92b70aa74d1b7ebc" "9c982ccf2ec4968cc0cd55f12af4660c", + "72", + "92a009a9f0d4cab8720e820b5f642540" + "a2b27b5416503f8fb3762223ebdb69da" + "085ac1e43e15996e458f3613d0f11d8c" + "387b2eaeb4302aeeb00d291612bb0c00", + ), + # TEST 3 + ( + generator_ed25519, + "c5aa8df43f9f837bedb7442f31dcb7b1" "66d38535076f094b85ce3a2e0b4458f7", + "fc51cd8e6218a1a38da47ed00230f058" "0816ed13ba3303ac5deb911548908025", + "af82", + "6291d657deec24024827e69c3abe01a3" + "0ce548a284743a445e3680d7db5ac3ac" + "18ff9b538d16f290ae67f760984dc659" + "4a7c15e9716ed28dc027beceea1ec40a", + ), + # TEST 1024 + ( + generator_ed25519, + "f5e5767cf153319517630f226876b86c" "8160cc583bc013744c6bf255f5cc0ee5", + "278117fc144c72340f67d0f2316e8386" "ceffbf2b2428c9c51fef7c597f1d426e", + "08b8b2b733424243760fe426a4b54908" + "632110a66c2f6591eabd3345e3e4eb98" + "fa6e264bf09efe12ee50f8f54e9f77b1" + "e355f6c50544e23fb1433ddf73be84d8" + "79de7c0046dc4996d9e773f4bc9efe57" + "38829adb26c81b37c93a1b270b20329d" + "658675fc6ea534e0810a4432826bf58c" + "941efb65d57a338bbd2e26640f89ffbc" + "1a858efcb8550ee3a5e1998bd177e93a" + "7363c344fe6b199ee5d02e82d522c4fe" + "ba15452f80288a821a579116ec6dad2b" + "3b310da903401aa62100ab5d1a36553e" + "06203b33890cc9b832f79ef80560ccb9" + "a39ce767967ed628c6ad573cb116dbef" + "efd75499da96bd68a8a97b928a8bbc10" + "3b6621fcde2beca1231d206be6cd9ec7" + "aff6f6c94fcd7204ed3455c68c83f4a4" + "1da4af2b74ef5c53f1d8ac70bdcb7ed1" + "85ce81bd84359d44254d95629e9855a9" + "4a7c1958d1f8ada5d0532ed8a5aa3fb2" + "d17ba70eb6248e594e1a2297acbbb39d" + "502f1a8c6eb6f1ce22b3de1a1f40cc24" + "554119a831a9aad6079cad88425de6bd" + "e1a9187ebb6092cf67bf2b13fd65f270" + "88d78b7e883c8759d2c4f5c65adb7553" + "878ad575f9fad878e80a0c9ba63bcbcc" + "2732e69485bbc9c90bfbd62481d9089b" + "eccf80cfe2df16a2cf65bd92dd597b07" + "07e0917af48bbb75fed413d238f5555a" + "7a569d80c3414a8d0859dc65a46128ba" + "b27af87a71314f318c782b23ebfe808b" + "82b0ce26401d2e22f04d83d1255dc51a" + "ddd3b75a2b1ae0784504df543af8969b" + "e3ea7082ff7fc9888c144da2af58429e" + "c96031dbcad3dad9af0dcbaaaf268cb8" + "fcffead94f3c7ca495e056a9b47acdb7" + "51fb73e666c6c655ade8297297d07ad1" + "ba5e43f1bca32301651339e22904cc8c" + "42f58c30c04aafdb038dda0847dd988d" + "cda6f3bfd15c4b4c4525004aa06eeff8" + "ca61783aacec57fb3d1f92b0fe2fd1a8" + "5f6724517b65e614ad6808d6f6ee34df" + "f7310fdc82aebfd904b01e1dc54b2927" + "094b2db68d6f903b68401adebf5a7e08" + "d78ff4ef5d63653a65040cf9bfd4aca7" + "984a74d37145986780fc0b16ac451649" + "de6188a7dbdf191f64b5fc5e2ab47b57" + "f7f7276cd419c17a3ca8e1b939ae49e4" + "88acba6b965610b5480109c8b17b80e1" + "b7b750dfc7598d5d5011fd2dcc5600a3" + "2ef5b52a1ecc820e308aa342721aac09" + "43bf6686b64b2579376504ccc493d97e" + "6aed3fb0f9cd71a43dd497f01f17c0e2" + "cb3797aa2a2f256656168e6c496afc5f" + "b93246f6b1116398a346f1a641f3b041" + "e989f7914f90cc2c7fff357876e506b5" + "0d334ba77c225bc307ba537152f3f161" + "0e4eafe595f6d9d90d11faa933a15ef1" + "369546868a7f3a45a96768d40fd9d034" + "12c091c6315cf4fde7cb68606937380d" + "b2eaaa707b4c4185c32eddcdd306705e" + "4dc1ffc872eeee475a64dfac86aba41c" + "0618983f8741c5ef68d3a101e8a3b8ca" + "c60c905c15fc910840b94c00a0b9d0", + "0aab4c900501b3e24d7cdf4663326a3a" + "87df5e4843b2cbdb67cbf6e460fec350" + "aa5371b1508f9f4528ecea23c436d94b" + "5e8fcd4f681e30a6ac00a9704a188a03", + ), + # TEST SHA(abc) + ( + generator_ed25519, + "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42", + "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf", + "ddaf35a193617abacc417349ae204131" + "12e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd" + "454d4423643ce80e2a9ac94fa54ca49f", + "dc2a4459e7369633a52b1bf277839a00" + "201009a3efbf3ecb69bea2186c26b589" + "09351fc9ac90b3ecfdfbc7c66431e030" + "3dca179c138ac17ad9bef1177331a704", + ), + # Blank + ( + generator_ed448, + "6c82a562cb808d10d632be89c8513ebf" + "6c929f34ddfa8c9f63c9960ef6e348a3" + "528c8a3fcc2f044e39a3fc5b94492f8f" + "032e7549a20098f95b", + "5fd7449b59b461fd2ce787ec616ad46a" + "1da1342485a70e1f8a0ea75d80e96778" + "edf124769b46c7061bd6783df1e50f6c" + "d1fa1abeafe8256180", + "", + "533a37f6bbe457251f023c0d88f976ae" + "2dfb504a843e34d2074fd823d41a591f" + "2b233f034f628281f2fd7a22ddd47d78" + "28c59bd0a21bfd3980ff0d2028d4b18a" + "9df63e006c5d1c2d345b925d8dc00b41" + "04852db99ac5c7cdda8530a113a0f4db" + "b61149f05a7363268c71d95808ff2e65" + "2600", + ), + # 1 octet + ( + generator_ed448, + "c4eab05d357007c632f3dbb48489924d" + "552b08fe0c353a0d4a1f00acda2c463a" + "fbea67c5e8d2877c5e3bc397a659949e" + "f8021e954e0a12274e", + "43ba28f430cdff456ae531545f7ecd0a" + "c834a55d9358c0372bfa0c6c6798c086" + "6aea01eb00742802b8438ea4cb82169c" + "235160627b4c3a9480", + "03", + "26b8f91727bd62897af15e41eb43c377" + "efb9c610d48f2335cb0bd0087810f435" + "2541b143c4b981b7e18f62de8ccdf633" + "fc1bf037ab7cd779805e0dbcc0aae1cb" + "cee1afb2e027df36bc04dcecbf154336" + "c19f0af7e0a6472905e799f1953d2a0f" + "f3348ab21aa4adafd1d234441cf807c0" + "3a00", + ), + # 11 octets + ( + generator_ed448, + "cd23d24f714274e744343237b93290f5" + "11f6425f98e64459ff203e8985083ffd" + "f60500553abc0e05cd02184bdb89c4cc" + "d67e187951267eb328", + "dcea9e78f35a1bf3499a831b10b86c90" + "aac01cd84b67a0109b55a36e9328b1e3" + "65fce161d71ce7131a543ea4cb5f7e9f" + "1d8b00696447001400", + "0c3e544074ec63b0265e0c", + "1f0a8888ce25e8d458a21130879b840a" + "9089d999aaba039eaf3e3afa090a09d3" + "89dba82c4ff2ae8ac5cdfb7c55e94d5d" + "961a29fe0109941e00b8dbdeea6d3b05" + "1068df7254c0cdc129cbe62db2dc957d" + "bb47b51fd3f213fb8698f064774250a5" + "028961c9bf8ffd973fe5d5c206492b14" + "0e00", + ), + # 12 octets + ( + generator_ed448, + "258cdd4ada32ed9c9ff54e63756ae582" + "fb8fab2ac721f2c8e676a72768513d93" + "9f63dddb55609133f29adf86ec9929dc" + "cb52c1c5fd2ff7e21b", + "3ba16da0c6f2cc1f30187740756f5e79" + "8d6bc5fc015d7c63cc9510ee3fd44adc" + "24d8e968b6e46e6f94d19b945361726b" + "d75e149ef09817f580", + "64a65f3cdedcdd66811e2915", + "7eeeab7c4e50fb799b418ee5e3197ff6" + "bf15d43a14c34389b59dd1a7b1b85b4a" + "e90438aca634bea45e3a2695f1270f07" + "fdcdf7c62b8efeaf00b45c2c96ba457e" + "b1a8bf075a3db28e5c24f6b923ed4ad7" + "47c3c9e03c7079efb87cb110d3a99861" + "e72003cbae6d6b8b827e4e6c143064ff" + "3c00", + ), + # 13 octets + ( + generator_ed448, + "7ef4e84544236752fbb56b8f31a23a10" + "e42814f5f55ca037cdcc11c64c9a3b29" + "49c1bb60700314611732a6c2fea98eeb" + "c0266a11a93970100e", + "b3da079b0aa493a5772029f0467baebe" + "e5a8112d9d3a22532361da294f7bb381" + "5c5dc59e176b4d9f381ca0938e13c6c0" + "7b174be65dfa578e80", + "64a65f3cdedcdd66811e2915e7", + "6a12066f55331b6c22acd5d5bfc5d712" + "28fbda80ae8dec26bdd306743c5027cb" + "4890810c162c027468675ecf645a8317" + "6c0d7323a2ccde2d80efe5a1268e8aca" + "1d6fbc194d3f77c44986eb4ab4177919" + "ad8bec33eb47bbb5fc6e28196fd1caf5" + "6b4e7e0ba5519234d047155ac727a105" + "3100", + ), + # 64 octets + ( + generator_ed448, + "d65df341ad13e008567688baedda8e9d" + "cdc17dc024974ea5b4227b6530e339bf" + "f21f99e68ca6968f3cca6dfe0fb9f4fa" + "b4fa135d5542ea3f01", + "df9705f58edbab802c7f8363cfe5560a" + "b1c6132c20a9f1dd163483a26f8ac53a" + "39d6808bf4a1dfbd261b099bb03b3fb5" + "0906cb28bd8a081f00", + "bd0f6a3747cd561bdddf4640a332461a" + "4a30a12a434cd0bf40d766d9c6d458e5" + "512204a30c17d1f50b5079631f64eb31" + "12182da3005835461113718d1a5ef944", + "554bc2480860b49eab8532d2a533b7d5" + "78ef473eeb58c98bb2d0e1ce488a98b1" + "8dfde9b9b90775e67f47d4a1c3482058" + "efc9f40d2ca033a0801b63d45b3b722e" + "f552bad3b4ccb667da350192b61c508c" + "f7b6b5adadc2c8d9a446ef003fb05cba" + "5f30e88e36ec2703b349ca229c267083" + "3900", + ), + # 256 octets + ( + generator_ed448, + "2ec5fe3c17045abdb136a5e6a913e32a" + "b75ae68b53d2fc149b77e504132d3756" + "9b7e766ba74a19bd6162343a21c8590a" + "a9cebca9014c636df5", + "79756f014dcfe2079f5dd9e718be4171" + "e2ef2486a08f25186f6bff43a9936b9b" + "fe12402b08ae65798a3d81e22e9ec80e" + "7690862ef3d4ed3a00", + "15777532b0bdd0d1389f636c5f6b9ba7" + "34c90af572877e2d272dd078aa1e567c" + "fa80e12928bb542330e8409f31745041" + "07ecd5efac61ae7504dabe2a602ede89" + "e5cca6257a7c77e27a702b3ae39fc769" + "fc54f2395ae6a1178cab4738e543072f" + "c1c177fe71e92e25bf03e4ecb72f47b6" + "4d0465aaea4c7fad372536c8ba516a60" + "39c3c2a39f0e4d832be432dfa9a706a6" + "e5c7e19f397964ca4258002f7c0541b5" + "90316dbc5622b6b2a6fe7a4abffd9610" + "5eca76ea7b98816af0748c10df048ce0" + "12d901015a51f189f3888145c03650aa" + "23ce894c3bd889e030d565071c59f409" + "a9981b51878fd6fc110624dcbcde0bf7" + "a69ccce38fabdf86f3bef6044819de11", + "c650ddbb0601c19ca11439e1640dd931" + "f43c518ea5bea70d3dcde5f4191fe53f" + "00cf966546b72bcc7d58be2b9badef28" + "743954e3a44a23f880e8d4f1cfce2d7a" + "61452d26da05896f0a50da66a239a8a1" + "88b6d825b3305ad77b73fbac0836ecc6" + "0987fd08527c1a8e80d5823e65cafe2a" + "3d00", + ), + # 1023 octets + ( + generator_ed448, + "872d093780f5d3730df7c212664b37b8" + "a0f24f56810daa8382cd4fa3f77634ec" + "44dc54f1c2ed9bea86fafb7632d8be19" + "9ea165f5ad55dd9ce8", + "a81b2e8a70a5ac94ffdbcc9badfc3feb" + "0801f258578bb114ad44ece1ec0e799d" + "a08effb81c5d685c0c56f64eecaef8cd" + "f11cc38737838cf400", + "6ddf802e1aae4986935f7f981ba3f035" + "1d6273c0a0c22c9c0e8339168e675412" + "a3debfaf435ed651558007db4384b650" + "fcc07e3b586a27a4f7a00ac8a6fec2cd" + "86ae4bf1570c41e6a40c931db27b2faa" + "15a8cedd52cff7362c4e6e23daec0fbc" + "3a79b6806e316efcc7b68119bf46bc76" + "a26067a53f296dafdbdc11c77f7777e9" + "72660cf4b6a9b369a6665f02e0cc9b6e" + "dfad136b4fabe723d2813db3136cfde9" + "b6d044322fee2947952e031b73ab5c60" + "3349b307bdc27bc6cb8b8bbd7bd32321" + "9b8033a581b59eadebb09b3c4f3d2277" + "d4f0343624acc817804728b25ab79717" + "2b4c5c21a22f9c7839d64300232eb66e" + "53f31c723fa37fe387c7d3e50bdf9813" + "a30e5bb12cf4cd930c40cfb4e1fc6225" + "92a49588794494d56d24ea4b40c89fc0" + "596cc9ebb961c8cb10adde976a5d602b" + "1c3f85b9b9a001ed3c6a4d3b1437f520" + "96cd1956d042a597d561a596ecd3d173" + "5a8d570ea0ec27225a2c4aaff26306d1" + "526c1af3ca6d9cf5a2c98f47e1c46db9" + "a33234cfd4d81f2c98538a09ebe76998" + "d0d8fd25997c7d255c6d66ece6fa56f1" + "1144950f027795e653008f4bd7ca2dee" + "85d8e90f3dc315130ce2a00375a318c7" + "c3d97be2c8ce5b6db41a6254ff264fa6" + "155baee3b0773c0f497c573f19bb4f42" + "40281f0b1f4f7be857a4e59d416c06b4" + "c50fa09e1810ddc6b1467baeac5a3668" + "d11b6ecaa901440016f389f80acc4db9" + "77025e7f5924388c7e340a732e554440" + "e76570f8dd71b7d640b3450d1fd5f041" + "0a18f9a3494f707c717b79b4bf75c984" + "00b096b21653b5d217cf3565c9597456" + "f70703497a078763829bc01bb1cbc8fa" + "04eadc9a6e3f6699587a9e75c94e5bab" + "0036e0b2e711392cff0047d0d6b05bd2" + "a588bc109718954259f1d86678a579a3" + "120f19cfb2963f177aeb70f2d4844826" + "262e51b80271272068ef5b3856fa8535" + "aa2a88b2d41f2a0e2fda7624c2850272" + "ac4a2f561f8f2f7a318bfd5caf969614" + "9e4ac824ad3460538fdc25421beec2cc" + "6818162d06bbed0c40a387192349db67" + "a118bada6cd5ab0140ee273204f628aa" + "d1c135f770279a651e24d8c14d75a605" + "9d76b96a6fd857def5e0b354b27ab937" + "a5815d16b5fae407ff18222c6d1ed263" + "be68c95f32d908bd895cd76207ae7264" + "87567f9a67dad79abec316f683b17f2d" + "02bf07e0ac8b5bc6162cf94697b3c27c" + "d1fea49b27f23ba2901871962506520c" + "392da8b6ad0d99f7013fbc06c2c17a56" + "9500c8a7696481c1cd33e9b14e40b82e" + "79a5f5db82571ba97bae3ad3e0479515" + "bb0e2b0f3bfcd1fd33034efc6245eddd" + "7ee2086ddae2600d8ca73e214e8c2b0b" + "db2b047c6a464a562ed77b73d2d841c4" + "b34973551257713b753632efba348169" + "abc90a68f42611a40126d7cb21b58695" + "568186f7e569d2ff0f9e745d0487dd2e" + "b997cafc5abf9dd102e62ff66cba87", + "e301345a41a39a4d72fff8df69c98075" + "a0cc082b802fc9b2b6bc503f926b65bd" + "df7f4c8f1cb49f6396afc8a70abe6d8a" + "ef0db478d4c6b2970076c6a0484fe76d" + "76b3a97625d79f1ce240e7c576750d29" + "5528286f719b413de9ada3e8eb78ed57" + "3603ce30d8bb761785dc30dbc320869e" + "1a00", + ), +] + + +@pytest.mark.parametrize( + "generator,private_key,public_key,message,signature", + TEST_VECTORS, +) +def test_vectors(generator, private_key, public_key, message, signature): + private_key = a2b_hex(private_key) + public_key = a2b_hex(public_key) + message = a2b_hex(message) + signature = a2b_hex(signature) + + sig_key = PrivateKey(generator, private_key) + ver_key = PublicKey(generator, public_key) + + assert sig_key.public_key().public_key() == ver_key.public_key() + + gen_sig = sig_key.sign(message) + + assert gen_sig == signature + + assert ver_key.verify(message, signature) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py new file mode 100644 index 000000000..85faef4d7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py @@ -0,0 +1,199 @@ +import pytest + +try: + import unittest2 as unittest +except ImportError: + import unittest +from hypothesis import given, settings +import hypothesis.strategies as st + +try: + from hypothesis import HealthCheck + + HC_PRESENT = True +except ImportError: # pragma: no cover + HC_PRESENT = False +from .numbertheory import inverse_mod +from .ellipticcurve import CurveFp, INFINITY, Point + + +HYP_SETTINGS = {} +if HC_PRESENT: # pragma: no branch + HYP_SETTINGS["suppress_health_check"] = [HealthCheck.too_slow] + HYP_SETTINGS["deadline"] = 5000 + + +# NIST Curve P-192: +p = 6277101735386680763835789423207666416083908700390324961279 +r = 6277101735386680763835789423176059013767194773182842284081 +# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 +# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 +b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 +Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 +Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 + +c192 = CurveFp(p, -3, b) +p192 = Point(c192, Gx, Gy, r) + +c_23 = CurveFp(23, 1, 1) +g_23 = Point(c_23, 13, 7, 7) + + +HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) +HYP_SLOW_SETTINGS["max_examples"] = 10 + + +@settings(**HYP_SLOW_SETTINGS) +@given(st.integers(min_value=1, max_value=r + 1)) +def test_p192_mult_tests(multiple): + inv_m = inverse_mod(multiple, r) + + p1 = p192 * multiple + assert p1 * inv_m == p192 + + +def add_n_times(point, n): + ret = INFINITY + i = 0 + while i <= n: + yield ret + ret = ret + point + i += 1 + + +# From X9.62 I.1 (p. 96): +@pytest.mark.parametrize( + "p, m, check", + [(g_23, n, exp) for n, exp in enumerate(add_n_times(g_23, 8))], + ids=["g_23 test with mult {0}".format(i) for i in range(9)], +) +def test_add_and_mult_equivalence(p, m, check): + assert p * m == check + + +class TestCurve(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.c_23 = CurveFp(23, 1, 1) + + def test_equality_curves(self): + self.assertEqual(self.c_23, CurveFp(23, 1, 1)) + + def test_inequality_curves(self): + c192 = CurveFp(p, -3, b) + self.assertNotEqual(self.c_23, c192) + + def test_usability_in_a_hashed_collection_curves(self): + {self.c_23: None} + + def test_hashability_curves(self): + hash(self.c_23) + + def test_conflation_curves(self): + ne1, ne2, ne3 = CurveFp(24, 1, 1), CurveFp(23, 2, 1), CurveFp(23, 1, 2) + eq1, eq2, eq3 = CurveFp(23, 1, 1), CurveFp(23, 1, 1), self.c_23 + self.assertEqual(len(set((c_23, eq1, eq2, eq3))), 1) + self.assertEqual(len(set((c_23, ne1, ne2, ne3))), 4) + self.assertDictEqual({c_23: None}, {eq1: None}) + self.assertIn(eq2, {eq3: None}) + + +class TestPoint(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.c_23 = CurveFp(23, 1, 1) + cls.g_23 = Point(cls.c_23, 13, 7, 7) + + p = 6277101735386680763835789423207666416083908700390324961279 + r = 6277101735386680763835789423176059013767194773182842284081 + # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 + # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 + b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 + Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 + Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 + + cls.c192 = CurveFp(p, -3, b) + cls.p192 = Point(cls.c192, Gx, Gy, r) + + def test_p192(self): + # Checking against some sample computations presented + # in X9.62: + d = 651056770906015076056810763456358567190100156695615665659 + Q = d * self.p192 + self.assertEqual( + Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 + ) + + k = 6140507067065001063065065565667405560006161556565665656654 + R = k * self.p192 + self.assertEqual( + R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + ) + self.assertEqual( + R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + ) + + u1 = 2563697409189434185194736134579731015366492496392189760599 + u2 = 6266643813348617967186477710235785849136406323338782220568 + temp = u1 * self.p192 + u2 * Q + self.assertEqual( + temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + ) + self.assertEqual( + temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + ) + + def test_double_infinity(self): + p1 = INFINITY + p3 = p1.double() + self.assertEqual(p1, p3) + self.assertEqual(p3.x(), p1.x()) + self.assertEqual(p3.y(), p3.y()) + + def test_double(self): + x1, y1, x3, y3 = (3, 10, 7, 12) + + p1 = Point(self.c_23, x1, y1) + p3 = p1.double() + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_multiply(self): + x1, y1, m, x3, y3 = (3, 10, 2, 7, 12) + p1 = Point(self.c_23, x1, y1) + p3 = p1 * m + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + # Trivial tests from X9.62 B.3: + def test_add(self): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + + x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20) + p1 = Point(self.c_23, x1, y1) + p2 = Point(self.c_23, x2, y2) + p3 = p1 + p2 + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_add_as_double(self): + """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" + + x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) + p1 = Point(self.c_23, x1, y1) + p2 = Point(self.c_23, x2, y2) + p3 = p1 + p2 + self.assertEqual(p3.x(), x3) + self.assertEqual(p3.y(), y3) + + def test_equality_points(self): + self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7)) + + def test_inequality_points(self): + c = CurveFp(100, -3, 100) + p = Point(c, 100, 100, 100) + self.assertNotEqual(self.g_23, p) + + def test_inequality_points_diff_types(self): + c = CurveFp(100, -3, 100) + self.assertNotEqual(self.g_23, c) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py new file mode 100644 index 000000000..1f5280482 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py @@ -0,0 +1,657 @@ +import pickle + +try: + import unittest2 as unittest +except ImportError: + import unittest + +import os +import sys +import signal +import pytest +import threading +import platform +import hypothesis.strategies as st +from hypothesis import given, assume, settings, example + +from .ellipticcurve import CurveFp, PointJacobi, INFINITY +from .ecdsa import ( + generator_256, + curve_256, + generator_224, + generator_brainpoolp160r1, + curve_brainpoolp160r1, + generator_112r2, +) +from .numbertheory import inverse_mod +from .util import randrange + + +NO_OLD_SETTINGS = {} +if sys.version_info > (2, 7): # pragma: no branch + NO_OLD_SETTINGS["deadline"] = 5000 + + +class TestJacobi(unittest.TestCase): + def test___init__(self): + curve = object() + x = 2 + y = 3 + z = 1 + order = 4 + pj = PointJacobi(curve, x, y, z, order) + + self.assertEqual(pj.order(), order) + self.assertIs(pj.curve(), curve) + self.assertEqual(pj.x(), x) + self.assertEqual(pj.y(), y) + + def test_add_with_different_curves(self): + p_a = PointJacobi.from_affine(generator_256) + p_b = PointJacobi.from_affine(generator_224) + + with self.assertRaises(ValueError): + p_a + p_b + + def test_compare_different_curves(self): + self.assertNotEqual(generator_256, generator_224) + + def test_equality_with_non_point(self): + pj = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(pj, "value") + + def test_conversion(self): + pj = PointJacobi.from_affine(generator_256) + pw = pj.to_affine() + + self.assertEqual(generator_256, pw) + + def test_single_double(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256.double() + + pj = pj.double() + + self.assertEqual(pj.x(), pw.x()) + self.assertEqual(pj.y(), pw.y()) + + def test_double_with_zero_point(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + pj = pj.double() + + self.assertIs(pj, INFINITY) + + def test_double_with_zero_equivalent_point(self): + pj = PointJacobi(curve_256, 0, curve_256.p(), 1) + + pj = pj.double() + + self.assertIs(pj, INFINITY) + + def test_double_with_zero_equivalent_point_non_1_z(self): + pj = PointJacobi(curve_256, 0, curve_256.p(), 2) + + pj = pj.double() + + self.assertIs(pj, INFINITY) + + def test_compare_with_affine_point(self): + pj = PointJacobi.from_affine(generator_256) + pa = pj.to_affine() + + self.assertEqual(pj, pa) + self.assertEqual(pa, pj) + + def test_to_affine_with_zero_point(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + pa = pj.to_affine() + + self.assertIs(pa, INFINITY) + + def test_add_with_affine_point(self): + pj = PointJacobi.from_affine(generator_256) + pa = pj.to_affine() + + s = pj + pa + + self.assertEqual(s, pj.double()) + + def test_radd_with_affine_point(self): + pj = PointJacobi.from_affine(generator_256) + pa = pj.to_affine() + + s = pa + pj + + self.assertEqual(s, pj.double()) + + def test_add_with_infinity(self): + pj = PointJacobi.from_affine(generator_256) + + s = pj + INFINITY + + self.assertEqual(s, pj) + + def test_add_zero_point_to_affine(self): + pa = PointJacobi.from_affine(generator_256).to_affine() + pj = PointJacobi(curve_256, 0, 0, 1) + + s = pj + pa + + self.assertIs(s, pa) + + def test_multiply_by_zero(self): + pj = PointJacobi.from_affine(generator_256) + + pj = pj * 0 + + self.assertIs(pj, INFINITY) + + def test_zero_point_multiply_by_one(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + pj = pj * 1 + + self.assertIs(pj, INFINITY) + + def test_multiply_by_one(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256 * 1 + + pj = pj * 1 + + self.assertEqual(pj.x(), pw.x()) + self.assertEqual(pj.y(), pw.y()) + + def test_multiply_by_two(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256 * 2 + + pj = pj * 2 + + self.assertEqual(pj.x(), pw.x()) + self.assertEqual(pj.y(), pw.y()) + + def test_rmul_by_two(self): + pj = PointJacobi.from_affine(generator_256) + pw = generator_256 * 2 + + pj = 2 * pj + + self.assertEqual(pj, pw) + + def test_compare_non_zero_with_infinity(self): + pj = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(pj, INFINITY) + + def test_compare_zero_point_with_infinity(self): + pj = PointJacobi(curve_256, 0, 0, 1) + + self.assertEqual(pj, INFINITY) + + def test_compare_double_with_multiply(self): + pj = PointJacobi.from_affine(generator_256) + dbl = pj.double() + mlpl = pj * 2 + + self.assertEqual(dbl, mlpl) + + @settings(max_examples=10) + @given( + st.integers( + min_value=0, max_value=int(generator_brainpoolp160r1.order()) + ) + ) + def test_multiplications(self, mul): + pj = PointJacobi.from_affine(generator_brainpoolp160r1) + pw = pj.to_affine() * mul + + pj = pj * mul + + self.assertEqual((pj.x(), pj.y()), (pw.x(), pw.y())) + self.assertEqual(pj, pw) + + @settings(max_examples=10) + @given( + st.integers( + min_value=0, max_value=int(generator_brainpoolp160r1.order()) + ) + ) + @example(0) + @example(int(generator_brainpoolp160r1.order())) + def test_precompute(self, mul): + precomp = generator_brainpoolp160r1 + self.assertTrue(precomp._PointJacobi__precompute) + pj = PointJacobi.from_affine(generator_brainpoolp160r1) + + a = precomp * mul + b = pj * mul + + self.assertEqual(a, b) + + @settings(max_examples=10) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + ) + @example(3, 3) + def test_add_scaled_points(self, a_mul, b_mul): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + @settings(max_examples=10) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), + ) + def test_add_one_scaled_point(self, a_mul, b_mul, new_z): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + p = curve_brainpoolp160r1.p() + + assume(inverse_mod(new_z, p)) + + new_zz = new_z * new_z % p + + b = PointJacobi( + curve_brainpoolp160r1, + b.x() * new_zz % p, + b.y() * new_zz * new_z % p, + new_z, + ) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + @settings(max_examples=10) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), + ) + @example(1, 1, 1) + @example(3, 3, 3) + @example(2, int(generator_brainpoolp160r1.order() - 2), 1) + @example(2, int(generator_brainpoolp160r1.order() - 2), 3) + def test_add_same_scale_points(self, a_mul, b_mul, new_z): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + p = curve_brainpoolp160r1.p() + + assume(inverse_mod(new_z, p)) + + new_zz = new_z * new_z % p + + a = PointJacobi( + curve_brainpoolp160r1, + a.x() * new_zz % p, + a.y() * new_zz * new_z % p, + new_z, + ) + b = PointJacobi( + curve_brainpoolp160r1, + b.x() * new_zz % p, + b.y() * new_zz * new_z % p, + new_z, + ) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + def test_add_same_scale_points_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + z1 = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + y = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + + c = a + a + + self.assertEqual(c, x + y) + + @settings(max_examples=14) + @given( + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.integers( + min_value=1, max_value=int(generator_brainpoolp160r1.order()) + ), + st.lists( + st.integers( + min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1) + ), + min_size=2, + max_size=2, + unique=True, + ), + ) + @example(2, 2, [2, 1]) + @example(2, 2, [2, 3]) + @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 3]) + @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 1]) + def test_add_different_scale_points(self, a_mul, b_mul, new_z): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1) + a = PointJacobi.from_affine(j_g * a_mul) + b = PointJacobi.from_affine(j_g * b_mul) + + p = curve_brainpoolp160r1.p() + + assume(inverse_mod(new_z[0], p)) + assume(inverse_mod(new_z[1], p)) + + new_zz0 = new_z[0] * new_z[0] % p + new_zz1 = new_z[1] * new_z[1] % p + + a = PointJacobi( + curve_brainpoolp160r1, + a.x() * new_zz0 % p, + a.y() * new_zz0 * new_z[0] % p, + new_z[0], + ) + b = PointJacobi( + curve_brainpoolp160r1, + b.x() * new_zz1 % p, + b.y() * new_zz1 * new_z[1] % p, + new_z[1], + ) + + c = a + b + + self.assertEqual(c, j_g * (a_mul + b_mul)) + + def test_add_different_scale_points_static(self): + j_g = generator_brainpoolp160r1 + p = curve_brainpoolp160r1.p() + a = j_g * 11 + a.scale() + z1 = 13 + x = PointJacobi( + curve_brainpoolp160r1, + a.x() * z1**2 % p, + a.y() * z1**3 % p, + z1, + ) + z2 = 29 + y = PointJacobi( + curve_brainpoolp160r1, + a.x() * z2**2 % p, + a.y() * z2**3 % p, + z2, + ) + + c = a + a + + self.assertEqual(c, x + y) + + def test_add_point_3_times(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertEqual(j_g * 3, j_g + j_g + j_g) + + def test_mul_without_order(self): + j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) + + self.assertEqual(j_g * generator_256.order(), INFINITY) + + def test_mul_add_inf(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertEqual(j_g, j_g.mul_add(1, INFINITY, 1)) + + def test_mul_add_same(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertEqual(j_g * 2, j_g.mul_add(1, j_g, 1)) + + def test_mul_add_precompute(self): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) + b = PointJacobi.from_affine(j_g * 255, True) + + self.assertEqual(j_g * 256, j_g + b) + self.assertEqual(j_g * (5 + 255 * 7), j_g * 5 + b * 7) + self.assertEqual(j_g * (5 + 255 * 7), j_g.mul_add(5, b, 7)) + + def test_mul_add_precompute_large(self): + j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) + b = PointJacobi.from_affine(j_g * 255, True) + + self.assertEqual(j_g * 256, j_g + b) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 + ) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) + ) + + def test_mul_add_to_mul(self): + j_g = PointJacobi.from_affine(generator_256) + + a = j_g * 3 + b = j_g.mul_add(2, j_g, 1) + + self.assertEqual(a, b) + + def test_mul_add_differnt(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = j_g * 2 + + self.assertEqual(j_g.mul_add(1, w_a, 1), j_g * 3) + + def test_mul_add_slightly_different(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = j_g * 2 + w_b = j_g * 3 + + self.assertEqual(w_a.mul_add(1, w_b, 3), w_a * 1 + w_b * 3) + + def test_mul_add(self): + j_g = PointJacobi.from_affine(generator_256) + + w_a = generator_256 * 255 + w_b = generator_256 * (0xA8 * 0xF0) + j_b = j_g * 0xA8 + + ret = j_g.mul_add(255, j_b, 0xF0) + + self.assertEqual(ret.to_affine(), w_a + w_b) + + def test_mul_add_large(self): + j_g = PointJacobi.from_affine(generator_256) + b = PointJacobi.from_affine(j_g * 255) + + self.assertEqual(j_g * 256, j_g + b) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 + ) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) + ) + + def test_mul_add_with_infinity_as_result(self): + j_g = PointJacobi.from_affine(generator_256) + + order = generator_256.order() + + b = PointJacobi.from_affine(generator_256 * 256) + + self.assertEqual(j_g.mul_add(order % 256, b, order // 256), INFINITY) + + def test_mul_add_without_order(self): + j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) + + order = generator_256.order() + + w_b = generator_256 * 34 + w_b.scale() + + b = PointJacobi(curve_256, w_b.x(), w_b.y(), 1) + + self.assertEqual(j_g.mul_add(order % 34, b, order // 34), INFINITY) + + def test_mul_add_with_doubled_negation_of_itself(self): + j_g = PointJacobi.from_affine(generator_256 * 17) + + dbl_neg = 2 * (-j_g) + + self.assertEqual(j_g.mul_add(4, dbl_neg, 2), INFINITY) + + def test_equality(self): + pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + pj2 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + self.assertEqual(pj1, pj2) + + def test_equality_with_invalid_object(self): + j_g = PointJacobi.from_affine(generator_256) + + self.assertNotEqual(j_g, 12) + + def test_equality_with_wrong_curves(self): + p_a = PointJacobi.from_affine(generator_256) + p_b = PointJacobi.from_affine(generator_224) + + self.assertNotEqual(p_a, p_b) + + def test_pickle(self): + pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) + self.assertEqual(pickle.loads(pickle.dumps(pj)), pj) + + @settings(**NO_OLD_SETTINGS) + @given(st.integers(min_value=1, max_value=10)) + def test_multithreading(self, thread_num): + # ensure that generator's precomputation table is filled + generator_112r2 * 2 + + # create a fresh point that doesn't have a filled precomputation table + gen = generator_112r2 + gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) + + self.assertEqual(gen._PointJacobi__precompute, []) + + def runner(generator): + order = generator.order() + for _ in range(10): + generator * randrange(order) + + threads = [] + for _ in range(thread_num): + threads.append(threading.Thread(target=runner, args=(gen,))) + + for t in threads: + t.start() + + runner(gen) + + for t in threads: + t.join() + + self.assertEqual( + gen._PointJacobi__precompute, + generator_112r2._PointJacobi__precompute, + ) + + @pytest.mark.skipif( + platform.system() == "Windows", + reason="there are no signals on Windows", + ) + def test_multithreading_with_interrupts(self): + thread_num = 10 + # ensure that generator's precomputation table is filled + generator_112r2 * 2 + + # create a fresh point that doesn't have a filled precomputation table + gen = generator_112r2 + gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) + + self.assertEqual(gen._PointJacobi__precompute, []) + + def runner(generator): + order = generator.order() + for _ in range(50): + generator * randrange(order) + + def interrupter(barrier_start, barrier_end, lock_exit): + # wait until MainThread can handle KeyboardInterrupt + barrier_start.release() + barrier_end.acquire() + os.kill(os.getpid(), signal.SIGINT) + lock_exit.release() + + threads = [] + for _ in range(thread_num): + threads.append(threading.Thread(target=runner, args=(gen,))) + + barrier_start = threading.Lock() + barrier_start.acquire() + barrier_end = threading.Lock() + barrier_end.acquire() + lock_exit = threading.Lock() + lock_exit.acquire() + + threads.append( + threading.Thread( + target=interrupter, + args=(barrier_start, barrier_end, lock_exit), + ) + ) + + for t in threads: + t.start() + + with self.assertRaises(KeyboardInterrupt): + # signal to interrupter that we can now handle the signal + barrier_start.acquire() + barrier_end.release() + runner(gen) + # use the lock to ensure we never go past the scope of + # assertRaises before the os.kill is called + lock_exit.acquire() + + for t in threads: + t.join() + + self.assertEqual( + gen._PointJacobi__precompute, + generator_112r2._PointJacobi__precompute, + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py new file mode 100644 index 000000000..25386b175 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py @@ -0,0 +1,959 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest + +try: + buffer +except NameError: + buffer = memoryview + +import os +import array +import pytest +import hashlib + +from .keys import VerifyingKey, SigningKey, MalformedPointError +from .der import ( + unpem, + UnexpectedDER, + encode_sequence, + encode_oid, + encode_bitstring, +) +from .util import ( + sigencode_string, + sigencode_der, + sigencode_strings, + sigdecode_string, + sigdecode_der, + sigdecode_strings, +) +from .curves import NIST256p, Curve, BRAINPOOLP160r1, Ed25519, Ed448 +from .ellipticcurve import Point, PointJacobi, CurveFp, INFINITY +from .ecdsa import generator_brainpoolp160r1 + + +class TestVerifyingKeyFromString(unittest.TestCase): + """ + Verify that ecdsa.keys.VerifyingKey.from_string() can be used with + bytes-like objects + """ + + @classmethod + def setUpClass(cls): + cls.key_bytes = ( + b"\x04L\xa2\x95\xdb\xc7Z\xd7\x1f\x93\nz\xcf\x97\xcf" + b"\xd7\xc2\xd9o\xfe8}X!\xae\xd4\xfah\xfa^\rpI\xba\xd1" + b"Y\xfb\x92xa\xebo+\x9cG\xfav\xca" + ) + cls.vk = VerifyingKey.from_string(cls.key_bytes) + + def test_bytes(self): + self.assertIsNotNone(self.vk) + self.assertIsInstance(self.vk, VerifyingKey) + self.assertEqual( + self.vk.pubkey.point.x(), + 105419898848891948935835657980914000059957975659675736097, + ) + self.assertEqual( + self.vk.pubkey.point.y(), + 4286866841217412202667522375431381222214611213481632495306, + ) + + def test_bytes_memoryview(self): + vk = VerifyingKey.from_string(buffer(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray(self): + vk = VerifyingKey.from_string(bytearray(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytesarray_memoryview(self): + vk = VerifyingKey.from_string(buffer(bytearray(self.key_bytes))) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_string(arr) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes_memoryview(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_string(buffer(arr)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_ints(self): + arr = array.array("I", self.key_bytes) + vk = VerifyingKey.from_string(arr) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_ints_memoryview(self): + arr = array.array("I", self.key_bytes) + vk = VerifyingKey.from_string(buffer(arr)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytes_uncompressed(self): + vk = VerifyingKey.from_string(b"\x04" + self.key_bytes) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray_uncompressed(self): + vk = VerifyingKey.from_string(bytearray(b"\x04" + self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytes_compressed(self): + vk = VerifyingKey.from_string(b"\x02" + self.key_bytes[:24]) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray_compressed(self): + vk = VerifyingKey.from_string(bytearray(b"\x02" + self.key_bytes[:24])) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + +class TestVerifyingKeyFromDer(unittest.TestCase): + """ + Verify that ecdsa.keys.VerifyingKey.from_der() can be used with + bytes-like objects. + """ + + @classmethod + def setUpClass(cls): + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" + ) + key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" + "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" + "-----END PUBLIC KEY-----\n" + ) + cls.key_pem = key_str + + cls.key_bytes = unpem(key_str) + assert isinstance(cls.key_bytes, bytes) + cls.vk = VerifyingKey.from_pem(key_str) + cls.sk = SigningKey.from_pem(prv_key_str) + + key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8\n" + "Poqzgjau4kfxBPyZimeRfuY/9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" + "-----END PUBLIC KEY-----\n" + ) + cls.vk2 = VerifyingKey.from_pem(key_str) + + cls.sk2 = SigningKey.generate(vk.curve) + + def test_load_key_with_explicit_parameters(self): + pub_key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" + "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" + "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" + "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" + "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" + "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" + "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" + "-----END PUBLIC KEY-----" + ) + pk = VerifyingKey.from_pem(pub_key_str) + + pk_exp = VerifyingKey.from_string( + b"\x04\x8a\xf5\x52\x48\x18\xb3\x98\xe6\x6c\x57\x3b\x8a\xdd\x7f" + b"\x60\x8d\x97\x4f\xff\xc8\x95\xa1\x23\x30\xd6\x5f\xb7\x5a\x30" + b"\x8e\xaa\x41\x60\x7d\x84\xac\x91\xe4\xe1\x4e\x4f\xa7\x85\xaf" + b"\x5a\xad\x71\x98\x7c\x08\x66\x5b\x07\xec\x88\x38\x2a\x67\x9f" + b"\x09\xf4\x7a\x4a\x65", + curve=NIST256p, + ) + self.assertEqual(pk, pk_exp) + + def test_load_key_with_explicit_with_explicit_disabled(self): + pub_key_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" + "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" + "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" + "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" + "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" + "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" + "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" + "-----END PUBLIC KEY-----" + ) + with self.assertRaises(UnexpectedDER): + VerifyingKey.from_pem( + pub_key_str, valid_curve_encodings=["named_curve"] + ) + + def test_load_key_with_disabled_format(self): + with self.assertRaises(MalformedPointError) as e: + VerifyingKey.from_der(self.key_bytes, valid_encodings=["raw"]) + + self.assertIn("enabled (raw) encodings", str(e.exception)) + + def test_custom_hashfunc(self): + vk = VerifyingKey.from_der(self.key_bytes, hashlib.sha256) + + self.assertIs(vk.default_hashfunc, hashlib.sha256) + + def test_from_pem_with_custom_hashfunc(self): + vk = VerifyingKey.from_pem(self.key_pem, hashlib.sha256) + + self.assertIs(vk.default_hashfunc, hashlib.sha256) + + def test_bytes(self): + vk = VerifyingKey.from_der(self.key_bytes) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytes_memoryview(self): + vk = VerifyingKey.from_der(buffer(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytearray(self): + vk = VerifyingKey.from_der(bytearray(self.key_bytes)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_bytesarray_memoryview(self): + vk = VerifyingKey.from_der(buffer(bytearray(self.key_bytes))) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_der(arr) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_array_array_of_bytes_memoryview(self): + arr = array.array("B", self.key_bytes) + vk = VerifyingKey.from_der(buffer(arr)) + + self.assertEqual(self.vk.to_string(), vk.to_string()) + + def test_equality_on_verifying_keys(self): + self.assertEqual(self.vk, self.sk.get_verifying_key()) + + def test_inequality_on_verifying_keys(self): + self.assertNotEqual(self.vk, self.vk2) + + def test_inequality_on_verifying_keys_not_implemented(self): + self.assertNotEqual(self.vk, None) + + def test_VerifyingKey_inequality_on_same_curve(self): + self.assertNotEqual(self.vk, self.sk2.verifying_key) + + def test_SigningKey_inequality_on_same_curve(self): + self.assertNotEqual(self.sk, self.sk2) + + def test_inequality_on_wrong_types(self): + self.assertNotEqual(self.vk, self.sk) + + def test_from_public_point_old(self): + pj = self.vk.pubkey.point + point = Point(pj.curve(), pj.x(), pj.y()) + + vk = VerifyingKey.from_public_point(point, self.vk.curve) + + self.assertEqual(vk, self.vk) + + def test_ed25519_VerifyingKey_repr__(self): + sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) + string = repr(sk.verifying_key) + + self.assertEqual( + "VerifyingKey.from_string(" + "bytearray(b'K\\x0c\\xfbZH\\x8e\\x8c\\x8c\\x07\\xee\\xda\\xfb" + "\\xe1\\x97\\xcd\\x90\\x18\\x02\\x15h]\\xfe\\xbe\\xcbB\\xba\\xe6r" + "\\x10\\xae\\xf1P'), Ed25519, None)", + string, + ) + + def test_edwards_from_public_point(self): + point = Ed25519.generator + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_point(point, Ed25519) + + self.assertIn("incompatible with Edwards", str(e.exception)) + + def test_edwards_precompute_no_side_effect(self): + sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) + vk = sk.verifying_key + vk2 = VerifyingKey.from_string(vk.to_string(), Ed25519) + vk.precompute() + + self.assertEqual(vk, vk2) + + def test_parse_malfomed_eddsa_der_pubkey(self): + der_str = encode_sequence( + encode_sequence(encode_oid(*Ed25519.oid)), + encode_bitstring(bytes(Ed25519.generator.to_bytes()), 0), + encode_bitstring(b"\x00", 0), + ) + + with self.assertRaises(UnexpectedDER) as e: + VerifyingKey.from_der(der_str) + + self.assertIn("trailing junk after public key", str(e.exception)) + + def test_edwards_from_public_key_recovery(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_key_recovery(b"", b"", Ed25519) + + self.assertIn("unsupported for Edwards", str(e.exception)) + + def test_edwards_from_public_key_recovery_with_digest(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_public_key_recovery_with_digest( + b"", b"", Ed25519 + ) + + self.assertIn("unsupported for Edwards", str(e.exception)) + + def test_load_ed25519_from_pem(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + self.assertIsInstance(vk.curve, Curve) + self.assertIs(vk.curve, Ed25519) + + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk_2 = VerifyingKey.from_string(vk_str, Ed25519) + + self.assertEqual(vk, vk_2) + + def test_export_ed255_to_pem(self): + vk_str = ( + b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" + b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" + ) + + vk = VerifyingKey.from_string(vk_str, Ed25519) + + vk_pem = ( + b"-----BEGIN PUBLIC KEY-----\n" + b"MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + b"-----END PUBLIC KEY-----\n" + ) + + self.assertEqual(vk_pem, vk.to_pem()) + + def test_ed25519_export_import(self): + sk = SigningKey.generate(Ed25519) + vk = sk.verifying_key + + vk2 = VerifyingKey.from_pem(vk.to_pem()) + + self.assertEqual(vk, vk2) + + def test_ed25519_sig_verify(self): + vk_pem = ( + "-----BEGIN PUBLIC KEY-----\n" + "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(vk_pem) + + data = b"data\n" + + # signature created by OpenSSL 3.0.0 beta1 + sig = ( + b"\x64\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" + b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" + b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" + b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" + ) + + self.assertTrue(vk.verify(sig, data)) + + def test_ed448_from_pem(self): + pem_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(pem_str) + + self.assertIsInstance(vk.curve, Curve) + self.assertIs(vk.curve, Ed448) + + vk_str = ( + b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" + b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" + b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" + b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" + ) + + vk2 = VerifyingKey.from_string(vk_str, Ed448) + + self.assertEqual(vk, vk2) + + def test_ed448_to_pem(self): + vk_str = ( + b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" + b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" + b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" + b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" + ) + vk = VerifyingKey.from_string(vk_str, Ed448) + + vk_pem = ( + b"-----BEGIN PUBLIC KEY-----\n" + b"MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + b"dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + b"-----END PUBLIC KEY-----\n" + ) + + self.assertEqual(vk_pem, vk.to_pem()) + + def test_ed448_export_import(self): + sk = SigningKey.generate(Ed448) + vk = sk.verifying_key + + vk2 = VerifyingKey.from_pem(vk.to_pem()) + + self.assertEqual(vk, vk2) + + def test_ed448_sig_verify(self): + pem_str = ( + "-----BEGIN PUBLIC KEY-----\n" + "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" + "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" + "-----END PUBLIC KEY-----\n" + ) + + vk = VerifyingKey.from_pem(pem_str) + + data = b"data\n" + + # signature created by OpenSSL 3.0.0 beta1 + sig = ( + b"\x68\xed\x2c\x70\x35\x22\xca\x1c\x35\x03\xf3\xaa\x51\x33\x3d\x00" + b"\xc0\xae\xb0\x54\xc5\xdc\x7f\x6f\x30\x57\xb4\x1d\xcb\xe9\xec\xfa" + b"\xc8\x45\x3e\x51\xc1\xcb\x60\x02\x6a\xd0\x43\x11\x0b\x5f\x9b\xfa" + b"\x32\x88\xb2\x38\x6b\xed\xac\x09\x00\x78\xb1\x7b\x5d\x7e\xf8\x16" + b"\x31\xdd\x1b\x3f\x98\xa0\xce\x19\xe7\xd8\x1c\x9f\x30\xac\x2f\xd4" + b"\x1e\x55\xbf\x21\x98\xf6\x4c\x8c\xbe\x81\xa5\x2d\x80\x4c\x62\x53" + b"\x91\xd5\xee\x03\x30\xc6\x17\x66\x4b\x9e\x0c\x8d\x40\xd0\xad\xae" + b"\x0a\x00" + ) + + self.assertTrue(vk.verify(sig, data)) + + +class TestSigningKey(unittest.TestCase): + """ + Verify that ecdsa.keys.SigningKey.from_der() can be used with + bytes-like objects. + """ + + @classmethod + def setUpClass(cls): + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" + ) + cls.sk1 = SigningKey.from_pem(prv_key_str) + + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEVTBTAgEBBBheyEIL1u+SUqlC6YkE\n" + "PKKfVh+lJXcOscWhNAMyAAS4gXfQhO8X9eRWOUCAKDYPn1m0pNcmTmLaBlHc5Ho1\n" + "pMW0XPUVk0I6i1V7nCCZ82w=\n" + "-----END PRIVATE KEY-----\n" + ) + cls.sk1_pkcs8 = SigningKey.from_pem(prv_key_str) + + prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MHcCAQEEIKlL2EAm5NPPZuXwxRf4nXMk0A80y6UUbiQ17be/qFhRoAoGCCqGSM49\n" + "AwEHoUQDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8Poqzgjau4kfxBPyZimeRfuY/\n" + "9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" + "-----END EC PRIVATE KEY-----\n" + ) + cls.sk2 = SigningKey.from_pem(prv_key_str) + + def test_decoding_explicit_curve_parameters(self): + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" + "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" + "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" + "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" + "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" + "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" + "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" + "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(prv_key_str) + + sk2 = SigningKey.from_string( + b"\x21\x7b\x51\x11\xf5\x26\x47\x5e\xab\x65\xb9\xaf\x0c\x60\xf6" + b"\x94\x01\x05\x3d\x96\xb6\x0c\xb3\xf2\xcf\x47\x33\x4a\x36\xb9" + b"\xf3\x20", + curve=NIST256p, + ) + + self.assertEqual(sk, sk2) + + def test_decoding_explicit_curve_parameters_with_explicit_disabled(self): + prv_key_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" + "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" + "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" + "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" + "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" + "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" + "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" + "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" + "-----END PRIVATE KEY-----\n" + ) + + with self.assertRaises(UnexpectedDER): + SigningKey.from_pem( + prv_key_str, valid_curve_encodings=["named_curve"] + ) + + def test_equality_on_signing_keys(self): + sk = SigningKey.from_secret_exponent( + self.sk1.privkey.secret_multiplier, self.sk1.curve + ) + self.assertEqual(self.sk1, sk) + self.assertEqual(self.sk1_pkcs8, sk) + + def test_verify_with_empty_message(self): + sig = self.sk1.sign(b"") + + self.assertTrue(sig) + + vk = self.sk1.verifying_key + + self.assertTrue(vk.verify(sig, b"")) + + def test_verify_with_precompute(self): + sig = self.sk1.sign(b"message") + + vk = self.sk1.verifying_key + + vk.precompute() + + self.assertTrue(vk.verify(sig, b"message")) + + def test_compare_verifying_key_with_precompute(self): + vk1 = self.sk1.verifying_key + vk1.precompute() + + vk2 = self.sk1_pkcs8.verifying_key + + self.assertEqual(vk1, vk2) + + def test_verify_with_lazy_precompute(self): + sig = self.sk2.sign(b"other message") + + vk = self.sk2.verifying_key + + vk.precompute(lazy=True) + + self.assertTrue(vk.verify(sig, b"other message")) + + def test_inequality_on_signing_keys(self): + self.assertNotEqual(self.sk1, self.sk2) + + def test_inequality_on_signing_keys_not_implemented(self): + self.assertNotEqual(self.sk1, None) + + def test_ed25519_from_pem(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + "-----END PRIVATE KEY-----\n" + ) + + sk = SigningKey.from_pem(pem_str) + + sk_str = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + self.assertEqual(sk, sk_str) + + def test_ed25519_to_pem(self): + sk = SigningKey.from_string( + b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" + b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", + Ed25519, + ) + + pem_str = ( + b"-----BEGIN PRIVATE KEY-----\n" + b"MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" + b"-----END PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) + + def test_ed25519_to_and_from_pem(self): + sk = SigningKey.generate(Ed25519) + + decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) + + self.assertEqual(sk, decoded) + + def test_ed448_from_pem(self): + pem_str = ( + "-----BEGIN PRIVATE KEY-----\n" + "MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" + "OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" + "-----END PRIVATE KEY-----\n" + ) + sk = SigningKey.from_pem(pem_str) + + sk_str = SigningKey.from_string( + b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" + b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" + b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" + b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", + Ed448, + ) + + self.assertEqual(sk, sk_str) + + def test_ed448_to_pem(self): + sk = SigningKey.from_string( + b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" + b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" + b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" + b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", + Ed448, + ) + pem_str = ( + b"-----BEGIN PRIVATE KEY-----\n" + b"MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" + b"OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" + b"-----END PRIVATE KEY-----\n" + ) + + self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) + + def test_ed448_encode_decode(self): + sk = SigningKey.generate(Ed448) + + decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) + + self.assertEqual(decoded, sk) + + +class TestTrivialCurve(unittest.TestCase): + @classmethod + def setUpClass(cls): + # To test what happens with r or s in signing happens to be zero we + # need to find a scalar that creates one of the points on a curve that + # has x coordinate equal to zero. + # Even for secp112r2 curve that's non trivial so use this toy + # curve, for which we can iterate over all points quickly + curve = CurveFp(163, 84, 58) + gen = PointJacobi(curve, 2, 87, 1, 167, generator=True) + + cls.toy_curve = Curve("toy_p8", curve, gen, (1, 2, 0)) + + cls.sk = SigningKey.from_secret_exponent( + 140, + cls.toy_curve, + hashfunc=hashlib.sha1, + ) + + def test_generator_sanity(self): + gen = self.toy_curve.generator + + self.assertEqual(gen * gen.order(), INFINITY) + + def test_public_key_sanity(self): + self.assertEqual(self.sk.verifying_key.to_string(), b"\x98\x1e") + + def test_deterministic_sign(self): + sig = self.sk.sign_deterministic(b"message") + + self.assertEqual(sig, b"-.") + + self.assertTrue(self.sk.verifying_key.verify(sig, b"message")) + + def test_deterministic_sign_random_message(self): + msg = os.urandom(32) + sig = self.sk.sign_deterministic(msg) + self.assertEqual(len(sig), 2) + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + def test_deterministic_sign_that_rises_R_zero_error(self): + # the raised RSZeroError is caught and handled internally by + # sign_deterministic methods + msg = b"\x00\x4f" + sig = self.sk.sign_deterministic(msg) + self.assertEqual(sig, b"\x36\x9e") + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + def test_deterministic_sign_that_rises_S_zero_error(self): + msg = b"\x01\x6d" + sig = self.sk.sign_deterministic(msg) + self.assertEqual(sig, b"\x49\x6c") + self.assertTrue(self.sk.verifying_key.verify(sig, msg)) + + +# test VerifyingKey.verify() +prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" +) +key_bytes = unpem(prv_key_str) +assert isinstance(key_bytes, bytes) +sk = SigningKey.from_der(key_bytes) +vk = sk.verifying_key + +data = ( + b"some string for signing" + b"contents don't really matter" + b"but do include also some crazy values: " + b"\x00\x01\t\r\n\x00\x00\x00\xff\xf0" +) +assert len(data) % 4 == 0 +sha1 = hashlib.sha1() +sha1.update(data) +data_hash = sha1.digest() +assert isinstance(data_hash, bytes) +sig_raw = sk.sign(data, sigencode=sigencode_string) +assert isinstance(sig_raw, bytes) +sig_der = sk.sign(data, sigencode=sigencode_der) +assert isinstance(sig_der, bytes) +sig_strings = sk.sign(data, sigencode=sigencode_strings) +assert isinstance(sig_strings[0], bytes) + +verifiers = [] +for modifier, fun in [ + ("bytes", lambda x: x), + ("bytes memoryview", lambda x: buffer(x)), + ("bytearray", lambda x: bytearray(x)), + ("bytearray memoryview", lambda x: buffer(bytearray(x))), + ("array.array of bytes", lambda x: array.array("B", x)), + ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), + ("array.array of ints", lambda x: array.array("I", x)), + ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), +]: + if "ints" in modifier: + conv = lambda x: x + else: + conv = fun + for sig_format, signature, decoder, mod_apply in [ + ("raw", sig_raw, sigdecode_string, lambda x: conv(x)), + ("der", sig_der, sigdecode_der, lambda x: conv(x)), + ( + "strings", + sig_strings, + sigdecode_strings, + lambda x: tuple(conv(i) for i in x), + ), + ]: + for method_name, vrf_mthd, vrf_data in [ + ("verify", vk.verify, data), + ("verify_digest", vk.verify_digest, data_hash), + ]: + verifiers.append( + pytest.param( + signature, + decoder, + mod_apply, + fun, + vrf_mthd, + vrf_data, + id="{2}-{0}-{1}".format(modifier, sig_format, method_name), + ) + ) + + +@pytest.mark.parametrize( + "signature,decoder,mod_apply,fun,vrf_mthd,vrf_data", verifiers +) +def test_VerifyingKey_verify( + signature, decoder, mod_apply, fun, vrf_mthd, vrf_data +): + sig = mod_apply(signature) + + assert vrf_mthd(sig, fun(vrf_data), sigdecode=decoder) + + +# test SigningKey.from_string() +prv_key_bytes = ( + b"^\xc8B\x0b\xd6\xef\x92R\xa9B\xe9\x89\x04<\xa2" + b"\x9fV\x1f\xa5%w\x0e\xb1\xc5" +) +assert len(prv_key_bytes) == 24 +converters = [] +for modifier, convert in [ + ("bytes", lambda x: x), + ("bytes memoryview", buffer), + ("bytearray", bytearray), + ("bytearray memoryview", lambda x: buffer(bytearray(x))), + ("array.array of bytes", lambda x: array.array("B", x)), + ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), + ("array.array of ints", lambda x: array.array("I", x)), + ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), +]: + converters.append(pytest.param(convert, id=modifier)) + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_from_string(convert): + key = convert(prv_key_bytes) + sk = SigningKey.from_string(key) + + assert sk.to_string() == prv_key_bytes + + +# test SigningKey.from_der() +prv_key_str = ( + "-----BEGIN EC PRIVATE KEY-----\n" + "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" + "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" + "bA==\n" + "-----END EC PRIVATE KEY-----\n" +) +key_bytes = unpem(prv_key_str) +assert isinstance(key_bytes, bytes) + +# last two converters are for array.array of ints, those require input +# that's multiple of 4, which no curve we support produces +@pytest.mark.parametrize("convert", converters[:-2]) +def test_SigningKey_from_der(convert): + key = convert(key_bytes) + sk = SigningKey.from_der(key) + + assert sk.to_string() == prv_key_bytes + + +# test SigningKey.sign_deterministic() +extra_entropy = b"\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11" + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign_deterministic(convert): + sig = sk.sign_deterministic( + convert(data), extra_entropy=convert(extra_entropy) + ) + + vk.verify(sig, data) + + +# test SigningKey.sign_digest_deterministic() +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign_digest_deterministic(convert): + sig = sk.sign_digest_deterministic( + convert(data_hash), extra_entropy=convert(extra_entropy) + ) + + vk.verify(sig, data) + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign(convert): + sig = sk.sign(convert(data)) + + vk.verify(sig, data) + + +@pytest.mark.parametrize("convert", converters) +def test_SigningKey_sign_digest(convert): + sig = sk.sign_digest(convert(data_hash)) + + vk.verify(sig, data) + + +def test_SigningKey_with_unlikely_value(): + sk = SigningKey.from_secret_exponent(NIST256p.order - 1, curve=NIST256p) + vk = sk.verifying_key + sig = sk.sign(b"hello") + assert vk.verify(sig, b"hello") + + +def test_SigningKey_with_custom_curve_old_point(): + generator = generator_brainpoolp160r1 + generator = Point( + generator.curve(), + generator.x(), + generator.y(), + generator.order(), + ) + + curve = Curve( + "BRAINPOOLP160r1", + generator.curve(), + generator, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), + ) + + sk = SigningKey.from_secret_exponent(12, curve) + + sk2 = SigningKey.from_secret_exponent(12, BRAINPOOLP160r1) + + assert sk.privkey == sk2.privkey + + +def test_VerifyingKey_inequality_with_different_curves(): + sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) + sk2 = SigningKey.from_secret_exponent(2, NIST256p) + + assert sk1.verifying_key != sk2.verifying_key + + +def test_VerifyingKey_inequality_with_different_secret_points(): + sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) + sk2 = SigningKey.from_secret_exponent(3, BRAINPOOLP160r1) + + assert sk1.verifying_key != sk2.verifying_key + + +def test_SigningKey_from_pem_pkcs8v2_EdDSA(): + pem = """-----BEGIN PRIVATE KEY----- + MFMCAQEwBQYDK2VwBCIEICc2F2ag1n1QP0jY+g9qWx5sDkx0s/HdNi3cSRHw+zsI + oSMDIQA+HQ2xCif8a/LMWR2m5HaCm5I2pKe/cc8OiRANMHxjKQ== + -----END PRIVATE KEY-----""" + + sk = SigningKey.from_pem(pem) + assert sk.curve == Ed25519 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py new file mode 100644 index 000000000..8e1b611d1 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py @@ -0,0 +1,370 @@ +from __future__ import with_statement, division + +import hashlib + +try: + from hashlib import algorithms_available +except ImportError: # pragma: no cover + algorithms_available = [ + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + ] +# skip algorithms broken by change to OpenSSL 3.0 and early versions +# of hashlib that list algorithms that require the legacy provider to work +# https://bugs.python.org/issue38820 +algorithms_available = [ + i + for i in algorithms_available + if i not in ("mdc2", "md2", "md4", "whirlpool", "ripemd160") +] +from functools import partial +import pytest +import sys +import hypothesis.strategies as st +from hypothesis import note, assume, given, settings, example + +from .keys import SigningKey +from .keys import BadSignatureError +from .util import sigencode_der, sigencode_string +from .util import sigdecode_der, sigdecode_string +from .curves import curves +from .der import ( + encode_integer, + encode_bitstring, + encode_octet_string, + encode_oid, + encode_sequence, + encode_constructed, +) +from .ellipticcurve import CurveEdTw + + +example_data = b"some data to sign" +"""Since the data is hashed for processing, really any string will do.""" + + +hash_and_size = [ + (name, hashlib.new(name).digest_size) for name in algorithms_available +] +"""Pairs of hash names and their output sizes. +Needed for pairing with curves as we don't support hashes +bigger than order sizes of curves.""" + + +keys_and_sigs = [] +"""Name of the curve+hash combination, VerifyingKey and DER signature.""" + + +# for hypothesis strategy shrinking we want smallest curves and hashes first +for curve in sorted(curves, key=lambda x: x.baselen): + for hash_alg in [ + name + for name, size in sorted(hash_and_size, key=lambda x: x[1]) + if 0 < size <= curve.baselen + ]: + sk = SigningKey.generate( + curve, hashfunc=partial(hashlib.new, hash_alg) + ) + + keys_and_sigs.append( + ( + "{0} {1}".format(curve, hash_alg), + sk.verifying_key, + sk.sign(example_data, sigencode=sigencode_der), + ) + ) + + +# first make sure that the signatures can be verified +@pytest.mark.parametrize( + "verifying_key,signature", + [pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs], +) +def test_signatures(verifying_key, signature): + assert verifying_key.verify( + signature, example_data, sigdecode=sigdecode_der + ) + + +@st.composite +def st_fuzzed_sig(draw, keys_and_sigs): + """ + Hypothesis strategy that generates pairs of VerifyingKey and malformed + signatures created by fuzzing of a valid signature. + """ + name, verifying_key, old_sig = draw(st.sampled_from(keys_and_sigs)) + note("Configuration: {0}".format(name)) + + sig = bytearray(old_sig) + + # decide which bytes should be removed + to_remove = draw( + st.lists(st.integers(min_value=0, max_value=len(sig) - 1), unique=True) + ) + to_remove.sort() + for i in reversed(to_remove): + del sig[i] + note("Remove bytes: {0}".format(to_remove)) + + # decide which bytes of the original signature should be changed + if sig: # pragma: no branch + xors = draw( + st.dictionaries( + st.integers(min_value=0, max_value=len(sig) - 1), + st.integers(min_value=1, max_value=255), + ) + ) + for i, val in xors.items(): + sig[i] ^= val + note("xors: {0}".format(xors)) + + # decide where new data should be inserted + insert_pos = draw(st.integers(min_value=0, max_value=len(sig))) + # NIST521p signature is about 140 bytes long, test slightly longer + insert_data = draw(st.binary(max_size=256)) + + sig = sig[:insert_pos] + insert_data + sig[insert_pos:] + note( + "Inserted at position {0} bytes: {1!r}".format(insert_pos, insert_data) + ) + + sig = bytes(sig) + # make sure that there was performed at least one mutation on the data + assume(to_remove or xors or insert_data) + # and that the mutations didn't cancel each-other out + assume(sig != old_sig) + + return verifying_key, sig + + +params = {} +# not supported in hypothesis 2.0.0 +if sys.version_info >= (2, 7): # pragma: no branch + from hypothesis import HealthCheck + + # deadline=5s because NIST521p are slow to verify + params["deadline"] = 5000 + params["suppress_health_check"] = [ + HealthCheck.data_too_large, + HealthCheck.filter_too_much, + HealthCheck.too_slow, + ] + +slow_params = dict(params) +slow_params["max_examples"] = 10 + + +@settings(**params) +@given(st_fuzzed_sig(keys_and_sigs)) +def test_fuzzed_der_signatures(args): + verifying_key, sig = args + + with pytest.raises(BadSignatureError): + verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) + + +@st.composite +def st_random_der_ecdsa_sig_value(draw): + """ + Hypothesis strategy for selecting random values and encoding them + to ECDSA-Sig-Value object:: + + ECDSA-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + """ + name, verifying_key, _ = draw(st.sampled_from(keys_and_sigs)) + note("Configuration: {0}".format(name)) + order = int(verifying_key.curve.order) + + # the encode_integer doesn't support negative numbers, would be nice + # to generate them too, but we have coverage for remove_integer() + # verifying that it doesn't accept them, so meh. + # Test all numbers around the ones that can show up (around order) + # way smaller and slightly bigger + r = draw( + st.integers(min_value=0, max_value=order << 4) + | st.integers(min_value=order >> 2, max_value=order + 1) + ) + s = draw( + st.integers(min_value=0, max_value=order << 4) + | st.integers(min_value=order >> 2, max_value=order + 1) + ) + + sig = encode_sequence(encode_integer(r), encode_integer(s)) + + return verifying_key, sig + + +@settings(**slow_params) +@given(st_random_der_ecdsa_sig_value()) +def test_random_der_ecdsa_sig_value(params): + """ + Check if random values encoded in ECDSA-Sig-Value structure are rejected + as signature. + """ + verifying_key, sig = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) + + +def st_der_integer(*args, **kwargs): + """ + Hypothesis strategy that returns a random positive integer as DER + INTEGER. + Parameters are passed to hypothesis.strategy.integer. + """ + if "min_value" not in kwargs: # pragma: no branch + kwargs["min_value"] = 0 + return st.builds(encode_integer, st.integers(*args, **kwargs)) + + +@st.composite +def st_der_bit_string(draw, *args, **kwargs): + """ + Hypothesis strategy that returns a random DER BIT STRING. + Parameters are passed to hypothesis.strategy.binary. + """ + data = draw(st.binary(*args, **kwargs)) + if data: + unused = draw(st.integers(min_value=0, max_value=7)) + data = bytearray(data) + data[-1] &= -(2**unused) + data = bytes(data) + else: + unused = 0 + return encode_bitstring(data, unused) + + +def st_der_octet_string(*args, **kwargs): + """ + Hypothesis strategy that returns a random DER OCTET STRING object. + Parameters are passed to hypothesis.strategy.binary + """ + return st.builds(encode_octet_string, st.binary(*args, **kwargs)) + + +def st_der_null(): + """ + Hypothesis strategy that returns DER NULL object. + """ + return st.just(b"\x05\x00") + + +@st.composite +def st_der_oid(draw): + """ + Hypothesis strategy that returns DER OBJECT IDENTIFIER objects. + """ + first = draw(st.integers(min_value=0, max_value=2)) + if first < 2: + second = draw(st.integers(min_value=0, max_value=39)) + else: + second = draw(st.integers(min_value=0, max_value=2**512)) + rest = draw( + st.lists(st.integers(min_value=0, max_value=2**512), max_size=50) + ) + return encode_oid(first, second, *rest) + + +def st_der(): + """ + Hypothesis strategy that returns random DER structures. + + A valid DER structure is any primitive object, an octet encoding + of a valid DER structure, sequence of valid DER objects or a constructed + encoding of any of the above. + """ + return st.recursive( + st.just(b"") + | st_der_integer(max_value=2**4096) + | st_der_bit_string(max_size=1024**2) + | st_der_octet_string(max_size=1024**2) + | st_der_null() + | st_der_oid(), + lambda children: st.builds( + lambda x: encode_octet_string(x), st.one_of(children) + ) + | st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children)) + | st.builds( + lambda x: encode_sequence(*x), st.lists(children, max_size=200) + ) + | st.builds( + lambda tag, x: encode_constructed(tag, x), + st.integers(min_value=0, max_value=0x3F), + st.one_of(children), + ), + max_leaves=40, + ) + + +@settings(**params) +@given(st.sampled_from(keys_and_sigs), st_der()) +def test_random_der_as_signature(params, der): + """Check if random DER structures are rejected as signature""" + name, verifying_key, _ = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(der, example_data, sigdecode=sigdecode_der) + + +@settings(**params) +@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) +@example( + keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) +) +@example( + keys_and_sigs[0], + encode_sequence(encode_integer(1), encode_integer(1)) + b"\x00", +) +@example(keys_and_sigs[0], encode_sequence(*[encode_integer(1)] * 3)) +def test_random_bytes_as_signature(params, der): + """Check if random bytes are rejected as signature""" + name, verifying_key, _ = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(der, example_data, sigdecode=sigdecode_der) + + +keys_and_string_sigs = [ + ( + name, + verifying_key, + sigencode_string( + *sigdecode_der(sig, verifying_key.curve.order), + order=verifying_key.curve.order + ), + ) + for name, verifying_key, sig in keys_and_sigs + if not isinstance(verifying_key.curve.curve, CurveEdTw) +] +""" +Name of the curve+hash combination, VerifyingKey and signature as a +byte string. +""" + + +keys_and_string_sigs += [ + ( + name, + verifying_key, + sig, + ) + for name, verifying_key, sig in keys_and_sigs + if isinstance(verifying_key.curve.curve, CurveEdTw) +] + + +@settings(**params) +@given(st_fuzzed_sig(keys_and_string_sigs)) +def test_fuzzed_string_signatures(params): + verifying_key, sig = params + + with pytest.raises(BadSignatureError): + verifying_key.verify(sig, example_data, sigdecode=sigdecode_string) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py new file mode 100644 index 000000000..8bc787f1c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py @@ -0,0 +1,433 @@ +import operator +from functools import reduce + +try: + import unittest2 as unittest +except ImportError: + import unittest +import hypothesis.strategies as st +import pytest +from hypothesis import given, settings, example + +try: + from hypothesis import HealthCheck + + HC_PRESENT = True +except ImportError: # pragma: no cover + HC_PRESENT = False +from .numbertheory import ( + SquareRootError, + JacobiError, + factorization, + gcd, + lcm, + jacobi, + inverse_mod, + is_prime, + next_prime, + smallprimes, + square_root_mod_prime, +) + + +BIGPRIMES = ( + 999671, + 999683, + 999721, + 999727, + 999749, + 999763, + 999769, + 999773, + 999809, + 999853, + 999863, + 999883, + 999907, + 999917, + 999931, + 999953, + 999959, + 999961, + 999979, + 999983, +) + + +@pytest.mark.parametrize( + "prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])] +) +def test_next_prime(prime, next_p): + assert next_prime(prime) == next_p + + +@pytest.mark.parametrize("val", [-1, 0, 1]) +def test_next_prime_with_nums_less_2(val): + assert next_prime(val) == 2 + + +@pytest.mark.parametrize("prime", smallprimes) +def test_square_root_mod_prime_for_small_primes(prime): + squares = set() + for num in range(0, 1 + prime // 2): + sq = num * num % prime + squares.add(sq) + root = square_root_mod_prime(sq, prime) + # tested for real with TestNumbertheory.test_square_root_mod_prime + assert root * root % prime == sq + + for nonsquare in range(0, prime): + if nonsquare in squares: + continue + with pytest.raises(SquareRootError): + square_root_mod_prime(nonsquare, prime) + + +def test_square_root_mod_prime_for_2(): + a = square_root_mod_prime(1, 2) + assert a == 1 + + +def test_square_root_mod_prime_for_small_prime(): + root = square_root_mod_prime(98**2 % 101, 101) + assert root * root % 101 == 9 + + +def test_square_root_mod_prime_for_p_congruent_5(): + p = 13 + assert p % 8 == 5 + + root = square_root_mod_prime(3, p) + assert root * root % p == 3 + + +def test_square_root_mod_prime_for_p_congruent_5_large_d(): + p = 29 + assert p % 8 == 5 + + root = square_root_mod_prime(4, p) + assert root * root % p == 4 + + +class TestSquareRootModPrime(unittest.TestCase): + def test_power_of_2_p(self): + with self.assertRaises(JacobiError): + square_root_mod_prime(12, 32) + + def test_no_square(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(12, 31) + + self.assertIn("no square root", str(e.exception)) + + def test_non_prime(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(12, 33) + + self.assertIn("p is not prime", str(e.exception)) + + def test_non_prime_with_negative(self): + with self.assertRaises(SquareRootError) as e: + square_root_mod_prime(697 - 1, 697) + + self.assertIn("p is not prime", str(e.exception)) + + +@st.composite +def st_two_nums_rel_prime(draw): + # 521-bit is the biggest curve we operate on, use 1024 for a bit + # of breathing space + mod = draw(st.integers(min_value=2, max_value=2**1024)) + num = draw( + st.integers(min_value=1, max_value=mod - 1).filter( + lambda x: gcd(x, mod) == 1 + ) + ) + return num, mod + + +@st.composite +def st_primes(draw, *args, **kwargs): + if "min_value" not in kwargs: # pragma: no branch + kwargs["min_value"] = 1 + prime = draw( + st.sampled_from(smallprimes) + | st.integers(*args, **kwargs).filter(is_prime) + ) + return prime + + +@st.composite +def st_num_square_prime(draw): + prime = draw(st_primes(max_value=2**1024)) + num = draw(st.integers(min_value=0, max_value=1 + prime // 2)) + sq = num * num % prime + return sq, prime + + +@st.composite +def st_comp_with_com_fac(draw): + """ + Strategy that returns lists of numbers, all having a common factor. + """ + primes = draw( + st.lists(st_primes(max_value=2**512), min_size=1, max_size=10) + ) + # select random prime(s) that will make the common factor of composites + com_fac_primes = draw( + st.lists(st.sampled_from(primes), min_size=1, max_size=20) + ) + com_fac = reduce(operator.mul, com_fac_primes, 1) + + # select at most 20 lists (returned numbers), + # each having at most 30 primes (factors) including none (then the number + # will be 1) + comp_primes = draw( + st.integers(min_value=1, max_value=20).flatmap( + lambda n: st.lists( + st.lists(st.sampled_from(primes), max_size=30), + min_size=1, + max_size=n, + ) + ) + ) + + return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes] + + +@st.composite +def st_comp_no_com_fac(draw): + """ + Strategy that returns lists of numbers that don't have a common factor. + """ + primes = draw( + st.lists( + st_primes(max_value=2**512), min_size=2, max_size=10, unique=True + ) + ) + # first select the primes that will create the uncommon factor + # between returned numbers + uncom_fac_primes = draw( + st.lists( + st.sampled_from(primes), + min_size=1, + max_size=len(primes) - 1, + unique=True, + ) + ) + uncom_fac = reduce(operator.mul, uncom_fac_primes, 1) + + # then build composites from leftover primes + leftover_primes = [i for i in primes if i not in uncom_fac_primes] + + assert leftover_primes + assert uncom_fac_primes + + # select at most 20 lists, each having at most 30 primes + # selected from the leftover_primes list + number_primes = draw( + st.integers(min_value=1, max_value=20).flatmap( + lambda n: st.lists( + st.lists(st.sampled_from(leftover_primes), max_size=30), + min_size=1, + max_size=n, + ) + ) + ) + + numbers = [reduce(operator.mul, nums, 1) for nums in number_primes] + + insert_at = draw(st.integers(min_value=0, max_value=len(numbers))) + numbers.insert(insert_at, uncom_fac) + return numbers + + +HYP_SETTINGS = {} +if HC_PRESENT: # pragma: no branch + HYP_SETTINGS["suppress_health_check"] = [ + HealthCheck.filter_too_much, + HealthCheck.too_slow, + ] + # the factorization() sometimes takes a long time to finish + HYP_SETTINGS["deadline"] = 5000 + + +HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) +HYP_SLOW_SETTINGS["max_examples"] = 10 + + +class TestIsPrime(unittest.TestCase): + def test_very_small_prime(self): + assert is_prime(23) + + def test_very_small_composite(self): + assert not is_prime(22) + + def test_small_prime(self): + assert is_prime(123456791) + + def test_special_composite(self): + assert not is_prime(10261) + + def test_medium_prime_1(self): + # nextPrime[2^256] + assert is_prime(2**256 + 0x129) + + def test_medium_prime_2(self): + # nextPrime(2^256+0x129) + assert is_prime(2**256 + 0x12D) + + def test_medium_trivial_composite(self): + assert not is_prime(2**256 + 0x130) + + def test_medium_non_trivial_composite(self): + assert not is_prime(2**256 + 0x12F) + + def test_large_prime(self): + # nextPrime[2^2048] + assert is_prime(2**2048 + 0x3D5) + + +class TestNumbertheory(unittest.TestCase): + def test_gcd(self): + assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5 + assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5 + assert gcd(3) == 3 + + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) + @settings(**HYP_SLOW_SETTINGS) + @given(st_comp_with_com_fac()) + def test_gcd_with_com_factor(self, numbers): + n = gcd(numbers) + assert 1 in numbers or n != 1 + for i in numbers: + assert i % n == 0 + + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) + @settings(**HYP_SLOW_SETTINGS) + @given(st_comp_no_com_fac()) + def test_gcd_with_uncom_factor(self, numbers): + n = gcd(numbers) + assert n == 1 + + @given( + st.lists( + st.integers(min_value=1, max_value=2**8192), + min_size=1, + max_size=20, + ) + ) + def test_gcd_with_random_numbers(self, numbers): + n = gcd(numbers) + for i in numbers: + # check that at least it's a divider + assert i % n == 0 + + def test_lcm(self): + assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7 + assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7 + assert lcm(3) == 3 + + @given( + st.lists( + st.integers(min_value=1, max_value=2**8192), + min_size=1, + max_size=20, + ) + ) + def test_lcm_with_random_numbers(self, numbers): + n = lcm(numbers) + for i in numbers: + assert n % i == 0 + + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) + @settings(**HYP_SETTINGS) + @given(st_num_square_prime()) + def test_square_root_mod_prime(self, vals): + square, prime = vals + + calc = square_root_mod_prime(square, prime) + assert calc * calc % prime == square + + @settings(**HYP_SETTINGS) + @given(st.integers(min_value=1, max_value=10**12)) + @example(265399 * 1526929) + @example(373297**2 * 553991) + def test_factorization(self, num): + factors = factorization(num) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == num + + def test_factorisation_smallprimes(self): + exp = 101 * 103 + assert 101 in smallprimes + assert 103 in smallprimes + factors = factorization(exp) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == exp + + def test_factorisation_not_smallprimes(self): + exp = 1231 * 1237 + assert 1231 not in smallprimes + assert 1237 not in smallprimes + factors = factorization(exp) + mult = 1 + for i in factors: + mult *= i[0] ** i[1] + assert mult == exp + + def test_jacobi_with_zero(self): + assert jacobi(0, 3) == 0 + + def test_jacobi_with_one(self): + assert jacobi(1, 3) == 1 + + @settings(**HYP_SETTINGS) + @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2)) + def test_jacobi(self, mod): + if is_prime(mod): + squares = set() + for root in range(1, mod): + assert jacobi(root * root, mod) == 1 + squares.add(root * root % mod) + for i in range(1, mod): + if i not in squares: + assert jacobi(i, mod) == -1 + else: + factors = factorization(mod) + for a in range(1, mod): + c = 1 + for i in factors: + c *= jacobi(a, i[0]) ** i[1] + assert c == jacobi(a, mod) + + @given(st_two_nums_rel_prime()) + def test_inverse_mod(self, nums): + num, mod = nums + + inv = inverse_mod(num, mod) + + assert 0 < inv < mod + assert num * inv % mod == 1 + + def test_inverse_mod_with_zero(self): + assert 0 == inverse_mod(0, 11) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py new file mode 100644 index 000000000..d61f50838 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py @@ -0,0 +1,2267 @@ +from __future__ import with_statement, division + +try: + import unittest2 as unittest +except ImportError: + import unittest +import os +import sys +import shutil +import subprocess +import pytest +from binascii import hexlify, unhexlify +from hashlib import sha1, sha256, sha384, sha512 +import hashlib +from functools import partial + +from hypothesis import given +import hypothesis.strategies as st + +from six import b, print_, binary_type +from .keys import SigningKey, VerifyingKey +from .keys import BadSignatureError, MalformedPointError, BadDigestError +from . import util +from .util import sigencode_der, sigencode_strings +from .util import sigdecode_der, sigdecode_strings +from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature +from .curves import Curve, UnknownCurveError +from .curves import ( + SECP112r1, + SECP112r2, + SECP128r1, + SECP160r1, + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + Ed25519, + Ed448, + curves, +) +from .ecdsa import ( + curve_brainpoolp224r1, + curve_brainpoolp256r1, + curve_brainpoolp384r1, + curve_brainpoolp512r1, +) +from .ellipticcurve import Point +from . import der +from . import rfc6979 +from . import ecdsa + + +class SubprocessError(Exception): + pass + + +def run_openssl(cmd): + OPENSSL = "openssl" + p = subprocess.Popen( + [OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + stdout, ignored = p.communicate() + if p.returncode != 0: + raise SubprocessError( + "cmd '%s %s' failed: rc=%s, stdout/err was %s" + % (OPENSSL, cmd, p.returncode, stdout) + ) + return stdout.decode() + + +class ECDSA(unittest.TestCase): + def test_basic(self): + priv = SigningKey.generate() + pub = priv.get_verifying_key() + + data = b("blahblah") + sig = priv.sign(data) + + self.assertTrue(pub.verify(sig, data)) + self.assertRaises(BadSignatureError, pub.verify, sig, data + b("bad")) + + pub2 = VerifyingKey.from_string(pub.to_string()) + self.assertTrue(pub2.verify(sig, data)) + + def test_deterministic(self): + data = b("blahblah") + secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) + + priv = SigningKey.from_secret_exponent(secexp, SECP256k1, sha256) + pub = priv.get_verifying_key() + + k = rfc6979.generate_k( + SECP256k1.generator.order(), secexp, sha256, sha256(data).digest() + ) + + sig1 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig1, data)) + + sig2 = priv.sign(data, k=k) + self.assertTrue(pub.verify(sig2, data)) + + sig3 = priv.sign_deterministic(data, sha256) + self.assertTrue(pub.verify(sig3, data)) + + self.assertEqual(sig1, sig2) + self.assertEqual(sig1, sig3) + + def test_bad_usage(self): + # sk=SigningKey() is wrong + self.assertRaises(TypeError, SigningKey) + self.assertRaises(TypeError, VerifyingKey) + + def test_lengths(self): + default = NIST192p + priv = SigningKey.generate() + pub = priv.get_verifying_key() + self.assertEqual(len(pub.to_string()), default.verifying_key_length) + sig = priv.sign(b("data")) + self.assertEqual(len(sig), default.signature_length) + for curve in ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + ): + priv = SigningKey.generate(curve=curve) + pub1 = priv.get_verifying_key() + pub2 = VerifyingKey.from_string(pub1.to_string(), curve) + self.assertEqual(pub1.to_string(), pub2.to_string()) + self.assertEqual(len(pub1.to_string()), curve.verifying_key_length) + sig = priv.sign(b("data")) + self.assertEqual(len(sig), curve.signature_length) + + def test_serialize(self): + seed = b("secret") + curve = NIST192p + secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) + secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) + self.assertEqual(secexp1, secexp2) + priv1 = SigningKey.from_secret_exponent(secexp1, curve) + priv2 = SigningKey.from_secret_exponent(secexp2, curve) + self.assertEqual( + hexlify(priv1.to_string()), hexlify(priv2.to_string()) + ) + self.assertEqual(priv1.to_pem(), priv2.to_pem()) + pub1 = priv1.get_verifying_key() + pub2 = priv2.get_verifying_key() + data = b("data") + sig1 = priv1.sign(data) + sig2 = priv2.sign(data) + self.assertTrue(pub1.verify(sig1, data)) + self.assertTrue(pub2.verify(sig1, data)) + self.assertTrue(pub1.verify(sig2, data)) + self.assertTrue(pub2.verify(sig2, data)) + self.assertEqual(hexlify(pub1.to_string()), hexlify(pub2.to_string())) + + def test_nonrandom(self): + s = b("all the entropy in the entire world, compressed into one line") + + def not_much_entropy(numbytes): + return s[:numbytes] + + # we control the entropy source, these two keys should be identical: + priv1 = SigningKey.generate(entropy=not_much_entropy) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual( + hexlify(priv1.get_verifying_key().to_string()), + hexlify(priv2.get_verifying_key().to_string()), + ) + # likewise, signatures should be identical. Obviously you'd never + # want to do this with keys you care about, because the secrecy of + # the private key depends upon using different random numbers for + # each signature + sig1 = priv1.sign(b("data"), entropy=not_much_entropy) + sig2 = priv2.sign(b("data"), entropy=not_much_entropy) + self.assertEqual(hexlify(sig1), hexlify(sig2)) + + def assertTruePrivkeysEqual(self, priv1, priv2): + self.assertEqual( + priv1.privkey.secret_multiplier, priv2.privkey.secret_multiplier + ) + self.assertEqual( + priv1.privkey.public_key.generator, + priv2.privkey.public_key.generator, + ) + + def test_privkey_creation(self): + s = b("all the entropy in the entire world, compressed into one line") + + def not_much_entropy(numbytes): + return s[:numbytes] + + priv1 = SigningKey.generate() + self.assertEqual(priv1.baselen, NIST192p.baselen) + + priv1 = SigningKey.generate(curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + priv1 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.generate(entropy=not_much_entropy) + self.assertEqual(priv2.baselen, NIST192p.baselen) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=3) + self.assertEqual(priv1.baselen, NIST192p.baselen) + priv2 = SigningKey.from_secret_exponent(secexp=3) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) + self.assertEqual(priv1.baselen, NIST224p.baselen) + + def test_privkey_strings(self): + priv1 = SigningKey.generate() + s1 = priv1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.baselen) + priv2 = SigningKey.from_string(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + priv1 = SigningKey.generate(curve=NIST256p) + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + def test_privkey_strings_brainpool(self): + priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) + s1 = priv1.to_pem() + self.assertEqual(type(s1), binary_type) + self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) + self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) + priv2 = SigningKey.from_pem(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + s1 = priv1.to_der() + self.assertEqual(type(s1), binary_type) + priv2 = SigningKey.from_der(s1) + self.assertTruePrivkeysEqual(priv1, priv2) + + def assertTruePubkeysEqual(self, pub1, pub2): + self.assertEqual(pub1.pubkey.point, pub2.pubkey.point) + self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator) + self.assertEqual(pub1.curve, pub2.curve) + + def test_pubkey_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST192p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1) + self.assertTruePubkeysEqual(pub1, pub2) + + priv1 = SigningKey.generate(curve=NIST256p) + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), NIST256p.verifying_key_length) + pub2 = VerifyingKey.from_string(s1, curve=NIST256p) + self.assertTruePubkeysEqual(pub1, pub2) + + pub1_der = pub1.to_der() + self.assertEqual(type(pub1_der), binary_type) + pub2 = VerifyingKey.from_der(pub1_der) + self.assertTruePubkeysEqual(pub1, pub2) + + self.assertRaises( + der.UnexpectedDER, VerifyingKey.from_der, pub1_der + b("junk") + ) + badpub = VerifyingKey.from_der(pub1_der) + + class FakeGenerator: + def order(self): + return 123456789 + + class FakeCurveFp: + def p(self): + return int( + "6525534529039240705020950546962731340" + "4541085228058844382513856749047873406763" + ) + + badcurve = Curve( + "unknown", FakeCurveFp(), FakeGenerator(), (1, 2, 3, 4, 5, 6), None + ) + badpub.curve = badcurve + badder = badpub.to_der() + self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) + + pem = pub1.to_pem() + self.assertEqual(type(pem), binary_type) + self.assertTrue(pem.startswith(b("-----BEGIN PUBLIC KEY-----")), pem) + self.assertTrue( + pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem + ) + pub2 = VerifyingKey.from_pem(pem) + self.assertTruePubkeysEqual(pub1, pub2) + + def test_pubkey_strings_brainpool(self): + priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) + pub1 = priv1.get_verifying_key() + s1 = pub1.to_string() + self.assertEqual(type(s1), binary_type) + self.assertEqual(len(s1), BRAINPOOLP512r1.verifying_key_length) + pub2 = VerifyingKey.from_string(s1, curve=BRAINPOOLP512r1) + self.assertTruePubkeysEqual(pub1, pub2) + + pub1_der = pub1.to_der() + self.assertEqual(type(pub1_der), binary_type) + pub2 = VerifyingKey.from_der(pub1_der) + self.assertTruePubkeysEqual(pub1, pub2) + + def test_vk_to_der_with_invalid_point_encoding(self): + sk = SigningKey.generate() + vk = sk.verifying_key + + with self.assertRaises(ValueError): + vk.to_der("raw") + + def test_sk_to_der_with_invalid_point_encoding(self): + sk = SigningKey.generate() + + with self.assertRaises(ValueError): + sk.to_der("raw") + + def test_vk_from_der_garbage_after_curve_oid(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + b( + "garbage" + ) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x00\xff", None) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_invalid_key_type(self): + type_oid_der = der.encode_oid(*(1, 2, 3)) + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x00\xff", None) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_garbage_after_point_string(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x00\xff", None) + b("garbage") + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_invalid_bitstring(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\x08\xff", None) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_with_invalid_length_of_encoding(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\xff" * 64, 0) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_der(to_decode) + + def test_vk_from_der_with_raw_encoding(self): + type_oid_der = encoded_oid_ecPublicKey + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) + point_der = der.encode_bitstring(b"\xff" * 48, 0) + to_decode = der.encode_sequence(enc_type_der, point_der) + + with self.assertRaises(der.UnexpectedDER): + VerifyingKey.from_der(to_decode) + + def test_signature_strings(self): + priv1 = SigningKey.generate() + pub1 = priv1.get_verifying_key() + data = b("data") + + sig = priv1.sign(data) + self.assertEqual(type(sig), binary_type) + self.assertEqual(len(sig), NIST192p.signature_length) + self.assertTrue(pub1.verify(sig, data)) + + sig = priv1.sign(data, sigencode=sigencode_strings) + self.assertEqual(type(sig), tuple) + self.assertEqual(len(sig), 2) + self.assertEqual(type(sig[0]), binary_type) + self.assertEqual(type(sig[1]), binary_type) + self.assertEqual(len(sig[0]), NIST192p.baselen) + self.assertEqual(len(sig[1]), NIST192p.baselen) + self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings)) + + sig_der = priv1.sign(data, sigencode=sigencode_der) + self.assertEqual(type(sig_der), binary_type) + self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) + + def test_sig_decode_strings_with_invalid_count(self): + with self.assertRaises(MalformedSignature): + sigdecode_strings([b("one"), b("two"), b("three")], 0xFF) + + def test_sig_decode_strings_with_wrong_r_len(self): + with self.assertRaises(MalformedSignature): + sigdecode_strings([b("one"), b("two")], 0xFF) + + def test_sig_decode_strings_with_wrong_s_len(self): + with self.assertRaises(MalformedSignature): + sigdecode_strings([b("\xa0"), b("\xb0\xff")], 0xFF) + + def test_verify_with_too_long_input(self): + sk = SigningKey.generate() + vk = sk.verifying_key + + with self.assertRaises(BadDigestError): + vk.verify_digest(None, b("\x00") * 128) + + def test_sk_from_secret_exponent_with_wrong_sec_exponent(self): + with self.assertRaises(MalformedPointError): + SigningKey.from_secret_exponent(0) + + def test_sk_from_string_with_wrong_len_string(self): + with self.assertRaises(MalformedPointError): + SigningKey.from_string(b("\x01")) + + def test_sk_from_der_with_junk_after_sequence(self): + ver_der = der.encode_integer(1) + to_decode = der.encode_sequence(ver_der) + b("garbage") + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_with_wrong_version(self): + ver_der = der.encode_integer(0) + to_decode = der.encode_sequence(ver_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_invalid_const_tag(self): + ver_der = der.encode_integer(1) + privkey_der = der.encode_octet_string(b("\x00\xff")) + curve_oid_der = der.encode_oid(*(1, 2, 3)) + const_der = der.encode_constructed(1, curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_garbage_after_privkey_oid(self): + ver_der = der.encode_integer(1) + privkey_der = der.encode_octet_string(b("\x00\xff")) + curve_oid_der = der.encode_oid(*(1, 2, 3)) + b("garbage") + const_der = der.encode_constructed(0, curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_der_with_short_privkey(self): + ver_der = der.encode_integer(1) + privkey_der = der.encode_octet_string(b("\x00\xff")) + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + const_der = der.encode_constructed(0, curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) + + sk = SigningKey.from_der(to_decode) + self.assertEqual(sk.privkey.secret_multiplier, 255) + + def test_sk_from_p8_der_with_wrong_version(self): + ver_der = der.encode_integer(2) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 840, 10045, 2, 1), + der.encode_oid(1, 2, 840, 10045, 3, 1, 1), + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + ) + to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_p8_der_with_wrong_algorithm(self): + ver_der = der.encode_integer(1) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 3), der.encode_oid(1, 2, 840, 10045, 3, 1, 1) + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + ) + to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_p8_der_with_trailing_junk_after_algorithm(self): + ver_der = der.encode_integer(1) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 840, 10045, 2, 1), + der.encode_oid(1, 2, 840, 10045, 3, 1, 1), + der.encode_octet_string(b"junk"), + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + ) + to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sk_from_p8_der_with_trailing_junk_after_key(self): + ver_der = der.encode_integer(1) + algorithm_der = der.encode_sequence( + der.encode_oid(1, 2, 840, 10045, 2, 1), + der.encode_oid(1, 2, 840, 10045, 3, 1, 1), + ) + privkey_der = der.encode_octet_string( + der.encode_sequence( + der.encode_integer(1), der.encode_octet_string(b"\x00\xff") + ) + + der.encode_integer(999) + ) + to_decode = der.encode_sequence( + ver_der, + algorithm_der, + privkey_der, + der.encode_octet_string(b"junk"), + ) + + with self.assertRaises(der.UnexpectedDER): + SigningKey.from_der(to_decode) + + def test_sign_with_too_long_hash(self): + sk = SigningKey.from_secret_exponent(12) + + with self.assertRaises(BadDigestError): + sk.sign_digest(b("\xff") * 64) + + def test_hashfunc(self): + sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256) + data = b("security level is 128 bits") + sig = sk.sign(data) + vk = VerifyingKey.from_string( + sk.get_verifying_key().to_string(), curve=NIST256p, hashfunc=sha256 + ) + self.assertTrue(vk.verify(sig, data)) + + sk2 = SigningKey.generate(curve=NIST256p) + sig2 = sk2.sign(data, hashfunc=sha256) + vk2 = VerifyingKey.from_string( + sk2.get_verifying_key().to_string(), + curve=NIST256p, + hashfunc=sha256, + ) + self.assertTrue(vk2.verify(sig2, data)) + + vk3 = VerifyingKey.from_string( + sk.get_verifying_key().to_string(), curve=NIST256p + ) + self.assertTrue(vk3.verify(sig, data, hashfunc=sha256)) + + def test_public_key_recovery(self): + # Create keys + curve = BRAINPOOLP160r1 + + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + + # Sign a message + data = b("blahblah") + signature = sk.sign(data) + + # Recover verifying keys + recovered_vks = VerifyingKey.from_public_key_recovery( + signature, data, curve + ) + + # Test if each pk is valid + for recovered_vk in recovered_vks: + # Test if recovered vk is valid for the data + self.assertTrue(recovered_vk.verify(signature, data)) + + # Test if properties are equal + self.assertEqual(vk.curve, recovered_vk.curve) + self.assertEqual( + vk.default_hashfunc, recovered_vk.default_hashfunc + ) + + # Test if original vk is the list of recovered keys + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], + ) + + def test_public_key_recovery_with_custom_hash(self): + # Create keys + curve = BRAINPOOLP160r1 + + sk = SigningKey.generate(curve=curve, hashfunc=sha256) + vk = sk.get_verifying_key() + + # Sign a message + data = b("blahblah") + signature = sk.sign(data) + + # Recover verifying keys + recovered_vks = VerifyingKey.from_public_key_recovery( + signature, data, curve, hashfunc=sha256, allow_truncate=True + ) + + # Test if each pk is valid + for recovered_vk in recovered_vks: + # Test if recovered vk is valid for the data + self.assertTrue(recovered_vk.verify(signature, data)) + + # Test if properties are equal + self.assertEqual(vk.curve, recovered_vk.curve) + self.assertEqual(sha256, recovered_vk.default_hashfunc) + + # Test if original vk is the list of recovered keys + self.assertIn( + vk.pubkey.point, + [recovered_vk.pubkey.point for recovered_vk in recovered_vks], + ) + + def test_encoding(self): + sk = SigningKey.from_secret_exponent(123456789) + vk = sk.verifying_key + + exp = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + self.assertEqual(vk.to_string(), exp) + self.assertEqual(vk.to_string("raw"), exp) + self.assertEqual(vk.to_string("uncompressed"), b("\x04") + exp) + self.assertEqual(vk.to_string("compressed"), b("\x02") + exp[:24]) + self.assertEqual(vk.to_string("hybrid"), b("\x06") + exp) + + def test_decoding(self): + sk = SigningKey.from_secret_exponent(123456789) + vk = sk.verifying_key + + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + from_raw = VerifyingKey.from_string(enc) + self.assertEqual(from_raw.pubkey.point, vk.pubkey.point) + + from_uncompressed = VerifyingKey.from_string(b("\x04") + enc) + self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) + + from_compressed = VerifyingKey.from_string(b("\x02") + enc[:24]) + self.assertEqual(from_compressed.pubkey.point, vk.pubkey.point) + + from_uncompressed = VerifyingKey.from_string(b("\x06") + enc) + self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) + + def test_uncompressed_decoding_as_only_alowed(self): + enc = b( + "\x04" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + vk = VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) + sk = SigningKey.from_secret_exponent(123456789) + + self.assertEqual(vk, sk.verifying_key) + + def test_raw_decoding_with_blocked_format(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) + + self.assertIn("hybrid", str(exp.exception)) + + def test_decoding_with_unknown_format(self): + with self.assertRaises(ValueError) as e: + VerifyingKey.from_string(b"", valid_encodings=("raw", "foobar")) + + self.assertIn("Only uncompressed, compressed", str(e.exception)) + + def test_uncompressed_decoding_with_blocked_format(self): + enc = b( + "\x04" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) + + self.assertIn("Invalid X9.62 encoding", str(exp.exception)) + + def test_hybrid_decoding_with_blocked_format(self): + enc = b( + "\x06" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) + + self.assertIn("Invalid X9.62 encoding", str(exp.exception)) + + def test_compressed_decoding_with_blocked_format(self): + enc = b( + "\x02" + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + )[:25] + with self.assertRaises(MalformedPointError) as exp: + VerifyingKey.from_string(enc, valid_encodings=("hybrid", "raw")) + + self.assertIn("(hybrid, raw)", str(exp.exception)) + + def test_decoding_with_malformed_uncompressed(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x02") + enc) + + def test_decoding_with_malformed_compressed(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x01") + enc[:24]) + + def test_decoding_with_inconsistent_hybrid(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x07") + enc) + + def test_decoding_with_point_not_on_curve(self): + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(enc[:47] + b("\x00")) + + def test_decoding_with_point_at_infinity(self): + # decoding it is unsupported, as it's not necessary to encode it + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x00")) + + def test_not_lying_on_curve(self): + enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p() + 1) + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x02") + enc) + + def test_from_string_with_invalid_curve_too_short_ver_key_len(self): + # both verifying_key_length and baselen are calculated internally + # by the Curve constructor, but since we depend on them verify + # that inconsistent values are detected + curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) + curve.verifying_key_length = 16 + curve.baselen = 32 + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x00") * 16, curve) + + def test_from_string_with_invalid_curve_too_long_ver_key_len(self): + # both verifying_key_length and baselen are calculated internally + # by the Curve constructor, but since we depend on them verify + # that inconsistent values are detected + curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) + curve.verifying_key_length = 16 + curve.baselen = 16 + + with self.assertRaises(MalformedPointError): + VerifyingKey.from_string(b("\x00") * 16, curve) + + +@pytest.mark.parametrize( + "val,even", [(i, j) for i in range(256) for j in [True, False]] +) +def test_VerifyingKey_decode_with_small_values(val, even): + enc = number_to_string(val, NIST192p.order) + + if even: + enc = b("\x02") + enc + else: + enc = b("\x03") + enc + + # small values can both be actual valid public keys and not, verify that + # only expected exceptions are raised if they are not + try: + vk = VerifyingKey.from_string(enc) + assert isinstance(vk, VerifyingKey) + except MalformedPointError: + assert True + + +params = [] +for curve in curves: + for enc in ["raw", "uncompressed", "compressed", "hybrid"]: + params.append( + pytest.param(curve, enc, id="{0}-{1}".format(curve.name, enc)) + ) + + +@pytest.mark.parametrize("curve,encoding", params) +def test_VerifyingKey_encode_decode(curve, encoding): + sk = SigningKey.generate(curve=curve) + vk = sk.verifying_key + + encoded = vk.to_string(encoding) + + from_enc = VerifyingKey.from_string(encoded, curve=curve) + + assert vk.pubkey.point == from_enc.pubkey.point + + +class OpenSSL(unittest.TestCase): + # test interoperability with OpenSSL tools. Note that openssl's ECDSA + # sign/verify arguments changed between 0.9.8 and 1.0.0: the early + # versions require "-ecdsa-with-SHA1", the later versions want just + # "-SHA1" (or to leave out that argument entirely, which means the + # signature will use some default digest algorithm, probably determined + # by the key, probably always SHA1). + # + # openssl ecparam -name secp224r1 -genkey -out privkey.pem + # openssl ec -in privkey.pem -text -noout # get the priv/pub keys + # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt + # openssl asn1parse -in data.sig -inform DER + # data.sig is 64 bytes, probably 56b plus ASN1 overhead + # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? + # openssl ec -in privkey.pem -pubout -out pubkey.pem + # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der + + OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") + ) + + def get_openssl_messagedigest_arg(self, hash_name): + v = run_openssl("version") + # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", + # or "OpenSSL 0.9.8o 01 Jun 2010" + vs = v.split()[1].split(".") + if vs >= ["1", "0", "0"]: # pragma: no cover + return "-{0}".format(hash_name) + else: # pragma: no cover + return "-ecdsa-with-{0}".format(hash_name) + + # sk: 1:OpenSSL->python 2:python->OpenSSL + # vk: 3:OpenSSL->python 4:python->OpenSSL + # sig: 5:OpenSSL->python 6:python->OpenSSL + + @pytest.mark.skipif( + "secp112r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r1", + ) + def test_from_openssl_secp112r1(self): + return self.do_test_from_openssl(SECP112r1) + + @pytest.mark.skipif( + "secp112r2" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r2", + ) + def test_from_openssl_secp112r2(self): + return self.do_test_from_openssl(SECP112r2) + + @pytest.mark.skipif( + "secp128r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp128r1", + ) + def test_from_openssl_secp128r1(self): + return self.do_test_from_openssl(SECP128r1) + + @pytest.mark.skipif( + "secp160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp160r1", + ) + def test_from_openssl_secp160r1(self): + return self.do_test_from_openssl(SECP160r1) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_from_openssl_nist192p(self): + return self.do_test_from_openssl(NIST192p) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_from_openssl_nist192p_sha256(self): + return self.do_test_from_openssl(NIST192p, "SHA256") + + @pytest.mark.skipif( + "secp224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp224r1", + ) + def test_from_openssl_nist224p(self): + return self.do_test_from_openssl(NIST224p) + + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_from_openssl_nist256p(self): + return self.do_test_from_openssl(NIST256p) + + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_from_openssl_nist256p_sha384(self): + return self.do_test_from_openssl(NIST256p, "SHA384") + + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_from_openssl_nist256p_sha512(self): + return self.do_test_from_openssl(NIST256p, "SHA512") + + @pytest.mark.skipif( + "secp384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp384r1", + ) + def test_from_openssl_nist384p(self): + return self.do_test_from_openssl(NIST384p) + + @pytest.mark.skipif( + "secp521r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp521r1", + ) + def test_from_openssl_nist521p(self): + return self.do_test_from_openssl(NIST521p) + + @pytest.mark.skipif( + "secp256k1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp256k1", + ) + def test_from_openssl_secp256k1(self): + return self.do_test_from_openssl(SECP256k1) + + @pytest.mark.skipif( + "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160r1", + ) + def test_from_openssl_brainpoolp160r1(self): + return self.do_test_from_openssl(BRAINPOOLP160r1) + + @pytest.mark.skipif( + "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192r1", + ) + def test_from_openssl_brainpoolp192r1(self): + return self.do_test_from_openssl(BRAINPOOLP192r1) + + @pytest.mark.skipif( + "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224r1", + ) + def test_from_openssl_brainpoolp224r1(self): + return self.do_test_from_openssl(BRAINPOOLP224r1) + + @pytest.mark.skipif( + "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256r1", + ) + def test_from_openssl_brainpoolp256r1(self): + return self.do_test_from_openssl(BRAINPOOLP256r1) + + @pytest.mark.skipif( + "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320r1", + ) + def test_from_openssl_brainpoolp320r1(self): + return self.do_test_from_openssl(BRAINPOOLP320r1) + + @pytest.mark.skipif( + "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384r1", + ) + def test_from_openssl_brainpoolp384r1(self): + return self.do_test_from_openssl(BRAINPOOLP384r1) + + @pytest.mark.skipif( + "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512r1", + ) + def test_from_openssl_brainpoolp512r1(self): + return self.do_test_from_openssl(BRAINPOOLP512r1) + + def do_test_from_openssl(self, curve, hash_name="SHA1"): + curvename = curve.openssl_name + assert curvename + # OpenSSL: create sk, vk, sign. + # Python: read vk(3), checksig(5), read sk(1), sign, check + mdarg = self.get_openssl_messagedigest_arg(hash_name) + if os.path.isdir("t"): # pragma: no cover + shutil.rmtree("t") + os.mkdir("t") + run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) + run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") + data = b("data") + with open("t/data.txt", "wb") as e: + e.write(data) + run_openssl( + "dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" + % mdarg + ) + with open("t/pubkey.pem", "rb") as e: + pubkey_pem = e.read() + vk = VerifyingKey.from_pem(pubkey_pem) # 3 + with open("t/data.sig", "rb") as e: + sig_der = e.read() + self.assertTrue( + vk.verify( + sig_der, + data, # 5 + hashfunc=partial(hashlib.new, hash_name), + sigdecode=sigdecode_der, + ) + ) + + with open("t/privkey.pem") as e: + fp = e.read() + sk = SigningKey.from_pem(fp) # 1 + sig = sk.sign(data, hashfunc=partial(hashlib.new, hash_name)) + self.assertTrue( + vk.verify(sig, data, hashfunc=partial(hashlib.new, hash_name)) + ) + + run_openssl( + "pkcs8 -topk8 -nocrypt " + "-in t/privkey.pem -outform pem -out t/privkey-p8.pem" + ) + with open("t/privkey-p8.pem", "rb") as e: + privkey_p8_pem = e.read() + sk_from_p8 = SigningKey.from_pem(privkey_p8_pem) + self.assertEqual(sk, sk_from_p8) + + @pytest.mark.skipif( + "secp112r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r1", + ) + def test_to_openssl_secp112r1(self): + self.do_test_to_openssl(SECP112r1) + + @pytest.mark.skipif( + "secp112r2" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp112r2", + ) + def test_to_openssl_secp112r2(self): + self.do_test_to_openssl(SECP112r2) + + @pytest.mark.skipif( + "secp128r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp128r1", + ) + def test_to_openssl_secp128r1(self): + self.do_test_to_openssl(SECP128r1) + + @pytest.mark.skipif( + "secp160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp160r1", + ) + def test_to_openssl_secp160r1(self): + self.do_test_to_openssl(SECP160r1) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_to_openssl_nist192p(self): + self.do_test_to_openssl(NIST192p) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_to_openssl_nist192p_sha256(self): + self.do_test_to_openssl(NIST192p, "SHA256") + + @pytest.mark.skipif( + "secp224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp224r1", + ) + def test_to_openssl_nist224p(self): + self.do_test_to_openssl(NIST224p) + + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_to_openssl_nist256p(self): + self.do_test_to_openssl(NIST256p) + + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_to_openssl_nist256p_sha384(self): + self.do_test_to_openssl(NIST256p, "SHA384") + + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) + def test_to_openssl_nist256p_sha512(self): + self.do_test_to_openssl(NIST256p, "SHA512") + + @pytest.mark.skipif( + "secp384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp384r1", + ) + def test_to_openssl_nist384p(self): + self.do_test_to_openssl(NIST384p) + + @pytest.mark.skipif( + "secp521r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp521r1", + ) + def test_to_openssl_nist521p(self): + self.do_test_to_openssl(NIST521p) + + @pytest.mark.skipif( + "secp256k1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp256k1", + ) + def test_to_openssl_secp256k1(self): + self.do_test_to_openssl(SECP256k1) + + @pytest.mark.skipif( + "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160r1", + ) + def test_to_openssl_brainpoolp160r1(self): + self.do_test_to_openssl(BRAINPOOLP160r1) + + @pytest.mark.skipif( + "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192r1", + ) + def test_to_openssl_brainpoolp192r1(self): + self.do_test_to_openssl(BRAINPOOLP192r1) + + @pytest.mark.skipif( + "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224r1", + ) + def test_to_openssl_brainpoolp224r1(self): + self.do_test_to_openssl(BRAINPOOLP224r1) + + @pytest.mark.skipif( + "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256r1", + ) + def test_to_openssl_brainpoolp256r1(self): + self.do_test_to_openssl(BRAINPOOLP256r1) + + @pytest.mark.skipif( + "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320r1", + ) + def test_to_openssl_brainpoolp320r1(self): + self.do_test_to_openssl(BRAINPOOLP320r1) + + @pytest.mark.skipif( + "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384r1", + ) + def test_to_openssl_brainpoolp384r1(self): + self.do_test_to_openssl(BRAINPOOLP384r1) + + @pytest.mark.skipif( + "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512r1", + ) + def test_to_openssl_brainpoolp512r1(self): + self.do_test_to_openssl(BRAINPOOLP512r1) + + def do_test_to_openssl(self, curve, hash_name="SHA1"): + curvename = curve.openssl_name + assert curvename + # Python: create sk, vk, sign. + # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check + mdarg = self.get_openssl_messagedigest_arg(hash_name) + if os.path.isdir("t"): # pragma: no cover + shutil.rmtree("t") + os.mkdir("t") + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + data = b("data") + with open("t/pubkey.der", "wb") as e: + e.write(vk.to_der()) # 4 + with open("t/pubkey.pem", "wb") as e: + e.write(vk.to_pem()) # 4 + sig_der = sk.sign( + data, + hashfunc=partial(hashlib.new, hash_name), + sigencode=sigencode_der, + ) + + with open("t/data.sig", "wb") as e: + e.write(sig_der) # 6 + with open("t/data.txt", "wb") as e: + e.write(data) + with open("t/baddata.txt", "wb") as e: + e.write(data + b("corrupt")) + + self.assertRaises( + SubprocessError, + run_openssl, + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" + % mdarg, + ) + run_openssl( + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" + % mdarg + ) + + with open("t/privkey.pem", "wb") as e: + e.write(sk.to_pem()) # 2 + run_openssl( + "dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" + % mdarg + ) + + with open("t/privkey-explicit.pem", "wb") as e: + e.write(sk.to_pem(curve_parameters_encoding="explicit")) + run_openssl( + "dgst %s -sign t/privkey-explicit.pem -out t/data.sig2 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" + % mdarg + ) + + with open("t/privkey-p8.pem", "wb") as e: + e.write(sk.to_pem(format="pkcs8")) + run_openssl( + "dgst %s -sign t/privkey-p8.pem -out t/data.sig3 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" + % mdarg + ) + + with open("t/privkey-p8-explicit.pem", "wb") as e: + e.write( + sk.to_pem(format="pkcs8", curve_parameters_encoding="explicit") + ) + run_openssl( + "dgst %s -sign t/privkey-p8-explicit.pem -out t/data.sig3 t/data.txt" + % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" + % mdarg + ) + + OPENSSL_SUPPORTED_TYPES = set() + try: + if "-rawin" in run_openssl("pkeyutl -help"): + OPENSSL_SUPPORTED_TYPES = set( + c.lower() + for c in ("ED25519", "ED448") + if c in run_openssl("list -public-key-methods") + ) + except SubprocessError: + pass + + def do_eddsa_test_to_openssl(self, curve): + curvename = curve.name.upper() + + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + + sk = SigningKey.generate(curve=curve) + vk = sk.get_verifying_key() + + data = b"data" + with open("t/pubkey.der", "wb") as e: + e.write(vk.to_der()) + with open("t/pubkey.pem", "wb") as e: + e.write(vk.to_pem()) + + sig = sk.sign(data) + + with open("t/data.sig", "wb") as e: + e.write(sig) + with open("t/data.txt", "wb") as e: + e.write(data) + with open("t/baddata.txt", "wb") as e: + e.write(data + b"corrupt") + + with self.assertRaises(SubprocessError): + run_openssl( + "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " + "-in t/baddata.txt -sigfile t/data.sig" + ) + run_openssl( + "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " + "-in t/data.txt -sigfile t/data.sig" + ) + + shutil.rmtree("t") + + # in practice at least OpenSSL 3.0.0 is needed to make EdDSA signatures + # earlier versions support EdDSA only in X.509 certificates + @pytest.mark.skipif( + "ed25519" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed25519", + ) + def test_to_openssl_ed25519(self): + return self.do_eddsa_test_to_openssl(Ed25519) + + @pytest.mark.skipif( + "ed448" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed448", + ) + def test_to_openssl_ed448(self): + return self.do_eddsa_test_to_openssl(Ed448) + + def do_eddsa_test_from_openssl(self, curve): + curvename = curve.name + + if os.path.isdir("t"): + shutil.rmtree("t") + os.mkdir("t") + + data = b"data" + + run_openssl( + "genpkey -algorithm {0} -outform PEM -out t/privkey.pem".format( + curvename + ) + ) + run_openssl( + "pkey -outform PEM -pubout -in t/privkey.pem -out t/pubkey.pem" + ) + + with open("t/data.txt", "wb") as e: + e.write(data) + run_openssl( + "pkeyutl -sign -inkey t/privkey.pem " + "-rawin -in t/data.txt -out t/data.sig" + ) + + with open("t/data.sig", "rb") as e: + sig = e.read() + with open("t/pubkey.pem", "rb") as e: + vk = VerifyingKey.from_pem(e.read()) + + self.assertIs(vk.curve, curve) + + vk.verify(sig, data) + + shutil.rmtree("t") + + @pytest.mark.skipif( + "ed25519" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed25519", + ) + def test_from_openssl_ed25519(self): + return self.do_eddsa_test_from_openssl(Ed25519) + + @pytest.mark.skipif( + "ed448" not in OPENSSL_SUPPORTED_TYPES, + reason="system openssl does not support signing with Ed448", + ) + def test_from_openssl_ed448(self): + return self.do_eddsa_test_from_openssl(Ed448) + + +class TooSmallCurve(unittest.TestCase): + OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") + ) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_sign_too_small_curve_dont_allow_truncate_raises(self): + sk = SigningKey.generate(curve=NIST192p) + data = b("data") + with self.assertRaises(BadDigestError): + sk.sign( + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigencode=sigencode_der, + allow_truncate=False, + ) + + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) + def test_verify_too_small_curve_dont_allow_truncate_raises(self): + sk = SigningKey.generate(curve=NIST192p) + vk = sk.get_verifying_key() + data = b("data") + sig_der = sk.sign( + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigencode=sigencode_der, + allow_truncate=True, + ) + with self.assertRaises(BadDigestError): + vk.verify( + sig_der, + data, + hashfunc=partial(hashlib.new, "SHA256"), + sigdecode=sigdecode_der, + allow_truncate=False, + ) + + +class DER(unittest.TestCase): + def test_integer(self): + self.assertEqual(der.encode_integer(0), b("\x02\x01\x00")) + self.assertEqual(der.encode_integer(1), b("\x02\x01\x01")) + self.assertEqual(der.encode_integer(127), b("\x02\x01\x7f")) + self.assertEqual(der.encode_integer(128), b("\x02\x02\x00\x80")) + self.assertEqual(der.encode_integer(256), b("\x02\x02\x01\x00")) + # self.assertEqual(der.encode_integer(-1), b("\x02\x01\xff")) + + def s(n): + return der.remove_integer(der.encode_integer(n) + b("junk")) + + self.assertEqual(s(0), (0, b("junk"))) + self.assertEqual(s(1), (1, b("junk"))) + self.assertEqual(s(127), (127, b("junk"))) + self.assertEqual(s(128), (128, b("junk"))) + self.assertEqual(s(256), (256, b("junk"))) + self.assertEqual( + s(1234567890123456789012345678901234567890), + (1234567890123456789012345678901234567890, b("junk")), + ) + + def test_number(self): + self.assertEqual(der.encode_number(0), b("\x00")) + self.assertEqual(der.encode_number(127), b("\x7f")) + self.assertEqual(der.encode_number(128), b("\x81\x00")) + self.assertEqual(der.encode_number(3 * 128 + 7), b("\x83\x07")) + # self.assertEqual(der.read_number("\x81\x9b" + "more"), (155, 2)) + # self.assertEqual(der.encode_number(155), b("\x81\x9b")) + for n in (0, 1, 2, 127, 128, 3 * 128 + 7, 840, 10045): # , 155): + x = der.encode_number(n) + b("more") + n1, llen = der.read_number(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b("more")) + + def test_length(self): + self.assertEqual(der.encode_length(0), b("\x00")) + self.assertEqual(der.encode_length(127), b("\x7f")) + self.assertEqual(der.encode_length(128), b("\x81\x80")) + self.assertEqual(der.encode_length(255), b("\x81\xff")) + self.assertEqual(der.encode_length(256), b("\x82\x01\x00")) + self.assertEqual(der.encode_length(3 * 256 + 7), b("\x82\x03\x07")) + self.assertEqual(der.read_length(b("\x81\x9b") + b("more")), (155, 2)) + self.assertEqual(der.encode_length(155), b("\x81\x9b")) + for n in (0, 1, 2, 127, 128, 255, 256, 3 * 256 + 7, 155): + x = der.encode_length(n) + b("more") + n1, llen = der.read_length(x) + self.assertEqual(n1, n) + self.assertEqual(x[llen:], b("more")) + + def test_sequence(self): + x = der.encode_sequence(b("ABC"), b("DEF")) + b("GHI") + self.assertEqual(x, b("\x30\x06ABCDEFGHI")) + x1, rest = der.remove_sequence(x) + self.assertEqual(x1, b("ABCDEF")) + self.assertEqual(rest, b("GHI")) + + def test_constructed(self): + x = der.encode_constructed(0, NIST224p.encoded_oid) + self.assertEqual(hexlify(x), b("a007") + b("06052b81040021")) + x = der.encode_constructed(1, unhexlify(b("0102030a0b0c"))) + self.assertEqual(hexlify(x), b("a106") + b("0102030a0b0c")) + + +class Util(unittest.TestCase): + def test_trytryagain(self): + tta = util.randrange_from_seed__trytryagain + for i in range(1000): + seed = "seed-%d" % i + for order in ( + 2**8 - 2, + 2**8 - 1, + 2**8, + 2**8 + 1, + 2**8 + 2, + 2**16 - 1, + 2**16 + 1, + ): + n = tta(seed, order) + self.assertTrue(1 <= n < order, (1, n, order)) + # this trytryagain *does* provide long-term stability + self.assertEqual( + ("%x" % (tta("seed", NIST224p.order))).encode(), + b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc"), + ) + + def test_trytryagain_single(self): + tta = util.randrange_from_seed__trytryagain + order = 2**8 - 2 + seed = b"text" + n = tta(seed, order) + # known issue: https://github.com/warner/python-ecdsa/issues/221 + if sys.version_info < (3, 0): # pragma: no branch + self.assertEqual(n, 228) + else: + self.assertEqual(n, 18) + + @given(st.integers(min_value=0, max_value=10**200)) + def test_randrange(self, i): + # util.randrange does not provide long-term stability: we might + # change the algorithm in the future. + entropy = util.PRNG("seed-%d" % i) + for order in ( + 2**8 - 2, + 2**8 - 1, + 2**8, + 2**16 - 1, + 2**16 + 1, + ): + # that oddball 2**16+1 takes half our runtime + n = util.randrange(order, entropy=entropy) + self.assertTrue(1 <= n < order, (1, n, order)) + + def OFF_test_prove_uniformity(self): # pragma: no cover + order = 2**8 - 2 + counts = dict([(i, 0) for i in range(1, order)]) + assert 0 not in counts + assert order not in counts + for i in range(1000000): + seed = "seed-%d" % i + n = util.randrange_from_seed__trytryagain(seed, order) + counts[n] += 1 + # this technique should use the full range + self.assertTrue(counts[order - 1]) + for i in range(1, order): + print_("%3d: %s" % (i, "*" * (counts[i] // 100))) + + +class RFC6979(unittest.TestCase): + # https://tools.ietf.org/html/rfc6979#appendix-A.1 + def _do(self, generator, secexp, hsh, hash_func, expected): + actual = rfc6979.generate_k(generator.order(), secexp, hash_func, hsh) + self.assertEqual(expected, actual) + + def test_SECP256k1(self): + """RFC doesn't contain test vectors for SECP256k1 used in bitcoin. + This vector has been computed by Golang reference implementation instead.""" + self._do( + generator=SECP256k1.generator, + secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16), + hsh=sha256(b("sample")).digest(), + hash_func=sha256, + expected=int( + "8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", + 16, + ), + ) + + def test_SECP256k1_2(self): + self._do( + generator=SECP256k1.generator, + secexp=int( + "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", + 16, + ), + hsh=sha256(b("sample")).digest(), + hash_func=sha256, + expected=int( + "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", + 16, + ), + ) + + def test_SECP256k1_3(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=sha256(b("Satoshi Nakamoto")).digest(), + hash_func=sha256, + expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, + ) + + def test_SECP256k1_4(self): + self._do( + generator=SECP256k1.generator, + secexp=0x1, + hsh=sha256( + b( + "All those moments will be lost in time, like tears in rain. Time to die..." + ) + ).digest(), + hash_func=sha256, + expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, + ) + + def test_SECP256k1_5(self): + self._do( + generator=SECP256k1.generator, + secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, + hsh=sha256(b("Satoshi Nakamoto")).digest(), + hash_func=sha256, + expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, + ) + + def test_SECP256k1_6(self): + self._do( + generator=SECP256k1.generator, + secexp=0xF8B8AF8CE3C7CCA5E300D33939540C10D45CE001B8F252BFBC57BA0342904181, + hsh=sha256(b("Alan Turing")).digest(), + hash_func=sha256, + expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, + ) + + def test_1(self): + # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 + self._do( + generator=Point( + None, + 0, + 0, + int("4000000000000000000020108A2E0CC0D99F8A5EF", 16), + ), + secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), + hsh=unhexlify( + b( + "AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF" + ) + ), + hash_func=sha256, + expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16), + ) + + def test_2(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=sha1(b("sample")).digest(), + hash_func=sha1, + expected=int( + "37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16 + ), + ) + + def test_3(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=sha256(b("sample")).digest(), + hash_func=sha256, + expected=int( + "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16 + ), + ) + + def test_4(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=sha512(b("sample")).digest(), + hash_func=sha512, + expected=int( + "A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16 + ), + ) + + def test_5(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=sha1(b("test")).digest(), + hash_func=sha1, + expected=int( + "D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16 + ), + ) + + def test_6(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=sha256(b("test")).digest(), + hash_func=sha256, + expected=int( + "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16 + ), + ) + + def test_7(self): + self._do( + generator=NIST192p.generator, + secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + hsh=sha512(b("test")).digest(), + hash_func=sha512, + expected=int( + "0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16 + ), + ) + + def test_8(self): + self._do( + generator=NIST521p.generator, + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), + hsh=sha1(b("sample")).digest(), + hash_func=sha1, + expected=int( + "089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", + 16, + ), + ) + + def test_9(self): + self._do( + generator=NIST521p.generator, + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), + hsh=sha256(b("sample")).digest(), + hash_func=sha256, + expected=int( + "0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", + 16, + ), + ) + + def test_10(self): + self._do( + generator=NIST521p.generator, + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), + hsh=sha512(b("test")).digest(), + hash_func=sha512, + expected=int( + "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", + 16, + ), + ) + + +class ECDH(unittest.TestCase): + def _do(self, curve, generator, dA, x_qA, y_qA, dB, x_qB, y_qB, x_Z, y_Z): + qA = dA * generator + qB = dB * generator + Z = dA * qB + self.assertEqual(Point(curve, x_qA, y_qA), qA) + self.assertEqual(Point(curve, x_qB, y_qB), qB) + self.assertTrue( + (dA * qB) + == (dA * dB * generator) + == (dB * dA * generator) + == (dB * qA) + ) + self.assertEqual(Point(curve, x_Z, y_Z), Z) + + +class RFC6932(ECDH): + # https://tools.ietf.org/html/rfc6932#appendix-A.1 + + def test_brainpoolP224r1(self): + self._do( + curve=curve_brainpoolp224r1, + generator=BRAINPOOLP224r1.generator, + dA=int( + "7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", 16 + ), + x_qA=int( + "B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", 16 + ), + y_qA=int( + "46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", 16 + ), + dB=int( + "63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", 16 + ), + x_qB=int( + "2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", 16 + ), + y_qB=int( + "9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", 16 + ), + x_Z=int( + "312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", 16 + ), + y_Z=int( + "6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", 16 + ), + ) + + def test_brainpoolP256r1(self): + self._do( + curve=curve_brainpoolp256r1, + generator=BRAINPOOLP256r1.generator, + dA=int( + "041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" + "49699", + 16, + ), + x_qA=int( + "78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" + "CC0D206", + 16, + ), + y_qA=int( + "A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" + "B05F92B", + 16, + ), + dB=int( + "06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" + "804D3", + 16, + ), + x_qB=int( + "8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" + "341F0DB", + 16, + ), + y_qB=int( + "148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" + "4FDE40D", + 16, + ), + x_Z=int( + "05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" + "E23708", + 16, + ), + y_Z=int( + "6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" + "FD134C", + 16, + ), + ) + + def test_brainpoolP384r1(self): + self._do( + curve=curve_brainpoolp384r1, + generator=BRAINPOOLP384r1.generator, + dA=int( + "014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" + "828D97E095884CA72B73FDABD5910DF0FA76A", + 16, + ), + x_qA=int( + "45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" + "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", + 16, + ), + y_qA=int( + "8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" + "2DC530A2CD89C859BB4B4B768497F49AB8CC859", + 16, + ), + dB=int( + "6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" + "5C90095B976F3529957506E1224A861711D54", + 16, + ), + x_qB=int( + "01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" + "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", + 16, + ), + y_qB=int( + "3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" + "8EE47487ED8044858D31D848F7A95C635A347AC", + 16, + ), + x_Z=int( + "04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" + "2F370A1F98D3FA73FD0C0747C632E12F1423EC", + 16, + ), + y_Z=int( + "7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" + "5D05085F5A01A9382D05BF72D96698FE3FF64E", + 16, + ), + ) + + def test_brainpoolP512r1(self): + self._do( + curve=curve_brainpoolp512r1, + generator=BRAINPOOLP512r1.generator, + dA=int( + "636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" + "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" + "4CCE6FE333", + 16, + ), + x_qA=int( + "0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" + "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" + "AFC3EF0971CCDA", + 16, + ), + y_qA=int( + "A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" + "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" + "8EFC7343EA642E", + 16, + ), + dB=int( + "0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" + "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" + "99B86E5C8B", + 16, + ), + x_qB=int( + "5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" + "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" + "E739E11720D323", + 16, + ), + y_qB=int( + "96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" + "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" + "54EE1BA35E04BE", + 16, + ), + x_Z=int( + "1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" + "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" + "10E5811B191E", + 16, + ), + y_Z=int( + "2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" + "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" + "4F9FE9EAABA6", + 16, + ), + ) + + +class RFC7027(ECDH): + # https://tools.ietf.org/html/rfc7027#appendix-A + + def test_brainpoolP256r1(self): + self._do( + curve=curve_brainpoolp256r1, + generator=BRAINPOOLP256r1.generator, + dA=int( + "81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" + "04F1D", + 16, + ), + x_qA=int( + "44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" + "3100BE5", + 16, + ), + y_qA=int( + "8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" + "B089BDC", + 16, + ), + dB=int( + "55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" + "76BD3", + 16, + ), + x_qB=int( + "8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" + "1B39F7B", + 16, + ), + y_qB=int( + "990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" + "47CEC6A", + 16, + ), + x_Z=int( + "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" + "18BF2B", + 16, + ), + y_Z=int( + "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" + "2504DE", + 16, + ), + ) + + def test_brainpoolP384r1(self): + self._do( + curve=curve_brainpoolp384r1, + generator=BRAINPOOLP384r1.generator, + dA=int( + "1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" + "D65D6F15EB5D1EE1610DF870795143627D042", + 16, + ), + x_qA=int( + "68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" + "588F885AB698C852D4A6E77A252D6380FCAF068", + 16, + ), + y_qA=int( + "55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" + "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", + 16, + ), + dB=int( + "032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" + "4E01F8BA5E0324309DB6A9831497ABAC96670", + 16, + ), + x_qB=int( + "4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" + "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", + 16, + ), + y_qB=int( + "62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" + "9185329B5B275903D192F8D4E1F32FE9CC78C48", + 16, + ), + x_Z=int( + "0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" + "E239BBADF6403715C35D4FB2A5444F575D4F42", + 16, + ), + y_Z=int( + "0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" + "E9E598157290F8756066975F1DB34B2324B7BD", + 16, + ), + ) + + def test_brainpoolP512r1(self): + self._do( + curve=curve_brainpoolp512r1, + generator=BRAINPOOLP512r1.generator, + dA=int( + "16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" + "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" + "AD57665422", + 16, + ), + x_qA=int( + "0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" + "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" + "36D11640FD09FD", + 16, + ), + y_qA=int( + "72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" + "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" + "E82A6AD147FDE7", + 16, + ), + dB=int( + "230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" + "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" + "3666B25429", + 16, + ), + x_qB=int( + "9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" + "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" + "EDA55A5473199F", + 16, + ), + y_qB=int( + "2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" + "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" + "512B71876285FA", + 16, + ), + x_Z=int( + "A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" + "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" + "454B21C4CD1F", + 16, + ), + y_Z=int( + "7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" + "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" + "2BE6A26680A2", + 16, + ), + ) + + +# https://tools.ietf.org/html/rfc4754#page-5 +@pytest.mark.parametrize( + "w, gwx, gwy, k, msg, md, r, s, curve", + [ + pytest.param( + "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", + "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", + "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", + "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", + b"abc", + sha256, + "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", + "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", + NIST256p, + id="ECDSA-256", + ), + pytest.param( + "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" + "62E528C38B2A81B35309668D73524D9F", + "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" + "5CE10C9DFEC21797415710721F437922", + "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" + "3D383B91C5E7EDAA2B714CC99D5743CA", + "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" + "854D7FA992F934D927376285E63414FA", + b"abc", + sha384, + "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" + "08E07C9C63F2D21A07DCB56A6AF56EB3", + "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" + "CBB9F516CE0FA7D2FF630863A00E8B9F", + NIST384p, + id="ECDSA-384", + ), + pytest.param( + "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" + "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" + "5FA1", + "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" + "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" + "52A8", + "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" + "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" + "E643", + "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" + "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" + "6C2F", + b"abc", + sha512, + "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" + "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" + "2251", + "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" + "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" + "2660", + NIST521p, + id="ECDSA-521", + ), + ], +) +def test_RFC4754_vectors(w, gwx, gwy, k, msg, md, r, s, curve): + sk = SigningKey.from_string(unhexlify(w), curve) + vk = VerifyingKey.from_string(unhexlify(gwx + gwy), curve) + assert sk.verifying_key == vk + sig = sk.sign(msg, hashfunc=md, sigencode=sigencode_strings, k=int(k, 16)) + + assert sig == (unhexlify(r), unhexlify(s)) + + assert vk.verify(sig, msg, md, sigdecode_strings) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py new file mode 100644 index 000000000..0a84b9c70 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py @@ -0,0 +1,180 @@ +# Copyright Mateusz Kobos, (c) 2011 +# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ +# released under the MIT licence + +try: + import unittest2 as unittest +except ImportError: + import unittest +import threading +import time +import copy +from ._rwlock import RWLock + + +class Writer(threading.Thread): + def __init__( + self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write + ): + """ + @param buffer_: common buffer_ shared by the readers and writers + @type buffer_: list + @type rw_lock: L{RWLock} + @param init_sleep_time: sleep time before doing any action + @type init_sleep_time: C{float} + @param sleep_time: sleep time while in critical section + @type sleep_time: C{float} + @param to_write: data that will be appended to the buffer + """ + threading.Thread.__init__(self) + self.__buffer = buffer_ + self.__rw_lock = rw_lock + self.__init_sleep_time = init_sleep_time + self.__sleep_time = sleep_time + self.__to_write = to_write + self.entry_time = None + """Time of entry to the critical section""" + self.exit_time = None + """Time of exit from the critical section""" + + def run(self): + time.sleep(self.__init_sleep_time) + self.__rw_lock.writer_acquire() + self.entry_time = time.time() + time.sleep(self.__sleep_time) + self.__buffer.append(self.__to_write) + self.exit_time = time.time() + self.__rw_lock.writer_release() + + +class Reader(threading.Thread): + def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time): + """ + @param buffer_: common buffer shared by the readers and writers + @type buffer_: list + @type rw_lock: L{RWLock} + @param init_sleep_time: sleep time before doing any action + @type init_sleep_time: C{float} + @param sleep_time: sleep time while in critical section + @type sleep_time: C{float} + """ + threading.Thread.__init__(self) + self.__buffer = buffer_ + self.__rw_lock = rw_lock + self.__init_sleep_time = init_sleep_time + self.__sleep_time = sleep_time + self.buffer_read = None + """a copy of a the buffer read while in critical section""" + self.entry_time = None + """Time of entry to the critical section""" + self.exit_time = None + """Time of exit from the critical section""" + + def run(self): + time.sleep(self.__init_sleep_time) + self.__rw_lock.reader_acquire() + self.entry_time = time.time() + time.sleep(self.__sleep_time) + self.buffer_read = copy.deepcopy(self.__buffer) + self.exit_time = time.time() + self.__rw_lock.reader_release() + + +class RWLockTestCase(unittest.TestCase): + def test_readers_nonexclusive_access(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Reader(buffer_, rw_lock, 0, 0)) + threads.append(Writer(buffer_, rw_lock, 0.2, 0.4, 1)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0.3)) + threads.append(Reader(buffer_, rw_lock, 0.5, 0)) + + self.__start_and_join_threads(threads) + + ## The third reader should enter after the second one but it should + ## exit before the second one exits + ## (i.e. the readers should be in the critical section + ## at the same time) + + self.assertEqual([], threads[0].buffer_read) + self.assertEqual([1], threads[2].buffer_read) + self.assertEqual([1], threads[3].buffer_read) + self.assertTrue(threads[1].exit_time <= threads[2].entry_time) + self.assertTrue(threads[2].entry_time <= threads[3].entry_time) + self.assertTrue(threads[3].exit_time < threads[2].exit_time) + + def test_writers_exclusive_access(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Writer(buffer_, rw_lock, 0, 0.4, 1)) + threads.append(Writer(buffer_, rw_lock, 0.1, 0, 2)) + threads.append(Reader(buffer_, rw_lock, 0.2, 0)) + + self.__start_and_join_threads(threads) + + ## The second writer should wait for the first one to exit + + self.assertEqual([1, 2], threads[2].buffer_read) + self.assertTrue(threads[0].exit_time <= threads[1].entry_time) + self.assertTrue(threads[1].exit_time <= threads[2].exit_time) + + def test_writer_priority(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) + threads.append(Reader(buffer_, rw_lock, 0.1, 0.4)) + threads.append(Writer(buffer_, rw_lock, 0.2, 0, 2)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0)) + + self.__start_and_join_threads(threads) + + ## The second writer should go before the second and the third reader + + self.assertEqual([1], threads[1].buffer_read) + self.assertEqual([1, 2], threads[3].buffer_read) + self.assertEqual([1, 2], threads[4].buffer_read) + self.assertTrue(threads[0].exit_time < threads[1].entry_time) + self.assertTrue(threads[1].exit_time <= threads[2].entry_time) + self.assertTrue(threads[2].exit_time <= threads[3].entry_time) + self.assertTrue(threads[2].exit_time <= threads[4].entry_time) + + def test_many_writers_priority(self): + (buffer_, rw_lock, threads) = self.__init_variables() + + threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) + threads.append(Reader(buffer_, rw_lock, 0.1, 0.6)) + threads.append(Writer(buffer_, rw_lock, 0.2, 0.1, 2)) + threads.append(Reader(buffer_, rw_lock, 0.3, 0)) + threads.append(Reader(buffer_, rw_lock, 0.4, 0)) + threads.append(Writer(buffer_, rw_lock, 0.5, 0.1, 3)) + + self.__start_and_join_threads(threads) + + ## The two last writers should go first -- after the first reader and + ## before the second and the third reader + + self.assertEqual([1], threads[1].buffer_read) + self.assertEqual([1, 2, 3], threads[3].buffer_read) + self.assertEqual([1, 2, 3], threads[4].buffer_read) + self.assertTrue(threads[0].exit_time < threads[1].entry_time) + self.assertTrue(threads[1].exit_time <= threads[2].entry_time) + self.assertTrue(threads[1].exit_time <= threads[5].entry_time) + self.assertTrue(threads[2].exit_time <= threads[3].entry_time) + self.assertTrue(threads[2].exit_time <= threads[4].entry_time) + self.assertTrue(threads[5].exit_time <= threads[3].entry_time) + self.assertTrue(threads[5].exit_time <= threads[4].entry_time) + + @staticmethod + def __init_variables(): + buffer_ = [] + rw_lock = RWLock() + threads = [] + return (buffer_, rw_lock, threads) + + @staticmethod + def __start_and_join_threads(threads): + for t in threads: + t.start() + for t in threads: + t.join() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py new file mode 100644 index 000000000..2c6bd15cd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py @@ -0,0 +1,111 @@ +try: + import unittest2 as unittest +except ImportError: + import unittest +import pytest + +try: + from gmpy2 import mpz + + GMPY = True +except ImportError: + try: + from gmpy import mpz + + GMPY = True + except ImportError: + GMPY = False + +from ._sha3 import shake_256 +from ._compat import bytes_to_int, int_to_bytes + +B2I_VECTORS = [ + (b"\x00\x01", "big", 1), + (b"\x00\x01", "little", 0x0100), + (b"", "big", 0), + (b"\x00", "little", 0), +] + + +@pytest.mark.parametrize("bytes_in,endian,int_out", B2I_VECTORS) +def test_bytes_to_int(bytes_in, endian, int_out): + out = bytes_to_int(bytes_in, endian) + assert out == int_out + + +class TestBytesToInt(unittest.TestCase): + def test_bytes_to_int_wrong_endian(self): + with self.assertRaises(ValueError): + bytes_to_int(b"\x00", "middle") + + def test_int_to_bytes_wrong_endian(self): + with self.assertRaises(ValueError): + int_to_bytes(0, byteorder="middle") + + +@pytest.mark.skipif(GMPY == False, reason="requites gmpy or gmpy2") +def test_int_to_bytes_with_gmpy(): + assert int_to_bytes(mpz(1)) == b"\x01" + + +I2B_VECTORS = [ + (0, None, "big", b""), + (0, 1, "big", b"\x00"), + (1, None, "big", b"\x01"), + (0x0100, None, "little", b"\x00\x01"), + (0x0100, 4, "little", b"\x00\x01\x00\x00"), + (1, 4, "big", b"\x00\x00\x00\x01"), +] + + +@pytest.mark.parametrize("int_in,length,endian,bytes_out", I2B_VECTORS) +def test_int_to_bytes(int_in, length, endian, bytes_out): + out = int_to_bytes(int_in, length, endian) + assert out == bytes_out + + +SHAKE_256_VECTORS = [ + ( + b"Message.", + 32, + b"\x78\xa1\x37\xbb\x33\xae\xe2\x72\xb1\x02\x4f\x39\x43\xe5\xcf\x0c" + b"\x4e\x9c\x72\x76\x2e\x34\x4c\xf8\xf9\xc3\x25\x9d\x4f\x91\x2c\x3a", + ), + ( + b"", + 32, + b"\x46\xb9\xdd\x2b\x0b\xa8\x8d\x13\x23\x3b\x3f\xeb\x74\x3e\xeb\x24" + b"\x3f\xcd\x52\xea\x62\xb8\x1b\x82\xb5\x0c\x27\x64\x6e\xd5\x76\x2f", + ), + ( + b"message", + 32, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" + b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75", + ), + ( + b"message", + 16, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51", + ), + ( + b"message", + 64, + b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" + b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75" + b"\x30\xd6\xba\x2a\x46\x65\xf1\x9d\xf0\x62\x25\xb1\x26\xd1\x3e\xed" + b"\x91\xd5\x0d\xe7\xb9\xcb\x65\xf3\x3a\x46\xae\xd3\x6c\x7d\xc5\xe8", + ), + ( + b"A" * 1024, + 32, + b"\xa5\xef\x7e\x30\x8b\xe8\x33\x64\xe5\x9c\xf3\xb5\xf3\xba\x20\xa3" + b"\x5a\xe7\x30\xfd\xbc\x33\x11\xbf\x83\x89\x50\x82\xb4\x41\xe9\xb3", + ), +] + + +@pytest.mark.parametrize("msg,olen,ohash", SHAKE_256_VECTORS) +def test_shake_256(msg, olen, ohash): + out = shake_256(msg, olen) + assert out == bytearray(ohash) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py new file mode 100644 index 000000000..9a5611053 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py @@ -0,0 +1,433 @@ +from __future__ import division + +import os +import math +import binascii +import sys +from hashlib import sha256 +from six import PY2, int2byte, b, next +from . import der +from ._compat import normalise_bytes + +# RFC5480: +# The "unrestricted" algorithm identifier is: +# id-ecPublicKey OBJECT IDENTIFIER ::= { +# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } + +oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) +encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) + +# RFC5480: +# The ECDH algorithm uses the following object identifier: +# id-ecDH OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) schemes(1) +# ecdh(12) } + +oid_ecDH = (1, 3, 132, 1, 12) + +# RFC5480: +# The ECMQV algorithm uses the following object identifier: +# id-ecMQV OBJECT IDENTIFIER ::= { +# iso(1) identified-organization(3) certicom(132) schemes(1) +# ecmqv(13) } + +oid_ecMQV = (1, 3, 132, 1, 13) + +if sys.version_info >= (3,): # pragma: no branch + + def entropy_to_bits(ent_256): + """Convert a bytestring to string of 0's and 1's""" + return bin(int.from_bytes(ent_256, "big"))[2:].zfill(len(ent_256) * 8) + +else: + + def entropy_to_bits(ent_256): + """Convert a bytestring to string of 0's and 1's""" + return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256) + + +if sys.version_info < (2, 7): # pragma: no branch + # Can't add a method to a built-in type so we are stuck with this + def bit_length(x): + return len(bin(x)) - 2 + +else: + + def bit_length(x): + return x.bit_length() or 1 + + +def orderlen(order): + return (1 + len("%x" % order)) // 2 # bytes + + +def randrange(order, entropy=None): + """Return a random integer k such that 1 <= k < order, uniformly + distributed across that range. Worst case should be a mean of 2 loops at + (2**k)+2. + + Note that this function is not declared to be forwards-compatible: we may + change the behavior in future releases. The entropy= argument (which + should get a callable that behaves like os.urandom) can be used to + achieve stability within a given release (for repeatable unit tests), but + should not be used as a long-term-compatible key generation algorithm. + """ + assert order > 1 + if entropy is None: + entropy = os.urandom + upper_2 = bit_length(order - 2) + upper_256 = upper_2 // 8 + 1 + while True: # I don't think this needs a counter with bit-wise randrange + ent_256 = entropy(upper_256) + ent_2 = entropy_to_bits(ent_256) + rand_num = int(ent_2[:upper_2], base=2) + 1 + if 0 < rand_num < order: + return rand_num + + +class PRNG: + # this returns a callable which, when invoked with an integer N, will + # return N pseudorandom bytes. Note: this is a short-term PRNG, meant + # primarily for the needs of randrange_from_seed__trytryagain(), which + # only needs to run it a few times per seed. It does not provide + # protection against state compromise (forward security). + def __init__(self, seed): + self.generator = self.block_generator(seed) + + def __call__(self, numbytes): + a = [next(self.generator) for i in range(numbytes)] + + if PY2: # pragma: no branch + return "".join(a) + else: + return bytes(a) + + def block_generator(self, seed): + counter = 0 + while True: + for byte in sha256( + ("prng-%d-%s" % (counter, seed)).encode() + ).digest(): + yield byte + counter += 1 + + +def randrange_from_seed__overshoot_modulo(seed, order): + # hash the data, then turn the digest into a number in [1,order). + # + # We use David-Sarah Hopwood's suggestion: turn it into a number that's + # sufficiently larger than the group order, then modulo it down to fit. + # This should give adequate (but not perfect) uniformity, and simple + # code. There are other choices: try-try-again is the main one. + base = PRNG(seed)(2 * orderlen(order)) + number = (int(binascii.hexlify(base), 16) % (order - 1)) + 1 + assert 1 <= number < order, (1, number, order) + return number + + +def lsb_of_ones(numbits): + return (1 << numbits) - 1 + + +def bits_and_bytes(order): + bits = int(math.log(order - 1, 2) + 1) + bytes = bits // 8 + extrabits = bits % 8 + return bits, bytes, extrabits + + +# the following randrange_from_seed__METHOD() functions take an +# arbitrarily-sized secret seed and turn it into a number that obeys the same +# range limits as randrange() above. They are meant for deriving consistent +# signing keys from a secret rather than generating them randomly, for +# example a protocol in which three signing keys are derived from a master +# secret. You should use a uniformly-distributed unguessable seed with about +# curve.baselen bytes of entropy. To use one, do this: +# seed = os.urandom(curve.baselen) # or other starting point +# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) +# sk = SigningKey.from_secret_exponent(secexp, curve) + + +def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): + # hash the seed, then turn the digest into a number in [1,order), but + # don't worry about trying to uniformly fill the range. This will lose, + # on average, four bits of entropy. + bits, _bytes, extrabits = bits_and_bytes(order) + if extrabits: + _bytes += 1 + base = hashmod(seed).digest()[:_bytes] + base = "\x00" * (_bytes - len(base)) + base + number = 1 + int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + + +def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): + # like string_to_randrange_truncate_bytes, but only lose an average of + # half a bit + bits = int(math.log(order - 1, 2) + 1) + maxbytes = (bits + 7) // 8 + base = hashmod(seed).digest()[:maxbytes] + base = "\x00" * (maxbytes - len(base)) + base + topbits = 8 * maxbytes - bits + if topbits: + base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] + number = 1 + int(binascii.hexlify(base), 16) + assert 1 <= number < order + return number + + +def randrange_from_seed__trytryagain(seed, order): + # figure out exactly how many bits we need (rounded up to the nearest + # bit), so we can reduce the chance of looping to less than 0.5 . This is + # specified to feed from a byte-oriented PRNG, and discards the + # high-order bits of the first byte as necessary to get the right number + # of bits. The average number of loops will range from 1.0 (when + # order=2**k-1) to 2.0 (when order=2**k+1). + assert order > 1 + bits, bytes, extrabits = bits_and_bytes(order) + generate = PRNG(seed) + while True: + extrabyte = b("") + if extrabits: + extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits)) + guess = string_to_number(extrabyte + generate(bytes)) + 1 + if 1 <= guess < order: + return guess + + +def number_to_string(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2 * l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + assert len(string) == l, (len(string), l) + return string + + +def number_to_string_crop(num, order): + l = orderlen(order) + fmt_str = "%0" + str(2 * l) + "x" + string = binascii.unhexlify((fmt_str % num).encode()) + return string[:l] + + +def string_to_number(string): + return int(binascii.hexlify(string), 16) + + +def string_to_number_fixedlen(string, order): + l = orderlen(order) + assert len(string) == l, (len(string), l) + return int(binascii.hexlify(string), 16) + + +# these methods are useful for the sigencode= argument to SK.sign() and the +# sigdecode= argument to VK.verify(), and control how the signature is packed +# or unpacked. + + +def sigencode_strings(r, s, order): + r_str = number_to_string(r, order) + s_str = number_to_string(s, order) + return (r_str, s_str) + + +def sigencode_string(r, s, order): + """ + Encode the signature to raw format (:term:`raw encoding`) + + It's expected that this function will be used as a `sigencode=` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: raw encoding of ECDSA signature + :rtype: bytes + """ + # for any given curve, the size of the signature numbers is + # fixed, so just use simple concatenation + r_str, s_str = sigencode_strings(r, s, order) + return r_str + s_str + + +def sigencode_der(r, s, order): + """ + Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. + + Encodes the signature to the following :term:`ASN.1` structure:: + + Ecdsa-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + + It's expected that this function will be used as a `sigencode=` parameter + in :func:`ecdsa.keys.SigningKey.sign` method. + + :param int r: first parameter of the signature + :param int s: second parameter of the signature + :param int order: the order of the curve over which the signature was + computed + + :return: DER encoding of ECDSA signature + :rtype: bytes + """ + return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + + +# canonical versions of sigencode methods +# these enforce low S values, by negating the value (modulo the order) if +# above order/2 see CECKey::Sign() +# https://github.com/bitcoin/bitcoin/blob/master/src/key.cpp#L214 +def sigencode_strings_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_strings(r, s, order) + + +def sigencode_string_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_string(r, s, order) + + +def sigencode_der_canonize(r, s, order): + if s > order / 2: + s = order - s + return sigencode_der(r, s, order) + + +class MalformedSignature(Exception): + """ + Raised by decoding functions when the signature is malformed. + + Malformed in this context means that the relevant strings or integers + do not match what a signature over provided curve would create. Either + because the byte strings have incorrect lengths or because the encoded + values are too large. + """ + + pass + + +def sigdecode_string(signature, order): + """ + Decoder for :term:`raw encoding` of ECDSA signatures. + + raw encoding is a simple concatenation of the two integers that comprise + the signature, with each encoded using the same amount of bytes depending + on curve size/order. + + It's expected that this function will be used as the `sigdecode=` + parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. + + :param signature: encoded signature + :type signature: bytes like object + :param order: order of the curve over which the signature was computed + :type order: int + + :raises MalformedSignature: when the encoding of the signature is invalid + + :return: tuple with decoded 'r' and 's' values of signature + :rtype: tuple of ints + """ + signature = normalise_bytes(signature) + l = orderlen(order) + if not len(signature) == 2 * l: + raise MalformedSignature( + "Invalid length of signature, expected {0} bytes long, " + "provided string is {1} bytes long".format(2 * l, len(signature)) + ) + r = string_to_number_fixedlen(signature[:l], order) + s = string_to_number_fixedlen(signature[l:], order) + return r, s + + +def sigdecode_strings(rs_strings, order): + """ + Decode the signature from two strings. + + First string needs to be a big endian encoding of 'r', second needs to + be a big endian encoding of the 's' parameter of an ECDSA signature. + + It's expected that this function will be used as the `sigdecode=` + parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. + + :param list rs_strings: list of two bytes-like objects, each encoding one + parameter of signature + :param int order: order of the curve over which the signature was computed + + :raises MalformedSignature: when the encoding of the signature is invalid + + :return: tuple with decoded 'r' and 's' values of signature + :rtype: tuple of ints + """ + if not len(rs_strings) == 2: + raise MalformedSignature( + "Invalid number of strings provided: {0}, expected 2".format( + len(rs_strings) + ) + ) + (r_str, s_str) = rs_strings + r_str = normalise_bytes(r_str) + s_str = normalise_bytes(s_str) + l = orderlen(order) + if not len(r_str) == l: + raise MalformedSignature( + "Invalid length of first string ('r' parameter), " + "expected {0} bytes long, provided string is {1} " + "bytes long".format(l, len(r_str)) + ) + if not len(s_str) == l: + raise MalformedSignature( + "Invalid length of second string ('s' parameter), " + "expected {0} bytes long, provided string is {1} " + "bytes long".format(l, len(s_str)) + ) + r = string_to_number_fixedlen(r_str, order) + s = string_to_number_fixedlen(s_str, order) + return r, s + + +def sigdecode_der(sig_der, order): + """ + Decoder for DER format of ECDSA signatures. + + DER format of signature is one that uses the :term:`ASN.1` :term:`DER` + rules to encode it as a sequence of two integers:: + + Ecdsa-Sig-Value ::= SEQUENCE { + r INTEGER, + s INTEGER + } + + It's expected that this function will be used as as the `sigdecode=` + parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. + + :param sig_der: encoded signature + :type sig_der: bytes like object + :param order: order of the curve over which the signature was computed + :type order: int + + :raises UnexpectedDER: when the encoding of signature is invalid + + :return: tuple with decoded 'r' and 's' values of signature + :rtype: tuple of ints + """ + sig_der = normalise_bytes(sig_der) + # return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) + rs_strings, empty = der.remove_sequence(sig_der) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER sig: %s" % binascii.hexlify(empty) + ) + r, rest = der.remove_integer(rs_strings) + s, empty = der.remove_integer(rest) + if empty != b"": + raise der.UnexpectedDER( + "trailing junk after DER numbers: %s" % binascii.hexlify(empty) + ) + return r, s diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py new file mode 100644 index 000000000..c742493dd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os +import sys +from collections import namedtuple +from io import StringIO + +import espefuse.efuse.esp32 as esp32_efuse +import espefuse.efuse.esp32c2 as esp32c2_efuse +import espefuse.efuse.esp32c3 as esp32c3_efuse +import espefuse.efuse.esp32c6 as esp32c6_efuse +import espefuse.efuse.esp32h2beta1 as esp32h2beta1_efuse +import espefuse.efuse.esp32s2 as esp32s2_efuse +import espefuse.efuse.esp32s3 as esp32s3_efuse +import espefuse.efuse.esp32s3beta2 as esp32s3beta2_efuse + +import esptool + +DefChip = namedtuple("DefChip", ["chip_name", "efuse_lib", "chip_class"]) + +SUPPORTED_BURN_COMMANDS = [ + "read_protect_efuse", + "write_protect_efuse", + "burn_efuse", + "burn_block_data", + "burn_bit", + "burn_key", + "burn_key_digest", + "burn_custom_mac", + "set_flash_voltage", + "execute_scripts", +] + +SUPPORTED_COMMANDS = [ + "summary", + "dump", + "get_custom_mac", + "adc_info", + "check_error", +] + SUPPORTED_BURN_COMMANDS + +SUPPORTED_CHIPS = { + "esp32": DefChip("ESP32", esp32_efuse, esptool.targets.ESP32ROM), + "esp32c2": DefChip("ESP32-C2", esp32c2_efuse, esptool.targets.ESP32C2ROM), + "esp32c3": DefChip("ESP32-C3", esp32c3_efuse, esptool.targets.ESP32C3ROM), + "esp32c6": DefChip("ESP32-C6", esp32c6_efuse, esptool.targets.ESP32C6ROM), + "esp32h2beta1": DefChip( + "ESP32-H2(beta1)", esp32h2beta1_efuse, esptool.targets.ESP32H2BETA1ROM + ), + "esp32s2": DefChip("ESP32-S2", esp32s2_efuse, esptool.targets.ESP32S2ROM), + "esp32s3": DefChip("ESP32-S3", esp32s3_efuse, esptool.targets.ESP32S3ROM), + "esp32s3beta2": DefChip( + "ESP32-S3(beta2)", esp32s3beta2_efuse, esptool.targets.ESP32S3BETA2ROM + ), +} + + +def get_esp( + port, + baud, + connect_mode, + chip="auto", + skip_connect=False, + virt=False, + debug=False, + virt_efuse_file=None, +): + if chip not in ["auto"] + list(SUPPORTED_CHIPS.keys()): + raise esptool.FatalError("get_esp: Unsupported chip (%s)" % chip) + if virt: + efuse = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).efuse_lib + esp = efuse.EmulateEfuseController(virt_efuse_file, debug) + else: + if chip == "auto" and not skip_connect: + esp = esptool.cmds.detect_chip(port, baud, connect_mode) + else: + esp = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).chip_class( + port if not skip_connect else StringIO(), baud + ) + if not skip_connect: + esp.connect(connect_mode) + return esp + + +def get_efuses(esp, skip_connect=False, debug_mode=False, do_not_confirm=False): + for name in SUPPORTED_CHIPS: + if SUPPORTED_CHIPS[name].chip_name == esp.CHIP_NAME: + efuse = SUPPORTED_CHIPS[name].efuse_lib + return ( + efuse.EspEfuses(esp, skip_connect, debug_mode, do_not_confirm), + efuse.operations, + ) + else: + raise esptool.FatalError("get_efuses: Unsupported chip (%s)" % esp.CHIP_NAME) + + +def split_on_groups(all_args): + """ + This function splits the all_args list into groups, + where each item is a cmd with all its args. + + Example: + all_args: + ['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem', + 'burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', + 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS'] + + used_cmds: ['burn_key_digest', 'burn_key'] + groups: + [['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem'], + ['burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', + 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS']] + """ + + groups = [] + cmd = [] + used_cmds = [] + for item in all_args: + if item in SUPPORTED_COMMANDS: + used_cmds.append(item) + if cmd != []: + groups.append(cmd) + cmd = [] + cmd.append(item) + if cmd: + groups.append(cmd) + return groups, used_cmds + + +def main(custom_commandline=None): + """ + Main function for espefuse + + custom_commandline - Optional override for default arguments parsing + (that uses sys.argv), can be a list of custom arguments as strings. + Arguments and their values need to be added as individual items to the list + e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. + """ + init_parser = argparse.ArgumentParser( + description="espefuse.py v%s - [ESP32xx] efuse get/set tool" + % esptool.__version__, + prog="espefuse", + add_help=False, + ) + + init_parser.add_argument( + "--chip", + "-c", + help="Target chip type", + choices=["auto"] + list(SUPPORTED_CHIPS.keys()), + default=os.environ.get("ESPTOOL_CHIP", "auto"), + ) + + init_parser.add_argument( + "--baud", + "-b", + help="Serial port baud rate used when flashing/reading", + type=esptool.arg_auto_int, + default=os.environ.get("ESPTOOL_BAUD", esptool.loader.ESPLoader.ESP_ROM_BAUD), + ) + + init_parser.add_argument( + "--port", + "-p", + help="Serial port device", + default=os.environ.get("ESPTOOL_PORT", esptool.loader.ESPLoader.DEFAULT_PORT), + ) + + init_parser.add_argument( + "--before", + help="What to do before connecting to the chip", + choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], + default="default_reset", + ) + + init_parser.add_argument( + "--debug", + "-d", + help="Show debugging information (loglevel=DEBUG)", + action="store_true", + ) + init_parser.add_argument( + "--virt", + help="For host tests, the tool will work in the virtual mode " + "(without connecting to a chip).", + action="store_true", + ) + init_parser.add_argument( + "--path-efuse-file", + help="For host tests, saves efuse memory to file.", + type=str, + default=None, + ) + init_parser.add_argument( + "--do-not-confirm", + help="Do not pause for confirmation before permanently writing efuses. " + "Use with caution.", + action="store_true", + ) + + common_args, remaining_args = init_parser.parse_known_args(custom_commandline) + debug_mode = common_args.debug or ("dump" in remaining_args) + just_print_help = [ + True for arg in remaining_args if arg in ["--help", "-h"] + ] or remaining_args == [] + + print("espefuse.py v{}".format(esptool.__version__)) + + try: + esp = get_esp( + common_args.port, + common_args.baud, + common_args.before, + common_args.chip, + just_print_help, + common_args.virt, + common_args.debug, + common_args.path_efuse_file, + ) + except esptool.FatalError as e: + raise esptool.FatalError( + f"{e}\nPlease make sure that you have specified " + "the right port with the --port argument" + ) # TODO: Require the --port argument in the next major release, ESPTOOL-490 + + efuses, efuse_operations = get_efuses( + esp, just_print_help, debug_mode, common_args.do_not_confirm + ) + + parser = argparse.ArgumentParser(parents=[init_parser]) + subparsers = parser.add_subparsers( + dest="operation", help="Run espefuse.py {command} -h for additional help" + ) + + efuse_operations.add_commands(subparsers, efuses) + + grouped_remaining_args, used_cmds = split_on_groups(remaining_args) + if len(grouped_remaining_args) == 0: + parser.print_help() + parser.exit(1) + there_are_multiple_burn_commands_in_args = ( + sum(cmd in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 1 + ) + if there_are_multiple_burn_commands_in_args: + efuses.batch_mode_cnt += 1 + + for rem_args in grouped_remaining_args: + args, unused_args = parser.parse_known_args(rem_args, namespace=common_args) + if args.operation is None: + parser.print_help() + parser.exit(1) + assert len(unused_args) == 0, 'Not all commands were recognized "{}"'.format( + unused_args + ) + + operation_func = vars(efuse_operations)[args.operation] + # each 'operation' is a module-level function of the same name + print('\n=== Run "{}" command ==='.format(args.operation)) + operation_func(esp, efuses, args) + + if there_are_multiple_burn_commands_in_args: + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + raise esptool.FatalError("BURN was not done") + + if common_args.virt is False: + esp._port.close() + + +def _main(): + try: + main() + except esptool.FatalError as e: + print("\nA fatal error occurred: %s" % e) + sys.exit(2) + + +if __name__ == "__main__": + _main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py new file mode 100644 index 000000000..4b068d6e0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import espefuse + +if __name__ == "__main__": + espefuse._main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py new file mode 100644 index 000000000..7bc640704 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py @@ -0,0 +1,725 @@ +#!/usr/bin/env python +# +# This file describes the common eFuses structures for chips +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import re +import sys + +from bitstring import BitArray, BitString, CreationError + +import esptool + +from . import util + + +class CheckArgValue(object): + def __init__(self, efuses, name): + self.efuses = efuses + self.name = name + + def __call__(self, new_value_str): + def check_arg_value(efuse, new_value): + if efuse.efuse_type.startswith("bool"): + new_value = 1 if new_value is None else int(new_value, 0) + if new_value != 1: + raise esptool.FatalError( + "New value is not accepted for efuse '{}' " + "(will always burn 0->1), given value={}".format( + efuse.name, new_value + ) + ) + elif efuse.efuse_type.startswith(("int", "uint")): + if efuse.efuse_class == "bitcount": + if new_value is None: + # find the first unset bit and set it + old_value = efuse.get_raw() + new_value = old_value + bit = 1 + while new_value == old_value: + new_value = bit | old_value + bit <<= 1 + else: + new_value = int(new_value, 0) + else: + if new_value is None: + raise esptool.FatalError( + "New value required for efuse '{}' (given None)".format( + efuse.name + ) + ) + new_value = int(new_value, 0) + if new_value == 0: + raise esptool.FatalError( + "New value should not be 0 for '{}' " + "(given value= {})".format(efuse.name, new_value) + ) + elif efuse.efuse_type.startswith("bytes"): + if new_value is None: + raise esptool.FatalError( + "New value required for efuse '{}' " + "(given None)".format(efuse.name) + ) + if len(new_value) * 8 != efuse.bitarray.len: + raise esptool.FatalError( + "The length of efuse '{}' ({} bits) " + "(given len of the new value= {} bits)".format( + efuse.name, efuse.bitarray.len, len(new_value) * 8 + ) + ) + else: + raise esptool.FatalError( + "The '{}' type for the '{}' efuse is not supported yet.".format( + efuse.efuse_type, efuse.name + ) + ) + return new_value + + efuse = self.efuses[self.name] + new_value = efuse.check_format(new_value_str) + return check_arg_value(efuse, new_value) + + +class EfuseProtectBase(object): + # This class is used by EfuseBlockBase and EfuseFieldBase + + def get_read_disable_mask(self): + mask = 0 + if isinstance(self.read_disable_bit, list): + for i in self.read_disable_bit: + mask |= 1 << i + else: + mask = 1 << self.read_disable_bit + return mask + + def is_readable(self): + """Return true if the efuse is readable by software""" + num_bit = self.read_disable_bit + if num_bit is None: + return True # read cannot be disabled + return (self.parent["RD_DIS"].get() & (self.get_read_disable_mask())) == 0 + + def disable_read(self): + num_bit = self.read_disable_bit + if num_bit is None: + raise esptool.FatalError("This efuse cannot be read-disabled") + if not self.parent["RD_DIS"].is_writeable(): + raise esptool.FatalError( + "This efuse cannot be read-disabled due the to RD_DIS field is " + "already write-disabled" + ) + self.parent["RD_DIS"].save(self.get_read_disable_mask()) + + def is_writeable(self): + num_bit = self.write_disable_bit + if num_bit is None: + return True # write cannot be disabled + return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0 + + def disable_write(self): + num_bit = self.write_disable_bit + if not self.parent["WR_DIS"].is_writeable(): + raise esptool.FatalError( + "This efuse cannot be write-disabled due to the WR_DIS field is " + "already write-disabled" + ) + self.parent["WR_DIS"].save(1 << num_bit) + + def check_wr_rd_protect(self): + if not self.is_readable(): + error_msg = "\t{} is read-protected.".format(self.name) + "The written value can not be read, the efuse/block looks as all 0.\n" + error_msg += "\tBurn in this case may damage an already written value." + self.parent.print_error_msg(error_msg) + if not self.is_writeable(): + error_msg = "\t{} is write-protected. Burn is not possible.".format( + self.name + ) + self.parent.print_error_msg(error_msg) + + +class EfuseBlockBase(EfuseProtectBase): + def __init__(self, parent, param, skip_read=False): + self.parent = parent + self.name = param.name + self.alias = param.alias + self.id = param.id + self.rd_addr = param.rd_addr + self.wr_addr = param.wr_addr + self.write_disable_bit = param.write_disable_bit + self.read_disable_bit = param.read_disable_bit + self.len = param.len + self.key_purpose_name = param.key_purpose + bit_block_len = self.get_block_len() * 8 + self.bitarray = BitString(bit_block_len) + self.bitarray.set(0) + self.wr_bitarray = BitString(bit_block_len) + self.wr_bitarray.set(0) + self.fail = False + self.num_errors = 0 + if self.id == 0: + self.err_bitarray = BitString(bit_block_len) + self.err_bitarray.set(0) + else: + self.err_bitarray = None + + if not skip_read: + self.read() + + def get_block_len(self): + coding_scheme = self.get_coding_scheme() + if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: + return self.len * 4 + elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: + return (self.len * 3 // 4) * 4 + elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: + return self.len * 4 + else: + raise esptool.FatalError( + "Coding scheme (%d) not supported" % (coding_scheme) + ) + + def get_coding_scheme(self): + if self.id == 0: + return self.parent.REGS.CODING_SCHEME_NONE + else: + return self.parent.coding_scheme + + def get_raw(self, from_read=True): + if from_read: + return self.bitarray.bytes + else: + return self.wr_bitarray.bytes + + def get(self, from_read=True): + self.get_bitstring(from_read=from_read) + + def get_bitstring(self, from_read=True): + if from_read: + return self.bitarray + else: + return self.wr_bitarray + + def convert_to_bitstring(self, new_data): + if isinstance(new_data, BitArray): + return new_data + else: + return BitArray(bytes=new_data, length=len(new_data) * 8) + + def get_words(self): + def get_offsets(self): + return [x + self.rd_addr for x in range(0, self.get_block_len(), 4)] + + return [self.parent.read_reg(offs) for offs in get_offsets(self)] + + def read(self): + words = self.get_words() + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + self.bitarray.overwrite(data, pos=0) + self.print_block(self.bitarray, "read_regs") + + def print_block(self, bit_string, comment, debug=False): + if self.parent.debug or debug: + bit_string.pos = 0 + print( + "%-15s (%-16s) [%-2d] %s:" + % (self.name, " ".join(self.alias)[:16], self.id, comment), + " ".join( + [ + "%08x" % word + for word in bit_string.readlist( + "%d*uint:32" % (bit_string.len / 32) + )[::-1] + ] + ), + ) + + def check_wr_data(self): + wr_data = self.wr_bitarray + if wr_data.all(False): + # nothing to burn + if self.parent.debug: + print("[{:02}] {:20} nothing to burn".format(self.id, self.name)) + return False + if len(wr_data.bytes) != len(self.bitarray.bytes): + raise esptool.FatalError( + "Data does not fit: the block%d size is %d bytes, data is %d bytes" + % (self.id, len(self.bitarray.bytes), len(wr_data.bytes)) + ) + self.check_wr_rd_protect() + + if self.get_bitstring().all(False): + print( + "[{:02}] {:20} is empty, will burn the new value".format( + self.id, self.name + ) + ) + else: + # the written block in chip is not empty + if self.get_bitstring() == wr_data: + print( + "[{:02}] {:20} is already written the same value, " + "continue with EMPTY_BLOCK".format(self.id, self.name) + ) + wr_data.set(0) + else: + print("[{:02}] {:20} is not empty".format(self.id, self.name)) + print("\t(written ):", self.get_bitstring()) + print("\t(to write):", wr_data) + mask = self.get_bitstring() & wr_data + if mask == wr_data: + print( + "\tAll wr_data bits are set in the written block, " + "continue with EMPTY_BLOCK." + ) + wr_data.set(0) + else: + coding_scheme = self.get_coding_scheme() + if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: + print("\t(coding scheme = NONE)") + elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: + print("\t(coding scheme = RS)") + error_msg = ( + "\tBurn into %s is forbidden " + "(RS coding scheme does not allow this)." % (self.name) + ) + self.parent.print_error_msg(error_msg) + elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: + print("\t(coding scheme = 3/4)") + data_can_not_be_burn = False + for i in range(0, self.get_bitstring().len, 6 * 8): + rd_chunk = self.get_bitstring()[i : i + 6 * 8 :] + wr_chunk = wr_data[i : i + 6 * 8 :] + if rd_chunk.any(True): + if wr_chunk.any(True): + print( + "\twritten chunk [%d] and wr_chunk " + "are not empty. " % (i // (6 * 8)), + end="", + ) + if rd_chunk == wr_chunk: + print( + "wr_chunk == rd_chunk. " + "Countinue with empty chunk." + ) + wr_data[i : i + 6 * 8 :].set(0) + else: + print("wr_chunk != rd_chunk. Can not burn.") + print("\twritten ", rd_chunk) + print("\tto write", wr_chunk) + data_can_not_be_burn = True + if data_can_not_be_burn: + error_msg = ( + "\tBurn into %s is forbidden " + "(3/4 coding scheme does not allow this)." % (self.name) + ) + self.parent.print_error_msg(error_msg) + else: + raise esptool.FatalError( + "The coding scheme ({}) is not supported".format( + coding_scheme + ) + ) + + def save(self, new_data): + # new_data will be checked by check_wr_data() during burn_all() + # new_data (bytes) = [0][1][2] ... [N] (original data) + # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) + # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) + # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) + # in bitstring = [N] ... [2][1][0] (to get a correct bitstring + # need to reverse new_data) + # *[x] - means a byte. + data = BitString(bytes=new_data[::-1], length=len(new_data) * 8) + if self.parent.debug: + print( + "\twritten : {} ->\n\tto write: {}".format(self.get_bitstring(), data) + ) + self.wr_bitarray.overwrite(self.wr_bitarray | data, pos=0) + + def burn_words(self, words): + for burns in range(3): + self.parent.efuse_controller_setup() + if self.parent.debug: + print("Write data to BLOCK%d" % (self.id)) + write_reg_addr = self.wr_addr + for word in words: + # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data + # 32 bytes to EFUSE_PGM_DATA[0..7]_REG + # 12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after + # EFUSE_PGM_DATA_REG + # for esp32: + # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG + # for writing data + if self.parent.debug: + print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) + self.parent.write_reg(write_reg_addr, word) + write_reg_addr += 4 + + self.parent.write_efuses(self.id) + for _ in range(5): + self.parent.efuse_read() + self.parent.get_coding_scheme_warnings(silent=True) + if self.fail or self.num_errors: + print( + "Error in BLOCK%d, re-burn it again (#%d), to fix it. " + "fail_bit=%d, num_errors=%d" + % (self.id, burns, self.fail, self.num_errors) + ) + break + if not self.fail and self.num_errors == 0: + break + + def burn(self): + if self.wr_bitarray.all(False): + # nothing to burn + return + before_burn_bitarray = self.bitarray[:] + assert before_burn_bitarray is not self.bitarray + self.print_block(self.wr_bitarray, "to_write") + words = self.apply_coding_scheme() + self.burn_words(words) + self.read() + if not self.is_readable(): + print( + "{} ({}) is read-protected. " + "Read back the burn value is not possible.".format( + self.name, self.alias + ) + ) + if self.bitarray.all(False): + print("Read all '0'") + else: + # Should never happen + raise esptool.FatalError( + "The {} is read-protected but not all '0' ({})".format( + self.name, self.bitarray.hex + ) + ) + else: + if self.wr_bitarray == self.bitarray: + print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) + elif ( + self.wr_bitarray & self.bitarray == self.wr_bitarray + and self.bitarray & before_burn_bitarray == before_burn_bitarray + ): + print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) + else: + # Happens only when an efuse is written and read-protected + # in one command + self.print_block(self.wr_bitarray, "Expected") + self.print_block(self.bitarray, "Real ") + # Read-protected BLK0 values are reported back as zeros, + # raise error only for other blocks + if self.id != 0: + raise esptool.FatalError( + "Burn {} ({}) was not successful".format(self.name, self.alias) + ) + self.wr_bitarray.set(0) + + +class EspEfusesBase(object): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + _esp = None + blocks = [] + efuses = [] + coding_scheme = None + force_write_always = None + batch_mode_cnt = 0 + + def __iter__(self): + return self.efuses.__iter__() + + def get_crystal_freq(self): + return self._esp.get_crystal_freq() + + def read_efuse(self, n): + """Read the nth word of the ESP3x EFUSE region.""" + return self._esp.read_efuse(n) + + def read_reg(self, addr): + return self._esp.read_reg(addr) + + def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): + return self._esp.write_reg(addr, value, mask, delay_us, delay_after_us) + + def update_reg(self, addr, mask, new_val): + return self._esp.update_reg(addr, mask, new_val) + + def efuse_controller_setup(self): + pass + + def reconnect_chip(self, esp): + print("Re-connecting...") + baudrate = esp._port.baudrate + port = esp._port.port + esp._port.close() + return esptool.cmds.detect_chip(port, baudrate) + + def get_index_block_by_name(self, name): + for block in self.blocks: + if block.name == name or name in block.alias: + return block.id + return None + + def read_blocks(self): + for block in self.blocks: + block.read() + + def update_efuses(self): + for efuse in self.efuses: + efuse.update(self.blocks[efuse.block].bitarray) + + def burn_all(self, check_batch_mode=False): + if check_batch_mode: + if self.batch_mode_cnt != 0: + print( + "\nBatch mode is enabled, " + "the burn will be done at the end of the command." + ) + return False + print("\nCheck all blocks for burn...") + print("idx, BLOCK_NAME, Conclusion") + have_wr_data_for_burn = False + for block in self.blocks: + block.check_wr_data() + if not have_wr_data_for_burn and block.get_bitstring(from_read=False).any( + True + ): + have_wr_data_for_burn = True + if not have_wr_data_for_burn: + print("Nothing to burn, see messages above.") + return + EspEfusesBase.confirm("", self.do_not_confirm) + + # Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits. + for block in reversed(self.blocks): + old_fail = block.fail + old_num_errors = block.num_errors + block.burn() + if (block.fail and old_fail != block.fail) or ( + block.num_errors and block.num_errors > old_num_errors + ): + raise esptool.FatalError("Error(s) were detected in eFuses") + print("Reading updated efuses...") + self.read_coding_scheme() + self.read_blocks() + self.update_efuses() + return True + + @staticmethod + def confirm(action, do_not_confirm): + print( + "%s%s\nThis is an irreversible operation!" + % (action, "" if action.endswith("\n") else ". ") + ) + if not do_not_confirm: + print("Type 'BURN' (all capitals) to continue.") + # required for Pythons which disable line buffering, ie mingw in mintty + sys.stdout.flush() + yes = input() + if yes != "BURN": + print("Aborting.") + sys.exit(0) + + def print_error_msg(self, error_msg): + if self.force_write_always is not None: + if not self.force_write_always: + error_msg += "(use '--force-write-always' option to ignore it)" + if self.force_write_always: + print(error_msg, "Skipped because '--force-write-always' option.") + else: + raise esptool.FatalError(error_msg) + + +class EfuseFieldBase(EfuseProtectBase): + def __init__(self, parent, param): + self.category = param.category + self.parent = parent + self.block = param.block + self.word = param.word + self.pos = param.pos + self.write_disable_bit = param.write_disable_bit + self.read_disable_bit = param.read_disable_bit + self.name = param.name + self.efuse_class = param.class_type + self.efuse_type = param.type + self.description = param.description + self.dict_value = param.dictionary + self.fail = False + self.num_errors = 0 + if self.efuse_type.startswith("bool"): + field_len = 1 + else: + field_len = int(re.search(r"\d+", self.efuse_type).group()) + if self.efuse_type.startswith("bytes"): + field_len *= 8 + self.bitarray = BitString(field_len) + self.bit_len = field_len + self.bitarray.set(0) + self.update(self.parent.blocks[self.block].bitarray) + + def check_format(self, new_value_str): + if new_value_str is None: + return new_value_str + else: + if self.efuse_type.startswith("bytes"): + if new_value_str.startswith("0x"): + # cmd line: 0x0102030405060708 .... 112233ff (hex) + # regs: 112233ff ... 05060708 01020304 + # BLK: ff 33 22 11 ... 08 07 06 05 04 03 02 01 + return binascii.unhexlify(new_value_str[2:])[::-1] + else: + # cmd line: 0102030405060708 .... 112233ff (string) + # regs: 04030201 08070605 ... ff332211 + # BLK: 01 02 03 04 05 06 07 08 ... 11 22 33 ff + return binascii.unhexlify(new_value_str) + else: + return new_value_str + + def convert_to_bitstring(self, new_value): + if isinstance(new_value, BitArray): + return new_value + else: + if self.efuse_type.startswith("bytes"): + # new_value (bytes) = [0][1][2] ... [N] + # (original data) + # in string format = [0] [1] [2] ... [N] + # (util.hexify(data, " ")) + # in hex format = 0x[N]....[2][1][0] + # (from bitstring print(data)) + # in reg format = [3][2][1][0] ... [N][][][] + # (as it will be in the device) + # in bitstring = [N] ... [2][1][0] + # (to get a correct bitstring need to reverse new_value) + # *[x] - means a byte. + return BitArray(bytes=new_value[::-1], length=len(new_value) * 8) + else: + try: + return BitArray(self.efuse_type + "={}".format(new_value)) + except CreationError as err: + print( + "New value '{}' is not suitable for {} ({})".format( + new_value, self.name, self.efuse_type + ) + ) + raise esptool.FatalError(err) + + def check_new_value(self, bitarray_new_value): + bitarray_old_value = self.get_bitstring() | self.get_bitstring(from_read=False) + if bitarray_new_value.len != bitarray_old_value.len: + raise esptool.FatalError( + "For {} efuse, the length of the new value is wrong, " + "expected {} bits, was {} bits.".format( + self.name, bitarray_old_value.len, bitarray_new_value.len + ) + ) + if bitarray_new_value == bitarray_old_value: + error_msg = "\tThe same value for {} ".format(self.name) + error_msg += "is already burned. Do not change the efuse." + print(error_msg) + bitarray_new_value.set(0) + elif bitarray_new_value == self.get_bitstring(from_read=False): + error_msg = "\tThe same value for {} ".format(self.name) + error_msg += "is already prepared for the burn operation." + print(error_msg) + bitarray_new_value.set(0) + else: + if self.name not in ["WR_DIS", "RD_DIS"]: + # WR_DIS, RD_DIS fields can have already set bits. + # Do not neeed to check below condition for them. + if bitarray_new_value | bitarray_old_value != bitarray_new_value: + error_msg = "\tNew value contains some bits that cannot be cleared " + error_msg += "(value will be {})".format( + bitarray_old_value | bitarray_new_value + ) + self.parent.print_error_msg(error_msg) + self.check_wr_rd_protect() + + def save_to_block(self, bitarray_field): + block = self.parent.blocks[self.block] + wr_bitarray_temp = block.wr_bitarray.copy() + position = wr_bitarray_temp.length - ( + self.word * 32 + self.pos + bitarray_field.len + ) + wr_bitarray_temp.overwrite(bitarray_field, pos=position) + block.wr_bitarray |= wr_bitarray_temp + + def save(self, new_value): + bitarray_field = self.convert_to_bitstring(new_value) + self.check_new_value(bitarray_field) + self.save_to_block(bitarray_field) + + def update(self, bit_array_block): + if self.word is None or self.pos is None: + self.bitarray.overwrite(self.convert_to_bitstring(self.get()), pos=0) + return + field_len = self.bitarray.len + bit_array_block.pos = bit_array_block.length - ( + self.word * 32 + self.pos + field_len + ) + self.bitarray.overwrite(bit_array_block.read(field_len), pos=0) + err_bitarray = self.parent.blocks[self.block].err_bitarray + if err_bitarray is not None: + err_bitarray.pos = err_bitarray.length - ( + self.word * 32 + self.pos + field_len + ) + self.fail = not err_bitarray.read(field_len).all(False) + else: + self.fail = self.parent.blocks[self.block].fail + self.num_errors = self.parent.blocks[self.block].num_errors + + def get_raw(self, from_read=True): + """Return the raw (unformatted) numeric value of the efuse bits + + Returns a simple integer or (for some subclasses) a bitstring. + type: int or bool -> int + type: bytes -> bytearray + """ + return self.get_bitstring(from_read).read(self.efuse_type) + + def get(self, from_read=True): + """Get a formatted version of the efuse value, suitable for display + type: int or bool -> int + type: bytes -> string "01 02 03 04 05 06 07 08 ... ". + Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ... + """ + if self.efuse_type.startswith("bytes"): + return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") + else: + return self.get_raw(from_read) + + def get_meaning(self, from_read=True): + """Get the meaning of efuse from dict if possible, suitable for display""" + if self.dict_value: + try: + return self.dict_value[self.get_raw(from_read)] + except KeyError: + pass + return self.get(from_read) + + def get_bitstring(self, from_read=True): + if from_read: + self.bitarray.pos = 0 + return self.bitarray + else: + field_len = self.bitarray.len + block = self.parent.blocks[self.block] + block.wr_bitarray.pos = block.wr_bitarray.length - ( + self.word * 32 + self.pos + field_len + ) + return block.wr_bitarray.read(self.bitarray.len) + + def burn(self, new_value): + # Burn a efuse. Added for compatibility reason. + self.save(new_value) + self.parent.burn_all() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py new file mode 100644 index 000000000..c74bb5003 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py @@ -0,0 +1,675 @@ +#!/usr/bin/env python +# +# This file includes the common operations with eFuses for chips +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import json +import sys + +from bitstring import BitString + +import esptool + +from . import base_fields +from . import util + + +def add_common_commands(subparsers, efuses): + class ActionEfuseValuePair(argparse.Action): + def __init__(self, option_strings, dest, nargs=None, **kwargs): + self._nargs = nargs + self._choices = kwargs.get("efuse_choices") + self.efuses = kwargs.get("efuses") + del kwargs["efuse_choices"] + del kwargs["efuses"] + super(ActionEfuseValuePair, self).__init__( + option_strings, dest, nargs=nargs, **kwargs + ) + + def __call__(self, parser, namespace, values, option_string=None): + def check_efuse_name(efuse_name, efuse_list): + if efuse_name not in self._choices: + raise esptool.FatalError( + "Invalid the efuse name '{}'. " + "Available the efuse names: {}".format( + efuse_name, self._choices + ) + ) + + efuse_value_pairs = {} + if len(values) > 1: + if len(values) % 2: + raise esptool.FatalError( + "The list does not have a valid pair (name value) {}".format( + values + ) + ) + for i in range(0, len(values), 2): + efuse_name, new_value = values[i : i + 2 :] + check_efuse_name(efuse_name, self._choices) + check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) + efuse_value_pairs[efuse_name] = check_arg(new_value) + else: + # For the case of compatibility, when only the efuse_name is given + # Fields with 'bitcount' and 'bool' types can be without new_value arg + efuse_name = values[0] + check_efuse_name(efuse_name, self._choices) + check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) + efuse_value_pairs[efuse_name] = check_arg(None) + setattr(namespace, self.dest, efuse_value_pairs) + + burn = subparsers.add_parser( + "burn_efuse", help="Burn the efuse with the specified name" + ) + burn.add_argument( + "name_value_pairs", + help="Name of efuse register and New value pairs to burn", + action=ActionEfuseValuePair, + nargs="+", + metavar="[EFUSE_NAME VALUE] [{} VALUE".format( + " VALUE] [".join([e.name for e in efuses.efuses]) + ), + efuse_choices=[e.name for e in efuses.efuses], + efuses=efuses, + ) + + read_protect_efuse = subparsers.add_parser( + "read_protect_efuse", + help="Disable readback for the efuse with the specified name", + ) + read_protect_efuse.add_argument( + "efuse_name", + help="Name of efuse register to burn", + nargs="+", + choices=[e.name for e in efuses.efuses if e.read_disable_bit is not None], + ) + + write_protect_efuse = subparsers.add_parser( + "write_protect_efuse", + help="Disable writing to the efuse with the specified name", + ) + write_protect_efuse.add_argument( + "efuse_name", + help="Name of efuse register to burn", + nargs="+", + choices=[e.name for e in efuses.efuses if e.write_disable_bit is not None], + ) + + burn_block_data = subparsers.add_parser( + "burn_block_data", + help="Burn non-key data to EFUSE blocks. " + "(Don't use this command to burn key data for Flash Encryption or " + "ESP32 Secure Boot V1, as the byte order of keys is swapped (use burn_key)).", + ) + add_force_write_always(burn_block_data) + burn_block_data.add_argument( + "--offset", "-o", help="Byte offset in the efuse block", type=int, default=0 + ) + burn_block_data.add_argument( + "block", + help="Efuse block to burn.", + action="append", + choices=efuses.BURN_BLOCK_DATA_NAMES, + ) + burn_block_data.add_argument( + "datafile", + help="File containing data to burn into the efuse block", + action="append", + type=argparse.FileType("rb"), + ) + for _ in range(0, len(efuses.BURN_BLOCK_DATA_NAMES)): + burn_block_data.add_argument( + "block", + help="Efuse block to burn.", + metavar="BLOCK", + nargs="?", + action="append", + choices=efuses.BURN_BLOCK_DATA_NAMES, + ) + burn_block_data.add_argument( + "datafile", + nargs="?", + help="File containing data to burn into the efuse block", + metavar="DATAFILE", + action="append", + type=argparse.FileType("rb"), + ) + + set_bit_cmd = subparsers.add_parser("burn_bit", help="Burn bit in the efuse block.") + add_force_write_always(set_bit_cmd) + set_bit_cmd.add_argument( + "block", help="Efuse block to burn.", choices=efuses.BURN_BLOCK_DATA_NAMES + ) + set_bit_cmd.add_argument( + "bit_number", + help="Bit number in the efuse block [0..BLK_LEN-1]", + nargs="+", + type=int, + ) + + subparsers.add_parser( + "adc_info", + help="Display information about ADC calibration data stored in efuse.", + ) + + dump_cmd = subparsers.add_parser("dump", help="Dump raw hex values of all efuses") + dump_cmd.add_argument( + "--file_name", + help="Saves dump for each block into separate file. Provide the common " + "path name /path/blk.bin, it will create: blk0.bin, blk1.bin ... blkN.bin. " + "Use burn_block_data to write it back to another chip.", + ) + + summary_cmd = subparsers.add_parser( + "summary", help="Print human-readable summary of efuse values" + ) + summary_cmd.add_argument( + "--format", + help="Select the summary format", + choices=["summary", "json"], + default="summary", + ) + summary_cmd.add_argument( + "--file", + help="File to save the efuse summary", + type=argparse.FileType("w"), + default=sys.stdout, + ) + + execute_scripts = subparsers.add_parser( + "execute_scripts", help="Executes scripts to burn at one time." + ) + execute_scripts.add_argument( + "scripts", + help="The special format of python scripts.", + nargs="+", + type=argparse.FileType("r"), + ) + execute_scripts.add_argument( + "--index", + help="integer index. " + "It allows to retrieve unique data per chip from configfiles " + "and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).", + type=int, + ) + execute_scripts.add_argument( + "--configfiles", + help="List of configfiles with data", + nargs="?", + action="append", + type=argparse.FileType("r"), + ) + + check_error_cmd = subparsers.add_parser("check_error", help="Checks eFuse errors") + check_error_cmd.add_argument( + "--recovery", + help="Recovery of BLOCKs after encoding errors", + action="store_true", + ) + + +def add_force_write_always(p): + p.add_argument( + "--force-write-always", + help="Write the efuse even if it looks like it's already been written, " + "or is write protected. Note that this option can't disable write protection, " + "or clear any bit which has already been set.", + action="store_true", + ) + + +def summary(esp, efuses, args): + """Print a human-readable summary of efuse contents""" + ROW_FORMAT = "%-50s %-50s%s = %s %s %s" + human_output = args.format == "summary" + json_efuse = {} + if args.file != sys.stdout: + print("Saving efuse values to " + args.file.name) + if human_output: + print( + ROW_FORMAT.replace("-50", "-12") + % ( + "EFUSE_NAME (Block)", + "Description", + "", + "[Meaningful Value]", + "[Readable/Writeable]", + "(Hex Value)", + ), + file=args.file, + ) + print("-" * 88, file=args.file) + for category in sorted(set(e.category for e in efuses), key=lambda c: c.title()): + if human_output: + print("%s fuses:" % category.title(), file=args.file) + for e in (e for e in efuses if e.category == category): + if e.efuse_type.startswith("bytes"): + raw = "" + else: + raw = "({})".format(e.get_bitstring()) + (readable, writeable) = (e.is_readable(), e.is_writeable()) + if readable and writeable: + perms = "R/W" + elif readable: + perms = "R/-" + elif writeable: + perms = "-/W" + else: + perms = "-/-" + base_value = e.get_meaning() + value = str(base_value) + if not readable: + value = value.replace("0", "?") + if human_output: + print( + ROW_FORMAT + % ( + e.get_info(), + e.description[:50], + "\n " if len(value) > 20 else "", + value, + perms, + raw, + ), + file=args.file, + ) + desc_len = len(e.description[50:]) + if desc_len: + desc_len += 50 + for i in range(50, desc_len, 50): + print( + "%-50s %-50s" % ("", e.description[i : (50 + i)]), + file=args.file, + ) + if args.format == "json": + json_efuse[e.name] = { + "name": e.name, + "value": base_value if readable else value, + "readable": readable, + "writeable": writeable, + "description": e.description, + "category": e.category, + "block": e.block, + "word": e.word, + "pos": e.pos, + "efuse_type": e.efuse_type, + "bit_len": e.bit_len, + } + if human_output: + print("", file=args.file) + if human_output: + print(efuses.summary(), file=args.file) + warnings = efuses.get_coding_scheme_warnings() + if warnings: + print( + "WARNING: Coding scheme has encoding bit error warnings", file=args.file + ) + if args.file != sys.stdout: + args.file.close() + print("Done") + if args.format == "json": + json.dump(json_efuse, args.file, sort_keys=True, indent=4) + print("") + + +def dump(esp, efuses, args): + """Dump raw efuse data registers""" + # Using --debug option allows to print dump. + # Nothing to do here. The log will be printed + # during EspEfuses.__init__() in self.read_blocks() + if args.file_name: + # save dump to the file + for block in efuses.blocks: + file_dump_name = args.file_name + place_for_index = file_dump_name.find(".bin") + file_dump_name = ( + file_dump_name[:place_for_index] + + str(block.id) + + file_dump_name[place_for_index:] + ) + print(file_dump_name) + with open(file_dump_name, "wb") as f: + block.get_bitstring().byteswap() + block.get_bitstring().tofile(f) + + +def burn_efuse(esp, efuses, args): + def print_attention(blocked_efuses_after_burn): + if len(blocked_efuses_after_burn): + print( + " ATTENTION! This BLOCK uses NOT the NONE coding scheme " + "and after 'BURN', these efuses can not be burned in the feature:" + ) + for i in range(0, len(blocked_efuses_after_burn), 5): + print( + " ", + "".join("{}".format(blocked_efuses_after_burn[i : i + 5 :])), + ) + + efuse_name_list = [name for name in args.name_value_pairs.keys()] + burn_efuses_list = [efuses[name] for name in efuse_name_list] + old_value_list = [efuses[name].get_raw() for name in efuse_name_list] + new_value_list = [value for value in args.name_value_pairs.values()] + util.check_duplicate_name_in_list(efuse_name_list) + + attention = "" + print("The efuses to burn:") + for block in efuses.blocks: + burn_list_a_block = [e for e in burn_efuses_list if e.block == block.id] + if len(burn_list_a_block): + print(" from BLOCK%d" % (block.id)) + for field in burn_list_a_block: + print(" - %s" % (field.name)) + if ( + efuses.blocks[field.block].get_coding_scheme() + != efuses.REGS.CODING_SCHEME_NONE + ): + using_the_same_block_names = [ + e.name for e in efuses if e.block == field.block + ] + wr_names = [e.name for e in burn_list_a_block] + blocked_efuses_after_burn = [ + name + for name in using_the_same_block_names + if name not in wr_names + ] + attention = " (see 'ATTENTION!' above)" + if attention: + print_attention(blocked_efuses_after_burn) + + print("\nBurning efuses{}:".format(attention)) + for efuse, new_value in zip(burn_efuses_list, new_value_list): + print( + "\n - '{}' ({}) {} -> {}".format( + efuse.name, + efuse.description, + efuse.get_bitstring(), + efuse.convert_to_bitstring(new_value), + ) + ) + efuse.save(new_value) + + print() + if "ENABLE_SECURITY_DOWNLOAD" in efuse_name_list: + print( + "ENABLE_SECURITY_DOWNLOAD -> 1: eFuses will not be read back " + "for confirmation because this mode disables " + "any SRAM and register operations." + ) + print(" espefuse will not work.") + print(" esptool can read/write only flash.") + + if "DIS_DOWNLOAD_MODE" in efuse_name_list: + print( + "DIS_DOWNLOAD_MODE -> 1: eFuses will not be read back for " + "confirmation because this mode disables any communication with the chip." + ) + print( + " espefuse/esptool will not work because " + "they will not be able to connect to the chip." + ) + + if ( + esp.CHIP_NAME == "ESP32" + and esp.get_chip_revision() >= 300 + and "UART_DOWNLOAD_DIS" in efuse_name_list + ): + print( + "UART_DOWNLOAD_DIS -> 1: eFuses will be read for confirmation, " + "but after that connection to the chip will become impossible." + ) + print(" espefuse/esptool will not work.") + + if not efuses.burn_all(check_batch_mode=True): + return + + print("Checking efuses...") + raise_error = False + for efuse, old_value, new_value in zip( + burn_efuses_list, old_value_list, new_value_list + ): + if not efuse.is_readable(): + print( + "Efuse %s is read-protected. Read back the burn value is not possible." + % efuse.name + ) + else: + new_value = efuse.convert_to_bitstring(new_value) + burned_value = efuse.get_bitstring() + if burned_value != new_value: + print( + burned_value, + "->", + new_value, + "Efuse %s failed to burn. Protected?" % efuse.name, + ) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + print("Successful") + + +def read_protect_efuse(esp, efuses, args): + util.check_duplicate_name_in_list(args.efuse_name) + + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if not efuse.is_readable(): + print("Efuse %s is already read protected" % efuse.name) + else: + if esp.CHIP_NAME == "ESP32": + if ( + efuse_name == "BLOCK2" + and not efuses["ABS_DONE_0"].get() + and esp.get_chip_revision() >= 300 + ): + if efuses["ABS_DONE_1"].get(): + raise esptool.FatalError( + "Secure Boot V2 is on (ABS_DONE_1 = True), " + "BLOCK2 must be readable, stop this operation!" + ) + else: + print( + "If Secure Boot V2 is used, BLOCK2 must be readable, " + "please stop this operation!" + ) + elif esp.CHIP_NAME == "ESP32-C2": + error = ( + not efuses["XTS_KEY_LENGTH_256"].get() + and efuse_name == "BLOCK_KEY0" + ) + error |= efuses["SECURE_BOOT_EN"].get() and efuse_name in [ + "BLOCK_KEY0", + "BLOCK_KEY0_HI_128", + ] + if error: + raise esptool.FatalError( + "%s must be readable, stop this operation!" % efuse_name + ) + else: + for block in efuses.Blocks.BLOCKS: + block = efuses.Blocks.get(block) + if block.name == efuse_name and block.key_purpose is not None: + if not efuses[block.key_purpose].need_rd_protect( + efuses[block.key_purpose].get() + ): + raise esptool.FatalError( + "%s must be readable, stop this operation!" % efuse_name + ) + break + # make full list of which efuses will be disabled + # (ie share a read disable bit) + all_disabling = [ + e for e in efuses if e.read_disable_bit == efuse.read_disable_bit + ] + names = ", ".join(e.name for e in all_disabling) + print( + "Permanently read-disabling efuse%s %s" + % ("s" if len(all_disabling) > 1 else "", names) + ) + efuse.disable_read() + + if not efuses.burn_all(check_batch_mode=True): + return + + print("Checking efuses...") + raise_error = False + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if efuse.is_readable(): + print("Efuse %s is not read-protected." % efuse.name) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + print("Successful") + + +def write_protect_efuse(esp, efuses, args): + util.check_duplicate_name_in_list(args.efuse_name) + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if not efuse.is_writeable(): + print("Efuse %s is already write protected" % efuse.name) + else: + # make full list of which efuses will be disabled + # (ie share a write disable bit) + all_disabling = [ + e for e in efuses if e.write_disable_bit == efuse.write_disable_bit + ] + names = ", ".join(e.name for e in all_disabling) + print( + "Permanently write-disabling efuse%s %s" + % ("s" if len(all_disabling) > 1 else "", names) + ) + efuse.disable_write() + + if not efuses.burn_all(check_batch_mode=True): + return + + print("Checking efuses...") + raise_error = False + for efuse_name in args.efuse_name: + efuse = efuses[efuse_name] + if efuse.is_writeable(): + print("Efuse %s is not write-protected." % efuse.name) + raise_error = True + if raise_error: + raise esptool.FatalError("The burn was not successful.") + else: + print("Successful") + + +def burn_block_data(esp, efuses, args): + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + datafile_list = args.datafile[ + 0 : len([name for name in args.datafile if name is not None]) : + ] + efuses.force_write_always = args.force_write_always + + util.check_duplicate_name_in_list(block_name_list) + if args.offset and len(block_name_list) > 1: + raise esptool.FatalError( + "The 'offset' option is not applicable when a few blocks are passed. " + "With 'offset', should only one block be used." + ) + else: + offset = args.offset + if offset: + num_block = efuses.get_index_block_by_name(block_name_list[0]) + block = efuses.blocks[num_block] + num_bytes = block.get_block_len() + if offset >= num_bytes: + raise esptool.FatalError( + "Invalid offset: the block%d only holds %d bytes." + % (block.id, num_bytes) + ) + if len(block_name_list) != len(datafile_list): + raise esptool.FatalError( + "The number of block_name (%d) and datafile (%d) should be the same." + % (len(block_name_list), len(datafile_list)) + ) + + for block_name, datafile in zip(block_name_list, datafile_list): + num_block = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[num_block] + data = datafile.read() + num_bytes = block.get_block_len() + if offset != 0: + data = (b"\x00" * offset) + data + data = data + (b"\x00" * (num_bytes - len(data))) + if len(data) != num_bytes: + raise esptool.FatalError( + "Data does not fit: the block%d size is %d bytes, " + "data file is %d bytes, offset %d" + % (block.id, num_bytes, len(data), offset) + ) + print( + "[{:02}] {:20} size={:02} bytes, offset={:02} - > [{}].".format( + block.id, block.name, len(data), offset, util.hexify(data, " ") + ) + ) + block.save(data) + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_bit(esp, efuses, args): + efuses.force_write_always = args.force_write_always + num_block = efuses.get_index_block_by_name(args.block) + block = efuses.blocks[num_block] + data_block = BitString(block.get_block_len() * 8) + data_block.set(0) + try: + data_block.set(True, args.bit_number) + except IndexError: + raise esptool.FatalError( + "%s has bit_number in [0..%d]" % (args.block, data_block.len - 1) + ) + data_block.reverse() + print( + "bit_number: " + "[%-03d]........................................................[0]" + % (data_block.len - 1) + ) + print("BLOCK%-2d :" % block.id, data_block) + block.print_block(data_block, "regs_to_write", debug=True) + block.save(data_block.bytes[::-1]) + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def check_error(esp, efuses, args): + error_in_blocks = efuses.get_coding_scheme_warnings() + if args.recovery: + if error_in_blocks: + confirmed = False + for block in reversed(efuses.blocks): + if block.fail or block.num_errors > 0: + if not block.get_bitstring().all(False): + block.save(block.get_bitstring().bytes[::-1]) + if not confirmed: + confirmed = True + efuses.confirm( + "Recovery of block coding errors", args.do_not_confirm + ) + block.burn() + # Reset the recovery flag to run check_error() without it, + # just to check the new state of eFuse blocks. + args.recovery = False + check_error(esp, efuses, args) + else: + if error_in_blocks: + raise esptool.FatalError("Error(s) were detected in eFuses") + print("No errors detected") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py new file mode 100644 index 000000000..03329a314 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import re + +from bitstring import BitString + + +class EmulateEfuseControllerBase(object): + """The class for virtual efuse operations. Using for HOST_TEST.""" + + CHIP_NAME = "" + mem = None + debug = False + Blocks = None + Fields = None + REGS = None + + def __init__(self, efuse_file=None, debug=False): + self.debug = debug + self.efuse_file = efuse_file + if self.efuse_file: + try: + self.mem = BitString( + open(self.efuse_file, "a+b"), length=self.REGS.EFUSE_MEM_SIZE * 8 + ) + except ValueError: + # the file is empty or does not fit the length. + self.mem = BitString(length=self.REGS.EFUSE_MEM_SIZE * 8) + self.mem.set(0) + self.mem.tofile(open(self.efuse_file, "a+b")) + else: + # efuse_file is not provided + # it means we do not want to keep the result of efuse operations + self.mem = BitString(self.REGS.EFUSE_MEM_SIZE * 8) + self.mem.set(0) + + """ esptool method start >> """ + + def get_chip_description(self): + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" + + def get_chip_revision(self): + return self.get_major_chip_version() * 100 + self.get_minor_chip_version() + + def read_efuse(self, n, block=0): + """Read the nth word of the ESP3x EFUSE region.""" + blk = self.Blocks.get(self.Blocks.BLOCKS[block]) + return self.read_reg(blk.rd_addr + (4 * n)) + + def read_reg(self, addr): + self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) + return self.mem.read("uint:32") + + def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): + self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) + self.mem.overwrite("uint:32={}".format(value & mask)) + self.handle_writing_event(addr, value) + + def update_reg(self, addr, mask, new_val): + position = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) + self.mem.pos = position + cur_val = self.mem.read("uint:32") + self.mem.pos = position + self.mem.overwrite("uint:32={}".format(cur_val | (new_val & mask))) + + def write_efuse(self, n, value, block=0): + """Write the nth word of the ESP3x EFUSE region.""" + blk = self.Blocks.get(self.Blocks.BLOCKS[block]) + self.write_reg(blk.wr_addr + (4 * n), value) + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + self.save_to_file() + + def save_to_file(self): + if self.efuse_file: + with open(self.efuse_file, "wb") as f: + self.mem.tofile(f) + + def handle_coding_scheme(self, blk, data): + return data + + def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None): + for b in reversed(self.Blocks.BLOCKS): + blk = self.Blocks.get(b) + if updated_block is not None: + if blk.id != updated_block: + continue + data = self.read_block(blk.id, wr_regs=True) + if self.debug: + print(blk.name, data.hex) + plain_data = self.handle_coding_scheme(blk, data) + plain_data = self.check_wr_protection_area(blk.id, plain_data) + self.update_block(blk, plain_data) + + def clean_blocks_wr_regs(self): + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + for offset in range(0, blk.len * 4, 4): + wr_addr = blk.wr_addr + offset + self.write_reg(wr_addr, 0) + + def read_field(self, name, bitstring=True): + for e in self.Fields.EFUSES: + field = self.Fields.get(e) + if field.name == name: + self.read_block(field.block) + block = self.read_block(field.block) + if field.type.startswith("bool"): + field_len = 1 + else: + field_len = int(re.search(r"\d+", field.type).group()) + if field.type.startswith("bytes"): + field_len *= 8 + block.pos = block.length - (field.word * 32 + field.pos + field_len) + if bitstring: + return block.read(field_len) + else: + return block.read(field.type) + return None + + def get_bitlen_of_block(self, blk, wr=False): + return 32 * blk.len + + def read_block(self, idx, wr_regs=False): + block = None + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + if blk.id == idx: + blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs) + addr = blk.wr_addr if wr_regs else blk.rd_addr + self.mem.pos = self.mem.length - ( + (addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + blk_len_bits + ) + block = self.mem.read(blk_len_bits) + break + return block + + def update_block(self, blk, wr_data): + wr_data = self.read_block(blk.id) | wr_data + self.overwrite_mem_from_block(blk, wr_data) + + def overwrite_mem_from_block(self, blk, wr_data): + self.mem.pos = self.mem.length - ( + (blk.rd_addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + wr_data.len + ) + self.mem.overwrite(wr_data) + + def check_wr_protection_area(self, num_blk, wr_data): + # checks fields which have the write protection bit. + # if the write protection bit is set, we need to protect that area from changes. + write_disable_bit = self.read_field("WR_DIS", bitstring=False) + mask_wr_data = BitString(len(wr_data)) + mask_wr_data.set(0) + blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) + if blk.write_disable_bit is not None and write_disable_bit & ( + 1 << blk.write_disable_bit + ): + mask_wr_data.set(1) + else: + for e in self.Fields.EFUSES: + field = self.Fields.get(e) + if blk.id == field.block and field.block == num_blk: + if field.write_disable_bit is not None and write_disable_bit & ( + 1 << field.write_disable_bit + ): + data = self.read_field(field.name) + data.set(1) + mask_wr_data.pos = mask_wr_data.length - ( + field.word * 32 + field.pos + data.len + ) + mask_wr_data.overwrite(data) + mask_wr_data.invert() + return wr_data & mask_wr_data + + def check_rd_protection_area(self): + # checks fields which have the read protection bits. + # if the read protection bit is set then we need to reset this field to 0. + read_disable_bit = self.read_field("RD_DIS", bitstring=False) + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + block = self.read_block(blk.id) + if blk.read_disable_bit is not None and read_disable_bit & ( + 1 << blk.read_disable_bit + ): + block.set(0) + else: + for e in self.Fields.EFUSES: + field = self.Fields.get(e) + if ( + blk.id == field.block + and field.read_disable_bit is not None + and read_disable_bit & (1 << field.read_disable_bit) + ): + raw_data = self.read_field(field.name) + raw_data.set(0) + block.pos = block.length - ( + field.word * 32 + field.pos + raw_data.length + ) + block.overwrite(BitString(raw_data.length)) + self.overwrite_mem_from_block(blk, block) + + def clean_mem(self): + self.mem.set(0) + if self.efuse_file: + with open(self.efuse_file, "wb") as f: + self.mem.tofile(f) + + +class FatalError(RuntimeError): + """ + Wrapper class for runtime errors that aren't caused by internal bugs + """ + + def __init__(self, message): + RuntimeError.__init__(self, message) + + @staticmethod + def WithResult(message, result): + return FatalError(result) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py new file mode 100644 index 000000000..b0011e0b6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import time + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operations. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + + """ esptool method start >> """ + + def get_major_chip_version(self): + return 3 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def read_reg(self, addr): + if addr == self.REGS.APB_CTL_DATE_ADDR: + return self.REGS.APB_CTL_DATE_V << self.REGS.APB_CTL_DATE_S + else: + val = 0 + if addr == self.REGS.EFUSE_BLK0_RDATA3_REG: + val = self.REGS.EFUSE_RD_CHIP_VER_REV1 + if addr == self.REGS.EFUSE_BLK0_RDATA5_REG: + val = self.REGS.EFUSE_RD_CHIP_VER_REV2 + return val | super(EmulateEfuseController, self).read_reg(addr) + + """ << esptool method end """ + + def send_burn_cmd(self): + def wait_idle(): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: + return + raise FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) + wait_idle() + self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) + wait_idle() + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_REG_CMD: + if value == self.REGS.EFUSE_CMD_WRITE: + self.write_reg(addr, 0) + elif value == self.REGS.EFUSE_CMD_READ: + self.copy_blocks_wr_regs_to_rd_regs() + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.save_to_file() + + def read_raw_coding_scheme(self): + return ( + self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) + & self.REGS.EFUSE_CODING_SCHEME_MASK + ) + + def write_raw_coding_scheme(self, value): + self.write_efuse( + self.REGS.EFUSE_CODING_SCHEME_WORD, + value & self.REGS.EFUSE_CODING_SCHEME_MASK, + ) + self.send_burn_cmd() + if value != self.read_raw_coding_scheme(): + raise FatalError( + "Error during a burning process to set the new coding scheme" + ) + print("Set coding scheme = %d" % self.read_raw_coding_scheme()) + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + return 32 * blk.len + else: + coding_scheme = self.read_raw_coding_scheme() + if coding_scheme == self.REGS.CODING_SCHEME_NONE: + return 32 * blk.len + elif coding_scheme == self.REGS.CODING_SCHEME_34: + if wr: + return 32 * 8 + else: + return 32 * blk.len * 3 // 4 + else: + raise FatalError( + "The {} coding scheme is not supported".format(coding_scheme) + ) + + def handle_coding_scheme(self, blk, data): + # it verifies the coding scheme part of data and returns just data + if blk.id != 0 and self.read_raw_coding_scheme() == self.REGS.CODING_SCHEME_34: + # CODING_SCHEME 3/4 applied only for BLK1..3 + # Takes 24 byte sequence to be represented in 3/4 encoding, + # returns 8 words suitable for writing "encoded" to an efuse block + data.pos = 0 + for _ in range(0, 4): + xor_res = 0 + mul_res = 0 + chunk_data = data.readlist("8*uint:8") + chunk_data = chunk_data[::-1] + for i in range(0, 6): + byte_data = chunk_data[i] + xor_res ^= byte_data + mul_res += (i + 1) * bin(byte_data).count("1") + if xor_res != chunk_data[6] or mul_res != chunk_data[7]: + print( + "xor_res ", + xor_res, + chunk_data[6], + "mul_res", + mul_res, + chunk_data[7], + ) + raise FatalError("Error in coding scheme data") + # cut the coded data + for i in range(0, 4): + del data[i * 6 * 8 : (i * 6 * 8) + 16] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py new file mode 100644 index 000000000..dc9088026 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py @@ -0,0 +1,500 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +import esptool + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is the same as len of a block. + return self.len + + def __init__(self, parent, param, skip_read=False): + if skip_read: + parent.coding_scheme = parent.REGS.CODING_SCHEME_NONE + else: + if parent.coding_scheme is None: + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_34: + # CODING_SCHEME 3/4 applied only for BLK1..3 + # Takes 24 byte sequence to be represented in 3/4 encoding, + # returns 8 words suitable for writing "encoded" to an efuse block + if len(data) != 24: + raise esptool.FatalError("Should take 24 bytes for 3/4 encoding.") + data = data[:24] + outbits = b"" + while len(data) > 0: # process in chunks of 6 bytes + bits = data[0:6] + data = data[6:] + xor_res = 0 + mul_res = 0 + index = 1 + for b in struct.unpack("B" * 6, bits): + xor_res ^= b + mul_res += index * util.popcnt(b) + index += 1 + outbits += bits + outbits += struct.pack("BB", xor_res, mul_res) + words = struct.unpack("<" + "I" * (len(outbits) // 4), outbits) + # returns 8 words + else: + # CODING_SCHEME NONE applied for BLK0 and BLK1..3 + # BLK0 len = 7 words, BLK1..3 len = 8 words. + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 7 words for BLK0 or 8 words for BLK1..3 + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS_256 + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CUSTOM_MAC + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.ADC_CALIBRATION + ] + else: + if self.coding_scheme == self.REGS.CODING_SCHEME_NONE: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS_256 + ] + elif self.coding_scheme == self.REGS.CODING_SCHEME_34: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS_192 + ] + else: + raise esptool.FatalError( + "The coding scheme (%d) - is not supported" % self.coding_scheme + ) + if self["MAC_VERSION"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CUSTOM_MAC + ] + if self["BLK3_PART_RESERVE"].get(): + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.ADC_CALIBRATION + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.CUSTOM_MAC: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CUSTOM_MAC + ] + new_fields = True + for efuse in self.Fields.ADC_CALIBRATION: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.ADC_CALIBRATION + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = ( + self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) + & self.REGS.EFUSE_CODING_SCHEME_MASK + ) + + def print_status_regs(self): + print("") + print( + "{:27} 0x{:08x}".format( + "EFUSE_REG_DEC_STATUS", self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) + ) + ) + + def write_efuses(self, block): + """Write the values in the efuse write registers to + the efuse hardware, then refresh the efuse read registers. + """ + + # Configure clock + apb_freq = self.get_crystal_freq() + clk_sel0, clk_sel1, dac_clk_div = self.REGS.EFUSE_CLK_SETTINGS[apb_freq] + + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_MASK, dac_clk_div + ) + self.update_reg( + self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL0_MASK, clk_sel0 + ) + self.update_reg( + self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL1_MASK, clk_sel1 + ) + + self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_WRITE) + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) + + self.efuse_read() + return self.get_coding_scheme_warnings(silent=True) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) + self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) + self.wait_efuse_idle() + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors. + Meaningless for default coding scheme (0) + """ + err = ( + self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) + & self.REGS.EFUSE_REG_DEC_STATUS_MASK + ) + for block in self.blocks: + if block.id != 0: + block.num_errors = 0 + block.fail = err != 0 + if not silent and block.fail: + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or err) and not silent: + self.print_status_regs() + return err != 0 + + def summary(self): + if self["XPD_SDIO_FORCE"].get() == 0: + output = "Flash voltage (VDD_SDIO) determined by GPIO12 on reset " + "(High for 1.8V, Low/NC for 3.3V)." + elif self["XPD_SDIO_REG"].get() == 0: + output = "Flash voltage (VDD_SDIO) internal regulator disabled by efuse." + elif self["XPD_SDIO_TIEH"].get() == 0: + output = "Flash voltage (VDD_SDIO) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SDIO) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "spipin": EfuseSpiPinField, + "vref": EfuseVRefField, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + "pkg": EfusePkg, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + return "%s (BLOCK%d):" % (self.name, self.block) + + +class EfuseMacField(EfuseField): + """ + Supports: MAC and CUSTOM_MAC fields. + (if MAC_VERSION == 1 then the CUSTOM_MAC is used) + """ + + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + @staticmethod + def get_and_check(raw_mac, stored_crc): + computed_crc = EfuseMacField.calc_crc(raw_mac) + if computed_crc == stored_crc: + valid_msg = "(CRC 0x%02x OK)" % stored_crc + else: + valid_msg = "(CRC 0x%02x invalid - calculated 0x%02x)" % ( + stored_crc, + computed_crc, + ) + return "%s %s" % (util.hexify(raw_mac, ":"), valid_msg) + + @staticmethod + def calc_crc(raw_mac): + """ + This algorithm is the equivalent of esp_crc8() in ESP32 ROM code + + This is CRC-8 w/ inverted polynomial value 0x8C & initial value 0x00. + """ + result = 0x00 + for b in struct.unpack("B" * 6, raw_mac): + result ^= b + for _ in range(8): + lsb = result & 1 + result >>= 1 + if lsb != 0: + result ^= 0x8C + return result + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + stored_crc = self.parent["CUSTOM_MAC_CRC"].get(from_read) + else: + mac = self.get_raw(from_read) + stored_crc = self.parent["MAC_CRC"].get(from_read) + return EfuseMacField.get_and_check(mac, stored_crc) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + # Writing the BLK3: + # - MAC_VERSION = 1 + # - CUSTOM_MAC = AB:CD:EF:01:02:03 + # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) + mac_version = self.parent["MAC_VERSION"] + if mac_version.get() == 0: + mac_version_value = 1 + print_field(mac_version, hex(mac_version_value)) + mac_version.save(mac_version_value) + else: + if mac_version.get() != 1: + if not self.parent.force_write_always: + raise esptool.FatalError( + "MAC_VERSION = {}, should be 0 or 1.".format( + mac_version.get() + ) + ) + + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + + crc_val = self.calc_crc(new_value) + crc_field = self.parent["CUSTOM_MAC_CRC"] + print_field(crc_field, hex(crc_val)) + crc_field.save(crc_val) + else: + # Writing the BLK0 default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + rev_bit0 = self.parent["CHIP_VER_REV1"].get(from_read) + rev_bit1 = self.parent["CHIP_VER_REV2"].get(from_read) + apb_ctl_date = self.parent.read_reg(self.parent.REGS.APB_CTL_DATE_ADDR) + rev_bit2 = ( + apb_ctl_date >> self.parent.REGS.APB_CTL_DATE_S + ) & self.parent.REGS.APB_CTL_DATE_V + combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 + + revision = { + 0: 0, + 1: 1, + 3: 2, + 7: 3, + }.get(combine_value, 0) + return revision + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfusePkg(EfuseField): + def get(self, from_read=True): + lo_bits = self.parent["CHIP_PACKAGE"].get(from_read) + hi_bits = self.parent["CHIP_PACKAGE_4BIT"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseSpiPinField(EfuseField): + def get(self, from_read=True): + val = self.get_raw(from_read) + if val >= 30: + val += 2 # values 30,31 map to 32, 33 + return val + + def check_format(self, new_value_str): + if new_value_str is None: + return new_value_str + + new_value_int = int(new_value_str, 0) + + if new_value_int in [30, 31]: + raise esptool.FatalError( + "IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only." + ) + elif new_value_int > 33: + raise esptool.FatalError( + "IO pin %d cannot be set for SPI flash. 0-29, 32 & 33 only." + % new_value_int + ) + elif new_value_int in [32, 33]: + return str(new_value_int - 2) + else: + return new_value_str + + +class EfuseVRefField(EfuseField): + VREF_OFFSET = 1100 # ideal efuse value in mV + VREF_STEP_SIZE = 7 # 1 count in efuse == 7mV + VREF_SIGN_BIT = 0x10 + VREF_MAG_BITS = 0x0F + + def get(self, from_read=True): + val = self.get_raw(from_read) + # sign-magnitude format + if val & self.VREF_SIGN_BIT: + val = -(val & self.VREF_MAG_BITS) + else: + val = val & self.VREF_MAG_BITS + val *= self.VREF_STEP_SIZE + return self.VREF_OFFSET + val + + def save(self, new_value): + raise esptool.FatalError("Writing to VRef is not supported.") + + +class EfuseAdcPointCalibration(EfuseField): + TP_OFFSET = { # See TP_xxxx_OFFSET in esp_adc_cal.c in ESP-IDF + "ADC1_TP_LOW": 278, + "ADC2_TP_LOW": 421, + "ADC1_TP_HIGH": 3265, + "ADC2_TP_HIGH": 3406, + } + SIGN_BIT = (0x40, 0x100) # LOW, HIGH (2s complement format) + STEP_SIZE = 4 + + def get(self, from_read=True): + idx = 0 if self.name.endswith("LOW") else 1 + sign_bit = self.SIGN_BIT[idx] + offset = self.TP_OFFSET[self.name] + raw = self.get_raw() + delta = (raw & (sign_bit - 1)) - (raw & sign_bit) + return offset + (delta * self.STEP_SIZE) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py new file mode 100644 index 000000000..589e7db7d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_MEM_SIZE = 0x011C + 4 + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x3FF5A000 + EFUSE_REG_CONF = DR_REG_EFUSE_BASE + 0x0FC + EFUSE_CONF_WRITE = 0x5A5A + EFUSE_CONF_READ = 0x5AA5 + EFUSE_REG_CMD = DR_REG_EFUSE_BASE + 0x104 + EFUSE_CMD_OP_MASK = 0x3 + EFUSE_CMD_WRITE = 0x2 + EFUSE_CMD_READ = 0x1 + + # 3/4 Coding scheme warnings registers + EFUSE_REG_DEC_STATUS = DR_REG_EFUSE_BASE + 0x11C + EFUSE_REG_DEC_STATUS_MASK = 0xFFF + + # Coding Scheme + EFUSE_CODING_SCHEME_WORD = 6 + EFUSE_CODING_SCHEME_MASK = 0x3 + + # Efuse clock control + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x118 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x0F8 + EFUSE_DAC_CLK_DIV_MASK = 0xFF + EFUSE_CLK_SEL0_MASK = 0x00FF + EFUSE_CLK_SEL1_MASK = 0xFF00 + + EFUSE_CLK_SETTINGS = { + # APB freq: clk_sel0, clk_sel1, dac_clk_div + # Taken from TRM chapter "eFuse Controller": Timing Configuration + # 80 is here for completeness only as esptool never sets an 80MHz APB clock + 26: (250, 255, 52), + 40: (160, 255, 80), + 80: (80, 128, 100), + } + + DR_REG_SYSCON_BASE = 0x3FF66000 + APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C + APB_CTL_DATE_V = 0x1 + APB_CTL_DATE_S = 31 + + EFUSE_BLK0_RDATA3_REG = DR_REG_EFUSE_BASE + 0x00C + EFUSE_RD_CHIP_VER_REV1 = 1 << 15 + + EFUSE_BLK0_RDATA5_REG = DR_REG_EFUSE_BASE + 0x014 + EFUSE_RD_CHIP_VER_REV2 = 1 << 20 + + +# fmt: off +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_regs + 0x000, __base_regs + 0x01C, None, None, 7, None), + ("BLOCK1", ["flash_encryption"], 1, __base_regs + 0x038, __base_regs + 0x098, 7, 0, 8, None), + ("BLOCK2", ["secure_boot_v1", "secure_boot_v2"], 2, __base_regs + 0x058, __base_regs + 0x0B8, 8, 1, 8, None), + ("BLOCK3", [], 3, __base_regs + 0x078, __base_regs + 0x0D8, 9, 2, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # Lists of efuse fields + EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('WR_DIS', "efuse", 0, 0, 0, "uint:16", 1, None, None, "Efuse write disable mask", None), + ('RD_DIS', "efuse", 0, 0, 16, "uint:4", 0, None, None, "Efuse read disable mask", None), + ('CODING_SCHEME', "efuse", 0, 6, 0, "uint:2", 10, 3, None, "Efuse variable block length scheme", + {0: "NONE (BLK1-3 len=256 bits)", + 1: "3/4 (BLK1-3 len=192 bits)", + 2: "REPEAT (BLK1-3 len=128 bits) not supported", + 3: "NONE (BLK1-3 len=256 bits)"}), + ('KEY_STATUS', "efuse", 0, 6, 10, "bool", 10, 3, None, "Usage of efuse block 3 (reserved)", None), + ('MAC', "identity", 0, 1, 0, "bytes:6", 3, None, "mac", "Factory MAC Address", None), + ('MAC_CRC', "identity", 0, 2, 16, "uint:8", 3, None, None, "CRC8 for factory MAC address", None), + ('CHIP_VER_REV1', "identity", 0, 3, 15, "bool", 3, None, None, "Silicon Revision 1", None), + ('CHIP_VER_REV2', "identity", 0, 5, 20, "bool", 6, None, None, "Silicon Revision 2", None), + ("WAFER_VERSION_MINOR", "identity", 0, 5, 24, "uint:2", 6, None, None, "WAFER VERSION MINOR", None), + ('CHIP_PACKAGE', "identity", 0, 3, 9, "uint:3", 3, None, None, "Chip package identifier", None), + ('CHIP_PACKAGE_4BIT', "identity", 0, 3, 2, "uint:1", 3, None, None, "Chip package identifier #4bit", None), + ('XPD_SDIO_FORCE', "config", 0, 4, 16, "bool", 5, None, None, "Ignore MTDI pin (GPIO12) for VDD_SDIO on reset", None), + ('XPD_SDIO_REG', "config", 0, 4, 14, "bool", 5, None, None, "If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset", None), + ('XPD_SDIO_TIEH', "config", 0, 4, 15, "bool", 5, None, None, "If XPD_SDIO_FORCE & XPD_SDIO_REG", + {1: "3.3V", + 0: "1.8V"}), + ('CLK8M_FREQ', "config", 0, 4, 0, "uint:8", None, None, None, "8MHz clock freq override", None), + ('SPI_PAD_CONFIG_CLK', "config", 0, 5, 0, "uint:5", 6, None, "spipin", "Override SD_CLK pad (GPIO6/SPICLK)", None), + ('SPI_PAD_CONFIG_Q', "config", 0, 5, 5, "uint:5", 6, None, "spipin", "Override SD_DATA_0 pad (GPIO7/SPIQ)", None), + ('SPI_PAD_CONFIG_D', "config", 0, 5, 10, "uint:5", 6, None, "spipin", "Override SD_DATA_1 pad (GPIO8/SPID)", None), + ('SPI_PAD_CONFIG_HD', "config", 0, 3, 4, "uint:5", 6, None, "spipin", "Override SD_DATA_2 pad (GPIO9/SPIHD)", None), + ('SPI_PAD_CONFIG_CS0', "config", 0, 5, 15, "uint:5", 6, None, "spipin", "Override SD_CMD pad (GPIO11/SPICS0)", None), + ('DISABLE_SDIO_HOST', "config", 0, 6, 3, "bool", None, None, None, "Disable SDIO host", None), + ('FLASH_CRYPT_CNT', "security", 0, 0, 20, "uint:7", 2, None, "bitcount", "Flash encryption mode counter", None), + ('UART_DOWNLOAD_DIS', "security", 0, 0, 27, "bool", 2, None, None, "Disable UART download mode (ESP32 rev3 only)", None), + ('FLASH_CRYPT_CONFIG', "security", 0, 5, 28, "uint:4", 10, 3, None, "Flash encryption config (key tweak bits)", None), + ('CONSOLE_DEBUG_DISABLE', "security", 0, 6, 2, "bool", 15, None, None, "Disable ROM BASIC interpreter fallback", None), + ('ABS_DONE_0', "security", 0, 6, 4, "bool", 12, None, None, "Secure boot V1 is enabled for bootloader image", None), + ('ABS_DONE_1', "security", 0, 6, 5, "bool", 13, None, None, "Secure boot V2 is enabled for bootloader image", None), + ('JTAG_DISABLE', "security", 0, 6, 6, "bool", 14, None, None, "Disable JTAG", None), + ('DISABLE_DL_ENCRYPT', "security", 0, 6, 7, "bool", 15, None, None, "Disable flash encryption in UART bootloader", None), + ('DISABLE_DL_DECRYPT', "security", 0, 6, 8, "bool", 15, None, None, "Disable flash decryption in UART bootloader", None), + ('DISABLE_DL_CACHE', "security", 0, 6, 9, "bool", 15, None, None, "Disable flash cache in UART bootloader", None), + ('BLK3_PART_RESERVE', "calibration", 0, 3, 14, "bool", 10, 3, None, "BLOCK3 partially served for ADC calibration data", None), + ('ADC_VREF', "calibration", 0, 4, 8, "uint:5", 0, None, "vref", "Voltage reference calibration", None), + ('MAC_VERSION', "identity", 3, 5, 24, "uint:8", 9, 2, None, "Version of the MAC field", + {1: "Custom MAC in BLOCK3"}), + ] + + # if MAC_VERSION is set "1", these efuse fields are in BLOCK3: + CUSTOM_MAC = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('CUSTOM_MAC', "identity", 3, 0, 8, "bytes:6", 9, 2, "mac", "Custom MAC", None), + ('CUSTOM_MAC_CRC', "identity", 3, 0, 0, "uint:8", 9, 2, None, "CRC of custom MAC", None), + ] + + # The len of fields depends on coding scheme: for CODING_SCHEME_NONE + KEYBLOCKS_256 = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK1', "security", 1, 0, 0, "bytes:32", 7, 0, "keyblock", "Flash encryption key", None), + ('BLOCK2', "security", 2, 0, 0, "bytes:32", 8, 1, "keyblock", "Secure boot key", None), + ('BLOCK3', "security", 3, 0, 0, "bytes:32", 9, 2, "keyblock", "Variable Block 3", None), + ] + + # The len of fields depends on coding scheme: for CODING_SCHEME_34 + KEYBLOCKS_192 = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK1', "security", 1, 0, 0, "bytes:24", 7, 0, "keyblock", "Flash encryption key", None), + ('BLOCK2', "security", 2, 0, 0, "bytes:24", 8, 1, "keyblock", "Secure boot key", None), + ('BLOCK3', "security", 3, 0, 0, "bytes:24", 9, 2, "keyblock", "Variable Block 3", None), + ] + + # if BLK3_PART_RESERVE is set, these efuse fields are in BLOCK3: + ADC_CALIBRATION = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('ADC1_TP_LOW', "calibration", 3, 3, 0, "uint:7", 9, 2, "adc_tp", "ADC1 150mV reading", None), + ('ADC1_TP_HIGH', "calibration", 3, 3, 7, "uint:9", 9, 2, "adc_tp", "ADC1 850mV reading", None), + ('ADC2_TP_LOW', "calibration", 3, 3, 16, "uint:7", 9, 2, "adc_tp", "ADC2 150mV reading", None), + ('ADC2_TP_HIGH', "calibration", 3, 3, 23, "uint:9", 9, 2, "adc_tp", "ADC2 850mV reading", None), + ] + + CALC = [ + ("WAFER_VERSION_MAJOR", "identity", 0, None, None, "uint:3", None, None, "wafer", "calc WAFER VERSION MAJOR from CHIP_VER_REV1 and CHIP_VER_REV2 and apb_ctl_date (read only)", None), + ('PKG_VERSION', "identity", 0, None, None, "uint:4", None, None, "pkg", "calc Chip package = CHIP_PACKAGE_4BIT << 3 + CHIP_PACKAGE (read only)", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py new file mode 100644 index 000000000..421bc186a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python +# +# This file includes the operations with eFuses for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + p = subparsers.add_parser( + "burn_key", + help="Burn a 256-bit key to EFUSE: %s" % ", ".join(efuses.BLOCKS_FOR_KEYS), + ) + p.add_argument( + "--no-protect-key", + help="Disable default read- and write-protecting of the key. " + "If this option is not set, once the key is flashed " + "it cannot be read back or changed.", + action="store_true", + ) + add_force_write_always(p) + p.add_argument( + "block", + help='Key block to burn. "flash_encryption" (block1), ' + '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + p.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + for _ in efuses.BLOCKS_FOR_KEYS: + p.add_argument( + "block", + help='Key block to burn. "flash_encryption" (block1), ' + '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', + metavar="BLOCK", + nargs="?", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + p.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + metavar="KEYFILE", + nargs="?", + action="append", + type=argparse.FileType("rb"), + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest " + "to eFuse for use with Secure Boot V2", + ) + burn_key_digest.add_argument( + "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") + ) + burn_key_digest.add_argument( + "--no-protect-key", + help="Disable default write-protecting of the key digest. " + "If this option is not set, once the key is flashed it cannot be changed.", + action="store_true", + ) + add_force_write_always(burn_key_digest) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or low at reset " + "without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format " + "with bytes separated by colons " + "(e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + # Writing to BLK3: + # - MAC_VERSION = 1 + # - CUSTOM_MAC = AA:CD:EF:01:02:03 + # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + version = efuses["MAC_VERSION"].get() + if version > 0: + print( + "Custom MAC Address version {}: {}".format( + version, efuses["CUSTOM_MAC"].get() + ) + ) + else: + print("Custom MAC Address is not set in the device.") + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["XPD_SDIO_FORCE"] + sdio_tieh = efuses["XPD_SDIO_TIEH"] + sdio_reg = efuses["XPD_SDIO_REG"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SDIO). " + "SPI flash will need to be powered from an external source.\n" + "The following efuse is burned: XPD_SDIO_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n" + "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse XPD_SDIO_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n" + "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH." + print(msg) + sdio_force.save(1) # Disable GPIO12 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SDIO setting complete.") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + adc_vref = efuses["ADC_VREF"] + blk3_reserve = efuses["BLK3_PART_RESERVE"] + + vref_raw = adc_vref.get_raw() + if vref_raw == 0: + print("ADC VRef calibration: None (1100mV nominal)") + else: + print("ADC VRef calibration: %dmV" % adc_vref.get()) + + if blk3_reserve.get(): + print("ADC readings stored in efuse BLOCK3:") + print(" ADC1 Low reading (150mV): %d" % efuses["ADC1_TP_LOW"].get()) + print(" ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get()) + print(" ADC2 Low reading (150mV): %d" % efuses["ADC2_TP_LOW"].get()) + print(" ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get()) + + +def burn_key(esp, efuses, args): + datafile_list = args.keyfile[ + 0 : len([keyfile for keyfile in args.keyfile if keyfile is not None]) : + ] + block_name_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + efuses.force_write_always = args.force_write_always + no_protect_key = args.no_protect_key + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list): + raise esptool.FatalError( + "The number of blocks (%d) and datafile (%d) should be the same." + % (len(block_name_list), len(datafile_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile in zip(block_name_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + data = datafile.read() + revers_msg = None + if block_name in ("flash_encryption", "secure_boot_v1"): + revers_msg = "\tReversing the byte order" + data = data[::-1] + print(" - %s -> [%s]" % (efuse.name, util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. " + "Key file must be %d bytes (%d bits) of raw binary key data." + % (len(data), num_bytes, num_bytes * 8) + ) + + efuse.save(data) + + if block_name in ("flash_encryption", "secure_boot_v1"): + if not no_protect_key: + print("\tDisabling read to key block") + efuse.disable_read() + + if not no_protect_key: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if args.no_protect_key: + print("Key is left unprotected as per --no-protect-key argument.") + + msg = "Burn keys in efuse blocks.\n" + if no_protect_key: + msg += ( + "The key block will left readable and writeable (due to --no-protect-key)" + ) + else: + msg += "The key block will be read and write protected " + "(no further changes or readback)" + print(msg, "\n") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + if efuses.coding_scheme == efuses.REGS.CODING_SCHEME_34: + raise esptool.FatalError("burn_key_digest only works with 'None' coding scheme") + + chip_revision = esp.get_chip_revision() + if chip_revision < 300: + raise esptool.FatalError( + "Incorrect chip revision for Secure boot v2. " + "Detected: v%d.%d. Expected: >= v3.0" + % (chip_revision / 100, chip_revision % 100) + ) + + digest = espsecure._digest_sbv2_public_key(args.keyfile) + efuse = efuses["BLOCK2"] + num_bytes = efuse.bit_len // 8 + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. " + "Digest must be %d bytes (%d bits) of raw binary key data." + % (len(digest), num_bytes, num_bytes * 8) + ) + print(" - %s -> [%s]" % (efuse.name, util.hexify(digest, " "))) + + efuse.save(digest) + if not args.no_protect_key: + print("Disabling write to efuse %s..." % (efuse.name)) + efuse.disable_write() + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py new file mode 100644 index 000000000..43e714c9a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from bitstring import BitString + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-C2" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 1 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data + + def check_rd_protection_area(self): + # checks fields which have the read protection bits. + # if the read protection bit is set then we need to reset this field to 0. + + def get_read_disable_mask(blk): + mask = 0 + if isinstance(blk.read_disable_bit, list): + for i in blk.read_disable_bit: + mask |= 1 << i + else: + mask = 1 << blk.read_disable_bit + return mask + + read_disable_bit = self.read_field("RD_DIS", bitstring=False) + for b in self.Blocks.BLOCKS: + blk = self.Blocks.get(b) + block = self.read_block(blk.id) + if ( + blk.read_disable_bit is not None + and read_disable_bit & get_read_disable_mask(blk) + ): + if isinstance(blk.read_disable_bit, list): + if read_disable_bit & (1 << blk.read_disable_bit[0]): + block.set( + 0, [i for i in range(blk.len * 32 // 2, blk.len * 32)] + ) + if read_disable_bit & (1 << blk.read_disable_bit[1]): + block.set(0, [i for i in range(0, blk.len * 32 // 2)]) + else: + block.set(0) + else: + for e in self.Fields.EFUSES: + field = self.Fields.get(e) + if ( + blk.id == field.block + and field.read_disable_bit is not None + and read_disable_bit & get_read_disable_mask(field) + ): + raw_data = self.read_field(field.name) + raw_data.set(0) + block.pos = block.length - ( + field.word * 32 + field.pos + raw_data.length + ) + block.overwrite(BitString(raw_data.length)) + self.overwrite_mem_from_block(blk, block) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py new file mode 100644 index 000000000..fc634ac26 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py @@ -0,0 +1,417 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-C2": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-C2 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MINOR"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + xtal_freq = self.get_crystal_freq() + if xtal_freq not in [26, 40]: + raise esptool.FatalError( + "The eFuse supports only xtal=26M and 40M (xtal was %d)" % xtal_freq + ) + + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + tpgm_inactive_val = 200 if xtal_freq == 40 else 130 + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, + self.REGS.EFUSE_TPGM_INACTIVE_M, + tpgm_inactive_val, + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR_REG + offs * 4) + for offs in range(1) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None), # User purposes (software-only use) + ( + "XTS_AES_128_KEY", + 1, + None, + ), # (whole 256bits) XTS_AES_128_KEY (flash/PSRAM encryption) + ( + "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", + 2, + None, + ), # (lo 128bits) XTS_AES_128_KEY (flash/PSRAM encryption) + ( + "SECURE_BOOT_DIGEST", + 3, + "DIGEST", + ), # (hi 128bits)SECURE_BOOT_DIGEST (Secure Boot key digest) + ] + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py new file mode 100644 index 000000000..9540a3290 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x60008800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_PGM_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x88 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x8C + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x90 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x94 + EFUSE_RD_REPEAT_ERR_REG = DR_REG_EFUSE_BASE + 0x80 + EFUSE_RD_RS_ERR_REG = DR_REG_EFUSE_BASE + 0x84 + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR_REG, 0x7, 0, 3), # BLOCK1 + (EFUSE_RD_RS_ERR_REG, 0x7, 4, 7), # BLOCK2 + (EFUSE_RD_RS_ERR_REG, 0x7, 8, 11), # BLOCK3 + ] + + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x118 + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + EFUSE_WR_TIM_CONF0_REG = DR_REG_EFUSE_BASE + 0x110 + EFUSE_TPGM_INACTIVE_S = 8 + EFUSE_TPGM_INACTIVE_M = 0xFF << EFUSE_TPGM_INACTIVE_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", ["BLOCK0"], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 2, None), + ("BLOCK1", ["BLOCK1"], 1, __base_rd_regs + 0x034, __base_wr_regs, 5, None, 3, None), + ("BLOCK2", ["BLOCK2"], 2, __base_rd_regs + 0x040, __base_wr_regs, 6, None, 8, None), + ("BLOCK_KEY0", ["BLOCK3"], 3, __base_rd_regs + 0x060, __base_wr_regs, 7, [0, 1], 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + def get_blocks_for_keys(self): + return ['BLOCK_KEY0'] + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:8", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:2", 0, None, None, "Disables software reading from BLOCK3", None), + ("WDT_DELAY_SEL", "WDT config", 0, 1, 2, "uint:2", 1, None, None, "RTC WDT timeout threshold", None), + ("DIS_PAD_JTAG", "jtag config", 0, 1, 4, "bool", 1, None, None, "Permanently disable JTAG access via pads" + "USB JTAG is controlled separately", None), + ("DIS_DOWNLOAD_ICACHE", "security", 0, 1, 5, "bool", 1, None, None, "Disables iCache in download mode", None), + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 6, "bool", 2, None, None, "Disables flash encryption in Download boot modes", + None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 1, 7, "uint:3", 2, None, None, "Enables encryption and decryption, when an SPI boot" + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("XTS_KEY_LENGTH_256", "security", 0, 1, 10, "bool", 2, None, None, "Flash encryption key length", + {0: "128 bits key", + 1: "256 bits key"}), + ("UART_PRINT_CONTROL", "config", 0, 1, 11, "uint:2", 3, None, None, "Set UART boot message output mode", + {0: "Force print", + 1: "Low-level print controlled by GPIO 8", + 3: "High-level print controlled by GPIO 8", + 7: "Print force disabled"}), + ("FORCE_SEND_RESUME", "config", 0, 1, 13, "bool", 3, None, None, "Force ROM code to send a resume cmd during SPI boot", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 1, 14, "bool", 3, None, None, "Disables all Download boot modes", None), + ("DIS_DIRECT_BOOT", "config", 0, 1, 15, "bool", 3, None, None, "Disable direct_boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 1, 16, "bool", 3, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("FLASH_TPUW", "flash config", 0, 1, 17, "uint:4", 3, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("SECURE_BOOT_EN", "security", 0, 1, 21, "bool", 2, None, None, "Configures secure boot", None), + ("SECURE_VERSION", "identity", 0, 1, 22, "uint:4", 4, None, "bitcount", "Secure version (anti-rollback feature)", None), + ("CUSTOM_MAC_USED", "identity", 0, 1, 26, "bool", 4, None, None, "Enable CUSTOM_MAC programming", None), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 1, 27, "bool", 4, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 1, 28, "bool", 4, None, None, "Disables check of blk version major", None), + + # + # Parameters in BLOCK1 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("CUSTOM_MAC", "identity", 1, 0, 0, "bytes:6", 5, None, 'mac', "Custom MAC addr", None), + + # + # Parameters in BLOCK2 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 2, 0, 0, "bytes:6", 6, None, 'mac', "Factory MAC Address", None), + ("WAFER_VERSION_MINOR", "identity", 2, 1, 16, "uint:4", 6, None, None, "Minor WAFER version", None), + ("WAFER_VERSION_MAJOR", "identity", 2, 1, 20, "uint:2", 6, None, None, "Major WAFER version", None), + ("PKG_VERSION", "identity", 2, 1, 22, "uint:3", 6, None, None, "Package version", None), + ("BLK_VERSION_MINOR", "identity", 2, 1, 25, "uint:3", 6, None, None, "Minor version of BLOCK2", + {0: "No calibration", 1: "With calibration"}), + + ("BLK_VERSION_MAJOR", "identity", 2, 1, 28, "uint:2", 6, None, None, "Major version of BLOCK2", None), + ("LDO_VOL_BIAS_CONFIG_HIGH", "ldo", 2, 2, 0, "uint:27", 6, None, None, "", None), + ("PVT_LOW", "pvt", 2, 2, 27, "uint:5", 6, None, None, "", None), + ("PVT_HIGH", "pvt", 2, 3, 0, "uint:10", 6, None, None, "", None), + ("ADC_CALIBRATION_0", "adc_calib", 2, 3, 10, "uint:22", 6, None, None, "", None), + ("ADC_CALIBRATION_1", "adc_calib", 2, 4, 0, "uint:32", 6, None, None, "", None), + ("ADC_CALIBRATION_2", "adc_calib", 2, 5, 0, "uint:32", 6, None, None, "", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_KEY0', "security", 3, 0, 0, "bytes:32", 7, [0, 1], "keyblock", "BLOCK_KEY0 - 256-bits. 256-bit key of Flash Encryption", None), + ('BLOCK_KEY0_LOW_128', "security", 3, 0, 0, "bytes:16", 7, 0, "keyblock", "BLOCK_KEY0 - lower 128-bits. 128-bit key of Flash Encryption", None), + ('BLOCK_KEY0_HI_128', "security", 3, 4, 0, "bytes:16", 7, 1, "keyblock", "BLOCK_KEY0 - higher 128-bits. 128-bits key of Secure Boot.", None), + ] + + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py new file mode 100644 index 000000000..e3297efc8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py @@ -0,0 +1,349 @@ +#!/usr/bin/env python +# +# This file includes the operations with eFuses for ESP32-C2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software.", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 128/256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in range(1): + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 128/256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse an ECDSA public key and burn the digest " + "to higher 128-bits of BLOCK_KEY0", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " + "at reset without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK1." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format " + "with bytes separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + efuses["CUSTOM_MAC_USED"].save(1) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MINOR"].get() == 1: + print(" RF_REF_I_BIAS_CONFIG: {}".format(efuses["RF_REF_I_BIAS_CONFIG"].get())) + + print(" LDO_VOL_BIAS_CONFIG_LOW: {}".format(efuses["LDO_VOL_BIAS_CONFIG_LOW"].get())) + print(" LDO_VOL_BIAS_CONFIG_HIGH: {}".format(efuses["LDO_VOL_BIAS_CONFIG_HIGH"].get())) + + print(" PVT_LOW: {}".format(efuses["PVT_LOW"].get())) + print(" PVT_HIGH: {}".format(efuses["PVT_HIGH"].get())) + + print(" ADC_CALIBRATION_0: {}".format(efuses["ADC_CALIBRATION_0"].get())) + print(" ADC_CALIBRATION_1: {}".format(efuses["ADC_CALIBRATION_1"].get())) + print(" ADC_CALIBRATION_2: {}".format(efuses["ADC_CALIBRATION_2"].get())) + + else: + print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(keypurpose_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and " + "keypurpose (%d) should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + assert 1 <= len(block_name_list) <= 2, "Unexpected case" + + if len(block_name_list) == 2: + incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False + permitted_purposes = [ + "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", + "SECURE_BOOT_DIGEST", + ] + incompatible |= ( + keypurpose_list[0] in permitted_purposes + and keypurpose_list[1] not in permitted_purposes + ) + if incompatible: + raise esptool.FatalError( + "These keypurposes are incompatible %s" % (keypurpose_list) + ) + + print("Burn keys to blocks:") + for datafile, keypurpose in zip(datafile_list, keypurpose_list): + data = datafile if isinstance(datafile, bytes) else datafile.read() + + if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS": + efuse = efuses["BLOCK_KEY0_LOW_128"] + elif keypurpose == "SECURE_BOOT_DIGEST": + efuse = efuses["BLOCK_KEY0_HI_128"] + if len(data) == 32: + print( + "\tProgramming only left-most 128-bits from SHA256 hash of " + "public key to highest 128-bits of BLOCK KEY0" + ) + data = data[:16] + elif len(data) != efuse.bit_len // 8: + raise esptool.FatalError( + "Wrong length of this file for SECURE_BOOT_DIGEST. " + "Got %d (expected %d or %d)" % (len(data), 32, efuse.bit_len // 8) + ) + assert len(data) == 16, "Only 16 bytes expected" + else: + efuse = efuses["BLOCK_KEY0"] + + num_bytes = efuse.bit_len // 8 + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if keypurpose.startswith("XTS_AES_"): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. " + "Key file must be %d bytes (%d bits) of raw binary key data." + % (len(data), num_bytes, num_bytes * 8) + ) + + if keypurpose.startswith("XTS_AES_"): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage + # of checking it as the whole field. + efuse.save(data) + + if keypurpose == "XTS_AES_128_KEY": + if efuses["XTS_KEY_LENGTH_256"].get(): + print("\t'XTS_KEY_LENGTH_256' is already '1'") + else: + print("\tXTS_KEY_LENGTH_256 -> 1") + efuses["XTS_KEY_LENGTH_256"].save(1) + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + datafile = args.keyfile + args.keypurpose = ["SECURE_BOOT_DIGEST"] + args.block = ["BLOCK_KEY0"] + digest = espsecure._digest_sbv2_public_key(datafile) + digest = digest[:16] + num_bytes = efuses["BLOCK_KEY0_HI_128"].bit_len // 8 + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. " + "Digest must be %d bytes (%d bits) of raw binary key data." + % (len(digest), num_bytes, num_bytes * 8) + ) + burn_key(esp, efuses, args, digest=[digest]) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py new file mode 100644 index 000000000..d65ac0293 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-C3" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 4 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py new file mode 100644 index 000000000..29088db6c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py @@ -0,0 +1,466 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-C3": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-C3 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg_f, fail_bit = self.REGS.BLOCK_FAIL_BIT[block.id] + if fail_bit is None: + block.fail = False + else: + block.fail = self.read_reg(addr_reg_f) & (1 << fail_bit) != 0 + + addr_reg_n, num_mask, num_offs = self.REGS.BLOCK_NUM_ERRORS[block.id] + if num_mask is None or num_offs is None: + block.num_errors = 0 + else: + block.num_errors = ( + self.read_reg(addr_reg_n) >> num_offs + ) & num_mask + + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py new file mode 100644 index 000000000..d8a4b2bd8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x60008800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + # this chip has a design error so fail_bit is shifted by one block but err_num is in the correct place + BLOCK_FAIL_BIT = [ + # error_reg, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 7), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 11), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 15), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 19), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 23), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 27), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 31), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR1_REG, 3), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 7), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, None), # BLOCK_SYS_DATA2 + ] + + BLOCK_NUM_ERRORS = [ + # error_reg, err_num_mask, err_num_offs + (EFUSE_RD_REPEAT_ERR0_REG, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Table 51: Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), + ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), + ("DIS_USB_JTAG", "usb config", 0, 1, 9, "bool", 2, None, None, "Disables USB JTAG. " + "JTAG access via pads is controlled separately", None), + ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), + ("DIS_USB_DEVICE", "usb config", 0, 1, 11, "bool", 2, None, None, "Disables USB DEVICE", None), + ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), + ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), + ("SOFT_DIS_JTAG", "jtag config", 0, 1, 16, "uint:3", 2, None, None, "Software disables JTAG. When software disabled, " + "JTAG can be activated temporarily by HMAC peripheral", + None), + ("DIS_PAD_JTAG", "jtag config", 0, 1, 19, "bool", 2, None, None, "Permanently disable JTAG access via pads. " + "USB JTAG is controlled separately.", None), + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", + None), + ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), + ("VDD_SPI_AS_GPIO", "config", 0, 1, 26, "bool", 30, None, None, "Set this bit to vdd spi pin function as gpio", None), + ("BTLC_GPIO_ENABLE", "config", 0, 1, 27, "uint:2", 30, None, None, "Enable btlc gpio", None), + ("POWERGLITCH_EN", "config", 0, 1, 29, "bool", 30, None, None, "Set this bit to enable power glitch function", None), + ("POWER_GLITCH_DSENSE", "config", 0, 1, 30, "uint:2", 30, None, None, "Sample delay configuration of power glitch", None), + ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "bool", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), + ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), + ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), + ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), + ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), + ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), + ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), + ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), + ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), + ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), + ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", + None), + ("FLASH_TPUW", "flash config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), + ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), + ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), + ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "usb config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " + "UART download boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", + {0: "Enabled", + 1: "Enable when GPIO8 is low at reset", + 2: "Enable when GPIO8 is high at reset", + 3: "Disabled"}), + ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Force ROM code to send a resume command during SPI boot" + "during SPI boot", None), + ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", + None), + ("ERR_RST_ENABLE", "config", 0, 4, 31, "bool", 19, None, None, "Use BLOCK0 to check error record registers", + {0: "without check", + 1: "with check"}), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), + # + # Table 53: Parameters in BLOCK1-10 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), + ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), + ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), + ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), + ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), + ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), + ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), + ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), + ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), + ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), + ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), + ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), + + ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), + ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), + ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), + ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), + ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), + + ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), + ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", + {0: "No calibration", + 1: "With calibration"}), + ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), + ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), + ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), + ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), + ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), + ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), + ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), + ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, "keyblock", "System data (part 2)", None), + ] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), + ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), + ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), + ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), + ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), + ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), + ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), + ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), + ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), + ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), + ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), + ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), + ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), + ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), + ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), + ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), + ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), + ] + + CALC = [ + ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py new file mode 100644 index 000000000..efbbdcbee --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python +# +# This file includes the operations with eFuses for ESP32-C3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' " + "because write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py new file mode 100644 index 000000000..35d316189 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-C6" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py new file mode 100644 index 000000000..9e652596b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py @@ -0,0 +1,466 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-C6": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-C6 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg_f, fail_bit = self.REGS.BLOCK_FAIL_BIT[block.id] + if fail_bit is None: + block.fail = False + else: + block.fail = self.read_reg(addr_reg_f) & (1 << fail_bit) != 0 + + addr_reg_n, num_mask, num_offs = self.REGS.BLOCK_NUM_ERRORS[block.id] + if num_mask is None or num_offs is None: + block.num_errors = 0 + else: + block.num_errors = ( + self.read_reg(addr_reg_n) >> num_offs + ) & num_mask + + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py new file mode 100644 index 000000000..ab0c8b500 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x600B0800 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + # this chip has a design error so fail_bit is shifted by one block but err_num is in the correct place + BLOCK_FAIL_BIT = [ + # error_reg, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 7), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 11), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 15), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 19), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 23), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 27), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 31), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR1_REG, 3), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 7), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, None), # BLOCK_SYS_DATA2 + ] + + BLOCK_NUM_ERRORS = [ + # error_reg, err_num_mask, err_num_offs + (EFUSE_RD_REPEAT_ERR0_REG, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Table 51: Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), + ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), + ("DIS_USB_JTAG", "usb config", 0, 1, 9, "bool", 2, None, None, "Disables USB JTAG. " + "JTAG access via pads is controlled separately", None), + ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), + ("DIS_USB_DEVICE", "usb config", 0, 1, 11, "bool", 2, None, None, "Disables USB DEVICE", None), + ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), + ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), + ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), + ("JTAG_SEL_ENABLE", "jtag config", 0, 1, 15, "bool", 2, None, None, "Set this bit to enable selection between " + "usb_to_jtag and pad_to_jtag through strapping " + "gpio10 when both reg_dis_usb_jtag and " + "reg_dis_pad_jtag are equal to 0.", None), + ("SOFT_DIS_JTAG", "jtag config", 0, 1, 16, "uint:3", 2, None, None, "Software disables JTAG. When software disabled, " + "JTAG can be activated temporarily by HMAC peripheral", + None), + ("DIS_PAD_JTAG", "jtag config", 0, 1, 19, "bool", 2, None, None, "Permanently disable JTAG access via pads. " + "USB JTAG is controlled separately.", None), + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", + None), + ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), + ("VDD_SPI_AS_GPIO", "config", 0, 1, 26, "bool", 30, None, None, "Set this bit to vdd spi pin function as gpio", None), + ("BTLC_GPIO_ENABLE", "config", 0, 1, 27, "uint:2", 30, None, None, "Enable btlc gpio", None), + ("POWERGLITCH_EN", "config", 0, 1, 29, "bool", 30, None, None, "Set this bit to enable power glitch function", None), + ("POWER_GLITCH_DSENSE", "config", 0, 1, 30, "uint:2", 30, None, None, "Sample delay configuration of power glitch", None), + ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "bool", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), + ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), + ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), + ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), + ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), + ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), + ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), + ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), + ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), + ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), + ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", + None), + ("FLASH_TPUW", "flash config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), + ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), + ("UART_PRINT_CHANNEL", "config", 0, 4, 2, "bool", 18, None, None, "Selects the default UART for printing boot msg", + {0: "UART0", + 1: "UART1"}), + ("FLASH_ECC_MODE", "flash config", 0, 4, 3, "bool", 18, None, None, "Set this bit to set flsah ecc mode.", + {0: "flash ecc 16to18 byte mode", + 1: "flash ecc 16to17 byte mode"}), + ("DIS_USB_DOWNLOAD_MODE", "usb config", 0, 4, 4, "bool", 18, None, None, "Disables use of USB in UART download boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", + {0: "Enabled", + 1: "Enable when GPIO8 is low at reset", + 2: "Enable when GPIO8 is high at reset", + 3: "Disabled"}), + ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "GPIO33-GPIO37 power supply selection in ROM code", + {0: "VDD3P3_CPU", + 1: "VDD_SPI"}), + ("FLASH_TYPE", "flash config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", + {0: "4 data lines", + 1: "8 data lines"}), + ("FLASH_PAGE_SIZE", "flash config", 0, 4, 10, "uint:2", 18, None, None, "Flash page size", None), + ("FLASH_ECC_EN", "flash config", 0, 4, 12, "bool", 18, None, None, "Enable ECC for flash boot", None), + ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Force ROM code to send a resume command during SPI boot" + "during SPI boot", None), + ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", + None), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), + # + # Table 53: Parameters in BLOCK1-10 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), + ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), + ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), + ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), + ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), + ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), + ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), + ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), + ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), + ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), + ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), + ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), + + ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), + ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), + ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), + ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), + ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), + + ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), + ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", + {0: "No calibration", + 1: "With calibration"}), + ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), + ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), + ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), + ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), + ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), + ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), + ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), + ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, "keyblock", "System data (part 2)", None), + ] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), + ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), + ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), + ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), + ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), + ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), + ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), + ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), + ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), + ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), + ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), + ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), + ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), + ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), + ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), + ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), + ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), + ] + + CALC = [ + ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py new file mode 100644 index 000000000..b4b2cff65 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python +# +# This file includes the operations with eFuses for ESP32-C6 chip +# +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support " + "post-write data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' " + "because write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py new file mode 100644 index 000000000..4721d4e6b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-H2(beta1)" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 32 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py new file mode 100644 index 000000000..49b76c21c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py @@ -0,0 +1,454 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-H2(beta1)": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-H2(beta1) chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLOCK2_VERSION"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 32: + raise esptool.FatalError( + "The eFuse supports only xtal=32M (xtal was %d)" % apb_freq + ) + + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" + return "" + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + self.parent["MAC_EXT"].get_raw( + from_read + ) + elif self.name == "MAC": + mac = self.get_raw(from_read) + self.parent["MAC_EXT"].get_raw(from_read) + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py new file mode 100644 index 000000000..f3c123d60 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x6001A000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Table 51: Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), + ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), + ("DIS_USB_JTAG", "usb config", 0, 1, 9, "bool", 2, None, None, "Disables USB JTAG. " + "JTAG access via pads is controlled separately", None), + ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), + ("DIS_USB_DEVICE", "usb config", 0, 1, 11, "bool", 2, None, None, "Disables USB DEVICE", None), + ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), + ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), + ("JTAG_SEL_ENABLE", "jtag config", 0, 1, 15, "bool", 2, None, None, "Set this bit to enable selection between " + "usb_to_jtag and pad_to_jtag through strapping " + "gpio10 when both reg_dis_usb_jtag and " + "reg_dis_pad_jtag are equal to 0.", None), + ("SOFT_DIS_JTAG", "jtag config", 0, 1, 16, "uint:3", 2, None, None, "Software disables JTAG. When software disabled, " + "JTAG can be activated temporarily by HMAC peripheral", + None), + ("DIS_PAD_JTAG", "jtag config", 0, 1, 19, "bool", 2, None, None, "Permanently disable JTAG access via pads. " + "USB JTAG is controlled separately.", None), + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", + None), + ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), + ("VDD_SPI_AS_GPIO", "config", 0, 1, 26, "bool", 30, None, None, "Set this bit to vdd spi pin function as gpio", None), + ("BTLC_GPIO_ENABLE", "config", 0, 1, 27, "uint:2", 30, None, None, "Enable btlc gpio", None), + ("POWERGLITCH_EN", "config", 0, 1, 29, "bool", 30, None, None, "Set this bit to enable power glitch function", None), + ("POWER_GLITCH_DSENSE", "config", 0, 1, 30, "uint:2", 30, None, None, "Sample delay configuration of power glitch", None), + ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "bool", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), + ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), + ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), + ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), + ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), + ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), + ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), + ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), + ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), + ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), + ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", + None), + ("FLASH_TPUW", "flash config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), + ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), + ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), + ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "usb config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " + "UART download boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", + {0: "Enabled", + 1: "Enable when GPIO8 is low at reset", + 2: "Enable when GPIO8 is high at reset", + 3: "Disabled"}), + ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Force ROM code to send a resume command during SPI boot" + "during SPI boot", None), + ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", + None), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), + # + # Table 53: Parameters in BLOCK1-10 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), + ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), + ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), + ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), + ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), + ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), + ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), + ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), + ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), + ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), + ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), + ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), + ("WAFER_VERSION", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER version", + {0: "(revision 0)", 1: "(revision 1)"}), + ("PKG_VERSION", "identity", 1, 3, 21, "uint:4", 20, None, None, "Package version", + {0: "ESP32-H2(beta1)"}), + ("BLOCK1_VERSION", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK1 efuse version", None), + ("MAC_EXT", "identity", 1, 3, 27, "bytes:2", 20, None, "mac", "MAC extension", None), + ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), + ("BLOCK2_VERSION", "identity", 2, 4, 4, "uint:3", 21, None, None, "Version of BLOCK2", + {0: "No calibration", + 1: "With calibration"}), + ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), + ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), + ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), + ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), + ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), + ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), + ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), + ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, "keyblock", "System data (part 2)", None), + ] + + # if BLOCK2_VERSION is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), + ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), + ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), + ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), + ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), + ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), + ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), + ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), + ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), + ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), + ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), + ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), + ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), + ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), + ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), + ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), + ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py new file mode 100644 index 000000000..744df5e08 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py @@ -0,0 +1,414 @@ +#!/usr/bin/env python +# +# This file includes the operations with eFuses for ESP32-H2 chip +# +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] will remain " + "readable anyway. For the rest keypurposes the read-protection will be " + "defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " + "at reset without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03). " + "Final CUSTOM_MAC = CUSTOM_MAC[48] + MAC_EXT[16]", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + raise esptool.FatalError("set_flash_voltage is not supported!") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLOCK2_VERSION"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLOCK2_VERSION = {}".format(efuses["BLOCK2_VERSION"].get_meaning())) + # fmt: on + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because write " + "protection bit is set." % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) of raw " + "binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py new file mode 100644 index 000000000..241c79c00 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-S2 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-S2" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 1 + + def get_minor_chip_version(self): + return 0 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": None, + "api_version": None, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py new file mode 100644 index 000000000..87120e40c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py @@ -0,0 +1,524 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32S2 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-S2": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-S2 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MINOR"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + ( + EFUSE_TSUP_A, + EFUSE_TPGM, + EFUSE_THP_A, + EFUSE_TPGM_INACTIVE, + ) = self.REGS.EFUSE_PROGRAMMING_TIMING_PARAMETERS[apb_freq] + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_TSUP_A_M, EFUSE_TSUP_A + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_TPGM_M, EFUSE_TPGM + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_THP_A_M, EFUSE_THP_A + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF0_REG, + self.REGS.EFUSE_TPGM_INACTIVE_M, + EFUSE_TPGM_INACTIVE, + ) + + ( + EFUSE_DAC_CLK_DIV, + EFUSE_PWR_ON_NUM, + EFUSE_PWR_OFF_NUM, + ) = self.REGS.VDDQ_TIMING_PARAMETERS[apb_freq] + self.update_reg( + self.REGS.EFUSE_DAC_CONF_REG, + self.REGS.EFUSE_DAC_CLK_DIV_M, + EFUSE_DAC_CLK_DIV, + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF1_REG, + self.REGS.EFUSE_PWR_ON_NUM_M, + EFUSE_PWR_ON_NUM, + ) + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, + self.REGS.EFUSE_PWR_OFF_NUM_M, + EFUSE_PWR_OFF_NUM, + ) + + EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A = self.REGS.EFUSE_READING_PARAMETERS[ + apb_freq + ] + # self.update_reg( + # self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TSUR_A_M, EFUSE_TSUR_A + # ) + self.update_reg( + self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TRD_M, EFUSE_TRD + ) + self.update_reg( + self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_THR_A_M, EFUSE_THR_A + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + if self["VDD_SPI_FORCE"].get() == 0: + output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " + "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" + output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " + "VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V)." + elif self["VDD_SPI_XPD"].get() == 0: + output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." + elif self["VDD_SPI_TIEH"].get() == 0: + output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py new file mode 100644 index 000000000..1d9e8c4cf --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x3f41A000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1c8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1cc + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1d0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1d4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x194 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x198 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1e8 + EFUSE_DAC_CLK_DIV_S = 0 + EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S + + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_TSUR_A_S = 16 + EFUSE_TSUR_A_M = 0xFF << EFUSE_TSUR_A_S + EFUSE_TRD_S = 8 + EFUSE_TRD_M = 0xFF << EFUSE_TRD_S + EFUSE_THR_A_S = 0 + EFUSE_THR_A_M = 0xFF << EFUSE_THR_A_S + + EFUSE_WR_TIM_CONF0_REG = DR_REG_EFUSE_BASE + 0x1F0 + EFUSE_TPGM_S = 16 + EFUSE_TPGM_M = 0xFFFF << EFUSE_TPGM_S + EFUSE_TPGM_INACTIVE_S = 8 + EFUSE_TPGM_INACTIVE_M = 0xFF << EFUSE_TPGM_INACTIVE_S + EFUSE_THP_A_S = 0 + EFUSE_THP_A_M = 0xFF << EFUSE_THP_A_S + + # EFUSE_WR_TIM_CONF1_REG + EFUSE_PWR_ON_NUM_S = 8 + EFUSE_PWR_ON_NUM_M = 0xFFFF << EFUSE_PWR_ON_NUM_S + EFUSE_TSUP_A_S = 0 + EFUSE_TSUP_A_M = 0xFF << EFUSE_TSUP_A_S + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + # Configure clock + EFUSE_PROGRAMMING_TIMING_PARAMETERS = { + # APB Frequency: ( EFUSE_TSUP_A, EFUSE_TPGM, EFUSE_THP_A, EFUSE_TPGM_INACTIVE ) + # Taken from TRM chapter "eFuse Controller": eFuse-Programming Timing + 80: (0x2, 0x320, 0x2, 0x4), + 40: (0x1, 0x190, 0x1, 0x2), + 20: (0x1, 0xC8, 0x1, 0x1), + } + + VDDQ_TIMING_PARAMETERS = { + # APB Frequency: ( EFUSE_DAC_CLK_DIV, EFUSE_PWR_ON_NUM, EFUSE_PWR_OFF_NUM ) + # Taken from TRM chapter "eFuse Controller": eFuse VDDQ Timing Setting + 80: (0xA0, 0xA200, 0x100), + 40: (0x50, 0x5100, 0x80), + 20: (0x28, 0x2880, 0x40), + } + + EFUSE_READING_PARAMETERS = { + # APB Frequency: ( EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A ) + # Taken from TRM chapter "eFuse Controller": eFuse-Read Timing + 80: (0x2, 0x4, 0x2), + 40: (0x1, 0x2, 0x1), + 20: (0x1, 0x1, 0x1), + } + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Table 51: Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), + ("DIS_RTC_RAM_BOOT", "config", 0, 1, 7, "bool", 1, None, None, "Disables boot from RTC RAM", None), + ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), + ("DIS_DCACHE", "config", 0, 1, 9, "bool", 2, None, None, "Disables DCache", None), + ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), + ("DIS_DOWNLOAD_DCACHE", "config", 0, 1, 11, "bool", 2, None, None, "Disables Dcache when SoC is in Download mode", None), + ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), + ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), + ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), + ("DIS_BOOT_REMAP", "config", 0, 1, 15, "bool", 2, None, None, "Disables capability to Remap RAM to ROM address space", + None), + ("SOFT_DIS_JTAG", "security", 0, 1, 17, "bool", 2, None, None, "Software disables JTAG. When software disabled, " + "JTAG can be activated temporarily by HMAC peripheral", + None), + ("HARD_DIS_JTAG", "security", 0, 1, 18, "bool", 2, None, None, "Hardware disables JTAG permanently", None), + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 19, "bool", 2, None, None, "Disables flash encryption when in download boot modes", + None), + ("USB_EXCHG_PINS", "usb config", 0, 1, 24, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), + ("EXT_PHY_ENABLE", "usb config", 0, 1, 25, "bool", 30, None, None, "Enables external USB PHY", None), + ("USB_FORCE_NOPERSIST", "usb config", 0, 1, 26, "bool", 30, None, None, "Forces to set USB BVALID to 1", None), + ("BLOCK0_VERSION", "identity", 0, 1, 27, "uint:2", 30, None, None, "BLOCK0 efuse version", None), + ("VDD_SPI_FORCE", "VDD_SPI config", 0, 2, 6, "bool", 3, None, None, "Force using VDD_SPI_XPD and VDD_SPI_TIEH " + "to configure VDD_SPI LDO", None), + ("VDD_SPI_XPD", "VDD_SPI config", 0, 2, 4, "bool", 3, None, None, "The VDD_SPI regulator is powered on", None), + ("VDD_SPI_TIEH", "VDD_SPI config", 0, 2, 5, "bool", 3, None, None, "The VDD_SPI power supply voltage at reset", + {0: "Connect to 1.8V LDO", + 1: "Connect to VDD3P3_RTC_IO"}), + ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "uint:2", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), + ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), + ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), + ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), + ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), + ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), + ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), + ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), + ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), + ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), + ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", + None), + ("FLASH_TPUW", "config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), + ("DIS_LEGACY_SPI_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables Legacy SPI boot mode", None), + ("UART_PRINT_CHANNEL", "config", 0, 4, 2, "bool", 18, None, None, "Selects the default UART for printing boot msg", + + {0: "UART0", + 1: "UART1"}), + ("DIS_USB_DOWNLOAD_MODE", "config", 0, 4, 4, "bool", 18, None, None, "Disables use of USB in UART download boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", + + {0: "Enabled", + 1: "Enable when GPIO 46 is low at reset", + 2: "Enable when GPIO 46 is high at rest", + 3: "Disabled"}), + ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "Sets default power supply for GPIO33..37, " + "set when SPI flash is initialized", + + {0: "VDD3P3_CPU", + 1: "VDD_SPI"}), + ("FLASH_TYPE", "config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", + + {0: "4 data lines", + 1: "8 data lines"}), + ("FORCE_SEND_RESUME", "config", 0, 4, 10, "bool", 18, None, None, "Forces ROM code to send an SPI flash resume command " + "during SPI boot", None), + ("SECURE_VERSION", "identity", 0, 4, 11, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", + None), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), + # + # Table 53: Parameters in BLOCK1-10 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), + ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), + ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), + ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), + ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), + ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), + ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), + ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), + ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), + ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), + ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), + ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), + + ("WAFER_VERSION_MAJOR", "identity", 1, 3, 18, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), + ("WAFER_VERSION_MINOR_HI", "identity", 1, 3, 20, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), + ("FLASH_VERSION", "identity", 1, 3, 21, "uint:4", 20, None, None, "Flash version", + {0: "No Embedded Flash", + 1: "Embedded Flash 2MB", + 2: "Embedded Flash 4MB"}), + ("BLK_VERSION_MAJOR", "identity", 1, 3, 25, "uint:2", 20, None, None, "BLOCK version major", None), + ("PSRAM_VERSION", "identity", 1, 3, 28, "uint:4", 20, None, None, "PSRAM version", + {0: "No Embedded PSRAM", + 1: "Embedded PSRAM 2MB", + 2: "Embedded PSRAM 4MB"}), + ("PKG_VERSION", "identity", 1, 4, 0, "uint:4", 20, None, None, "Package version", None), + ("WAFER_VERSION_MINOR_LO", "identity", 1, 4, 4, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), + + ('OPTIONAL_UNIQUE_ID', "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), + ("BLK_VERSION_MINOR", "identity", 2, 4, 4, "uint:3", 21, None, None, "BLOCK version minor", + {0: "No calibration", + 1: "With ADC calibration V1", + 2: "With ADC calibration V2"}), + ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), + ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), + ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), + ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), + ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), + ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), + ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), + ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, None, "System data (part 2)", None), + ] + + # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), + ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), + ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), + ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), + ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), + ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), + ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), + ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), + ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), + ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), + ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), + ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), + ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), + ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), + ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), + ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), + ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), + ] + + CALC = [ + ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py new file mode 100644 index 000000000..7c4148a81 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python +# +# This file includes the operations with eFuses for ESP32S2 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. For the rest keypurposes the read-protection " + "will be defined the option (Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. " + "This means GPIO45 can be high or low at reset without " + "changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["VDD_SPI_FORCE"] + sdio_tieh = efuses["VDD_SPI_TIEH"] + sdio_reg = efuses["VDD_SPI_XPD"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SPI). SPI flash will " + "need to be powered from an external source.\n" + "The following efuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse VDD_SPI_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + print(msg) + + sdio_force.save(1) # Disable GPIO45 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SPI setting complete.") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MINOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) + # fmt: on + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of + # checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because " + "write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) of raw " + "binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py new file mode 100644 index 000000000..762f2c522 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-S3" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 2 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py new file mode 100644 index 000000000..06a9d18a0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py @@ -0,0 +1,478 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-S3": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-S3 chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + if self["VDD_SPI_FORCE"].get() == 0: + output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " + "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" + output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " + "VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V)." + elif self["VDD_SPI_XPD"].get() == 0: + output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." + elif self["VDD_SPI_TIEH"].get() == 0: + output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not sensible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py new file mode 100644 index 000000000..35d6a2f8e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py @@ -0,0 +1,256 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_ADDR_MASK = 0x00000FFF + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x60007000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Table 51: Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), + ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), + ("DIS_DCACHE", "config", 0, 1, 9, "bool", 2, None, None, "Disables DCache", None), + ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), + ("DIS_DOWNLOAD_DCACHE", "config", 0, 1, 11, "bool", 2, None, None, "Disables Dcache when SoC is in Download mode", None), + ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), + ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), + ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), + ("DIS_APP_CPU", "config", 0, 1, 15, "bool", 2, None, None, "Disables APP CPU", None), + ("SOFT_DIS_JTAG", "security", 0, 1, 16, "uint:3", 31, None, None, "Software disables JTAG by programming " + "odd number of 1 bit(s). " + "JTAG can be re-enabled via HMAC peripheral", + None), + ("HARD_DIS_JTAG", "security", 0, 1, 19, "bool", 2, None, None, "Hardware disables JTAG permanently", None), + + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", + None), + ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), + ("EXT_PHY_ENABLE", "usb config", 0, 1, 26, "bool", 30, None, None, "Enables external USB PHY", None), + ("BTLC_GPIO_ENABLE", "usb config", 0, 1, 27, "uint:2", 30, None, None, "Enables BTLC GPIO", None), + ("VDD_SPI_XPD", "VDD_SPI config", 0, 2, 4, "bool", 3, None, None, "The VDD_SPI regulator is powered on", None), + ("VDD_SPI_TIEH", "VDD_SPI config", 0, 2, 5, "bool", 3, None, None, "The VDD_SPI power supply voltage at reset", + {0: "Connect to 1.8V LDO", + 1: "Connect to VDD_RTC_IO"}), + ("VDD_SPI_FORCE", "VDD_SPI config", 0, 2, 6, "bool", 3, None, None, "Force using VDD_SPI_XPD and VDD_SPI_TIEH " + "to configure VDD_SPI LDO", None), + ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "uint:2", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "Revokes use of secure boot key digest 0", None), + ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "Revokes use of secure boot key digest 1", None), + ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "Revokes use of secure boot key digest 2", None), + ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), + ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), + ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), + ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), + ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), + ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), + ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), + ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", + None), + ("DIS_USB_JTAG", "usb config", 0, 3, 22, "bool", 2, None, None, "Disable usb_serial_jtag-to-jtag function", None), + ("DIS_USB_SERIAL_JTAG", "usb config", 0, 3, 23, "bool", 2, None, None, "Disable usb_serial_jtag module", None), + ("STRAP_JTAG_SEL", "security", 0, 3, 24, "bool", 2, None, None, "Enable selection between usb_to_jtag" + "or pad_to_jtag through GPIO3", None), + ("USB_PHY_SEL", "usb config", 0, 3, 25, "bool", 2, None, None, "Select internal/external PHY for USB OTG" + "and usb_serial_jtag", None), + ("FLASH_TPUW", "config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), + ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), + ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), + ("FLASH_ECC_MODE", "config", 0, 4, 3, "bool", 18, None, None, "Configures the ECC mode for SPI flash", + {0: "16-byte to 18-byte mode", + 1: "16-byte to 17-byte mode"}), + ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " + "UART download boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", + {0: "Enabled", + 1: "Enable when GPIO 46 is low at reset", + 2: "Enable when GPIO 46 is high at rest", + 3: "Disabled"}), + ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "Sets default power supply for GPIO33..37", + {0: "VDD3P3_CPU", + 1: "VDD_SPI"}), + ("FLASH_TYPE", "config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", + {0: "4 data lines", + 1: "8 data lines"}), + ("FLASH_PAGE_SIZE", "config", 0, 4, 10, "uint:2", 18, None, None, "Sets the size of flash page", None), + ("FLASH_ECC_EN", "config", 0, 4, 12, "bool", 18, None, None, "Enables ECC in Flash boot mode", None), + ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Forces ROM code to send an SPI flash resume command " + "during SPI boot", None), + ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", + None), + ("DIS_USB_OTG_DOWNLOAD_MODE", "config", 0, 4, 31, "bool", 19, None, None, "Disables USB-OTG download feature in " + "UART download boot mode", None), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), + # + # Table 53: Parameters in BLOCK1-10 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), + ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), + ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), + ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), + ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), + ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), + ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), + ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), + ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), + ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), + ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), + ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), + + ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), + ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), + ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), + ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), + ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), + + ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), + ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", + {0: "No calibration", + 1: "With calibration"}), + ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), + ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), + ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), + ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), + ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), + ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), + ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), + ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, None, "System data (part 2)", None), + ] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "??? Temperature calibration", None), + ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 1", None), + ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 2", None), + ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 3", None), + ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 4", None), + ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 5", None), + ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 6", None), + ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 7", None), + ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 8", None), + ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 9", None), + ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 10", None), + ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 11", None), + ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 12", None), + ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 13", None), + ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 14", None), + ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 15", None), + ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 16", None), + ] + + CALC = [ + ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py new file mode 100644 index 000000000..6b2d5a72a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python +# This file includes the operations with eFuses for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. " + "For the rest keypurposes the read-protection will be defined the option " + "(Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " + "without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with " + "bytes separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["VDD_SPI_FORCE"] + sdio_tieh = efuses["VDD_SPI_TIEH"] + sdio_reg = efuses["VDD_SPI_XPD"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SPI). " + "SPI flash will need to be powered from an external source.\n" + "The following efuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse VDD_SPI_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + print(msg) + + sdio_force.save(1) # Disable GPIO45 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SPI setting complete.") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of + # checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because " + "write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py new file mode 100644 index 000000000..a3b55a802 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py @@ -0,0 +1,3 @@ +from . import operations +from .emulate_efuse_controller import EmulateEfuseController +from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py new file mode 100644 index 000000000..01d7c9750 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# +# This file describes eFuses controller for ESP32-S3(beta2) chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError + + +class EmulateEfuseController(EmulateEfuseControllerBase): + """The class for virtual efuse operation. Using for HOST_TEST.""" + + CHIP_NAME = "ESP32-S3(beta2)" + mem = None + debug = False + Blocks = EfuseDefineBlocks + Fields = EfuseDefineFields + REGS = EfuseDefineRegisters + + def __init__(self, efuse_file=None, debug=False): + super(EmulateEfuseController, self).__init__(efuse_file, debug) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + + """ esptool method start >>""" + + def get_major_chip_version(self): + return 0 + + def get_minor_chip_version(self): + return 2 + + def get_crystal_freq(self): + return 40 # MHz (common for all chips) + + def get_security_info(self): + return { + "flags": 0, + "flash_crypt_cnt": 0, + "key_purposes": 0, + "chip_id": 0, + "api_version": 0, + } + + """ << esptool method end """ + + def handle_writing_event(self, addr, value): + if addr == self.REGS.EFUSE_CMD_REG: + if value & self.REGS.EFUSE_PGM_CMD: + self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) + self.clean_blocks_wr_regs() + self.check_rd_protection_area() + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + elif value == self.REGS.EFUSE_READ_CMD: + self.write_reg(addr, 0) + self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) + self.save_to_file() + + def get_bitlen_of_block(self, blk, wr=False): + if blk.id == 0: + if wr: + return 32 * 8 + else: + return 32 * blk.len + else: + if wr: + rs_coding = 32 * 3 + return 32 * 8 + rs_coding + else: + return 32 * blk.len + + def handle_coding_scheme(self, blk, data): + if blk.id != 0: + # CODING_SCHEME RS applied only for all blocks except BLK0. + coded_bytes = 12 + data.pos = coded_bytes * 8 + plain_data = data.readlist("32*uint:8")[::-1] + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(coded_bytes) + # 32 byte of data + 12 bytes RS + calc_encoded_data = list(rs.encode([x for x in plain_data])) + data.pos = 0 + if calc_encoded_data != data.readlist("44*uint:8")[::-1]: + raise FatalError("Error in coding scheme data") + data = data[coded_bytes * 8 :] + if blk.len < 8: + data = data[(8 - blk.len) * 32 :] + return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py new file mode 100644 index 000000000..7eaed8523 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py @@ -0,0 +1,478 @@ +#!/usr/bin/env python +# +# This file describes eFuses for ESP32-S3 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import struct +import time + +from bitstring import BitArray + +import esptool + +import reedsolo + +from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters +from .. import base_fields +from .. import util + + +class EfuseBlock(base_fields.EfuseBlockBase): + def len_of_burn_unit(self): + # The writing register window is 8 registers for any blocks. + # len in bytes + return 8 * 4 + + def __init__(self, parent, param, skip_read=False): + parent.read_coding_scheme() + super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) + + def apply_coding_scheme(self): + data = self.get_raw(from_read=False)[::-1] + if len(data) < self.len_of_burn_unit(): + add_empty_bytes = self.len_of_burn_unit() - len(data) + data = data + (b"\x00" * add_empty_bytes) + if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: + # takes 32 bytes + # apply RS encoding + rs = reedsolo.RSCodec(12) + # 32 byte of data + 12 bytes RS + encoded_data = rs.encode([x for x in data]) + words = struct.unpack("<" + "I" * 11, encoded_data) + # returns 11 words (8 words of data + 3 words of RS coding) + else: + # takes 32 bytes + words = struct.unpack("<" + ("I" * (len(data) // 4)), data) + # returns 8 words + return words + + +class EspEfuses(base_fields.EspEfusesBase): + """ + Wrapper object to manage the efuse fields in a connected ESP bootloader + """ + + Blocks = EfuseDefineBlocks() + Fields = EfuseDefineFields() + REGS = EfuseDefineRegisters + BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() + BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() + + debug = False + do_not_confirm = False + + def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): + self._esp = esp + self.debug = debug + self.do_not_confirm = do_not_confirm + if esp.CHIP_NAME != "ESP32-S3(beta2)": + raise esptool.FatalError( + "Expected the 'esp' param for ESP32-S3(beta2) chip but got for '%s'." + % (esp.CHIP_NAME) + ) + if not skip_connect: + flags = self._esp.get_security_info()["flags"] + GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 + if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: + raise esptool.FatalError( + "Secure Download Mode is enabled. The tool can not read eFuses." + ) + self.blocks = [ + EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) + for block in self.Blocks.BLOCKS + ] + if not skip_connect: + self.get_coding_scheme_warnings() + self.efuses = [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.KEYBLOCKS + ] + if skip_connect: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + else: + if self["BLK_VERSION_MAJOR"].get() == 1: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.CALC + ] + + def __getitem__(self, efuse_name): + """Return the efuse field with the given name""" + for e in self.efuses: + if efuse_name == e.name: + return e + new_fields = False + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: + e = self.Fields.get(efuse) + if e.name == efuse_name: + self.efuses += [ + EfuseField.from_tuple( + self, self.Fields.get(efuse), self.Fields.get(efuse).class_type + ) + for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES + ] + new_fields = True + if new_fields: + for e in self.efuses: + if efuse_name == e.name: + return e + raise KeyError + + def read_coding_scheme(self): + self.coding_scheme = self.REGS.CODING_SCHEME_RS + + def print_status_regs(self): + print("") + self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) + ) + ) + print( + "{:27} 0x{:08x}".format( + "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) + ) + ) + + def get_block_errors(self, block_num): + """Returns (error count, failure boolean flag)""" + return self.blocks[block_num].num_errors, self.blocks[block_num].fail + + def efuse_controller_setup(self): + self.set_efuse_timing() + self.clear_pgm_registers() + self.wait_efuse_idle() + + def write_efuses(self, block): + self.efuse_program(block) + return self.get_coding_scheme_warnings(silent=True) + + def clear_pgm_registers(self): + self.wait_efuse_idle() + for r in range( + self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 + ): + self.write_reg(r, 0) + + def wait_efuse_idle(self): + deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT + while time.time() < deadline: + # if self.read_reg(self.EFUSE_CMD_REG) == 0: + if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: + return + raise esptool.FatalError( + "Timed out waiting for Efuse controller command to complete" + ) + + def efuse_program(self, block): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) + self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) + self.wait_efuse_idle() + self.clear_pgm_registers() + self.efuse_read() + + def efuse_read(self): + self.wait_efuse_idle() + self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) + # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some + # efuse registers after each command is completed + # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. + try: + self.write_reg( + self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 + ) + self.wait_efuse_idle() + except esptool.FatalError: + secure_download_mode_before = self._esp.secure_download_mode + + try: + self._esp = self.reconnect_chip(self._esp) + except esptool.FatalError: + print("Can not re-connect to the chip") + if not self["DIS_DOWNLOAD_MODE"].get() and self[ + "DIS_DOWNLOAD_MODE" + ].get(from_read=False): + print( + "This is the correct behavior as we are actually burning " + "DIS_DOWNLOAD_MODE which disables the connection to the chip" + ) + print("DIS_DOWNLOAD_MODE is enabled") + print("Successful") + exit(0) # finish without errors + raise + + print("Established a connection with the chip") + if self._esp.secure_download_mode and not secure_download_mode_before: + print("Secure download mode is enabled") + if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ + "ENABLE_SECURITY_DOWNLOAD" + ].get(from_read=False): + print( + "espefuse tool can not continue to work in Secure download mode" + ) + print("ENABLE_SECURITY_DOWNLOAD is enabled") + print("Successful") + exit(0) # finish without errors + raise + + def set_efuse_timing(self): + """Set timing registers for burning efuses""" + # Configure clock + apb_freq = self.get_crystal_freq() + if apb_freq != 40: + raise esptool.FatalError( + "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq + ) + + self.update_reg( + self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 + ) + + def get_coding_scheme_warnings(self, silent=False): + """Check if the coding scheme has detected any errors.""" + old_addr_reg = 0 + reg_value = 0 + ret_fail = False + for block in self.blocks: + if block.id == 0: + words = [ + self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) + for offs in range(5) + ] + data = BitArray() + for word in reversed(words): + data.append("uint:32=%d" % word) + # pos=32 because EFUSE_WR_DIS goes first it is 32bit long + # and not under error control + block.err_bitarray.overwrite(data, pos=32) + block.num_errors = block.err_bitarray.count(True) + block.fail = block.num_errors != 0 + else: + addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ + block.id + ] + if err_num_mask is None or err_num_offs is None or fail_bit is None: + continue + if addr_reg != old_addr_reg: + old_addr_reg = addr_reg + reg_value = self.read_reg(addr_reg) + block.fail = reg_value & (1 << fail_bit) != 0 + block.num_errors = (reg_value >> err_num_offs) & err_num_mask + ret_fail |= block.fail + if not silent and (block.fail or block.num_errors): + print( + "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" + % (block.id, block.num_errors, block.fail) + ) + if (self.debug or ret_fail) and not silent: + self.print_status_regs() + return ret_fail + + def summary(self): + if self["VDD_SPI_FORCE"].get() == 0: + output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " + "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" + output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " + "VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V)." + elif self["VDD_SPI_XPD"].get() == 0: + output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." + elif self["VDD_SPI_TIEH"].get() == 0: + output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." + else: + output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." + return output + + +class EfuseField(base_fields.EfuseFieldBase): + @staticmethod + def from_tuple(parent, efuse_tuple, type_class): + return { + "mac": EfuseMacField, + "keypurpose": EfuseKeyPurposeField, + "t_sensor": EfuseTempSensor, + "adc_tp": EfuseAdcPointCalibration, + "wafer": EfuseWafer, + }.get(type_class, EfuseField)(parent, efuse_tuple) + + def get_info(self): + output = "%s (BLOCK%d)" % (self.name, self.block) + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output += ( + "[FAIL:%d]" % (fail) + if self.block == 0 + else "[ERRS:%d FAIL:%d]" % (errs, fail) + ) + if self.efuse_class == "keyblock": + name = self.parent.blocks[self.block].key_purpose_name + if name is not None: + output += "\n Purpose: %s\n " % (self.parent[name].get()) + return output + + +class EfuseWafer(EfuseField): + def get(self, from_read=True): + hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) + lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) + return (hi_bits << 3) + lo_bits + + def save(self, new_value): + raise esptool.FatalError("Burning %s is not supported" % self.name) + + +class EfuseTempSensor(EfuseField): + def get(self, from_read=True): + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * 0.1 + + +class EfuseAdcPointCalibration(EfuseField): + def get(self, from_read=True): + STEP_SIZE = 4 + value = self.get_bitstring(from_read) + sig = -1 if value[0] else 1 + return sig * value[1:].uint * STEP_SIZE + + +class EfuseMacField(EfuseField): + def check_format(self, new_value_str): + if new_value_str is None: + raise esptool.FatalError( + "Required MAC Address in AA:CD:EF:01:02:03 format!" + ) + if new_value_str.count(":") != 5: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal format " + "separated by colons (:)!" + ) + hexad = new_value_str.replace(":", "") + if len(hexad) != 12: + raise esptool.FatalError( + "MAC Address needs to be a 6-byte hexadecimal number " + "(12 hexadecimal characters)!" + ) + # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', + bindata = binascii.unhexlify(hexad) + # unicast address check according to + # https://tools.ietf.org/html/rfc7042#section-2.1 + if esptool.util.byte(bindata, 0) & 0x01: + raise esptool.FatalError("Custom MAC must be a unicast MAC!") + return bindata + + def check(self): + errs, fail = self.parent.get_block_errors(self.block) + if errs != 0 or fail: + output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) + else: + output = "OK" + return "(" + output + ")" + + def get(self, from_read=True): + if self.name == "CUSTOM_MAC": + mac = self.get_raw(from_read)[::-1] + else: + mac = self.get_raw(from_read) + return "%s %s" % (util.hexify(mac, ":"), self.check()) + + def save(self, new_value): + def print_field(e, new_value): + print( + " - '{}' ({}) {} -> {}".format( + e.name, e.description, e.get_bitstring(), new_value + ) + ) + + if self.name == "CUSTOM_MAC": + bitarray_mac = self.convert_to_bitstring(new_value) + print_field(self, bitarray_mac) + super(EfuseMacField, self).save(new_value) + else: + # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, + # as it's written in the factory. + raise esptool.FatalError("Writing Factory MAC address is not supported") + + +# fmt: off +class EfuseKeyPurposeField(EfuseField): + KEY_PURPOSES = [ + ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) + ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved + ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) + ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) + ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) + ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode + ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) + ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) + ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode + ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) + ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) + ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + ] +# fmt: on + + KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] + DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] + + def check_format(self, new_value_str): + # str convert to int: "XTS_AES_128_KEY" - > str(4) + # if int: 4 -> str(4) + raw_val = new_value_str + for purpose_name in self.KEY_PURPOSES: + if purpose_name[0] == new_value_str: + raw_val = str(purpose_name[1]) + break + if raw_val.isdigit(): + if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: + raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) + else: + raise esptool.FatalError("'%s' unknown name" % raw_val) + return raw_val + + def need_reverse(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[3] == "Reverse" + + def need_rd_protect(self, new_key_purpose): + for key in self.KEY_PURPOSES: + if key[0] == new_key_purpose: + return key[4] == "need_rd_protect" + + def get(self, from_read=True): + for p in self.KEY_PURPOSES: + if p[1] == self.get_raw(from_read): + return p[0] + return "FORBIDDEN_STATE" + + def save(self, new_value): + raw_val = int(self.check_format(str(new_value))) + return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py new file mode 100644 index 000000000..42d7a7cc8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32-S3(beta2) chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase + + +# fmt: off +class EfuseDefineRegisters(EfuseRegistersBase): + + EFUSE_ADDR_MASK = 0x00000FFF + EFUSE_MEM_SIZE = (0x01FC + 4) + + # EFUSE registers & command/conf values + DR_REG_EFUSE_BASE = 0x6001A000 + EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE + EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 + EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 + EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC + EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 + EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 + EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 + EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 + EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C + EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 + EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 + EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 + EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C + EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 + EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC + EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 + EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 + EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC + EFUSE_WRITE_OP_CODE = 0x5A5A + EFUSE_READ_OP_CODE = 0x5AA5 + EFUSE_PGM_CMD_MASK = 0x3 + EFUSE_PGM_CMD = 0x2 + EFUSE_READ_CMD = 0x1 + + BLOCK_ERRORS = [ + # error_reg, err_num_mask, err_num_offs, fail_bit + (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA + (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 + (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 + (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 + (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 + (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 + (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 + (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 + ] + + # EFUSE_WR_TIM_CONF2_REG + EFUSE_PWR_OFF_NUM_S = 0 + EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S + + +class EfuseDefineBlocks(EfuseBlocksBase): + + __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE + __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG + # List of efuse blocks + BLOCKS = [ + # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose + ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), + ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), + ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), + ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), + ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), + ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), + ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), + ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), + ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), + ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), + ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), + ] + + def get_burn_block_data_names(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseDefineFields(EfuseFieldsBase): + + # List of efuse fields from TRM the chapter eFuse Controller. + EFUSES = [ + # + # Table 51: Parameters in BLOCK0 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), + ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), + ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), + ("DIS_DCACHE", "config", 0, 1, 9, "bool", 2, None, None, "Disables DCache", None), + ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), + ("DIS_DOWNLOAD_DCACHE", "config", 0, 1, 11, "bool", 2, None, None, "Disables Dcache when SoC is in Download mode", None), + ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), + ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), + ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), + ("DIS_APP_CPU", "config", 0, 1, 15, "bool", 2, None, None, "Disables APP CPU", None), + ("SOFT_DIS_JTAG", "security", 0, 1, 16, "uint:3", 31, None, None, "Software disables JTAG by programming " + "odd number of 1 bit(s). " + "JTAG can be re-enabled via HMAC peripheral", + None), + ("HARD_DIS_JTAG", "security", 0, 1, 19, "bool", 2, None, None, "Hardware disables JTAG permanently", None), + + ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", + None), + ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), + ("EXT_PHY_ENABLE", "usb config", 0, 1, 26, "bool", 30, None, None, "Enables external USB PHY", None), + ("BTLC_GPIO_ENABLE", "usb config", 0, 1, 27, "uint:2", 30, None, None, "Enables BTLC GPIO", None), + ("VDD_SPI_XPD", "VDD_SPI config", 0, 2, 4, "bool", 3, None, None, "The VDD_SPI regulator is powered on", None), + ("VDD_SPI_TIEH", "VDD_SPI config", 0, 2, 5, "bool", 3, None, None, "The VDD_SPI power supply voltage at reset", + {0: "Connect to 1.8V LDO", + 1: "Connect to VDD_RTC_IO"}), + ("VDD_SPI_FORCE", "VDD_SPI config", 0, 2, 6, "bool", 3, None, None, "Force using VDD_SPI_XPD and VDD_SPI_TIEH " + "to configure VDD_SPI LDO", None), + ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "uint:2", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), + ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " + "mode is set. Enabled when 1 or 3 bits are set," + "disabled otherwise", + {0: "Disable", + 1: "Enable", + 3: "Disable", + 7: "Enable"}), + ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "Revokes use of secure boot key digest 0", None), + ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "Revokes use of secure boot key digest 1", None), + ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "Revokes use of secure boot key digest 2", None), + ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), + ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), + ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), + ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), + ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), + ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), + ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), + ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", + None), + ("FLASH_TPUW", "config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " + "unit is (ms/2). When the value is 15, delay is 7.5 ms", + None), + ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), + ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), + ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), + ("FLASH_ECC_MODE", "config", 0, 4, 3, "bool", 18, None, None, "Configures the ECC mode for SPI flash", + {0: "16-byte to 18-byte mode", + 1: "16-byte to 17-byte mode"}), + ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " + "UART download boot mode", None), + ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " + "(read/write flash only)", None), + ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", + {0: "Enabled", + 1: "Enable when GPIO 46 is low at reset", + 2: "Enable when GPIO 46 is high at rest", + 3: "Disabled"}), + ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "Sets default power supply for GPIO33..37", + {0: "VDD3P3_CPU", + 1: "VDD_SPI"}), + ("FLASH_TYPE", "config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", + {0: "4 data lines", + 1: "8 data lines"}), + ("FLASH_PAGE_SIZE", "config", 0, 4, 10, "uint:2", 18, None, None, "Sets the size of flash page", None), + ("FLASH_ECC_EN", "config", 0, 4, 12, "bool", 18, None, None, "Enables ECC in Flash boot mode", None), + ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Forces ROM code to send an SPI flash resume command " + "during SPI boot", None), + ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", + None), + ("DIS_USB_OTG_DOWNLOAD_MODE", "config", 0, 4, 31, "bool", 19, None, None, "Disables USB-OTG download feature in " + "UART download boot mode", None), + ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), + ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), + # + # Table 53: Parameters in BLOCK1-10 + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), + ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), + ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), + ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), + ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), + ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), + ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), + ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), + ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), + ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), + ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), + ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), + + ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), + ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), + ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), + ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), + ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), + + ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), + ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", + {0: "No calibration", + 1: "With calibration"}), + ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), + ] + + KEYBLOCKS = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), + ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), + ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), + ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), + ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), + ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), + ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), + ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, None, "System data (part 2)", None), + ] + + # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 + BLOCK2_CALIBRATION_EFUSES = [ + # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary + ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "??? Temperature calibration", None), + ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 1", None), + ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 2", None), + ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 3", None), + ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 4", None), + ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 5", None), + ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 6", None), + ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 7", None), + ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 8", None), + ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 9", None), + ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 10", None), + ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 11", None), + ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 12", None), + ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 13", None), + ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 14", None), + ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 15", None), + ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 16", None), + ] + + CALC = [ + ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), + ] +# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py new file mode 100644 index 000000000..009e55fce --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py @@ -0,0 +1,523 @@ +#!/usr/bin/env python +# This file includes the operations with eFuses for ESP32-S3(beta2) chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import io +import os # noqa: F401. It is used in IDF scripts +import traceback + +import espsecure + +import esptool + +from . import fields +from .. import util +from ..base_operations import ( + add_common_commands, + add_force_write_always, + burn_bit, + burn_block_data, + burn_efuse, + check_error, + dump, + read_protect_efuse, + summary, + write_protect_efuse, +) + + +def protect_options(p): + p.add_argument( + "--no-write-protect", + help="Disable write-protecting of the key. The key remains writable. " + "(The keys use the RS coding scheme that does not support post-write " + "data changes. Forced write can damage RS encoding bits.) " + "The write-protecting of keypurposes does not depend on the option, " + "it will be set anyway.", + action="store_true", + ) + p.add_argument( + "--no-read-protect", + help="Disable read-protecting of the key. The key remains readable software." + "The key with keypurpose[USER, RESERVED and *_DIGEST] " + "will remain readable anyway. " + "For the rest keypurposes the read-protection will be defined the option " + "(Read-protect by default).", + action="store_true", + ) + + +def add_commands(subparsers, efuses): + add_common_commands(subparsers, efuses) + burn_key = subparsers.add_parser( + "burn_key", help="Burn the key block with the specified name" + ) + protect_options(burn_key) + add_force_write_always(burn_key) + burn_key.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + action="append", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key.add_argument( + "keyfile", + help="File containing 256 bits of binary key data", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, + ) + + burn_key_digest = subparsers.add_parser( + "burn_key_digest", + help="Parse a RSA public key and burn the digest to key efuse block", + ) + protect_options(burn_key_digest) + add_force_write_always(burn_key_digest) + burn_key_digest.add_argument( + "block", + help="Key block to burn", + action="append", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + action="append", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + action="append", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + for _ in efuses.BLOCKS_FOR_KEYS: + burn_key_digest.add_argument( + "block", + help="Key block to burn", + nargs="?", + action="append", + metavar="BLOCK", + choices=efuses.BLOCKS_FOR_KEYS, + ) + burn_key_digest.add_argument( + "keyfile", + help="Key file to digest (PEM format)", + nargs="?", + action="append", + metavar="KEYFILE", + type=argparse.FileType("rb"), + ) + burn_key_digest.add_argument( + "keypurpose", + help="Purpose to set.", + nargs="?", + action="append", + metavar="KEYPURPOSE", + choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, + ) + + p = subparsers.add_parser( + "set_flash_voltage", + help="Permanently set the internal flash voltage regulator " + "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " + "without changing the flash voltage.", + ) + p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) + + p = subparsers.add_parser( + "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." + ) + p.add_argument( + "mac", + help="Custom MAC Address to burn given in hexadecimal format with bytes " + "separated by colons (e.g. AA:CD:EF:01:02:03).", + type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), + ) + add_force_write_always(p) + + p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") + + +def burn_custom_mac(esp, efuses, args): + efuses["CUSTOM_MAC"].save(args.mac) + if not efuses.burn_all(check_batch_mode=True): + return + get_custom_mac(esp, efuses, args) + print("Successful") + + +def get_custom_mac(esp, efuses, args): + print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) + + +def set_flash_voltage(esp, efuses, args): + sdio_force = efuses["VDD_SPI_FORCE"] + sdio_tieh = efuses["VDD_SPI_TIEH"] + sdio_reg = efuses["VDD_SPI_XPD"] + + # check efuses aren't burned in a way which makes this impossible + if args.voltage == "OFF" and sdio_reg.get() != 0: + raise esptool.FatalError( + "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" + ) + + if args.voltage == "1.8V" and sdio_tieh.get() != 0: + raise esptool.FatalError( + "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" + ) + + if args.voltage == "OFF": + msg = "Disable internal flash voltage regulator (VDD_SPI). " + "SPI flash will need to be powered from an external source.\n" + "The following efuse is burned: VDD_SPI_FORCE.\n" + "It is possible to later re-enable the internal regulator (%s) " % ( + "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" + ) + "by burning an additional efuse" + elif args.voltage == "1.8V": + msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" + "It is possible to later increase the voltage to 3.3V (permanently) " + "by burning additional efuse VDD_SPI_TIEH" + elif args.voltage == "3.3V": + msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" + "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." + print(msg) + + sdio_force.save(1) # Disable GPIO45 + if args.voltage != "OFF": + sdio_reg.save(1) # Enable internal regulator + if args.voltage == "3.3V": + sdio_tieh.save(1) + print("VDD_SPI setting complete.") + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def adc_info(esp, efuses, args): + print("") + # fmt: off + if efuses["BLK_VERSION_MAJOR"].get() == 1: + print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) + + print("") + print("ADC1 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) + + print("") + print("ADC2 readings stored in efuse BLOCK2:") + print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) + print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) + + print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) + print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) + + print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) + print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) + + print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) + print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) + else: + print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) + # fmt: on + + +def key_block_is_unused(block, key_purpose_block): + if not block.is_readable() or not block.is_writeable(): + return False + + if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): + return False + + if not block.get_bitstring().all(False): + return False + + return True + + +def get_next_key_block(efuses, current_key_block, block_name_list): + key_blocks = [b for b in efuses.blocks if b.key_purpose_name] + start = key_blocks.index(current_key_block) + + # Sort key blocks so that we pick the next free block (and loop around if necessary) + key_blocks = key_blocks[start:] + key_blocks[0:start] + + # Exclude any other blocks that will be be burned + key_blocks = [b for b in key_blocks if b.name not in block_name_list] + + for block in key_blocks: + key_purpose_block = efuses[block.key_purpose_name] + if key_block_is_unused(block, key_purpose_block): + return block + + return None + + +def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): + i = keypurpose_list.index("XTS_AES_256_KEY") + block_name = block_name_list[i] + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + data = datafile_list[i].read() + if len(data) != 64: + raise esptool.FatalError( + "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) + ) + + key_block_2 = get_next_key_block(efuses, block, block_name_list) + if not key_block_2: + raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") + + keypurpose_list.append("XTS_AES_256_KEY_1") + datafile_list.append(io.BytesIO(data[:32])) + block_name_list.append(block_name) + + keypurpose_list.append("XTS_AES_256_KEY_2") + datafile_list.append(io.BytesIO(data[32:])) + block_name_list.append(key_block_2.name) + + keypurpose_list.pop(i) + datafile_list.pop(i) + block_name_list.pop(i) + + +def burn_key(esp, efuses, args, digest=None): + if digest is None: + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + else: + datafile_list = digest[0 : len([name for name in digest if name is not None]) :] + efuses.force_write_always = args.force_write_always + block_name_list = args.block[ + 0 : len([name for name in args.block if name is not None]) : + ] + keypurpose_list = args.keypurpose[ + 0 : len([name for name in args.keypurpose if name is not None]) : + ] + + if "XTS_AES_256_KEY" in keypurpose_list: + # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into + # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 + split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) + + util.check_duplicate_name_in_list(block_name_list) + if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( + keypurpose_list + ): + raise esptool.FatalError( + "The number of blocks (%d), datafile (%d) and keypurpose (%d) " + "should be the same." + % (len(block_name_list), len(datafile_list), len(keypurpose_list)) + ) + + print("Burn keys to blocks:") + for block_name, datafile, keypurpose in zip( + block_name_list, datafile_list, keypurpose_list + ): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + + block_num = efuses.get_index_block_by_name(block_name) + block = efuses.blocks[block_num] + + if digest is None: + data = datafile.read() + else: + data = datafile + + print(" - %s" % (efuse.name), end=" ") + revers_msg = None + if efuses[block.key_purpose_name].need_reverse(keypurpose): + revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" + data = data[::-1] + print("-> [%s]" % (util.hexify(data, " "))) + if revers_msg: + print(revers_msg) + if len(data) != num_bytes: + raise esptool.FatalError( + "Incorrect key file size %d. Key file must be %d bytes (%d bits) " + "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) + ) + + if efuses[block.key_purpose_name].need_rd_protect(keypurpose): + read_protect = False if args.no_read_protect else True + else: + read_protect = False + write_protect = not args.no_write_protect + + # using efuse instead of a block gives the advantage of + # checking it as the whole field. + efuse.save(data) + + disable_wr_protect_key_purpose = False + if efuses[block.key_purpose_name].get() != keypurpose: + if efuses[block.key_purpose_name].is_writeable(): + print( + "\t'%s': '%s' -> '%s'." + % ( + block.key_purpose_name, + efuses[block.key_purpose_name].get(), + keypurpose, + ) + ) + efuses[block.key_purpose_name].save(keypurpose) + disable_wr_protect_key_purpose = True + else: + raise esptool.FatalError( + "It is not possible to change '%s' to '%s' because " + "write protection bit is set." + % (block.key_purpose_name, keypurpose) + ) + else: + print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) + if efuses[block.key_purpose_name].is_writeable(): + disable_wr_protect_key_purpose = True + + if disable_wr_protect_key_purpose: + print("\tDisabling write to '%s'." % block.key_purpose_name) + efuses[block.key_purpose_name].disable_write() + + if read_protect: + print("\tDisabling read to key block") + efuse.disable_read() + + if write_protect: + print("\tDisabling write to key block") + efuse.disable_write() + print("") + + if not write_protect: + print("Keys will remain writeable (due to --no-write-protect)") + if args.no_read_protect: + print("Keys will remain readable (due to --no-read-protect)") + + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") + + +def burn_key_digest(esp, efuses, args): + digest_list = [] + datafile_list = args.keyfile[ + 0 : len([name for name in args.keyfile if name is not None]) : + ] + block_list = args.block[ + 0 : len([block for block in args.block if block is not None]) : + ] + for block_name, datafile in zip(block_list, datafile_list): + efuse = None + for block in efuses.blocks: + if block_name == block.name or block_name in block.alias: + efuse = efuses[block.name] + if efuse is None: + raise esptool.FatalError("Unknown block name - %s" % (block_name)) + num_bytes = efuse.bit_len // 8 + digest = espsecure._digest_sbv2_public_key(datafile) + if len(digest) != num_bytes: + raise esptool.FatalError( + "Incorrect digest size %d. Digest must be %d bytes (%d bits) " + "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) + ) + digest_list.append(digest) + burn_key(esp, efuses, args, digest=digest_list) + + +def espefuse(esp, efuses, args, command): + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest="operation") + add_commands(subparsers, efuses) + try: + cmd_line_args = parser.parse_args(command.split()) + except SystemExit: + traceback.print_stack() + raise esptool.FatalError('"{}" - incorrect command'.format(command)) + if cmd_line_args.operation == "execute_scripts": + configfiles = cmd_line_args.configfiles + index = cmd_line_args.index + # copy arguments from args to cmd_line_args + vars(cmd_line_args).update(vars(args)) + if cmd_line_args.operation == "execute_scripts": + cmd_line_args.configfiles = configfiles + cmd_line_args.index = index + if cmd_line_args.operation is None: + parser.print_help() + parser.exit(1) + operation_func = globals()[cmd_line_args.operation] + # each 'operation' is a module-level function of the same name + operation_func(esp, efuses, cmd_line_args) + + +def execute_scripts(esp, efuses, args): + efuses.batch_mode_cnt += 1 + del args.operation + scripts = args.scripts + del args.scripts + + for file in scripts: + with open(file.name, "r") as file: + exec(compile(file.read(), file.name, "exec")) + + if args.debug: + for block in efuses.blocks: + data = block.get_bitstring(from_read=False) + block.print_block(data, "regs_for_burn", args.debug) + + efuses.batch_mode_cnt -= 1 + if not efuses.burn_all(check_batch_mode=True): + return + print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py new file mode 100644 index 000000000..3db5694ee --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# +# This file describes eFuses fields and registers for ESP32 chip +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from collections import namedtuple + + +class EfuseRegistersBase(object): + # Coding Scheme values + CODING_SCHEME_NONE = 0 + CODING_SCHEME_34 = 1 + CODING_SCHEME_REPEAT = 2 + CODING_SCHEME_NONE_RECOVERY = 3 + CODING_SCHEME_RS = 4 + + EFUSE_BURN_TIMEOUT = 0.250 # seconds + + +class EfuseBlocksBase(object): + + BLOCKS = None + NamedtupleBlock = namedtuple( + "Block", + "name alias id rd_addr wr_addr write_disable_bit " + "read_disable_bit len key_purpose", + ) + + @staticmethod + def get(tuple_block): + return EfuseBlocksBase.NamedtupleBlock._make(tuple_block) + + def get_blocks_for_keys(self): + list_of_names = [] + for block in self.BLOCKS: + blk = self.get(block) + if blk.id > 0: + if blk.name: + list_of_names.append(blk.name) + if blk.alias: + for alias in blk.alias: + list_of_names.append(alias) + return list_of_names + + +class EfuseFieldsBase(object): + + NamedtupleField = namedtuple( + "Efuse", + "name category block word pos type write_disable_bit " + "read_disable_bit class_type description dictionary", + ) + + @staticmethod + def get(tuple_field): + return EfuseFieldsBase.NamedtupleField._make(tuple_field) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py new file mode 100644 index 000000000..7c4bee287 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# +# This file consists of the common useful functions for eFuse +# +# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import esptool + + +def hexify(bitstring, separator=""): + as_bytes = tuple(b for b in bitstring) + return separator.join(("%02x" % b) for b in as_bytes) + + +def popcnt(b): + """Return number of "1" bits set in 'b'""" + return len([x for x in bin(b) if x == "1"]) + + +def check_duplicate_name_in_list(name_list): + duples_name = [name for i, name in enumerate(name_list) if name in name_list[:i]] + if duples_name != []: + raise esptool.FatalError( + "Found repeated {} in the name list".format(duples_name) + ) + + +class SdkConfig(object): + def __init__(self, path_to_file): + self.sdkconfig = dict() + if path_to_file is None: + return + with open(path_to_file, "r") as file: + for line in file.readlines(): + if line.startswith("#"): + continue + config = line.strip().split("=", 1) + if len(config) == 2: + self.sdkconfig[config[0]] = ( + True if config[1] == "y" else config[1].strip('"') + ) + + def __getitem__(self, config_name): + try: + return self.sdkconfig[config_name] + except KeyError: + return False diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py new file mode 100644 index 000000000..33d08da63 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py @@ -0,0 +1,1558 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import argparse +import hashlib +import operator +import os +import struct +import sys +import zlib +from collections import namedtuple + +from cryptography import exceptions +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa, utils +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.utils import int_to_bytes + +import ecdsa + +import esptool + +SIG_BLOCK_MAGIC = 0xE7 + +# Scheme used in Secure Boot V2 +SIG_BLOCK_VERSION_RSA = 0x02 +SIG_BLOCK_VERSION_ECDSA = 0x03 + +# Curve IDs used in Secure Boot V2 ECDSA signature blocks +CURVE_ID_P192 = 1 +CURVE_ID_P256 = 2 + + +def get_chunks(source, chunk_len): + """Returns an iterator over 'chunk_len' chunks of 'source'""" + return (source[i : i + chunk_len] for i in range(0, len(source), chunk_len)) + + +def endian_swap_words(source): + """Endian-swap each word in 'source' bitstring""" + assert len(source) % 4 == 0 + words = "I" * (len(source) // 4) + return struct.pack("<" + words, *struct.unpack(">" + words, source)) + + +def swap_word_order(source): + """Swap the order of the words in 'source' bitstring""" + assert len(source) % 4 == 0 + words = "I" * (len(source) // 4) + return struct.pack(words, *reversed(struct.unpack(words, source))) + + +def _load_hardware_key(keyfile): + """Load a 128/256/512-bit key, similar to stored in efuse, from a file + + 128-bit keys will be extended to 256-bit using the SHA256 of the key + 192-bit keys will be extended to 256-bit using the same algorithm used + by hardware if 3/4 Coding Scheme is set. + """ + key = keyfile.read() + if len(key) not in [16, 24, 32, 64]: + raise esptool.FatalError( + "Key file contains wrong length (%d bytes), 16, 24, 32 or 64 expected." + % len(key) + ) + if len(key) == 16: + key = _sha256_digest(key) + print("Using 128-bit key (extended)") + elif len(key) == 24: + key = key + key[8:16] + assert len(key) == 32 + print("Using 192-bit key (extended)") + elif len(key) == 32: + print("Using 256-bit key") + else: + print("Using 512-bit key") + return key + + +def digest_secure_bootloader(args): + """Calculate the digest of a bootloader image, in the same way the hardware + secure boot engine would do so. Can be used with a pre-loaded key to update a + secure bootloader.""" + _check_output_is_not_input(args.keyfile, args.output) + _check_output_is_not_input(args.image, args.output) + _check_output_is_not_input(args.iv, args.output) + if args.iv is not None: + print("WARNING: --iv argument is for TESTING PURPOSES ONLY") + iv = args.iv.read(128) + else: + iv = os.urandom(128) + plaintext_image = args.image.read() + args.image.seek(0) + + # secure boot engine reads in 128 byte blocks (ie SHA512 block + # size), but also doesn't look for any appended SHA-256 digest + fw_image = esptool.bin_image.ESP32FirmwareImage(args.image) + if fw_image.append_digest: + if len(plaintext_image) % 128 <= 32: + # ROM bootloader will read to the end of the 128 byte block, but not + # to the end of the SHA-256 digest at the end + new_len = len(plaintext_image) - (len(plaintext_image) % 128) + plaintext_image = plaintext_image[:new_len] + + # if image isn't 128 byte multiple then pad with 0xFF (ie unwritten flash) + # as this is what the secure boot engine will see + if len(plaintext_image) % 128 != 0: + plaintext_image += b"\xFF" * (128 - (len(plaintext_image) % 128)) + + plaintext = iv + plaintext_image + + # Secure Boot digest algorithm in hardware uses AES256 ECB to + # produce a ciphertext, then feeds output through SHA-512 to + # produce the digest. Each block in/out of ECB is reordered + # (due to hardware quirks not for security.) + + key = _load_hardware_key(args.keyfile) + backend = default_backend() + cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) + encryptor = cipher.encryptor() + digest = hashlib.sha512() + + for block in get_chunks(plaintext, 16): + block = block[::-1] # reverse each input block + + cipher_block = encryptor.update(block) + # reverse and then byte swap each word in the output block + cipher_block = cipher_block[::-1] + for block in get_chunks(cipher_block, 4): + # Python hashlib can build each SHA block internally + digest.update(block[::-1]) + + if args.output is None: + args.output = os.path.splitext(args.image.name)[0] + "-digest-0x0000.bin" + with open(args.output, "wb") as f: + f.write(iv) + digest = digest.digest() + for word in get_chunks(digest, 4): + f.write(word[::-1]) # swap word order in the result + f.write(b"\xFF" * (0x1000 - f.tell())) # pad to 0x1000 + f.write(plaintext_image) + print("digest+image written to %s" % args.output) + + +def _generate_ecdsa_signing_key(curve_id, keyfile): + sk = ecdsa.SigningKey.generate(curve=curve_id) + with open(keyfile, "wb") as f: + f.write(sk.to_pem()) + + +def generate_signing_key(args): + if os.path.exists(args.keyfile): + raise esptool.FatalError("ERROR: Key file %s already exists" % args.keyfile) + if args.version == "1": + if hasattr(args, "scheme"): + if args.scheme != "ecdsa256" and args.scheme is not None: + raise esptool.FatalError("ERROR: V1 only supports ECDSA256") + """ + Generate an ECDSA signing key for signing secure boot images (post-bootloader) + """ + _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) + print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile) + elif args.version == "2": + if args.scheme == "rsa3072" or args.scheme is None: + """Generate a RSA 3072 signing key for signing secure boot images""" + private_key = rsa.generate_private_key( + public_exponent=65537, key_size=3072, backend=default_backend() + ).private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + with open(args.keyfile, "wb") as f: + f.write(private_key) + print("RSA 3072 private key in PEM format written to %s" % args.keyfile) + elif args.scheme == "ecdsa192": + """Generate a ECDSA 192 signing key for signing secure boot images""" + _generate_ecdsa_signing_key(ecdsa.NIST192p, args.keyfile) + print( + "ECDSA NIST192p private key in PEM format written to %s" % args.keyfile + ) + elif args.scheme == "ecdsa256": + """Generate a ECDSA 256 signing key for signing secure boot images""" + _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) + print( + "ECDSA NIST256p private key in PEM format written to %s" % args.keyfile + ) + else: + raise esptool.FatalError( + "ERROR: Unsupported signing scheme (%s)" % args.scheme + ) + + +def _load_ecdsa_signing_key(keyfile): + """Load ECDSA signing key for Secure Boot V1 only""" + sk = ecdsa.SigningKey.from_pem(keyfile.read()) + if sk.curve != ecdsa.NIST256p: + raise esptool.FatalError( + "Signing key uses incorrect curve. ESP32 Secure Boot only supports " + "NIST256p (openssl calls this curve 'prime256v1" + ) + return sk + + +def _load_sbv2_signing_key(keydata): + """ + Load Secure Boot V2 signing key + + can be rsa.RSAPrivateKey or ec.EllipticCurvePrivateKey + """ + sk = serialization.load_pem_private_key( + keydata, password=None, backend=default_backend() + ) + if isinstance(sk, rsa.RSAPrivateKey): + if sk.key_size != 3072: + raise esptool.FatalError( + "Key file has length %d bits. Secure boot v2 only supports RSA-3072." + % sk.key_size + ) + return sk + if isinstance(sk, ec.EllipticCurvePrivateKey): + if not ( + isinstance(sk.curve, ec.SECP192R1) or isinstance(sk.curve, ec.SECP256R1) + ): + raise esptool.FatalError( + "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " + "NIST192p, NIST256p (aka prime192v1, prime256v1)" + ) + return sk + + raise esptool.FatalError("Unsupported signing key for Secure Boot V2") + + +def _load_sbv2_pub_key(keydata): + """ + Load Secure Boot V2 public key, can be rsa.RSAPublicKey or ec.EllipticCurvePublicKey + """ + vk = serialization.load_pem_public_key(keydata, backend=default_backend()) + if isinstance(vk, rsa.RSAPublicKey): + if vk.key_size != 3072: + raise esptool.FatalError( + "Key file has length %d bits. Secure boot v2 only supports RSA-3072." + % vk.key_size + ) + return vk + if isinstance(vk, ec.EllipticCurvePublicKey): + if not ( + isinstance(vk.curve, ec.SECP192R1) or isinstance(vk.curve, ec.SECP256R1) + ): + raise esptool.FatalError( + "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " + "NIST192p, NIST256p (aka prime192v1, prime256v1)" + ) + return vk + + raise esptool.FatalError("Unsupported public key for Secure Boot V2") + + +def _get_sbv2_pub_key(keyfile): + key_data = keyfile.read() + if b"-BEGIN RSA PRIVATE KEY" in key_data or b"-BEGIN EC PRIVATE KEY" in key_data: + return _load_sbv2_signing_key(key_data).public_key() + elif b"-BEGIN PUBLIC KEY" in key_data: + vk = _load_sbv2_pub_key(key_data) + else: + raise esptool.FatalError( + "Verification key does not appear to be an RSA Private or " + "Public key in PEM format. Unsupported" + ) + return vk + + +def _get_sbv2_rsa_primitives(public_key): + primitives = namedtuple("primitives", ["n", "e", "m", "rinv"]) + numbers = public_key.public_numbers() + primitives.n = numbers.n # + primitives.e = numbers.e # two public key components + + # Note: this cheats and calls a private 'rsa' method to get the modular + # inverse calculation. + primitives.m = -rsa._modinv(primitives.n, 1 << 32) + + rr = 1 << (public_key.key_size * 2) + primitives.rinv = rr % primitives.n + return primitives + + +def _microecc_format(a, b, curve_len): + """ + Given two numbers (curve coordinates or (r,s) signature), write them out as a + little-endian byte sequence suitable for micro-ecc + "native little endian" mode + """ + byte_len = int(curve_len / 8) + ab = int_to_bytes(a, byte_len)[::-1] + int_to_bytes(b, byte_len)[::-1] + assert len(ab) == 48 or len(ab) == 64 + return ab + + +def sign_data(args): + _check_output_is_not_input(args.keyfile, args.output) + _check_output_is_not_input(args.datafile, args.output) + if args.version == "1": + return sign_secure_boot_v1(args) + elif args.version == "2": + return sign_secure_boot_v2(args) + + +def sign_secure_boot_v1(args): + """ + Sign a data file with a ECDSA private key, append binary signature to file contents + """ + if len(args.keyfile) > 1: + raise esptool.FatalError("Secure Boot V1 only supports one signing key") + sk = _load_ecdsa_signing_key(args.keyfile[0]) + + # calculate signature of binary data + binary_content = args.datafile.read() + signature = sk.sign_deterministic(binary_content, hashlib.sha256) + + # back-verify signature + vk = sk.get_verifying_key() + vk.verify(signature, binary_content, hashlib.sha256) # throws exception on failure + + if args.output is None or os.path.abspath(args.output) == os.path.abspath( + args.datafile.name + ): # append signature to input file + args.datafile.close() + outfile = open(args.datafile.name, "ab") + else: # write file & signature to new file + outfile = open(args.output, "wb") + outfile.write(binary_content) + outfile.write( + struct.pack("I", 0) + ) # Version indicator, allow for different curves/formats later + outfile.write(signature) + outfile.close() + print( + "Signed %d bytes of data from %s with key %s" + % (len(binary_content), args.datafile.name, args.keyfile[0].name) + ) + + +def sign_secure_boot_v2(args): + """ + Sign a firmware app image with an RSA private key using RSA-PSS, + or ECDSA private key using P192 or P256. + + Write output file with a Secure Boot V2 header appended. + """ + SECTOR_SIZE = 4096 + SIG_BLOCK_SIZE = 1216 + SIG_BLOCK_MAX_COUNT = 3 + + signature_sector = b"" + key_count = len(args.keyfile) + contents = args.datafile.read() + + if key_count > SIG_BLOCK_MAX_COUNT: + print( + "WARNING: Upto %d signing keys are supported for ESP32-S2. " + "For ESP32-ECO3 only 1 signing key is supported", + SIG_BLOCK_MAX_COUNT, + ) + + if len(contents) % SECTOR_SIZE != 0: + pad_by = SECTOR_SIZE - (len(contents) % SECTOR_SIZE) + print( + "Padding data contents by %d bytes " + "so signature sector aligns at sector boundary" % pad_by + ) + contents += b"\xff" * pad_by + elif args.append_signatures: + sig_block_num = 0 + + while sig_block_num < SIG_BLOCK_MAX_COUNT: + sig_block = validate_signature_block(contents, sig_block_num) + if sig_block is None: + break + signature_sector += ( + sig_block # Signature sector is populated with already valid blocks + ) + sig_block_num += 1 + + assert len(signature_sector) % SIG_BLOCK_SIZE == 0 + + if sig_block_num == 0: + print( + "No valid signature blocks found. " + "Discarding --append-signature and proceeding to sign the image afresh." + ) + else: + print( + "%d valid signature block(s) already present in the signature sector." + % sig_block_num + ) + + empty_signature_blocks = SIG_BLOCK_MAX_COUNT - sig_block_num + if key_count > empty_signature_blocks: + raise esptool.FatalError( + "Number of keys(%d) more than the empty signature blocks.(%d)" + % (key_count, empty_signature_blocks) + ) + # Signature stripped off the content + # (the legitimate blocks are included in signature_sector) + contents = contents[: len(contents) - SECTOR_SIZE] + + print("%d signing key(s) found." % key_count) + # Calculate digest of data file + digest = hashlib.sha256() + digest.update(contents) + digest = digest.digest() + + for keyfile in args.keyfile: + private_key = _load_sbv2_signing_key(keyfile.read()) + + # Sign + if isinstance(private_key, rsa.RSAPrivateKey): + # RSA signature + signature = private_key.sign( + digest, + padding.PSS( + mgf=padding.MGF1(hashes.SHA256()), + salt_length=32, + ), + utils.Prehashed(hashes.SHA256()), + ) + rsa_primitives = _get_sbv2_rsa_primitives(private_key.public_key()) + + # Encode in signature block format + # + # Note: the [::-1] is to byte swap all of the bignum + # values (signatures, coefficients) to little endian + # for use with the RSA peripheral, rather than big endian + # which is conventionally used for RSA. + signature_block = struct.pack( + " 0 + and len(signature_sector) <= SIG_BLOCK_SIZE * 3 + and len(signature_sector) % SIG_BLOCK_SIZE == 0 + ) + total_sig_blocks = len(signature_sector) // SIG_BLOCK_SIZE + + # Pad signature_sector to sector + signature_sector = signature_sector + ( + b"\xff" * (SECTOR_SIZE - len(signature_sector)) + ) + assert len(signature_sector) == SECTOR_SIZE + + # Write to output file, or append to existing file + if args.output is None: + args.datafile.close() + args.output = args.datafile.name + with open(args.output, "wb") as f: + f.write(contents + signature_sector) + print( + "Signed %d bytes of data from %s. Signature sector now has %d signature blocks." + % (len(contents), args.datafile.name, total_sig_blocks) + ) + + +def verify_signature(args): + if args.version == "1": + return verify_signature_v1(args) + elif args.version == "2": + return verify_signature_v2(args) + + +def verify_signature_v1(args): + """Verify a previously signed binary image, using the ECDSA public key""" + key_data = args.keyfile.read() + if b"-BEGIN EC PRIVATE KEY" in key_data: + sk = ecdsa.SigningKey.from_pem(key_data) + vk = sk.get_verifying_key() + elif b"-BEGIN PUBLIC KEY" in key_data: + vk = ecdsa.VerifyingKey.from_pem(key_data) + elif len(key_data) == 64: + vk = ecdsa.VerifyingKey.from_string(key_data, curve=ecdsa.NIST256p) + else: + raise esptool.FatalError( + "Verification key does not appear to be an EC key in PEM format " + "or binary EC public key data. Unsupported" + ) + + if vk.curve != ecdsa.NIST256p: + raise esptool.FatalError( + "Public key uses incorrect curve. ESP32 Secure Boot only supports " + "NIST256p (openssl calls this curve 'prime256v1" + ) + + binary_content = args.datafile.read() + data = binary_content[0:-68] + sig_version, signature = struct.unpack("I64s", binary_content[-68:]) + if sig_version != 0: + raise esptool.FatalError( + "Signature block has version %d. This version of espsecure " + "only supports version 0." % sig_version + ) + print("Verifying %d bytes of data" % len(data)) + try: + if vk.verify(signature, data, hashlib.sha256): + print("Signature is valid") + else: + raise esptool.FatalError("Signature is not valid") + except ecdsa.keys.BadSignatureError: + raise esptool.FatalError("Signature is not valid") + + +def validate_signature_block(image_content, sig_blk_num): + SECTOR_SIZE = 4096 + SIG_BLOCK_SIZE = ( + 1216 # Refer to secure boot v2 signature block format for more details. + ) + + offset = -SECTOR_SIZE + sig_blk_num * SIG_BLOCK_SIZE + sig_blk = image_content[offset : offset + SIG_BLOCK_SIZE] + assert len(sig_blk) == SIG_BLOCK_SIZE + + # note: in case of ECDSA key, the exact fields in the middle are wrong + # (but unused here) + magic, version, _, _, _, _, _, _, blk_crc = struct.unpack( + "> 5 + key ^= ((mul1 * addr) | ((mul2 * addr) & mul2_mask)) & tweak_range + return int.to_bytes(key, length=32, byteorder="big", signed=False) + + +def generate_flash_encryption_key(args): + print("Writing %d random bits to key file %s" % (args.keylen, args.key_file.name)) + args.key_file.write(os.urandom(args.keylen // 8)) + + +def _flash_encryption_operation_esp32( + output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt +): + key = _load_hardware_key(keyfile) + + if flash_address % 16 != 0: + raise esptool.FatalError( + "Starting flash address 0x%x must be a multiple of 16" % flash_address + ) + + if flash_crypt_conf == 0: + print("WARNING: Setting FLASH_CRYPT_CONF to zero is not recommended") + + tweak_range = _flash_encryption_tweak_range_bits(flash_crypt_conf) + key = int.from_bytes(key, byteorder="big", signed=False) + + backend = default_backend() + + cipher = None + block_offs = flash_address + while True: + block = input_file.read(16) + if len(block) == 0: + break + elif len(block) < 16: + if do_decrypt: + raise esptool.FatalError("Data length is not a multiple of 16 bytes") + pad = 16 - len(block) + block = block + os.urandom(pad) + print( + "Note: Padding with %d bytes of random data " + "(encrypted data must be multiple of 16 bytes long)" % pad + ) + + if block_offs % 32 == 0 or cipher is None: + # each bit of the flash encryption key is XORed with tweak bits + # derived from the offset of 32 byte block of flash + block_key = _flash_encryption_tweak_key(key, block_offs, tweak_range) + + if cipher is None: # first pass + cipher = Cipher(algorithms.AES(block_key), modes.ECB(), backend=backend) + + # note AES is used inverted for flash encryption, so + # "decrypting" flash uses AES encrypt algorithm and vice + # versa. (This does not weaken AES.) + actor = cipher.encryptor() if do_decrypt else cipher.decryptor() + else: + # performance hack: changing the key using pyca-cryptography API + # requires recreating'actor'. + # With openssl backend, this re-initializes the openssl cipher context. + # To save some time, manually call EVP_CipherInit_ex() in the openssl + # backend to update the key. + # If it fails, fall back to recreating the entire context via public API + try: + backend = actor._ctx._backend + res = backend._lib.EVP_CipherInit_ex( + actor._ctx._ctx, + backend._ffi.NULL, + backend._ffi.NULL, + backend._ffi.from_buffer(block_key), + backend._ffi.NULL, + actor._ctx._operation, + ) + backend.openssl_assert(res != 0) + except AttributeError: + # backend is not an openssl backend, or implementation has changed: + # fall back to the slow safe version + cipher.algorithm.key = block_key + actor = cipher.encryptor() if do_decrypt else cipher.decryptor() + + block = block[::-1] # reverse input block byte order + block = actor.update(block) + + output_file.write(block[::-1]) # reverse output block byte order + block_offs += 16 + + +def _flash_encryption_operation_aes_xts( + output_file, input_file, flash_address, keyfile, do_decrypt +): + """ + Apply the AES-XTS algorithm with the hardware addressing scheme used by Espressif + + key = AES-XTS key (32 or 64 bytes) + flash_address = address in flash to encrypt at. Must be multiple of 16 bytes. + indata = Data to encrypt/decrypt. Must be multiple of 16 bytes. + encrypt = True to Encrypt indata, False to decrypt indata. + + Returns a bitstring of the ciphertext or plaintext result. + """ + + backend = default_backend() + key = _load_hardware_key(keyfile) + indata = input_file.read() + + if flash_address % 16 != 0: + raise esptool.FatalError( + "Starting flash address 0x%x must be a multiple of 16" % flash_address + ) + + if len(indata) % 16 != 0: + raise esptool.FatalError( + "Input data length (%d) must be a multiple of 16" % len(indata) + ) + + if len(indata) == 0: + raise esptool.FatalError("Input data must be longer than 0") + + # left pad for a 1024-bit aligned address + pad_left = flash_address % 0x80 + indata = (b"\x00" * pad_left) + indata + + # right pad for full 1024-bit blocks + pad_right = len(indata) % 0x80 + if pad_right > 0: + pad_right = 0x80 - pad_right + indata = indata + (b"\x00" * pad_right) + + inblocks = _split_blocks(indata, 0x80) # split into 1024 bit blocks + + output = [] + for inblock in inblocks: # for each block + tweak = struct.pack(" 0: + if not self.file_obj: + self.file_obj = open(self.path, "wb") + self.file_obj.write(payload) + + def close(self): + if self.file_obj: + self.file_obj.close() + self.file_obj = None + + @property + def name(self): + return self.path + + +def main(custom_commandline=None): + """ + Main function for espsecure + + custom_commandline - Optional override for default arguments parsing + (that uses sys.argv), can be a list of custom arguments as strings. + Arguments and their values need to be added as individual items to the list + e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. + """ + parser = argparse.ArgumentParser( + description="espsecure.py v%s - ESP32 Secure Boot & Flash Encryption tool" + % esptool.__version__, + prog="espsecure", + ) + + subparsers = parser.add_subparsers( + dest="operation", help="Run espsecure.py {command} -h for additional help" + ) + + p = subparsers.add_parser( + "digest_secure_bootloader", + help="Take a bootloader binary image and a secure boot key, " + "and output a combined digest+binary suitable for flashing along " + "with the precalculated secure boot key.", + ) + p.add_argument( + "--keyfile", + "-k", + help="256 bit key for secure boot digest.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument("--output", "-o", help="Output file for signed digest image.") + p.add_argument( + "--iv", + help="128 byte IV file. Supply a file for testing purposes only, " + "if not supplied an IV will be randomly generated.", + type=argparse.FileType("rb"), + ) + p.add_argument( + "image", + help="Bootloader image file to calculate digest from", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "generate_signing_key", + help="Generate a private key for signing secure boot images " + "as per the secure boot version. " + "Key file is generated in PEM format, " + "Secure Boot V1 - ECDSA NIST256p private key. " + "Secure Boot V2 - RSA 3072, ECDSA NIST256p, ECDSA NIST192p private key.", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot signing scheme to use.", + choices=["1", "2"], + default="1", + ) + p.add_argument( + "--scheme", + "-s", + help="Scheme of secure boot signing.", + choices=["rsa3072", "ecdsa192", "ecdsa256"], + required=False, + ) + p.add_argument( + "keyfile", help="Filename for private key file (embedded public key)" + ) + + p = subparsers.add_parser( + "sign_data", + help="Sign a data file for use with secure boot. " + "Signing algorithm is deterministic ECDSA w/ SHA-512 (V1) " + "or either RSA-PSS or ECDSA w/ SHA-256 (V2).", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot signing scheme to use.", + choices=["1", "2"], + required=True, + ) + p.add_argument( + "--keyfile", + "-k", + help="Private key file for signing. Key is in PEM format.", + type=argparse.FileType("rb"), + required=True, + nargs="+", + ) + p.add_argument( + "--append_signatures", + "-a", + help="Append signature block(s) to already signed image" + "Valid only for ESP32-S2.", + action="store_true", + ) + p.add_argument( + "--output", + "-o", + help="Output file for signed digest image. Default is to sign the input file.", + ) + p.add_argument( + "datafile", + help="File to sign. For version 1, this can be any file. " + "For version 2, this must be a valid app image.", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "verify_signature", + help='Verify a data file previously signed by "sign_data", ' + "using the public key.", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot scheme to use.", + choices=["1", "2"], + required=True, + ) + p.add_argument( + "--keyfile", + "-k", + help="Public key file for verification. " + "Can be private or public key in PEM format.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "datafile", + help="Signed data file to verify signature.", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "extract_public_key", + help="Extract the public verification key for signatures, " + "save it as a raw binary file.", + ) + p.add_argument( + "--version", + "-v", + help="Version of the secure boot signing scheme to use.", + choices=["1", "2"], + default="1", + ) + p.add_argument( + "--keyfile", + "-k", + help="Private key file (PEM format) to extract the " + "public verification key from.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "public_keyfile", help="File to save new public key into", type=OutFileType() + ) + + # Kept for compatibility purpose. We can deprecate this in a future release + p = subparsers.add_parser( + "digest_rsa_public_key", + help="Generate an SHA-256 digest of the RSA public key. " + "This digest is burned into the eFuse and asserts the legitimacy " + "of the public key for Secure boot v2.", + ) + p.add_argument( + "--keyfile", + "-k", + help="Public key file for verification. " + "Can be private or public key in PEM format.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument("--output", "-o", help="Output file for the digest.", required=True) + + p = subparsers.add_parser( + "digest_sbv2_public_key", + help="Generate an SHA-256 digest of the public key. " + "This digest is burned into the eFuse and asserts the legitimacy " + "of the public key for Secure boot v2.", + ) + p.add_argument( + "--keyfile", + "-k", + help="Public key file for verification. " + "Can be private or public key in PEM format.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument("--output", "-o", help="Output file for the digest.", required=True) + + p = subparsers.add_parser( + "signature_info_v2", + help="Reads the signature block and provides the signature block information.", + ) + p.add_argument( + "datafile", + help="Secure boot v2 signed data file.", + type=argparse.FileType("rb"), + ) + + p = subparsers.add_parser( + "digest_private_key", + help="Generate an SHA-256 digest of the private signing key. " + "This can be used as a reproducible secure bootloader or flash encryption key.", + ) + p.add_argument( + "--keyfile", + "-k", + help="Private key file (PEM format) to generate a digest from.", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "--keylen", + "-l", + help="Length of private key digest file to generate (in bits). " + "3/4 Coding Scheme requires 192 bit key.", + choices=[192, 256], + default=256, + type=int, + ) + p.add_argument( + "digest_file", help="File to write 32 byte digest into", type=OutFileType() + ) + + p = subparsers.add_parser( + "generate_flash_encryption_key", + help="Generate a development-use flash encryption key with random data.", + ) + p.add_argument( + "--keylen", + "-l", + help="Length of private key digest file to generate (in bits). " + "3/4 Coding Scheme requires 192 bit key.", + choices=[128, 192, 256, 512], + default=256, + type=int, + ) + p.add_argument( + "key_file", + help="File to write 16, 24, 32 or 64 byte key into", + type=OutFileType(), + ) + + p = subparsers.add_parser( + "decrypt_flash_data", + help="Decrypt some data read from encrypted flash (using known key)", + ) + p.add_argument( + "encrypted_file", + help="File with encrypted flash contents", + type=argparse.FileType("rb"), + ) + p.add_argument( + "--aes_xts", + "-x", + help="Decrypt data using AES-XTS as used on " + "ESP32-S2, ESP32-C2, ESP32-C3 and ESP32-C6", + action="store_true", + ) + p.add_argument( + "--keyfile", + "-k", + help="File with flash encryption key", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "--output", + "-o", + help="Output file for plaintext data.", + type=OutFileType(), + required=True, + ) + p.add_argument( + "--address", + "-a", + help="Address offset in flash that file was read from.", + required=True, + type=esptool.arg_auto_int, + ) + p.add_argument( + "--flash_crypt_conf", + help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", + required=False, + default=0xF, + type=esptool.arg_auto_int, + ) + + p = subparsers.add_parser( + "encrypt_flash_data", + help="Encrypt some data suitable for encrypted flash (using known key)", + ) + p.add_argument( + "--aes_xts", + "-x", + help="Encrypt data using AES-XTS as used on " + "ESP32-S2, ESP32-C2, ESP32-C3 and ESP32-C6", + action="store_true", + ) + p.add_argument( + "--keyfile", + "-k", + help="File with flash encryption key", + type=argparse.FileType("rb"), + required=True, + ) + p.add_argument( + "--output", + "-o", + help="Output file for encrypted data.", + type=OutFileType(), + required=True, + ) + p.add_argument( + "--address", + "-a", + help="Address offset in flash where file will be flashed.", + required=True, + type=esptool.arg_auto_int, + ) + p.add_argument( + "--flash_crypt_conf", + help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", + required=False, + default=0xF, + type=esptool.arg_auto_int, + ) + p.add_argument( + "plaintext_file", + help="File with plaintext content for encrypting", + type=argparse.FileType("rb"), + ) + + args = parser.parse_args(custom_commandline) + print("espsecure.py v%s" % esptool.__version__) + if args.operation is None: + parser.print_help() + parser.exit(1) + + try: + # each 'operation' is a module-level function of the same name + operation_func = globals()[args.operation] + operation_func(args) + finally: + for arg_name in vars(args): + obj = getattr(args, arg_name) + if isinstance(obj, OutFileType): + obj.close() + + +def _main(): + try: + main() + except esptool.FatalError as e: + print("\nA fatal error occurred: %s" % e) + sys.exit(2) + + +if __name__ == "__main__": + _main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py new file mode 100644 index 000000000..8c8bce68f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import espsecure + +if __name__ == "__main__": + espsecure._main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA new file mode 100644 index 000000000..4b98bc87b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA @@ -0,0 +1,57 @@ +Metadata-Version: 2.1 +Name: esptool +Version: 4.4 +Summary: A serial utility to communicate & flash code to Espressif chips. +Home-page: https://github.com/espressif/esptool/ +Author: Fredrik Ahlberg (themadinventor) & Angus Gratton (projectgus) & Espressif Systems +Author-email: +License: GPLv2+ +Project-URL: Documentation, https://docs.espressif.com/projects/esptool/ +Project-URL: Source, https://github.com/espressif/esptool/ +Project-URL: Tracker, https://github.com/espressif/esptool/issues/ +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Topic :: Software Development :: Embedded Systems +Classifier: Environment :: Console +Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Requires-Python: >=3.7 +License-File: LICENSE +Requires-Dist: bitstring (<4,>=3.1.6) +Requires-Dist: cryptography (>=2.1.4) +Requires-Dist: ecdsa (>=0.16.0) +Requires-Dist: pyserial (>=3.0) +Requires-Dist: reedsolo (<=1.5.4,>=1.5.3) +Provides-Extra: dev +Requires-Dist: flake8 (>=3.2.0) ; extra == 'dev' +Requires-Dist: flake8-import-order ; extra == 'dev' +Requires-Dist: flake8-gl-codeclimate ; extra == 'dev' +Requires-Dist: pyelftools ; extra == 'dev' +Requires-Dist: coverage (~=6.0) ; extra == 'dev' +Requires-Dist: black ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Requires-Dist: pytest ; extra == 'dev' +Requires-Dist: pytest-rerunfailures ; extra == 'dev' + + +========== +esptool.py +========== +A Python-based, open-source, platform-independent utility to communicate with the ROM bootloader in Espressif chips. + +The esptool.py project is `hosted on github `_. + +Documentation +------------- +Visit online `esptool documentation `_ or run ``esptool.py -h``. + +Contributing +------------ +Please see the `contributions guide `_. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD new file mode 100644 index 000000000..833bb6279 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD @@ -0,0 +1,158 @@ +../../Scripts/espefuse.exe,sha256=dkn2als4JPVDDMaP0OTrNeLDak-Zkn3lxsv00iYU6ck,108448 +../../Scripts/espsecure.exe,sha256=fZ2fj9NAep5RwjYl49P6P-0i1GqTwek9uzwMrKsrsqI,108449 +../../Scripts/esptool.exe,sha256=jdWpeMW-4tgSEwF3CwbxmHIYrIhY3DMUX_lreFiGRyA,108447 +espefuse/__init__.py,sha256=IxmhijQR_moobpjCSXFLV4wA9hFFNCWbeGHTztinLQU,8950 +espefuse/__main__.py,sha256=pP8CMGPe1rCVGDJqgZqYLPnCgD5gLQTf1Z4s2vW4gyU,208 +espefuse/__pycache__/__init__.cpython-310.pyc,, +espefuse/__pycache__/__main__.cpython-310.pyc,, +espefuse/efuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +espefuse/efuse/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/__pycache__/base_fields.cpython-310.pyc,, +espefuse/efuse/__pycache__/base_operations.cpython-310.pyc,, +espefuse/efuse/__pycache__/emulate_efuse_controller_base.cpython-310.pyc,, +espefuse/efuse/__pycache__/mem_definition_base.cpython-310.pyc,, +espefuse/efuse/__pycache__/util.cpython-310.pyc,, +espefuse/efuse/base_fields.py,sha256=StmMxfERIDRE8Z9FaNctTkR2ztVOJiKy6TPiuxv0KPA,29506 +espefuse/efuse/base_operations.py,sha256=IqkJ_PgPHdVxTIa20o9qfUULjZ8vi8njG9Ax0WEONxk,24905 +espefuse/efuse/emulate_efuse_controller_base.py,sha256=oylxiJ7twW9OWwJ1WbC881g3W-UcXKLHH_xY2aztJyU,8717 +espefuse/efuse/esp32/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32/emulate_efuse_controller.py,sha256=AyJMxiEtNiI2v5EjGC4GdVD-thWkj1-oq_-yaf3yiK0,5274 +espefuse/efuse/esp32/fields.py,sha256=Guq9Civ35x6Z0W6b59DL691HYGzbw82Ns59Fqx8TOlM,18056 +espefuse/efuse/esp32/mem_definition.py,sha256=vn-gklCueJA0mbefbBxTWQFXmI8c6Zwj7A_Pgnk4qRk,10858 +espefuse/efuse/esp32/operations.py,sha256=KvgZ-Lj-fVDcr9X82u-sLFqWCo6_LeizrYZXMTyJNWY,12085 +espefuse/efuse/esp32c2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32c2/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32c2/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32c2/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32c2/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32c2/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32c2/emulate_efuse_controller.py,sha256=SSjvDnObh8vNY8WeMgTKHda7TMjF2kjjS4qsHGNdUk4,5094 +espefuse/efuse/esp32c2/fields.py,sha256=STyN-hwvV5bGn_dkvfjiFfY0ZNbhTEFO9xdTsAWjLps,15510 +espefuse/efuse/esp32c2/mem_definition.py,sha256=TO9kTOpvofOQmu7qchwWLZOKpLvHoAb_D1B0VXyJFUI,11581 +espefuse/efuse/esp32c2/operations.py,sha256=bxpzMAhLlqmC0jEJpoBuZeqcsz4bu9WG2QvO7RVH9jQ,12028 +espefuse/efuse/esp32c3/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32c3/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32c3/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32c3/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32c3/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32c3/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32c3/emulate_efuse_controller.py,sha256=rWP_e27_zajC8TRqVk3H4c1pyORkWi4myRW7yeTMYQo,3067 +espefuse/efuse/esp32c3/fields.py,sha256=pimQHVxIHRCqrouiSo5DVjlWzYRgl-xE24hFs5Q1XW8,18328 +espefuse/efuse/esp32c3/mem_definition.py,sha256=o65aXtvHCA4SWiqt-ZZygb7uQQcHVyeMODBFbC5hI-k,20804 +espefuse/efuse/esp32c3/operations.py,sha256=VqqkrU_YAmZF_YUCY43rhu0Mn4r07mR7gJ-cGSlTqw8,14701 +espefuse/efuse/esp32c6/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32c6/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32c6/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32c6/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32c6/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32c6/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32c6/emulate_efuse_controller.py,sha256=KjLSV_Z4nVsZOkbKQwLYwrBfUQA7wcHGiHjepcDKrm0,3062 +espefuse/efuse/esp32c6/fields.py,sha256=HoPUSP2sTvutdQAf_0Y_CvziMHq3r5g9Ppda4NhHh9Q,18323 +espefuse/efuse/esp32c6/mem_definition.py,sha256=WzoM0qkrq9Cnfn6VoMsXs6tYGwsngIIF8Ad_ffgdL6c,22138 +espefuse/efuse/esp32c6/operations.py,sha256=-7KbxPgsSzgpStfxfdnBSLPgXwyLv-hmjOPKp_VC9hY,14696 +espefuse/efuse/esp32h2beta1/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32h2beta1/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32h2beta1/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py,sha256=t02Qm5HVoXuHM5UHa4nU92zW4puUPi14dJhALvUbBLk,3074 +espefuse/efuse/esp32h2beta1/fields.py,sha256=94R3ub-9hBO9pzm5Y75KnURA7xOtJCWJHm6bmWVrtPs,18173 +espefuse/efuse/esp32h2beta1/mem_definition.py,sha256=7SLk03UWvcwYjCLpMB966vxwoBkieWqppkXlnQ38e4E,20226 +espefuse/efuse/esp32h2beta1/operations.py,sha256=zPF9a6xFywgCcmUVEUnLfRQlyvXWx9GNdwE-22r73wQ,14720 +espefuse/efuse/esp32s2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32s2/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32s2/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32s2/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32s2/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32s2/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32s2/emulate_efuse_controller.py,sha256=HR0pAp039_nFt8nd3GvfZkcosyMpvl9NxldtnAdc7wU,3073 +espefuse/efuse/esp32s2/fields.py,sha256=xbGHTfjqI7OenMt4AAWGz-r5g2RBhgFHVc-YCDD8Lug,20875 +espefuse/efuse/esp32s2/mem_definition.py,sha256=mn7zdqYPwFKB2TpsyVDhrJNYvYV1KEQvdnVswyuH9hY,23447 +espefuse/efuse/esp32s2/operations.py,sha256=FmyW79r1YKR11Po_CTx9gCoswKNTxBUYHplbbKM-TH8,18687 +espefuse/efuse/esp32s3/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32s3/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32s3/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32s3/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32s3/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32s3/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32s3/emulate_efuse_controller.py,sha256=o1mOn_kyoW6WOA6kAtofVh5LnuSohcajs51XVb8IM0Y,3067 +espefuse/efuse/esp32s3/fields.py,sha256=oI2k8XrHX2NgyIzyGZGXNxPut_hCbs6wLMt4VYrOKkE,19364 +espefuse/efuse/esp32s3/mem_definition.py,sha256=vaWKaEWvODSNeNDu88nw7ilErH5EA-sz8dVxR5jwDeQ,22532 +espefuse/efuse/esp32s3/operations.py,sha256=ZInCDIBVoYIkmFAnVJayonUzKbiLfqsdVgArTkNJlaY,18685 +espefuse/efuse/esp32s3beta2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 +espefuse/efuse/esp32s3beta2/__pycache__/__init__.cpython-310.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/emulate_efuse_controller.cpython-310.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/fields.cpython-310.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/mem_definition.cpython-310.pyc,, +espefuse/efuse/esp32s3beta2/__pycache__/operations.cpython-310.pyc,, +espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py,sha256=A775IK9X2c5d3-YPMo9UG_5JKoIWlvwQRQ3dt2d18Fc,3081 +espefuse/efuse/esp32s3beta2/fields.py,sha256=BJwQ-mzNRli2NPODeVqMEhOeUR-dD0VgeR5NAWCZna8,19378 +espefuse/efuse/esp32s3beta2/mem_definition.py,sha256=7_iyeXl55xP8MAylvmhWyBcfS8V3I5pFo34JyWbJslQ,21678 +espefuse/efuse/esp32s3beta2/operations.py,sha256=j97k5WN5qKCRI-tJ2DNXndItiJJstlsFQN6aqU85A9E,18692 +espefuse/efuse/mem_definition_base.py,sha256=hp20Xh18fztQ-z3jHEItG0yy30RiiuCQuThos7fQUww,1536 +espefuse/efuse/util.py,sha256=EaiTdZNMB4bIOJ_RacDKY2QsCruiwh4x9ezYgvr_GAU,1440 +espsecure/__init__.py,sha256=DgHXFbkrtrKpfh7imwWnPtnUK0N6BkzAg5mJnbVVh3o,53574 +espsecure/__main__.py,sha256=mKzSjejVZ7TjYNhTvXt_ew0zojFAhzdzgNo2BJjec1A,210 +espsecure/__pycache__/__init__.cpython-310.pyc,, +espsecure/__pycache__/__main__.cpython-310.pyc,, +esptool-4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +esptool-4.4.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092 +esptool-4.4.dist-info/METADATA,sha256=vUCjsNfMZNhi53efzY226aUHjCGFQS41ojxvOXK4u_A,2400 +esptool-4.4.dist-info/RECORD,, +esptool-4.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +esptool-4.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +esptool-4.4.dist-info/entry_points.txt,sha256=-hWHXAe1Iw9pcrfRGaz6tKWIwFZRm3ymAHNXUHzb4rA,132 +esptool-4.4.dist-info/top_level.txt,sha256=e2gykZuzaB-2vJ5_XCe55aAhEDxjeeS6MvOWLu8z6JM,27 +esptool/__init__.py,sha256=vyvoQTlq_hpcQfzIBc7V0JvV8CiC1ZSLKo9SRIMvrhU,34821 +esptool/__main__.py,sha256=S0uflKbalJG1-suKoRZmxaUSE7Pld-rrIr9bYYwHN9U,270 +esptool/__pycache__/__init__.cpython-310.pyc,, +esptool/__pycache__/__main__.cpython-310.pyc,, +esptool/__pycache__/bin_image.cpython-310.pyc,, +esptool/__pycache__/cmds.cpython-310.pyc,, +esptool/__pycache__/loader.cpython-310.pyc,, +esptool/__pycache__/util.cpython-310.pyc,, +esptool/bin_image.py,sha256=BRmqb4S81pM-zf1uNST7d8JRLmBgOMN5QJNCt6SWJ1s,45923 +esptool/cmds.py,sha256=MTDNVE6-VJBalYjqpY4_IpgZSaeZYmYeKK9F0fuXnUw,43708 +esptool/loader.py,sha256=Y5J5eKjxdnOsK-3VWAifCGHHwhcb0rjQgxXj6O54jCI,59612 +esptool/targets/__init__.py,sha256=X0Z-3YoofE4ol5foeHVfl4z3L_npAYBDsY79weXZUVY,807 +esptool/targets/__pycache__/__init__.cpython-310.pyc,, +esptool/targets/__pycache__/esp32.cpython-310.pyc,, +esptool/targets/__pycache__/esp32c2.cpython-310.pyc,, +esptool/targets/__pycache__/esp32c3.cpython-310.pyc,, +esptool/targets/__pycache__/esp32c6.cpython-310.pyc,, +esptool/targets/__pycache__/esp32c6beta.cpython-310.pyc,, +esptool/targets/__pycache__/esp32h2beta1.cpython-310.pyc,, +esptool/targets/__pycache__/esp32h2beta2.cpython-310.pyc,, +esptool/targets/__pycache__/esp32s2.cpython-310.pyc,, +esptool/targets/__pycache__/esp32s3.cpython-310.pyc,, +esptool/targets/__pycache__/esp32s3beta2.cpython-310.pyc,, +esptool/targets/__pycache__/esp8266.cpython-310.pyc,, +esptool/targets/esp32.py,sha256=N1GYWUCC-5gwQrJGDzHkQpW-4hFi0Ixuo_4yeRyA774,12188 +esptool/targets/esp32c2.py,sha256=evZw-tr4g7TkaoucsQcTvAOFCzGodLkcxEftIaWXp4Q,5697 +esptool/targets/esp32c3.py,sha256=sSVJhhC1URtOgfaRLC49-t6T-VBBoOIRKeTMmbnaTTg,5871 +esptool/targets/esp32c6.py,sha256=EwnFcnjj3eAqiDLOQ6q2YK4W8zsE3dy7VA5C8hV1zj8,5787 +esptool/targets/esp32c6beta.py,sha256=9zyZiyoMzP2BGVpVaLiuEciWbrL4svZ1UiNF_Satkmo,697 +esptool/targets/esp32h2beta1.py,sha256=aFnRdkG4lhkWF9npZC4GK-V_8bZq0yEvDpEVWy8oeU8,4970 +esptool/targets/esp32h2beta2.py,sha256=d1DE8b5XE4aZ0XLSxbB1mc1u00Nw8dDcz9JPjjY9zDk,1391 +esptool/targets/esp32s2.py,sha256=9NHzIv_vTLqQ62lOFcDzIh6_BABi-1ONY-sjAhHMfYk,10523 +esptool/targets/esp32s3.py,sha256=JZnI3VKjXGjwGjYS43DM-IqrQSO7e2Atr57MFPxd4TU,9592 +esptool/targets/esp32s3beta2.py,sha256=EfdXmk3zminZH6wpYvNnfQttwjBbMqXIYJrUdFXfi-g,1318 +esptool/targets/esp8266.py,sha256=xWRgokg_1rHv6-8hwXhdARkQ2gBEZI9mvUBvBSvx4RY,5932 +esptool/targets/stub_flasher/stub_flasher_32.json,sha256=Etgqi1hficCSH8ZOyT-gKP5743C8pEneflSnnM2OHxg,4703 +esptool/targets/stub_flasher/stub_flasher_32c2.json,sha256=h3bi_IHBEMSsBrKyDpqlftjyHVX0APbp2H5GzgOJFhU,4695 +esptool/targets/stub_flasher/stub_flasher_32c3.json,sha256=pzSkChVRPqgyM7zBtj0JsCwdgZCS6TR_o_efnmrjrp0,5131 +esptool/targets/stub_flasher/stub_flasher_32c6.json,sha256=qQ2rUv_uSOuEaUeQa7fLcEbACr57lnS0cShFcZ1Gq4I,4695 +esptool/targets/stub_flasher/stub_flasher_32c6beta.json,sha256=TQATNG269T70dsUf6sxUkRTSTcfwgIpakhCcGHYhBjI,4711 +esptool/targets/stub_flasher/stub_flasher_32h2beta1.json,sha256=9sfin8uYjzRE-D20V1aclwim2d9JWpuE7BEqY0A0cww,4711 +esptool/targets/stub_flasher/stub_flasher_32h2beta2.json,sha256=6QZT9ZVvERWWS5RKCdyiogvO___FVJiEdwHk-WpaARA,4711 +esptool/targets/stub_flasher/stub_flasher_32s2.json,sha256=joAAtknusB38uoEF4NS2W55Lezpb0EgljbZLXtjreUs,5883 +esptool/targets/stub_flasher/stub_flasher_32s3.json,sha256=LQTAc_NrIwhXLr8_vpQVl2lcI1OqrWD-be-EeVL5KYc,6863 +esptool/targets/stub_flasher/stub_flasher_32s3beta2.json,sha256=iMaFloTtK2EONnoGIPev__MNbe-CPl_dnRiuie9C9CA,6863 +esptool/targets/stub_flasher/stub_flasher_8266.json,sha256=pkkCxKtOuqSiM2VrTqLvDDGahqPB6tUxHM6xhz0Yqx4,12131 +esptool/util.py,sha256=eTDp7gWzdtbDilqJ9d9-tFqvMHgLuBEEgMXWuILPu5w,4829 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/REQUESTED b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/WHEEL new file mode 100644 index 000000000..57e3d840d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt new file mode 100644 index 000000000..6dd569c0f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt @@ -0,0 +1,4 @@ +[console_scripts] +espefuse.py = espefuse.__init__:_main +espsecure.py = espsecure.__init__:_main +esptool.py = esptool.__init__:_main diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt new file mode 100644 index 000000000..34928a1fc --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt @@ -0,0 +1,3 @@ +espefuse +espsecure +esptool diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py new file mode 100644 index 000000000..809ccc983 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py @@ -0,0 +1,1045 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +__all__ = [ + "chip_id", + "detect_chip", + "dump_mem", + "elf2image", + "erase_flash", + "erase_region", + "flash_id", + "get_security_info", + "image_info", + "load_ram", + "make_image", + "merge_bin", + "read_flash", + "read_flash_status", + "read_mac", + "read_mem", + "run", + "verify_flash", + "version", + "write_flash", + "write_flash_status", + "write_mem", +] + +__version__ = "4.4" + +import argparse +import inspect +import os +import shlex +import sys +import time + +from esptool.cmds import ( + chip_id, + detect_chip, + detect_flash_size, + dump_mem, + elf2image, + erase_flash, + erase_region, + flash_id, + get_security_info, + image_info, + load_ram, + make_image, + merge_bin, + read_flash, + read_flash_status, + read_mac, + read_mem, + run, + verify_flash, + version, + write_flash, + write_flash_status, + write_mem, +) +from esptool.loader import DEFAULT_CONNECT_ATTEMPTS, ESPLoader, list_ports +from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM +from esptool.util import ( + FatalError, + NotImplementedInROMError, + flash_size_bytes, +) + +import serial + + +def main(argv=None, esp=None): + """ + Main function for esptool + + argv - Optional override for default arguments parsing (that uses sys.argv), + can be a list of custom arguments as strings. Arguments and their values + need to be added as individual items to the list + e.g. "-b 115200" thus becomes ['-b', '115200']. + + esp - Optional override of the connected device previously + returned by get_default_connected_device() + """ + + external_esp = esp is not None + + parser = argparse.ArgumentParser( + description="esptool.py v%s - Espressif chips ROM Bootloader Utility" + % __version__, + prog="esptool", + ) + + parser.add_argument( + "--chip", + "-c", + help="Target chip type", + type=lambda c: c.lower().replace("-", ""), # support ESP32-S2, etc. + choices=["auto"] + CHIP_LIST, + default=os.environ.get("ESPTOOL_CHIP", "auto"), + ) + + parser.add_argument( + "--port", + "-p", + help="Serial port device", + default=os.environ.get("ESPTOOL_PORT", None), + ) + + parser.add_argument( + "--baud", + "-b", + help="Serial port baud rate used when flashing/reading", + type=arg_auto_int, + default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD), + ) + + parser.add_argument( + "--before", + help="What to do before connecting to the chip", + choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], + default=os.environ.get("ESPTOOL_BEFORE", "default_reset"), + ) + + parser.add_argument( + "--after", + "-a", + help="What to do after esptool.py is finished", + choices=["hard_reset", "soft_reset", "no_reset", "no_reset_stub"], + default=os.environ.get("ESPTOOL_AFTER", "hard_reset"), + ) + + parser.add_argument( + "--no-stub", + help="Disable launching the flasher stub, only talk to ROM bootloader. " + "Some features will not be available.", + action="store_true", + ) + + parser.add_argument( + "--trace", + "-t", + help="Enable trace-level output of esptool.py interactions.", + action="store_true", + ) + + parser.add_argument( + "--override-vddsdio", + help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", + choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, + nargs="?", + ) + + parser.add_argument( + "--connect-attempts", + help=( + "Number of attempts to connect, negative or 0 for infinite. " + "Default: %d." % DEFAULT_CONNECT_ATTEMPTS + ), + type=int, + default=os.environ.get("ESPTOOL_CONNECT_ATTEMPTS", DEFAULT_CONNECT_ATTEMPTS), + ) + + subparsers = parser.add_subparsers( + dest="operation", help="Run esptool.py {command} -h for additional help" + ) + + def add_spi_connection_arg(parent): + parent.add_argument( + "--spi-connection", + "-sc", + help="ESP32-only argument. Override default SPI Flash connection. " + "Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers " + "to use for SPI flash (CLK,Q,D,HD,CS).", + action=SpiConnectionAction, + ) + + parser_load_ram = subparsers.add_parser( + "load_ram", help="Download an image to RAM and execute" + ) + parser_load_ram.add_argument("filename", help="Firmware image") + + parser_dump_mem = subparsers.add_parser( + "dump_mem", help="Dump arbitrary memory to disk" + ) + parser_dump_mem.add_argument("address", help="Base address", type=arg_auto_int) + parser_dump_mem.add_argument( + "size", help="Size of region to dump", type=arg_auto_int + ) + parser_dump_mem.add_argument("filename", help="Name of binary dump") + + parser_read_mem = subparsers.add_parser( + "read_mem", help="Read arbitrary memory location" + ) + parser_read_mem.add_argument("address", help="Address to read", type=arg_auto_int) + + parser_write_mem = subparsers.add_parser( + "write_mem", help="Read-modify-write to arbitrary memory location" + ) + parser_write_mem.add_argument("address", help="Address to write", type=arg_auto_int) + parser_write_mem.add_argument("value", help="Value", type=arg_auto_int) + parser_write_mem.add_argument( + "mask", + help="Mask of bits to write", + type=arg_auto_int, + nargs="?", + default="0xFFFFFFFF", + ) + + def add_spi_flash_subparsers(parent, allow_keep, auto_detect): + """Add common parser arguments for SPI flash properties""" + extra_keep_args = ["keep"] if allow_keep else [] + + if auto_detect and allow_keep: + extra_fs_message = ", detect, or keep" + flash_sizes = ["detect", "keep"] + elif auto_detect: + extra_fs_message = ", or detect" + flash_sizes = ["detect"] + elif allow_keep: + extra_fs_message = ", or keep" + flash_sizes = ["keep"] + else: + extra_fs_message = "" + flash_sizes = [] + + parent.add_argument( + "--flash_freq", + "-ff", + help="SPI Flash frequency", + choices=extra_keep_args + + [ + "80m", + "60m", + "48m", + "40m", + "30m", + "26m", + "24m", + "20m", + "16m", + "15m", + "12m", + ], + default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None), + ) + parent.add_argument( + "--flash_mode", + "-fm", + help="SPI Flash mode", + choices=extra_keep_args + ["qio", "qout", "dio", "dout"], + default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"), + ) + parent.add_argument( + "--flash_size", + "-fs", + help="SPI Flash size in MegaBytes " + "(1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB) " + "plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)" + extra_fs_message, + choices=flash_sizes + + [ + "256KB", + "512KB", + "1MB", + "2MB", + "2MB-c1", + "4MB", + "4MB-c1", + "8MB", + "16MB", + "32MB", + "64MB", + "128MB", + ], + default=os.environ.get("ESPTOOL_FS", "keep" if allow_keep else "1MB"), + ) + add_spi_connection_arg(parent) + + parser_write_flash = subparsers.add_parser( + "write_flash", help="Write a binary blob to flash" + ) + + parser_write_flash.add_argument( + "addr_filename", + metavar="
      ", + help="Address followed by binary filename, separated by space", + action=AddrFilenamePairAction, + ) + parser_write_flash.add_argument( + "--erase-all", + "-e", + help="Erase all regions of flash (not just write areas) before programming", + action="store_true", + ) + + add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) + parser_write_flash.add_argument( + "--no-progress", "-p", help="Suppress progress output", action="store_true" + ) + parser_write_flash.add_argument( + "--verify", + help="Verify just-written data on flash " + "(mostly superfluous, data is read back during flashing)", + action="store_true", + ) + parser_write_flash.add_argument( + "--encrypt", + help="Apply flash encryption when writing data " + "(required correct efuse settings)", + action="store_true", + ) + # In order to not break backward compatibility, + # our list of encrypted files to flash is a new parameter + parser_write_flash.add_argument( + "--encrypt-files", + metavar="
      ", + help="Files to be encrypted on the flash. " + "Address followed by binary filename, separated by space.", + action=AddrFilenamePairAction, + ) + parser_write_flash.add_argument( + "--ignore-flash-encryption-efuse-setting", + help="Ignore flash encryption efuse settings ", + action="store_true", + ) + parser_write_flash.add_argument( + "--force", + help="Force write, skip security and compatibility checks. Use with caution!", + action="store_true", + ) + + compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) + compress_args.add_argument( + "--compress", + "-z", + help="Compress data in transfer (default unless --no-stub is specified)", + action="store_true", + default=None, + ) + compress_args.add_argument( + "--no-compress", + "-u", + help="Disable data compression during transfer " + "(default if --no-stub is specified)", + action="store_true", + ) + + subparsers.add_parser("run", help="Run application code in flash") + + parser_image_info = subparsers.add_parser( + "image_info", help="Dump headers from an application image" + ) + parser_image_info.add_argument("filename", help="Image file to parse") + parser_image_info.add_argument( + "--version", + "-v", + help="Output format version (1 - legacy, 2 - extended)", + choices=["1", "2"], + default="1", + ) + + parser_make_image = subparsers.add_parser( + "make_image", help="Create an application image from binary files" + ) + parser_make_image.add_argument("output", help="Output image file") + parser_make_image.add_argument( + "--segfile", "-f", action="append", help="Segment input file" + ) + parser_make_image.add_argument( + "--segaddr", + "-a", + action="append", + help="Segment base address", + type=arg_auto_int, + ) + parser_make_image.add_argument( + "--entrypoint", + "-e", + help="Address of entry point", + type=arg_auto_int, + default=0, + ) + + parser_elf2image = subparsers.add_parser( + "elf2image", help="Create an application image from ELF file" + ) + parser_elf2image.add_argument("input", help="Input ELF file") + parser_elf2image.add_argument( + "--output", + "-o", + help="Output filename prefix (for version 1 image), " + "or filename (for version 2 single image)", + type=str, + ) + parser_elf2image.add_argument( + "--version", + "-e", + help="Output image version", + choices=["1", "2", "3"], + default="1", + ) + parser_elf2image.add_argument( + # it kept for compatibility + # Minimum chip revision (deprecated, consider using --min-rev-full) + "--min-rev", + "-r", + help=argparse.SUPPRESS, + type=int, + choices=range(256), + metavar="{0, ... 255}", + default=0, + ) + parser_elf2image.add_argument( + "--min-rev-full", + help="Minimal chip revision (in format: major * 100 + minor)", + type=int, + choices=range(65536), + metavar="{0, ... 65535}", + default=0, + ) + parser_elf2image.add_argument( + "--max-rev-full", + help="Maximal chip revision (in format: major * 100 + minor)", + type=int, + choices=range(65536), + metavar="{0, ... 65535}", + default=65535, + ) + parser_elf2image.add_argument( + "--secure-pad", + action="store_true", + help="Pad image so once signed it will end on a 64KB boundary. " + "For Secure Boot v1 images only.", + ) + parser_elf2image.add_argument( + "--secure-pad-v2", + action="store_true", + help="Pad image to 64KB, so once signed its signature sector will" + "start at the next 64K block. For Secure Boot v2 images only.", + ) + parser_elf2image.add_argument( + "--elf-sha256-offset", + help="If set, insert SHA256 hash (32 bytes) of the input ELF file " + "at specified offset in the binary.", + type=arg_auto_int, + default=None, + ) + parser_elf2image.add_argument( + "--dont-append-digest", + dest="append_digest", + help="Don't append a SHA256 digest of the entire image after the checksum. " + "This argument is not supported and ignored for ESP8266.", + action="store_false", + default=True, + ) + parser_elf2image.add_argument( + "--use_segments", + help="If set, ELF segments will be used instead of ELF sections " + "to genereate the image.", + action="store_true", + ) + parser_elf2image.add_argument( + "--flash-mmu-page-size", + help="Change flash MMU page size.", + choices=["64KB", "32KB", "16KB", "8KB"], + ) + + add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) + + subparsers.add_parser("read_mac", help="Read MAC address from OTP ROM") + + subparsers.add_parser("chip_id", help="Read Chip ID from OTP ROM") + + parser_flash_id = subparsers.add_parser( + "flash_id", help="Read SPI flash manufacturer and device ID" + ) + add_spi_connection_arg(parser_flash_id) + + parser_read_status = subparsers.add_parser( + "read_flash_status", help="Read SPI flash status register" + ) + + add_spi_connection_arg(parser_read_status) + parser_read_status.add_argument( + "--bytes", + help="Number of bytes to read (1-3)", + type=int, + choices=[1, 2, 3], + default=2, + ) + + parser_write_status = subparsers.add_parser( + "write_flash_status", help="Write SPI flash status register" + ) + + add_spi_connection_arg(parser_write_status) + parser_write_status.add_argument( + "--non-volatile", + help="Write non-volatile bits (use with caution)", + action="store_true", + ) + parser_write_status.add_argument( + "--bytes", + help="Number of status bytes to write (1-3)", + type=int, + choices=[1, 2, 3], + default=2, + ) + parser_write_status.add_argument("value", help="New value", type=arg_auto_int) + + parser_read_flash = subparsers.add_parser( + "read_flash", help="Read SPI flash content" + ) + add_spi_connection_arg(parser_read_flash) + parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int) + parser_read_flash.add_argument( + "size", help="Size of region to dump", type=arg_auto_int + ) + parser_read_flash.add_argument("filename", help="Name of binary dump") + parser_read_flash.add_argument( + "--no-progress", "-p", help="Suppress progress output", action="store_true" + ) + + parser_verify_flash = subparsers.add_parser( + "verify_flash", help="Verify a binary blob against flash" + ) + parser_verify_flash.add_argument( + "addr_filename", + help="Address and binary file to verify there, separated by space", + action=AddrFilenamePairAction, + ) + parser_verify_flash.add_argument( + "--diff", "-d", help="Show differences", choices=["no", "yes"], default="no" + ) + add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) + + parser_erase_flash = subparsers.add_parser( + "erase_flash", help="Perform Chip Erase on SPI flash" + ) + parser_erase_flash.add_argument( + "--force", + help="Erase flash even if security features are enabled. Use with caution!", + action="store_true", + ) + add_spi_connection_arg(parser_erase_flash) + + parser_erase_region = subparsers.add_parser( + "erase_region", help="Erase a region of the flash" + ) + parser_erase_region.add_argument( + "--force", + help="Erase region even if security features are enabled. Use with caution!", + action="store_true", + ) + add_spi_connection_arg(parser_erase_region) + parser_erase_region.add_argument( + "address", help="Start address (must be multiple of 4096)", type=arg_auto_int + ) + parser_erase_region.add_argument( + "size", + help="Size of region to erase (must be multiple of 4096)", + type=arg_auto_int, + ) + + parser_merge_bin = subparsers.add_parser( + "merge_bin", + help="Merge multiple raw binary files into a single file for later flashing", + ) + + parser_merge_bin.add_argument( + "--output", "-o", help="Output filename", type=str, required=True + ) + parser_merge_bin.add_argument( + "--format", "-f", help="Format of the output file", choices="raw", default="raw" + ) # for future expansion + add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) + + parser_merge_bin.add_argument( + "--target-offset", + "-t", + help="Target offset where the output file will be flashed", + type=arg_auto_int, + default=0, + ) + parser_merge_bin.add_argument( + "--fill-flash-size", + help="If set, the final binary file will be padded with FF " + "bytes up to this flash size.", + choices=[ + "256KB", + "512KB", + "1MB", + "2MB", + "4MB", + "8MB", + "16MB", + "32MB", + "64MB", + "128MB", + ], + ) + parser_merge_bin.add_argument( + "addr_filename", + metavar="
      ", + help="Address followed by binary filename, separated by space", + action=AddrFilenamePairAction, + ) + + subparsers.add_parser("get_security_info", help="Get some security-related data") + + subparsers.add_parser("version", help="Print esptool version") + + # internal sanity check - every operation matches a module function of the same name + for operation in subparsers.choices.keys(): + assert operation in globals(), "%s should be a module function" % operation + + argv = expand_file_arguments(argv or sys.argv[1:]) + + args = parser.parse_args(argv) + print("esptool.py v%s" % __version__) + + # operation function can take 1 arg (args), 2 args (esp, arg) + # or be a member function of the ESPLoader class. + + if args.operation is None: + parser.print_help() + sys.exit(1) + + # Forbid the usage of both --encrypt, which means encrypt all the given files, + # and --encrypt-files, which represents the list of files to encrypt. + # The reason is that allowing both at the same time increases the chances of + # having contradictory lists (e.g. one file not available in one of list). + if ( + args.operation == "write_flash" + and args.encrypt + and args.encrypt_files is not None + ): + raise FatalError( + "Options --encrypt and --encrypt-files " + "must not be specified at the same time." + ) + + operation_func = globals()[args.operation] + operation_args = inspect.getfullargspec(operation_func).args + + if ( + operation_args[0] == "esp" + ): # operation function takes an ESPLoader connection object + if args.before != "no_reset_no_sync": + initial_baud = min( + ESPLoader.ESP_ROM_BAUD, args.baud + ) # don't sync faster than the default baud rate + else: + initial_baud = args.baud + + if args.port is None: + ser_list = get_port_list() + print("Found %d serial ports" % len(ser_list)) + else: + ser_list = [args.port] + esp = esp or get_default_connected_device( + ser_list, + port=args.port, + connect_attempts=args.connect_attempts, + initial_baud=initial_baud, + chip=args.chip, + trace=args.trace, + before=args.before, + ) + + if esp is None: + raise FatalError( + "Could not connect to an Espressif device " + "on any of the %d available serial ports." % len(ser_list) + ) + + if esp.secure_download_mode: + print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) + else: + print("Chip is %s" % (esp.get_chip_description())) + print("Features: %s" % ", ".join(esp.get_chip_features())) + print("Crystal is %dMHz" % esp.get_crystal_freq()) + read_mac(esp, args) + + if not args.no_stub: + if esp.secure_download_mode: + print( + "WARNING: Stub loader is not supported in Secure Download Mode, " + "setting --no-stub" + ) + args.no_stub = True + elif not esp.IS_STUB and esp.stub_is_disabled: + print( + "WARNING: Stub loader has been disabled for compatibility, " + "setting --no-stub" + ) + args.no_stub = True + else: + esp = esp.run_stub() + + if args.override_vddsdio: + esp.override_vddsdio(args.override_vddsdio) + + if args.baud > initial_baud: + try: + esp.change_baud(args.baud) + except NotImplementedInROMError: + print( + "WARNING: ROM doesn't support changing baud rate. " + "Keeping initial baud rate %d" % initial_baud + ) + + # override common SPI flash parameter stuff if configured to do so + if hasattr(args, "spi_connection") and args.spi_connection is not None: + if esp.CHIP_NAME != "ESP32": + raise FatalError( + "Chip %s does not support --spi-connection option." % esp.CHIP_NAME + ) + print("Configuring SPI flash mode...") + esp.flash_spi_attach(args.spi_connection) + elif args.no_stub: + print("Enabling default SPI flash mode...") + # ROM loader doesn't enable flash unless we explicitly do it + esp.flash_spi_attach(0) + + # XMC chip startup sequence + XMC_VENDOR_ID = 0x20 + + def is_xmc_chip_strict(): + id = esp.flash_id() + rdid = ((id & 0xFF) << 16) | ((id >> 16) & 0xFF) | (id & 0xFF00) + + vendor_id = (rdid >> 16) & 0xFF + mfid = (rdid >> 8) & 0xFF + cpid = rdid & 0xFF + + if vendor_id != XMC_VENDOR_ID: + return False + + matched = False + if mfid == 0x40: + if cpid >= 0x13 and cpid <= 0x20: + matched = True + elif mfid == 0x41: + if cpid >= 0x17 and cpid <= 0x20: + matched = True + elif mfid == 0x50: + if cpid >= 0x15 and cpid <= 0x16: + matched = True + return matched + + def flash_xmc_startup(): + # If the RDID value is a valid XMC one, may skip the flow + fast_check = True + if fast_check and is_xmc_chip_strict(): + return # Successful XMC flash chip boot-up detected by RDID, skipping. + + sfdp_mfid_addr = 0x10 + mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8) + if mf_id != XMC_VENDOR_ID: # Non-XMC chip detected by SFDP Read, skipping. + return + + print( + "WARNING: XMC flash chip boot-up failure detected! " + "Running XMC25QHxxC startup flow" + ) + esp.run_spiflash_command(0xB9) # Enter DPD + esp.run_spiflash_command(0x79) # Enter UDPD + esp.run_spiflash_command(0xFF) # Exit UDPD + time.sleep(0.002) # Delay tXUDPD + esp.run_spiflash_command(0xAB) # Release Power-Down + time.sleep(0.00002) + # Check for success + if not is_xmc_chip_strict(): + print("WARNING: XMC flash boot-up fix failed.") + print("XMC flash chip boot-up fix successful!") + + # Check flash chip connection + if not esp.secure_download_mode: + try: + flash_id = esp.flash_id() + if flash_id in (0xFFFFFF, 0x000000): + print( + "WARNING: Failed to communicate with the flash chip, " + "read/write operations will fail. " + "Try checking the chip connections or removing " + "any other hardware connected to IOs." + ) + except Exception as e: + esp.trace("Unable to verify flash chip connection ({}).".format(e)) + + # Check if XMC SPI flash chip booted-up successfully, fix if not + if not esp.secure_download_mode: + try: + flash_xmc_startup() + except Exception as e: + esp.trace( + "Unable to perform XMC flash chip startup sequence ({}).".format(e) + ) + + if hasattr(args, "flash_size"): + print("Configuring flash size...") + detect_flash_size(esp, args) + if args.flash_size != "keep": # TODO: should set this even with 'keep' + esp.flash_set_parameters(flash_size_bytes(args.flash_size)) + # Check if stub supports chosen flash size + if esp.IS_STUB and args.flash_size in ("32MB", "64MB", "128MB"): + print( + "WARNING: Flasher stub doesn't fully support flash size larger " + "than 16MB, in case of failure use --no-stub." + ) + + if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"): + if args.address + args.size > 0x1000000: + print( + "WARNING: Flasher stub doesn't fully support flash size larger " + "than 16MB, in case of failure use --no-stub." + ) + + try: + operation_func(esp, args) + finally: + try: # Clean up AddrFilenamePairAction files + for address, argfile in args.addr_filename: + argfile.close() + except AttributeError: + pass + + # Handle post-operation behaviour (reset or other) + if operation_func == load_ram: + # the ESP is now running the loaded image, so let it run + print("Exiting immediately.") + elif args.after == "hard_reset": + esp.hard_reset() + elif args.after == "soft_reset": + print("Soft resetting...") + # flash_finish will trigger a soft reset + esp.soft_reset(False) + elif args.after == "no_reset_stub": + print("Staying in flasher stub.") + else: # args.after == 'no_reset' + print("Staying in bootloader.") + if esp.IS_STUB: + esp.soft_reset(True) # exit stub back to ROM loader + + if not external_esp: + esp._port.close() + + else: + operation_func(args) + + +def arg_auto_int(x): + return int(x, 0) + + +def get_port_list(): + if list_ports is None: + raise FatalError( + "Listing all serial ports is currently not available. " + "Please try to specify the port when running esptool.py or update " + "the pyserial package to the latest version" + ) + return sorted(ports.device for ports in list_ports.comports()) + + +def expand_file_arguments(argv): + """ + Any argument starting with "@" gets replaced with all values read from a text file. + Text file arguments can be split by newline or by space. + Values are added "as-is", as if they were specified in this order + on the command line. + """ + new_args = [] + expanded = False + for arg in argv: + if arg.startswith("@"): + expanded = True + with open(arg[1:], "r") as f: + for line in f.readlines(): + new_args += shlex.split(line) + else: + new_args.append(arg) + if expanded: + print("esptool %s" % (" ".join(new_args[1:]))) + return new_args + return argv + + +def get_default_connected_device( + serial_list, + port, + connect_attempts, + initial_baud, + chip="auto", + trace=False, + before="default_reset", +): + _esp = None + for each_port in reversed(serial_list): + print("Serial port %s" % each_port) + try: + if chip == "auto": + _esp = detect_chip( + each_port, initial_baud, before, trace, connect_attempts + ) + else: + chip_class = CHIP_DEFS[chip] + _esp = chip_class(each_port, initial_baud, trace) + _esp.connect(before, connect_attempts) + break + except (FatalError, OSError) as err: + if port is not None: + raise + print("%s failed to connect: %s" % (each_port, err)) + if _esp and _esp._port: + _esp._port.close() + _esp = None + return _esp + + +class SpiConnectionAction(argparse.Action): + """ + Custom action to parse 'spi connection' override. + Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. + """ + + def __call__(self, parser, namespace, value, option_string=None): + if value.upper() == "SPI": + value = 0 + elif value.upper() == "HSPI": + value = 1 + elif "," in value: + values = value.split(",") + if len(values) != 5: + raise argparse.ArgumentError( + self, + "%s is not a valid list of comma-separate pin numbers. " + "Must be 5 numbers - CLK,Q,D,HD,CS." % value, + ) + try: + values = tuple(int(v, 0) for v in values) + except ValueError: + raise argparse.ArgumentError( + self, + "%s is not a valid argument. All pins must be numeric values" + % values, + ) + if any([v for v in values if v > 33 or v < 0]): + raise argparse.ArgumentError( + self, "Pin numbers must be in the range 0-33." + ) + # encode the pin numbers as a 32-bit integer with packed 6-bit values, + # the same way ESP32 ROM takes them + # TODO: make this less ESP32 ROM specific somehow... + clk, q, d, hd, cs = values + value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk + else: + raise argparse.ArgumentError( + self, + "%s is not a valid spi-connection value. " + "Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS)." + % value, + ) + setattr(namespace, self.dest, value) + + +class AddrFilenamePairAction(argparse.Action): + """Custom parser class for the address/filename pairs passed as arguments""" + + def __init__(self, option_strings, dest, nargs="+", **kwargs): + super(AddrFilenamePairAction, self).__init__( + option_strings, dest, nargs, **kwargs + ) + + def __call__(self, parser, namespace, values, option_string=None): + # validate pair arguments + pairs = [] + for i in range(0, len(values), 2): + try: + address = int(values[i], 0) + except ValueError: + raise argparse.ArgumentError( + self, 'Address "%s" must be a number' % values[i] + ) + try: + argfile = open(values[i + 1], "rb") + except IOError as e: + raise argparse.ArgumentError(self, e) + except IndexError: + raise argparse.ArgumentError( + self, + "Must be pairs of an address " + "and the binary filename to write there", + ) + pairs.append((address, argfile)) + + # Sort the addresses and check for overlapping + end = 0 + for address, argfile in sorted(pairs, key=lambda x: x[0]): + argfile.seek(0, 2) # seek to end + size = argfile.tell() + argfile.seek(0) + sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + sector_end = ( + (address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) + & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) + ) - 1 + if sector_start < end: + message = "Detected overlap at address: 0x%x for file: %s" % ( + address, + argfile.name, + ) + raise argparse.ArgumentError(self, message) + end = sector_end + setattr(namespace, self.dest, pairs) + + +def _main(): + try: + main() + except FatalError as e: + print(f"\nA fatal error occurred: {e}") + sys.exit(2) + except serial.serialutil.SerialException as e: + print(f"\nA serial exception error occurred: {e}") + print( + "Note: This error originates from pySerial. " + "It is likely not a problem with esptool, " + "but with the hardware connection or drivers." + ) + print( + "For troubleshooting steps visit: " + "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html" + ) + sys.exit(1) + + +if __name__ == "__main__": + _main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py new file mode 100644 index 000000000..e5b9358f6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import esptool + +if __name__ == "__main__": + esptool._main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py new file mode 100644 index 000000000..9c441d625 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py @@ -0,0 +1,1221 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import binascii +import copy +import hashlib +import io +import os +import re +import struct + +from .loader import ESPLoader +from .targets import ( + ESP32C2ROM, + ESP32C3ROM, + ESP32C6BETAROM, + ESP32C6ROM, + ESP32H2BETA1ROM, + ESP32H2BETA2ROM, + ESP32ROM, + ESP32S2ROM, + ESP32S3BETA2ROM, + ESP32S3ROM, + ESP8266ROM, +) +from .util import FatalError, byte, pad_to + + +def align_file_position(f, size): + """Align the position in the file to the next block of specified size""" + align = (size - 1) - (f.tell() % size) + f.seek(align, 1) + + +def LoadFirmwareImage(chip, image_file): + """ + Load a firmware image. Can be for any supported SoC. + + ESP8266 images will be examined to determine if they are original ROM firmware + images (ESP8266ROMFirmwareImage) or "v2" OTA bootloader images. + + Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) + or ESP8266V2FirmwareImage (v2). + """ + + def select_image_class(f, chip): + chip = re.sub(r"[-()]", "", chip.lower()) + if chip != "esp8266": + return { + "esp32": ESP32FirmwareImage, + "esp32s2": ESP32S2FirmwareImage, + "esp32s3beta2": ESP32S3BETA2FirmwareImage, + "esp32s3": ESP32S3FirmwareImage, + "esp32c3": ESP32C3FirmwareImage, + "esp32c6beta": ESP32C6BETAFirmwareImage, + "esp32h2beta1": ESP32H2BETA1FirmwareImage, + "esp32h2beta2": ESP32H2BETA2FirmwareImage, + "esp32c2": ESP32C2FirmwareImage, + "esp32c6": ESP32C6FirmwareImage, + }[chip](f) + else: # Otherwise, ESP8266 so look at magic to determine the image type + magic = ord(f.read(1)) + f.seek(0) + if magic == ESPLoader.ESP_IMAGE_MAGIC: + return ESP8266ROMFirmwareImage(f) + elif magic == ESP8266V2FirmwareImage.IMAGE_V2_MAGIC: + return ESP8266V2FirmwareImage(f) + else: + raise FatalError("Invalid image magic number: %d" % magic) + + if isinstance(image_file, str): + with open(image_file, "rb") as f: + return select_image_class(f, chip) + return select_image_class(image_file, chip) + + +class ImageSegment(object): + """Wrapper class for a segment in an ESP image + (very similar to a section in an ELFImage also)""" + + def __init__(self, addr, data, file_offs=None): + self.addr = addr + self.data = data + self.file_offs = file_offs + self.include_in_checksum = True + if self.addr != 0: + self.pad_to_alignment( + 4 + ) # pad all "real" ImageSegments 4 byte aligned length + + def copy_with_new_addr(self, new_addr): + """Return a new ImageSegment with same data, but mapped at + a new address.""" + return ImageSegment(new_addr, self.data, 0) + + def split_image(self, split_len): + """Return a new ImageSegment which splits "split_len" bytes + from the beginning of the data. Remaining bytes are kept in + this segment object (and the start address is adjusted to match.)""" + result = copy.copy(self) + result.data = self.data[:split_len] + self.data = self.data[split_len:] + self.addr += split_len + self.file_offs = None + result.file_offs = None + return result + + def __repr__(self): + r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) + if self.file_offs is not None: + r += " file_offs 0x%08x" % (self.file_offs) + return r + + def get_memory_type(self, image): + """ + Return a list describing the memory type(s) that is covered by this + segment's start address. + """ + return [ + map_range[2] + for map_range in image.ROM_LOADER.MEMORY_MAP + if map_range[0] <= self.addr < map_range[1] + ] + + def pad_to_alignment(self, alignment): + self.data = pad_to(self.data, alignment, b"\x00") + + +class ELFSection(ImageSegment): + """Wrapper class for a section in an ELF image, has a section + name as well as the common properties of an ImageSegment.""" + + def __init__(self, name, addr, data): + super(ELFSection, self).__init__(addr, data) + self.name = name.decode("utf-8") + + def __repr__(self): + return "%s %s" % (self.name, super(ELFSection, self).__repr__()) + + +class BaseFirmwareImage(object): + SEG_HEADER_LEN = 8 + SHA256_DIGEST_LEN = 32 + + """ Base class with common firmware image functions """ + + def __init__(self): + self.segments = [] + self.entrypoint = 0 + self.elf_sha256 = None + self.elf_sha256_offset = 0 + + def load_common_header(self, load_file, expected_magic): + ( + magic, + segments, + self.flash_mode, + self.flash_size_freq, + self.entrypoint, + ) = struct.unpack(" 16: + raise FatalError( + "Invalid segment count %d (max 16). " + "Usually this indicates a linker script problem." % len(self.segments) + ) + + def load_segment(self, f, is_irom_segment=False): + """Load the next segment from the image file""" + file_offs = f.tell() + (offset, size) = struct.unpack(" 0x40200000 or offset < 0x3FFE0000 or size > 65536: + print("WARNING: Suspicious segment 0x%x, length %d" % (offset, size)) + + def maybe_patch_segment_data(self, f, segment_data): + """ + If SHA256 digest of the ELF file needs to be inserted into this segment, do so. + Returns segment data. + """ + segment_len = len(segment_data) + file_pos = f.tell() # file_pos is position in the .bin file + if ( + self.elf_sha256_offset >= file_pos + and self.elf_sha256_offset < file_pos + segment_len + ): + # SHA256 digest needs to be patched into this binary segment, + # calculate offset of the digest inside the binary segment. + patch_offset = self.elf_sha256_offset - file_pos + # Sanity checks + if ( + patch_offset < self.SEG_HEADER_LEN + or patch_offset + self.SHA256_DIGEST_LEN > segment_len + ): + raise FatalError( + "Cannot place SHA256 digest on segment boundary" + "(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)" + % (self.elf_sha256_offset, file_pos, segment_len) + ) + # offset relative to the data part + patch_offset -= self.SEG_HEADER_LEN + if ( + segment_data[patch_offset : patch_offset + self.SHA256_DIGEST_LEN] + != b"\x00" * self.SHA256_DIGEST_LEN + ): + raise FatalError( + "Contents of segment at SHA256 digest offset 0x%x are not all zero." + " Refusing to overwrite." % self.elf_sha256_offset + ) + assert len(self.elf_sha256) == self.SHA256_DIGEST_LEN + segment_data = ( + segment_data[0:patch_offset] + + self.elf_sha256 + + segment_data[patch_offset + self.SHA256_DIGEST_LEN :] + ) + return segment_data + + def save_segment(self, f, segment, checksum=None): + """ + Save the next segment to the image file, + return next checksum value if provided + """ + segment_data = self.maybe_patch_segment_data(f, segment.data) + f.write(struct.pack(" 0: + if len(irom_segments) != 1: + raise FatalError( + "Found %d segments that could be irom0. Bad ELF file?" + % len(irom_segments) + ) + return irom_segments[0] + return None + + def get_non_irom_segments(self): + irom_segment = self.get_irom_segment() + return [s for s in self.segments if s != irom_segment] + + def merge_adjacent_segments(self): + if not self.segments: + return # nothing to merge + + segments = [] + # The easiest way to merge the sections is the browse them backward. + for i in range(len(self.segments) - 1, 0, -1): + # elem is the previous section, the one `next_elem` may need to be + # merged in + elem = self.segments[i - 1] + next_elem = self.segments[i] + if all( + ( + elem.get_memory_type(self) == next_elem.get_memory_type(self), + elem.include_in_checksum == next_elem.include_in_checksum, + next_elem.addr == elem.addr + len(elem.data), + ) + ): + # Merge any segment that ends where the next one starts, + # without spanning memory types + # + # (don't 'pad' any gaps here as they may be excluded from the image + # due to 'noinit' or other reasons.) + elem.data += next_elem.data + else: + # The section next_elem cannot be merged into the previous one, + # which means it needs to be part of the final segments. + # As we are browsing the list backward, the elements need to be + # inserted at the beginning of the final list. + segments.insert(0, next_elem) + + # The first segment will always be here as it cannot be merged into any + # "previous" section. + segments.insert(0, self.segments[0]) + + # note: we could sort segments here as well, but the ordering of segments is + # sometimes important for other reasons (like embedded ELF SHA-256), + # so we assume that the linker script will have produced any adjacent sections + # in linear order in the ELF, anyhow. + self.segments = segments + + def set_mmu_page_size(self, size): + """ + If supported, this should be overridden by the chip-specific class. + Gets called in elf2image. + """ + print( + "WARNING: Changing MMU page size is not supported on {}! " + "Defaulting to 64KB.".format(self.ROM_LOADER.CHIP_NAME) + ) + + +class ESP8266ROMFirmwareImage(BaseFirmwareImage): + """'Version 1' firmware image, segments loaded directly by the ROM bootloader.""" + + ROM_LOADER = ESP8266ROM + + def __init__(self, load_file=None): + super(ESP8266ROMFirmwareImage, self).__init__() + self.flash_mode = 0 + self.flash_size_freq = 0 + self.version = 1 + + if load_file is not None: + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """Derive a default output name from the ELF name.""" + return input_file + "-" + + def save(self, basename): + """Save a set of V1 images for flashing. Parameter is a base filename.""" + # IROM data goes in its own plain binary file + irom_segment = self.get_irom_segment() + if irom_segment is not None: + with open( + "%s0x%05x.bin" + % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), + "wb", + ) as f: + f.write(irom_segment.data) + + # everything but IROM goes at 0x00000 in an image file + normal_segments = self.get_non_irom_segments() + with open("%s0x00000.bin" % basename, "wb") as f: + self.write_common_header(f, normal_segments) + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + for segment in normal_segments: + checksum = self.save_segment(f, segment, checksum) + self.append_checksum(f, checksum) + + +ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage + + +class ESP8266V2FirmwareImage(BaseFirmwareImage): + """'Version 2' firmware image, segments loaded by software bootloader stub + (ie Espressif bootloader or rboot) + """ + + ROM_LOADER = ESP8266ROM + # First byte of the "v2" application image + IMAGE_V2_MAGIC = 0xEA + + # First 'segment' value in a "v2" application image, + # appears to be a constant version value? + IMAGE_V2_SEGMENT = 4 + + def __init__(self, load_file=None): + super(ESP8266V2FirmwareImage, self).__init__() + self.version = 2 + if load_file is not None: + segments = self.load_common_header(load_file, self.IMAGE_V2_MAGIC) + if segments != self.IMAGE_V2_SEGMENT: + # segment count is not really segment count here, + # but we expect to see '4' + print( + 'Warning: V2 header has unexpected "segment" count %d (usually 4)' + % segments + ) + + # irom segment comes before the second header + # + # the file is saved in the image with a zero load address + # in the header, so we need to calculate a load address + irom_segment = self.load_segment(load_file, True) + # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 + irom_segment.addr = 0 + irom_segment.include_in_checksum = False + + first_flash_mode = self.flash_mode + first_flash_size_freq = self.flash_size_freq + first_entrypoint = self.entrypoint + # load the second header + + segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) + + if first_flash_mode != self.flash_mode: + print( + "WARNING: Flash mode value in first header (0x%02x) disagrees " + "with second (0x%02x). Using second value." + % (first_flash_mode, self.flash_mode) + ) + if first_flash_size_freq != self.flash_size_freq: + print( + "WARNING: Flash size/freq value in first header (0x%02x) disagrees " + "with second (0x%02x). Using second value." + % (first_flash_size_freq, self.flash_size_freq) + ) + if first_entrypoint != self.entrypoint: + print( + "WARNING: Entrypoint address in first header (0x%08x) disagrees " + "with second header (0x%08x). Using second value." + % (first_entrypoint, self.entrypoint) + ) + + # load all the usual segments + for _ in range(segments): + self.load_segment(load_file) + self.checksum = self.read_checksum(load_file) + + self.verify() + + def default_output_name(self, input_file): + """Derive a default output name from the ELF name.""" + irom_segment = self.get_irom_segment() + if irom_segment is not None: + irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START + else: + irom_offs = 0 + return "%s-0x%05x.bin" % ( + os.path.splitext(input_file)[0], + irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1), + ) + + def save(self, filename): + with open(filename, "wb") as f: + # Save first header for irom0 segment + f.write( + struct.pack( + b" 0: + last_addr = flash_segments[0].addr + for segment in flash_segments[1:]: + if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: + raise FatalError( + "Segment loaded at 0x%08x lands in same 64KB flash mapping " + "as segment loaded at 0x%08x. Can't generate binary. " + "Suggest changing linker script or ELF to merge sections." + % (segment.addr, last_addr) + ) + last_addr = segment.addr + + def get_alignment_data_needed(segment): + # Actual alignment (in data bytes) required for a segment header: + # positioned so that after we write the next 8 byte header, + # file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN + # + # (this is because the segment's vaddr may not be IROM_ALIGNed, + # more likely is aligned IROM_ALIGN+0x18 + # to account for the binary file header + align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN + pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past + if pad_len == 0 or pad_len == self.IROM_ALIGN: + return 0 # already aligned + + # subtract SEG_HEADER_LEN a second time, + # as the padding block has a header as well + pad_len -= self.SEG_HEADER_LEN + if pad_len < 0: + pad_len += self.IROM_ALIGN + return pad_len + + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + pad_len = get_alignment_data_needed(segment) + if pad_len > 0: # need to pad + if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: + pad_segment = ram_segments[0].split_image(pad_len) + if len(ram_segments[0].data) == 0: + ram_segments.pop(0) + else: + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + else: + # write the flash segment + assert ( + f.tell() + 8 + ) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN + checksum = self.save_flash_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + if self.secure_pad: + # pad the image so that after signing it will end on a a 64KB boundary. + # This ensures all mapped flash content will be verified. + if not self.append_digest: + raise FatalError( + "secure_pad only applies if a SHA-256 digest " + "is also appended to the image" + ) + align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN + # 16 byte aligned checksum + # (force the alignment to simplify calculations) + checksum_space = 16 + if self.secure_pad == "1": + # after checksum: SHA-256 digest + + # (to be added by signing process) version, + # signature + 12 trailing bytes due to alignment + space_after_checksum = 32 + 4 + 64 + 12 + elif self.secure_pad == "2": # Secure Boot V2 + # after checksum: SHA-256 digest + + # signature sector, + # but we place signature sector after the 64KB boundary + space_after_checksum = 32 + pad_len = ( + self.IROM_ALIGN - align_past - checksum_space - space_after_checksum + ) % self.IROM_ALIGN + pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) + + checksum = self.save_segment(f, pad_segment, checksum) + total_segments += 1 + + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + if self.secure_pad: + assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + + def save_flash_segment(self, f, segment, checksum=None): + """ + Save the next segment to the image file, return next checksum value if provided + """ + segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN + segment_len_remainder = segment_end_pos % self.IROM_ALIGN + if segment_len_remainder < 0x24: + # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the + # last MMU page, if an IROM/DROM segment was < 0x24 bytes + # over the page boundary. + segment.data += b"\x00" * (0x24 - segment_len_remainder) + return self.save_segment(f, segment, checksum) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list( + struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) + ) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + self.chip_id = fields[4] + if self.chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: + print( + ( + "Unexpected chip id in image. Expected %d but value was %d. " + "Is this image for a different chip model?" + ) + % (self.ROM_LOADER.IMAGE_CHIP_ID, self.chip_id) + ) + + self.min_rev = fields[5] + self.min_rev_full = fields[6] + self.max_rev_full = fields[7] + + # reserved fields in the middle should all be zero + if any(f for f in fields[8:-1] if f != 0): + print( + "Warning: some reserved header fields have non-zero values. " + "This image may be from a newer esptool.py?" + ) + + append_digest = fields[-1] # last byte is append_digest + if append_digest in [0, 1]: + self.append_digest = append_digest == 1 + else: + raise RuntimeError( + "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", + append_digest, + ) + + def save_extended_header(self, save_file): + def join_byte(ln, hn): + return (ln & 0x0F) + ((hn & 0x0F) << 4) + + append_digest = 1 if self.append_digest else 0 + + fields = [ + self.wp_pin, + join_byte(self.clk_drv, self.q_drv), + join_byte(self.d_drv, self.cs_drv), + join_byte(self.hd_drv, self.wp_drv), + self.ROM_LOADER.IMAGE_CHIP_ID, + self.min_rev, + self.min_rev_full, + self.max_rev_full, + ] + fields += [0] * 4 # padding + fields += [append_digest] + + packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) + save_file.write(packed) + + +class ESP8266V3FirmwareImage(ESP32FirmwareImage): + """ESP8266 V3 firmware image is very similar to ESP32 image""" + + EXTENDED_HEADER_STRUCT_FMT = "B" * 16 + + def is_flash_addr(self, addr): + return addr > ESP8266ROM.IROM_MAP_START + + def save(self, filename): + total_segments = 0 + with io.BytesIO() as f: # write file to memory first + self.write_common_header(f, self.segments) + + checksum = ESPLoader.ESP_CHECKSUM_MAGIC + + # split segments into flash-mapped vs ram-loaded, + # and take copies so we can mutate them + flash_segments = [ + copy.deepcopy(s) + for s in sorted(self.segments, key=lambda s: s.addr) + if self.is_flash_addr(s.addr) and len(s.data) + ] + ram_segments = [ + copy.deepcopy(s) + for s in sorted(self.segments, key=lambda s: s.addr) + if not self.is_flash_addr(s.addr) and len(s.data) + ] + + # check for multiple ELF sections that are mapped in the same + # flash mapping region. This is usually a sign of a broken linker script, + # but if you have a legitimate use case then let us know + if len(flash_segments) > 0: + last_addr = flash_segments[0].addr + for segment in flash_segments[1:]: + if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: + raise FatalError( + "Segment loaded at 0x%08x lands in same 64KB flash mapping " + "as segment loaded at 0x%08x. Can't generate binary. " + "Suggest changing linker script or ELF to merge sections." + % (segment.addr, last_addr) + ) + last_addr = segment.addr + + # try to fit each flash segment on a 64kB aligned boundary + # by padding with parts of the non-flash segments... + while len(flash_segments) > 0: + segment = flash_segments[0] + # remove 8 bytes empty data for insert segment header + if segment.name == ".flash.rodata": + segment.data = segment.data[8:] + # write the flash segment + checksum = self.save_segment(f, segment, checksum) + flash_segments.pop(0) + total_segments += 1 + + # flash segments all written, so write any remaining RAM segments + for segment in ram_segments: + checksum = self.save_segment(f, segment, checksum) + total_segments += 1 + + # done writing segments + self.append_checksum(f, checksum) + image_length = f.tell() + + # kinda hacky: go back to the initial header and write the new segment count + # that includes padding segments. This header is not checksummed + f.seek(1) + f.write(bytes([total_segments])) + + if self.append_digest: + # calculate the SHA256 of the whole file and append it + f.seek(0) + digest = hashlib.sha256() + digest.update(f.read(image_length)) + f.write(digest.digest()) + + with open(filename, "wb") as real_file: + real_file.write(f.getvalue()) + + def load_extended_header(self, load_file): + def split_byte(n): + return (n & 0x0F, (n >> 4) & 0x0F) + + fields = list( + struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) + ) + + self.wp_pin = fields[0] + + # SPI pin drive stengths are two per byte + self.clk_drv, self.q_drv = split_byte(fields[1]) + self.d_drv, self.cs_drv = split_byte(fields[2]) + self.hd_drv, self.wp_drv = split_byte(fields[3]) + + if fields[15] in [0, 1]: + self.append_digest = fields[15] == 1 + else: + raise RuntimeError( + "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", + fields[15], + ) + + # remaining fields in the middle should all be zero + if any(f for f in fields[4:15] if f != 0): + print( + "Warning: some reserved header fields have non-zero values. " + "This image may be from a newer esptool.py?" + ) + + +ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage + + +class ESP32S2FirmwareImage(ESP32FirmwareImage): + """ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S2ROM + + +ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage + + +class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): + """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S3BETA2ROM + + +ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage + + +class ESP32S3FirmwareImage(ESP32FirmwareImage): + """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32S3ROM + + +ESP32S3ROM.BOOTLOADER_IMAGE = ESP32S3FirmwareImage + + +class ESP32C3FirmwareImage(ESP32FirmwareImage): + """ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C3ROM + + +ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage + + +class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): + """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C6BETAROM + + +ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage + + +class ESP32H2BETA1FirmwareImage(ESP32FirmwareImage): + """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H2BETA1ROM + + +ESP32H2BETA1ROM.BOOTLOADER_IMAGE = ESP32H2BETA1FirmwareImage + + +class ESP32H2BETA2FirmwareImage(ESP32FirmwareImage): + """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32H2BETA2ROM + + +ESP32H2BETA2ROM.BOOTLOADER_IMAGE = ESP32H2BETA2FirmwareImage + + +class ESP32C2FirmwareImage(ESP32FirmwareImage): + """ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C2ROM + + def set_mmu_page_size(self, size): + if size not in [16384, 32768, 65536]: + raise FatalError( + "{} bytes is not a valid ESP32-C2 page size, " + "select from 64KB, 32KB, 16KB.".format(size) + ) + self.IROM_ALIGN = size + + +ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage + + +class ESP32C6FirmwareImage(ESP32FirmwareImage): + """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" + + ROM_LOADER = ESP32C6ROM + + def set_mmu_page_size(self, size): + if size not in [8192, 16384, 32768, 65536]: + raise FatalError( + "{} bytes is not a valid ESP32-C6 page size, " + "select from 64KB, 32KB, 16KB, 8KB.".format(size) + ) + self.IROM_ALIGN = size + + +ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage + + +class ELFFile(object): + SEC_TYPE_PROGBITS = 0x01 + SEC_TYPE_STRTAB = 0x03 + SEC_TYPE_INITARRAY = 0x0E + SEC_TYPE_FINIARRAY = 0x0F + + PROG_SEC_TYPES = (SEC_TYPE_PROGBITS, SEC_TYPE_INITARRAY, SEC_TYPE_FINIARRAY) + + LEN_SEC_HEADER = 0x28 + + SEG_TYPE_LOAD = 0x01 + LEN_SEG_HEADER = 0x20 + + def __init__(self, name): + # Load sections from the ELF file + self.name = name + with open(self.name, "rb") as f: + self._read_elf_file(f) + + def get_section(self, section_name): + for s in self.sections: + if s.name == section_name: + return s + raise ValueError("No section %s in ELF file" % section_name) + + def _read_elf_file(self, f): + # read the ELF file header + LEN_FILE_HEADER = 0x34 + try: + ( + ident, + _type, + machine, + _version, + self.entrypoint, + _phoff, + shoff, + _flags, + _ehsize, + _phentsize, + _phnum, + shentsize, + shnum, + shstrndx, + ) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) + except struct.error as e: + raise FatalError( + "Failed to read a valid ELF header from %s: %s" % (self.name, e) + ) + + if byte(ident, 0) != 0x7F or ident[1:4] != b"ELF": + raise FatalError("%s has invalid ELF magic header" % self.name) + if machine not in [0x5E, 0xF3]: + raise FatalError( + "%s does not appear to be an Xtensa or an RISCV ELF file. " + "e_machine=%04x" % (self.name, machine) + ) + if shentsize != self.LEN_SEC_HEADER: + raise FatalError( + "%s has unexpected section header entry size 0x%x (not 0x%x)" + % (self.name, shentsize, self.LEN_SEC_HEADER) + ) + if shnum == 0: + raise FatalError("%s has 0 section headers" % (self.name)) + self._read_sections(f, shoff, shnum, shstrndx) + self._read_segments(f, _phoff, _phnum, shstrndx) + + def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): + f.seek(section_header_offs) + len_bytes = section_header_count * self.LEN_SEC_HEADER + section_header = f.read(len_bytes) + if len(section_header) == 0: + raise FatalError( + "No section header found at offset %04x in ELF file." + % section_header_offs + ) + if len(section_header) != (len_bytes): + raise FatalError( + "Only read 0x%x bytes from section header (expected 0x%x.) " + "Truncated ELF file?" % (len(section_header), len_bytes) + ) + + # walk through the section header and extract all sections + section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) + + def read_section_header(offs): + name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from( + " 0 + ] + self.sections = prog_sections + + def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): + f.seek(segment_header_offs) + len_bytes = segment_header_count * self.LEN_SEG_HEADER + segment_header = f.read(len_bytes) + if len(segment_header) == 0: + raise FatalError( + "No segment header found at offset %04x in ELF file." + % segment_header_offs + ) + if len(segment_header) != (len_bytes): + raise FatalError( + "Only read 0x%x bytes from segment header (expected 0x%x.) " + "Truncated ELF file?" % (len(segment_header), len_bytes) + ) + + # walk through the segment header and extract all segments + segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) + + def read_segment_header(offs): + ( + seg_type, + seg_offs, + _vaddr, + lma, + size, + _memsize, + _flags, + _align, + ) = struct.unpack_from(" 0 + ] + self.segments = prog_segments + + def sha256(self): + # return SHA256 hash of the input ELF file + sha256 = hashlib.sha256() + with open(self.name, "rb") as f: + sha256.update(f.read()) + return sha256.digest() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py new file mode 100644 index 000000000..d1d92fa7f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py @@ -0,0 +1,1160 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import hashlib +import io +import os +import struct +import sys +import time +import zlib + +from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage +from .bin_image import ( + ESP8266ROMFirmwareImage, + ESP8266V2FirmwareImage, + ESP8266V3FirmwareImage, +) +from .loader import ( + DEFAULT_CONNECT_ATTEMPTS, + DEFAULT_TIMEOUT, + ERASE_WRITE_TIMEOUT_PER_MB, + ESPLoader, + timeout_per_mb, +) +from .targets import CHIP_DEFS, CHIP_LIST, ESP8266ROM, ROM_LIST +from .util import ( + FatalError, + NotImplementedInROMError, + NotSupportedError, + UnsupportedCommandError, +) +from .util import ( + div_roundup, + flash_size_bytes, + hexify, + pad_to, + print_overwrite, +) + +DETECTED_FLASH_SIZES = { + 0x12: "256KB", + 0x13: "512KB", + 0x14: "1MB", + 0x15: "2MB", + 0x16: "4MB", + 0x17: "8MB", + 0x18: "16MB", + 0x19: "32MB", + 0x1A: "64MB", + 0x1B: "128MB", + 0x1C: "256MB", + 0x20: "64MB", + 0x21: "128MB", + 0x22: "256MB", + 0x32: "256KB", + 0x33: "512KB", + 0x34: "1MB", + 0x35: "2MB", + 0x36: "4MB", + 0x37: "8MB", + 0x38: "16MB", + 0x39: "32MB", + 0x3A: "64MB", +} + +FLASH_MODES = {"qio": 0, "qout": 1, "dio": 2, "dout": 3} + + +def detect_chip( + port=ESPLoader.DEFAULT_PORT, + baud=ESPLoader.ESP_ROM_BAUD, + connect_mode="default_reset", + trace_enabled=False, + connect_attempts=DEFAULT_CONNECT_ATTEMPTS, +): + """Use serial access to detect the chip type. + + First, get_security_info command is sent to detect the ID of the chip + (supported only by ESP32-C3 and later, works even in the Secure Download Mode). + If this fails, we reconnect and fall-back to reading the magic number. + It's mapped at a specific ROM address and has a different value on each chip model. + This way we use one memory read and compare it to the magic number for each chip. + + This routine automatically performs ESPLoader.connect() (passing + connect_mode parameter) as part of querying the chip. + """ + inst = None + detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) + if detect_port.serial_port.startswith("rfc2217:"): + detect_port.USES_RFC2217 = True + detect_port.connect(connect_mode, connect_attempts, detecting=True) + try: + print("Detecting chip type...", end="") + res = detect_port.check_command( + "get security info", ESPLoader.ESP_GET_SECURITY_INFO, b"" + ) + # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id + res = struct.unpack(", ) or a single +# argument. + + +def load_ram(esp, args): + image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) + + print("RAM boot...") + for seg in image.segments: + size = len(seg.data) + print("Downloading %d bytes at %08x..." % (size, seg.addr), end=" ") + sys.stdout.flush() + esp.mem_begin( + size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr + ) + + seq = 0 + while len(seg.data) > 0: + esp.mem_block(seg.data[0 : esp.ESP_RAM_BLOCK], seq) + seg.data = seg.data[esp.ESP_RAM_BLOCK :] + seq += 1 + print("done!") + + print("All segments done, executing at %08x" % image.entrypoint) + esp.mem_finish(image.entrypoint) + + +def read_mem(esp, args): + print("0x%08x = 0x%08x" % (args.address, esp.read_reg(args.address))) + + +def write_mem(esp, args): + esp.write_reg(args.address, args.value, args.mask, 0) + print("Wrote %08x, mask %08x to %08x" % (args.value, args.mask, args.address)) + + +def dump_mem(esp, args): + with open(args.filename, "wb") as f: + for i in range(args.size // 4): + d = esp.read_reg(args.address + (i * 4)) + f.write(struct.pack(b"> 16 + args.flash_size = DETECTED_FLASH_SIZES.get(size_id) + if args.flash_size is None: + print( + "Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x)," + " defaulting to 4MB" % (flash_id, size_id) + ) + args.flash_size = "4MB" + else: + print("Auto-detected Flash size:", args.flash_size) + + +def _update_image_flash_params(esp, address, args, image): + """ + Modify the flash mode & size bytes if this looks like an executable bootloader image + """ + if len(image) < 8: + return image # not long enough to be a bootloader image + + # unpack the (potential) image header + magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) + if address != esp.BOOTLOADER_FLASH_OFFSET: + return image # not flashing bootloader offset, so don't modify this + + if (args.flash_mode, args.flash_freq, args.flash_size) == ("keep",) * 3: + return image # all settings are 'keep', not modifying anything + + # easy check if this is an image: does it start with a magic byte? + if magic != esp.ESP_IMAGE_MAGIC: + print( + "Warning: Image file at 0x%x doesn't look like an image file, " + "so not changing any flash settings." % address + ) + return image + + # make sure this really is an image, and not just data that + # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted + # images that happen to start with a magic byte + try: + test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) + test_image.verify() + except Exception: + print( + "Warning: Image file at 0x%x is not a valid %s image, " + "so not changing any flash settings." % (address, esp.CHIP_NAME) + ) + return image + + # After the 8-byte header comes the extended header for chips others than ESP8266. + # The 15th byte of the extended header indicates if the image is protected by + # a SHA256 checksum. In that case we should not modify the header because + # the checksum check would fail. + sha_implies_keep = args.chip != "esp8266" and image[8 + 15] == 1 + + def print_keep_warning(arg_to_keep, arg_used): + print( + "Warning: Image file at {addr} is protected with a hash checksum, " + "so not changing the flash {arg} setting. " + "Use the --flash_{arg}=keep option instead of --flash_{arg}={arg_orig} " + "in order to remove this warning, or use the --dont-append-digest option " + "for the elf2image command in order to generate an image file " + "without a hash checksum".format( + addr=hex(address), arg=arg_to_keep, arg_orig=arg_used + ) + ) + + if args.flash_mode != "keep": + new_flash_mode = FLASH_MODES[args.flash_mode] + if flash_mode != new_flash_mode and sha_implies_keep: + print_keep_warning("mode", args.flash_mode) + else: + flash_mode = new_flash_mode + + flash_freq = flash_size_freq & 0x0F + if args.flash_freq != "keep": + new_flash_freq = esp.parse_flash_freq_arg(args.flash_freq) + if flash_freq != new_flash_freq and sha_implies_keep: + print_keep_warning("frequency", args.flash_freq) + else: + flash_freq = new_flash_freq + + flash_size = flash_size_freq & 0xF0 + if args.flash_size != "keep": + new_flash_size = esp.parse_flash_size_arg(args.flash_size) + if flash_size != new_flash_size and sha_implies_keep: + print_keep_warning("size", args.flash_size) + else: + flash_size = new_flash_size + + flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq) + if flash_params != image[2:4]: + print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params)) + image = image[0:2] + flash_params + image[4:] + return image + + +def write_flash(esp, args): + # set args.compress based on default behaviour: + # -> if either --compress or --no-compress is set, honour that + # -> otherwise, set --compress unless --no-stub is set + if args.compress is None and not args.no_compress: + args.compress = not args.no_stub + + if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: + # Check if secure boot is active + if esp.get_secure_boot_enabled(): + for address, _ in args.addr_filename: + if address < 0x8000: + raise FatalError( + "Secure Boot detected, writing to flash regions < 0x8000 " + "is disabled to protect the bootloader. " + "Use --force to override, " + "please use with caution, otherwise it may brick your device!" + ) + # Check if chip_id and min_rev in image are valid for the target in use + for _, argfile in args.addr_filename: + try: + image = LoadFirmwareImage(esp.CHIP_NAME, argfile) + except (FatalError, struct.error, RuntimeError): + continue + if image.chip_id != esp.IMAGE_CHIP_ID: + raise FatalError( + f"{argfile.name} is not an {esp.CHIP_NAME} image. " + "Use --force to flash anyway." + ) + + # this logic below decides which min_rev to use, min_rev or min/max_rev_full + if image.max_rev_full == 0: # image does not have max/min_rev_full fields + use_rev_full_fields = False + elif image.max_rev_full == 65535: # image has default value of max_rev_full + if ( + image.min_rev_full == 0 and image.min_rev != 0 + ): # min_rev_full is not set, min_rev is used + use_rev_full_fields = False + use_rev_full_fields = True + else: # max_rev_full set to a version + use_rev_full_fields = True + + if use_rev_full_fields: + rev = esp.get_chip_revision() + if rev < image.min_rev_full or rev > image.max_rev_full: + error_str = f"{argfile.name} requires chip revision in range " + error_str += ( + f"[v{image.min_rev_full // 100}.{image.min_rev_full % 100} - " + ) + if image.max_rev_full == 65535: + error_str += "max rev not set] " + else: + error_str += ( + f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}] " + ) + error_str += f"(this chip is revision v{rev // 100}.{rev % 100})" + raise FatalError(f"{error_str}. Use --force to flash anyway.") + else: + # In IDF, image.min_rev is set based on Kconfig option. + # For C3 chip, image.min_rev is the Minor revision + # while for the rest chips it is the Major revision. + if esp.CHIP_NAME == "ESP32-C3": + rev = esp.get_minor_chip_version() + else: + rev = esp.get_major_chip_version() + if rev < image.min_rev: + raise FatalError( + f"{argfile.name} requires chip revision " + f"{image.min_rev} or higher (this chip is revision {rev}). " + "Use --force to flash anyway." + ) + + # In case we have encrypted files to write, + # we first do few sanity checks before actual flash + if args.encrypt or args.encrypt_files is not None: + do_write = True + + if not esp.secure_download_mode: + if esp.get_encrypted_download_disabled(): + raise FatalError( + "This chip has encrypt functionality " + "in UART download mode disabled. " + "This is the Flash Encryption configuration for Production mode " + "instead of Development mode." + ) + + crypt_cfg_efuse = esp.get_flash_crypt_config() + + if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: + print("Unexpected FLASH_CRYPT_CONFIG value: 0x%x" % (crypt_cfg_efuse)) + do_write = False + + enc_key_valid = esp.is_flash_encryption_key_valid() + + if not enc_key_valid: + print("Flash encryption key is not programmed") + do_write = False + + # Determine which files list contain the ones to encrypt + files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files + + for address, argfile in files_to_encrypt: + if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: + print( + "File %s address 0x%x is not %d byte aligned, can't flash encrypted" + % (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN) + ) + do_write = False + + if not do_write and not args.ignore_flash_encryption_efuse_setting: + raise FatalError( + "Can't perform encrypted flash write, " + "consult Flash Encryption documentation for more information" + ) + + # verify file sizes fit in flash + if args.flash_size != "keep": # TODO: check this even with 'keep' + flash_end = flash_size_bytes(args.flash_size) + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + if address + argfile.tell() > flash_end: + raise FatalError( + "File %s (length %d) at offset %d " + "will not fit in %d bytes of flash. " + "Use --flash_size argument, or change flashing address." + % (argfile.name, argfile.tell(), address, flash_end) + ) + argfile.seek(0) + + if args.erase_all: + erase_flash(esp, args) + else: + for address, argfile in args.addr_filename: + argfile.seek(0, os.SEEK_END) + write_end = address + argfile.tell() + argfile.seek(0) + bytes_over = address % esp.FLASH_SECTOR_SIZE + if bytes_over != 0: + print( + "WARNING: Flash address {:#010x} is not aligned " + "to a {:#x} byte flash sector. " + "{:#x} bytes before this address will be erased.".format( + address, esp.FLASH_SECTOR_SIZE, bytes_over + ) + ) + # Print the address range of to-be-erased flash memory region + print( + "Flash will be erased from {:#010x} to {:#010x}...".format( + address - bytes_over, + div_roundup(write_end, esp.FLASH_SECTOR_SIZE) + * esp.FLASH_SECTOR_SIZE + - 1, + ) + ) + + """ Create a list describing all the files we have to flash. + Each entry holds an "encrypt" flag marking whether the file needs encryption or not. + This list needs to be sorted. + + First, append to each entry of our addr_filename list the flag args.encrypt + E.g., if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], + all_files will be [ + (0x1000, "partition.bin", args.encrypt), + (0x8000, "bootloader", args.encrypt) + ], + where, of course, args.encrypt is either True or False + """ + all_files = [ + (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename + ] + + """ + Now do the same with encrypt_files list, if defined. + In this case, the flag is True + """ + if args.encrypt_files is not None: + encrypted_files_flag = [ + (offs, filename, True) for (offs, filename) in args.encrypt_files + ] + + # Concatenate both lists and sort them. + # As both list are already sorted, we could simply do a merge instead, + # but for the sake of simplicity and because the lists are very small, + # let's use sorted. + all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) + + for address, argfile, encrypted in all_files: + compress = args.compress + + # Check whether we can compress the current file before flashing + if compress and encrypted: + print("\nWARNING: - compress and encrypt options are mutually exclusive ") + print("Will flash %s uncompressed" % argfile.name) + compress = False + + if args.no_stub: + print("Erasing flash...") + image = pad_to( + argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4 + ) + if len(image) == 0: + print("WARNING: File %s is empty" % argfile.name) + continue + image = _update_image_flash_params(esp, address, args, image) + calcmd5 = hashlib.md5(image).hexdigest() + uncsize = len(image) + if compress: + uncimage = image + image = zlib.compress(uncimage, 9) + # Decompress the compressed binary a block at a time, + # to dynamically calculate the timeout based on the real write size + decompress = zlib.decompressobj() + blocks = esp.flash_defl_begin(uncsize, len(image), address) + else: + blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) + argfile.seek(0) # in case we need it again + seq = 0 + bytes_sent = 0 # bytes sent on wire + bytes_written = 0 # bytes written to flash + t = time.time() + + timeout = DEFAULT_TIMEOUT + + while len(image) > 0: + print_overwrite( + "Writing at 0x%08x... (%d %%)" + % (address + bytes_written, 100 * (seq + 1) // blocks) + ) + sys.stdout.flush() + block = image[0 : esp.FLASH_WRITE_SIZE] + if compress: + # feeding each compressed block into the decompressor lets us + # see block-by-block how much will be written + block_uncompressed = len(decompress.decompress(block)) + bytes_written += block_uncompressed + block_timeout = max( + DEFAULT_TIMEOUT, + timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed), + ) + if not esp.IS_STUB: + timeout = ( + block_timeout # ROM code writes block to flash before ACKing + ) + esp.flash_defl_block(block, seq, timeout=timeout) + if esp.IS_STUB: + # Stub ACKs when block is received, + # then writes to flash while receiving the block after it + timeout = block_timeout + else: + # Pad the last block + block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block)) + if encrypted: + esp.flash_encrypt_block(block, seq) + else: + esp.flash_block(block, seq) + bytes_written += len(block) + bytes_sent += len(block) + image = image[esp.FLASH_WRITE_SIZE :] + seq += 1 + + if esp.IS_STUB: + # Stub only writes each block to flash after 'ack'ing the receive, + # so do a final dummy operation which will not be 'ack'ed + # until the last block has actually been written out to flash + esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) + + t = time.time() - t + speed_msg = "" + if compress: + if t > 0.0: + speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) + print_overwrite( + "Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s..." + % (uncsize, bytes_sent, address, t, speed_msg), + last_line=True, + ) + else: + if t > 0.0: + speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) + print_overwrite( + "Wrote %d bytes at 0x%08x in %.1f seconds%s..." + % (bytes_written, address, t, speed_msg), + last_line=True, + ) + + if not encrypted and not esp.secure_download_mode: + try: + res = esp.flash_md5sum(address, uncsize) + if res != calcmd5: + print("File md5: %s" % calcmd5) + print("Flash md5: %s" % res) + print( + "MD5 of 0xFF is %s" + % (hashlib.md5(b"\xFF" * uncsize).hexdigest()) + ) + raise FatalError("MD5 of file does not match data in flash!") + else: + print("Hash of data verified.") + except NotImplementedInROMError: + pass + + print("\nLeaving...") + + if esp.IS_STUB: + # skip sending flash_finish to ROM loader here, + # as it causes the loader to exit and run user code + esp.flash_begin(0, 0) + + # Get the "encrypted" flag for the last file flashed + # Note: all_files list contains triplets like: + # (address: Integer, filename: String, encrypted: Boolean) + last_file_encrypted = all_files[-1][2] + + # Check whether the last file flashed was compressed or not + if args.compress and not last_file_encrypted: + esp.flash_defl_finish(False) + else: + esp.flash_finish(False) + + if args.verify: + print("Verifying just-written flash...") + print( + "(This option is deprecated, " + "flash contents are now always read back after flashing.)" + ) + # If some encrypted files have been flashed, + # print a warning saying that we won't check them + if args.encrypt or args.encrypt_files is not None: + print("WARNING: - cannot verify encrypted files, they will be ignored") + # Call verify_flash function only if there is at least + # one non-encrypted file flashed + if not args.encrypt: + verify_flash(esp, args) + + +def image_info(args): + def v2(): + def get_key_from_value(dict, val): + """Get key from value in dictionary""" + for key, value in dict.items(): + if value == val: + return key + return None + + print() + title = "{} image header".format(args.chip.upper()) + print(title) + print("=" * len(title)) + print("Image version: {}".format(image.version)) + print( + "Entry point: {:#8x}".format(image.entrypoint) + if image.entrypoint != 0 + else "Entry point not set" + ) + + print("Segments: {}".format(len(image.segments))) + + # Flash size + flash_s_bits = image.flash_size_freq & 0xF0 # high four bits + flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits) + print( + "Flash size: {}".format(flash_s) + if flash_s is not None + else "WARNING: Invalid flash size ({:#02x})".format(flash_s_bits) + ) + + # Flash frequency + flash_fr_bits = image.flash_size_freq & 0x0F # low four bits + flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits) + print( + "Flash freq: {}".format(flash_fr) + if flash_fr is not None + else "WARNING: Invalid flash frequency ({:#02x})".format(flash_fr_bits) + ) + + # Flash mode + flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode) + print( + "Flash mode: {}".format(flash_mode.upper()) + if flash_mode is not None + else "WARNING: Invalid flash mode ({})".format(image.flash_mode) + ) + + # Extended header (ESP32 and later only) + if args.chip != "esp8266": + print() + title = "{} extended image header".format(args.chip.upper()) + print(title) + print("=" * len(title)) + print("WP pin: {:#02x}".format(image.wp_pin)) + print( + "Flash pins drive settings: " + "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, " + "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format( + image.clk_drv, + image.q_drv, + image.d_drv, + image.cs_drv, + image.hd_drv, + image.wp_drv, + ) + ) + print("Chip ID: {}".format(image.chip_id)) + print( + "Minimal chip revision: " + f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, " + f"(legacy min_rev = {image.min_rev})" + ) + print( + "Maximal chip revision: " + f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}" + ) + print() + + # Segments overview + title = "Segments information" + print(title) + print("=" * len(title)) + headers_str = "{:>7} {:>7} {:>10} {:>10} {:10}" + print( + headers_str.format( + "Segment", "Length", "Load addr", "File offs", "Memory types" + ) + ) + print( + "{} {} {} {} {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12) + ) + format_str = "{:7} {:#07x} {:#010x} {:#010x} {}" + app_desc = None + for idx, seg in enumerate(image.segments, start=1): + segs = seg.get_memory_type(image) + seg_name = ", ".join(segs) + if "DROM" in segs: # The DROM segment starts with the esp_app_desc_t struct + app_desc = seg.data[:256] + print( + format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name) + ) + print() + + # Footer + title = f"{args.chip.upper()} image footer" + print(title) + print("=" * len(title)) + calc_checksum = image.calculate_checksum() + print( + "Checksum: {:#02x} ({})".format( + image.checksum, + "valid" + if image.checksum == calc_checksum + else "invalid - calculated {:02x}".format(calc_checksum), + ) + ) + try: + digest_msg = "Not appended" + if image.append_digest: + is_valid = image.stored_digest == image.calc_digest + digest_msg = "{} ({})".format( + hexify(image.calc_digest, uppercase=False), + "valid" if is_valid else "invalid", + ) + print("Validation hash: {}".format(digest_msg)) + except AttributeError: + pass # ESP8266 image has no append_digest field + + if app_desc: + APP_DESC_STRUCT_FMT = " 1 else "")) + + image.verify() + + if args.output is None: + args.output = image.default_output_name(args.input) + image.save(args.output) + + print("Successfully created {} image.".format(args.chip)) + + +def read_mac(esp, args): + mac = esp.read_mac() + + def print_mac(label, mac): + print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac)))) + + print_mac("MAC", mac) + + +def chip_id(esp, args): + try: + chipid = esp.chip_id() + print("Chip ID: 0x%08x" % chipid) + except NotSupportedError: + print("Warning: %s has no Chip ID. Reading MAC instead." % esp.CHIP_NAME) + read_mac(esp, args) + + +def erase_flash(esp, args): + if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: + if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled(): + raise FatalError( + "Active security features detected, " + "erasing flash is disabled as a safety measure. " + "Use --force to override, " + "please use with caution, otherwise it may brick your device!" + ) + print("Erasing flash (this may take a while)...") + t = time.time() + esp.erase_flash() + print("Chip erase completed successfully in %.1fs" % (time.time() - t)) + + +def erase_region(esp, args): + if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: + if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled(): + raise FatalError( + "Active security features detected, " + "erasing flash is disabled as a safety measure. " + "Use --force to override, " + "please use with caution, otherwise it may brick your device!" + ) + print("Erasing region (may be slow depending on size)...") + t = time.time() + esp.erase_region(args.address, args.size) + print("Erase completed successfully in %.1f seconds." % (time.time() - t)) + + +def run(esp, args): + esp.run() + + +def flash_id(esp, args): + flash_id = esp.flash_id() + print("Manufacturer: %02x" % (flash_id & 0xFF)) + flid_lowbyte = (flash_id >> 16) & 0xFF + print("Device: %02x%02x" % ((flash_id >> 8) & 0xFF, flid_lowbyte)) + print( + "Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown")) + ) + + +def read_flash(esp, args): + if args.no_progress: + flash_progress = None + else: + + def flash_progress(progress, length): + msg = "%d (%d %%)" % (progress, progress * 100.0 / length) + padding = "\b" * len(msg) + if progress == length: + padding = "\n" + sys.stdout.write(msg + padding) + sys.stdout.flush() + + t = time.time() + data = esp.read_flash(args.address, args.size, flash_progress) + t = time.time() - t + speed_msg = " ({:.1f} kbit/s)".format(len(data) / t * 8 / 1000) if t > 0.0 else "" + print_overwrite( + "Read {:d} bytes at {:#010x} in {:.1f} seconds{}...".format( + len(data), args.address, t, speed_msg + ), + last_line=True, + ) + with open(args.filename, "wb") as f: + f.write(data) + + +def verify_flash(esp, args): + differences = False + + for address, argfile in args.addr_filename: + image = pad_to(argfile.read(), 4) + argfile.seek(0) # rewind in case we need it again + + image = _update_image_flash_params(esp, address, args, image) + + image_size = len(image) + print( + "Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s..." + % (image_size, image_size, address, argfile.name) + ) + # Try digest first, only read if there are differences. + digest = esp.flash_md5sum(address, image_size) + expected_digest = hashlib.md5(image).hexdigest() + if digest == expected_digest: + print("-- verify OK (digest matched)") + continue + else: + differences = True + if getattr(args, "diff", "no") != "yes": + print("-- verify FAILED (digest mismatch)") + continue + + flash = esp.read_flash(address, image_size) + assert flash != image + diff = [i for i in range(image_size) if flash[i] != image[i]] + print( + "-- verify FAILED: %d differences, first @ 0x%08x" + % (len(diff), address + diff[0]) + ) + for d in diff: + flash_byte = flash[d] + image_byte = image[d] + print(" %08x %02x %02x" % (address + d, flash_byte, image_byte)) + if differences: + raise FatalError("Verify failed.") + + +def read_flash_status(esp, args): + print("Status value: 0x%04x" % esp.read_status(args.bytes)) + + +def write_flash_status(esp, args): + fmt = "0x%%0%dx" % (args.bytes * 2) + args.value = args.value & ((1 << (args.bytes * 8)) - 1) + print(("Initial flash status: " + fmt) % esp.read_status(args.bytes)) + print(("Setting flash status: " + fmt) % args.value) + esp.write_status(args.value, args.bytes, args.non_volatile) + print(("After flash status: " + fmt) % esp.read_status(args.bytes)) + + +def get_security_info(esp, args): + si = esp.get_security_info() + # TODO: better display and tests + print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"]))) + print("Flash_Crypt_Cnt: {:#x}".format(si["flash_crypt_cnt"])) + print("Key_Purposes: {}".format(si["key_purposes"])) + if si["chip_id"] is not None and si["api_version"] is not None: + print("Chip_ID: {}".format(si["chip_id"])) + print("Api_Version: {}".format(si["api_version"])) + + +def merge_bin(args): + try: + chip_class = CHIP_DEFS[args.chip] + except KeyError: + msg = ( + "Please specify the chip argument" + if args.chip == "auto" + else "Invalid chip choice: '{}'".format(args.chip) + ) + msg = msg + " (choose from {})".format(", ".join(CHIP_LIST)) + raise FatalError(msg) + + # sort the files by offset. + # The AddrFilenamePairAction has already checked for overlap + input_files = sorted(args.addr_filename, key=lambda x: x[0]) + if not input_files: + raise FatalError("No input files specified") + first_addr = input_files[0][0] + if first_addr < args.target_offset: + raise FatalError( + "Output file target offset is 0x%x. Input file offset 0x%x is before this." + % (args.target_offset, first_addr) + ) + + if args.format != "raw": + raise FatalError( + "This version of esptool only supports the 'raw' output format" + ) + + with open(args.output, "wb") as of: + + def pad_to(flash_offs): + # account for output file offset if there is any + of.write(b"\xFF" * (flash_offs - args.target_offset - of.tell())) + + for addr, argfile in input_files: + pad_to(addr) + image = argfile.read() + image = _update_image_flash_params(chip_class, addr, args, image) + of.write(image) + if args.fill_flash_size: + pad_to(flash_size_bytes(args.fill_flash_size)) + print( + "Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" + % (of.tell(), args.output, args.target_offset) + ) + + +def version(args): + from . import __version__ + + print(__version__) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py new file mode 100644 index 000000000..d32312386 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py @@ -0,0 +1,1573 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import base64 +import hashlib +import itertools +import json +import os +import re +import string +import struct +import sys +import time + +from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError +from .util import byte, hexify, mask_to_shift, pad_to + +try: + import serial +except ImportError: + print( + "Pyserial is not installed for %s. " + "Check the README for installation instructions." % (sys.executable) + ) + raise + +# check 'serial' is 'pyserial' and not 'serial' +# ref. https://github.com/espressif/esptool/issues/269 +try: + if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__: + raise ImportError( + "esptool.py depends on pyserial, but there is a conflict with a currently " + "installed package named 'serial'.\n" + "You may work around this by 'pip uninstall serial; pip install pyserial' " + "but this may break other installed Python software " + "that depends on 'serial'.\n" + "There is no good fix for this right now, " + "apart from configuring virtualenvs. " + "See https://github.com/espressif/esptool/issues/269#issuecomment-385298196" + " for discussion of the underlying issue(s)." + ) +except TypeError: + pass # __doc__ returns None for pyserial + +try: + import serial.tools.list_ports as list_ports +except ImportError: + print( + "The installed version (%s) of pyserial appears to be too old for esptool.py " + "(Python interpreter %s). Check the README for installation instructions." + % (sys.VERSION, sys.executable) + ) + raise +except Exception: + if sys.platform == "darwin": + # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview + # ref https://github.com/espressif/esptool/issues/540 + list_ports = None + else: + raise + + +DEFAULT_TIMEOUT = 3 # timeout for most flash operations +START_FLASH_TIMEOUT = 20 # timeout for starting flash (may perform erase) +CHIP_ERASE_TIMEOUT = 120 # timeout for full chip erase +MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 # longest any command can run +SYNC_TIMEOUT = 0.1 # timeout for syncing with bootloader +MD5_TIMEOUT_PER_MB = 8 # timeout (per megabyte) for calculating md5sum +ERASE_REGION_TIMEOUT_PER_MB = 30 # timeout (per megabyte) for erasing a region +ERASE_WRITE_TIMEOUT_PER_MB = 40 # timeout (per megabyte) for erasing and writing data +MEM_END_ROM_TIMEOUT = 0.05 # short timeout for ESP_MEM_END, as it may never respond +DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write +DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection +WRITE_BLOCK_ATTEMPTS = 3 # number of times to try writing a data block + +STUBS_DIR = os.path.join(os.path.dirname(__file__), "./targets/stub_flasher/") + + +def get_stub_json_path(chip_name): + chip_name = re.sub(r"[-()]", "", chip_name.lower()) + chip_name = chip_name.replace("esp", "") + return STUBS_DIR + "stub_flasher_" + chip_name + ".json" + + +def timeout_per_mb(seconds_per_mb, size_bytes): + """Scales timeouts which are size-specific""" + result = seconds_per_mb * (size_bytes / 1e6) + if result < DEFAULT_TIMEOUT: + return DEFAULT_TIMEOUT + return result + + +def check_supported_function(func, check_func): + """ + Decorator implementation that wraps a check around an ESPLoader + bootloader function to check if it's supported. + + This is used to capture the multidimensional differences in + functionality between the ESP8266 & ESP32 (and later chips) ROM loaders, and the + software stub that runs on these. Not possible to do this cleanly + via inheritance alone. + """ + + def inner(*args, **kwargs): + obj = args[0] + if check_func(obj): + return func(*args, **kwargs) + else: + raise NotImplementedInROMError(obj, func) + + return inner + + +def stub_function_only(func): + """Attribute for a function only supported in the software stub loader""" + return check_supported_function(func, lambda o: o.IS_STUB) + + +def stub_and_esp32_function_only(func): + """Attribute for a function only supported by stubs or ESP32 and later chips ROM""" + return check_supported_function( + func, lambda o: o.IS_STUB or o.CHIP_NAME not in ["ESP8266"] + ) + + +def esp32s3_or_newer_function_only(func): + """Attribute for a function only supported by ESP32S3 and later chips ROM""" + return check_supported_function( + func, lambda o: o.CHIP_NAME not in ["ESP8266", "ESP32", "ESP32-S2"] + ) + + +class StubFlasher: + def __init__(self, json_path): + with open(json_path) as json_file: + stub = json.load(json_file) + + self.text = base64.b64decode(stub["text"]) + self.text_start = stub["text_start"] + self.entry = stub["entry"] + + try: + self.data = base64.b64decode(stub["data"]) + self.data_start = stub["data_start"] + except KeyError: + self.data = None + self.data_start = None + + +class ESPLoader(object): + """Base class providing access to ESP ROM & software stub bootloaders. + Subclasses provide ESP8266 & ESP32 Family specific functionality. + + Don't instantiate this base class directly, either instantiate a subclass or + call cmds.detect_chip() which will interrogate the chip and return the + appropriate subclass instance. + + """ + + CHIP_NAME = "Espressif device" + IS_STUB = False + + FPGA_SLOW_BOOT = False + + DEFAULT_PORT = "/dev/ttyUSB0" + + USES_RFC2217 = False + + # Commands supported by ESP8266 ROM bootloader + ESP_FLASH_BEGIN = 0x02 + ESP_FLASH_DATA = 0x03 + ESP_FLASH_END = 0x04 + ESP_MEM_BEGIN = 0x05 + ESP_MEM_END = 0x06 + ESP_MEM_DATA = 0x07 + ESP_SYNC = 0x08 + ESP_WRITE_REG = 0x09 + ESP_READ_REG = 0x0A + + # Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub) + ESP_SPI_SET_PARAMS = 0x0B + ESP_SPI_ATTACH = 0x0D + ESP_READ_FLASH_SLOW = 0x0E # ROM only, much slower than the stub flash read + ESP_CHANGE_BAUDRATE = 0x0F + ESP_FLASH_DEFL_BEGIN = 0x10 + ESP_FLASH_DEFL_DATA = 0x11 + ESP_FLASH_DEFL_END = 0x12 + ESP_SPI_FLASH_MD5 = 0x13 + + # Commands supported by ESP32-S2 and later chips ROM bootloader only + ESP_GET_SECURITY_INFO = 0x14 + + # Some commands supported by stub only + ESP_ERASE_FLASH = 0xD0 + ESP_ERASE_REGION = 0xD1 + ESP_READ_FLASH = 0xD2 + ESP_RUN_USER_CODE = 0xD3 + + # Flash encryption encrypted data command + ESP_FLASH_ENCRYPT_DATA = 0xD4 + + # Response code(s) sent by ROM + ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received + + # Maximum block sized for RAM and Flash writes, respectively. + ESP_RAM_BLOCK = 0x1800 + + FLASH_WRITE_SIZE = 0x400 + + # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. + ESP_ROM_BAUD = 115200 + + # First byte of the application image + ESP_IMAGE_MAGIC = 0xE9 + + # Initial state for the checksum routine + ESP_CHECKSUM_MAGIC = 0xEF + + # Flash sector size, minimum unit of erase. + FLASH_SECTOR_SIZE = 0x1000 + + UART_DATE_REG_ADDR = 0x60000078 + + # This ROM address has a different value on each chip model + CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 + + UART_CLKDIV_MASK = 0xFFFFF + + # Memory addresses + IROM_MAP_START = 0x40200000 + IROM_MAP_END = 0x40300000 + + # The number of bytes in the UART response that signify command status + STATUS_BYTES_LENGTH = 2 + + # Bootloader flashing offset + BOOTLOADER_FLASH_OFFSET = 0x0 + + # ROM supports an encrypted flashing mode + SUPPORTS_ENCRYPTED_FLASH = False + + # Response to ESP_SYNC might indicate that flasher stub is running + # instead of the ROM bootloader + sync_stub_detected = False + + # Device PIDs + USB_JTAG_SERIAL_PID = 0x1001 + + # Chip IDs that are no longer supported by esptool + UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"} + + def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): + """Base constructor for ESPLoader bootloader interaction + + Don't call this constructor, either instantiate a specific + ROM class directly, or use cmds.detect_chip(). + + This base class has all of the instance methods for bootloader + functionality supported across various chips & stub + loaders. Subclasses replace the functions they don't support + with ones which throw NotImplementedInROMError(). + + """ + # True if esptool detects the ROM is in Secure Download Mode + self.secure_download_mode = False + # True if esptool detects conditions which require the stub to be disabled + self.stub_is_disabled = False + + if isinstance(port, str): + try: + self._port = serial.serial_for_url(port) + except serial.serialutil.SerialException: + raise FatalError(f"Could not open {port}, the port doesn't exist") + else: + self._port = port + self._slip_reader = slip_reader(self._port, self.trace) + # setting baud rate in a separate step is a workaround for + # CH341 driver on some Linux versions (this opens at 9600 then + # sets), shouldn't matter for other platforms/drivers. See + # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 + self._set_port_baudrate(baud) + self._trace_enabled = trace_enabled + # set write timeout, to prevent esptool blocked at write forever. + try: + self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT + except NotImplementedError: + # no write timeout for RFC2217 ports + # need to set the property back to None or it will continue to fail + self._port.write_timeout = None + + @property + def serial_port(self): + return self._port.port + + def _set_port_baudrate(self, baud): + try: + self._port.baudrate = baud + except IOError: + raise FatalError( + "Failed to set baud rate %d. The driver may not support this rate." + % baud + ) + + def read(self): + """Read a SLIP packet from the serial port""" + return next(self._slip_reader) + + def write(self, packet): + """Write bytes to the serial port while performing SLIP escaping""" + buf = ( + b"\xc0" + + (packet.replace(b"\xdb", b"\xdb\xdd").replace(b"\xc0", b"\xdb\xdc")) + + b"\xc0" + ) + self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) + self._port.write(buf) + + def trace(self, message, *format_args): + if self._trace_enabled: + now = time.time() + try: + + delta = now - self._last_trace + except AttributeError: + delta = 0.0 + self._last_trace = now + prefix = "TRACE +%.3f " % delta + print(prefix + (message % format_args)) + + @staticmethod + def checksum(data, state=ESP_CHECKSUM_MAGIC): + """Calculate checksum of a blob, as it is defined by the ROM""" + for b in data: + state ^= b + + return state + + def command( + self, + op=None, + data=b"", + chk=0, + wait_response=True, + timeout=DEFAULT_TIMEOUT, + ): + """Send a request and read the response""" + saved_timeout = self._port.timeout + new_timeout = min(timeout, MAX_TIMEOUT) + if new_timeout != saved_timeout: + self._port.timeout = new_timeout + + try: + if op is not None: + self.trace( + "command op=0x%02x data len=%s wait_response=%d " + "timeout=%.3f data=%s", + op, + len(data), + 1 if wait_response else 0, + timeout, + HexFormatter(data), + ) + pkt = struct.pack(b" self.STATUS_BYTES_LENGTH: + return data[: -self.STATUS_BYTES_LENGTH] + else: + # otherwise, just return the 'val' field which comes from the reply header + # (this is used by read_reg) + return val + + def flush_input(self): + self._port.flushInput() + self._slip_reader = slip_reader(self._port, self.trace) + + def sync(self): + val, _ = self.command( + self.ESP_SYNC, b"\x07\x07\x12\x20" + 32 * b"\x55", timeout=SYNC_TIMEOUT + ) + + # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. + # If we receive 0 then it probably indicates that the chip wasn't or couldn't be + # reseted properly and esptool is talking to the flasher stub. + self.sync_stub_detected = val == 0 + + for _ in range(7): + val, _ = self.command() + self.sync_stub_detected &= val == 0 + + def _setDTR(self, state): + self._port.setDTR(state) + + def _setRTS(self, state): + self._port.setRTS(state) + # Work-around for adapters on Windows using the usbser.sys driver: + # generate a dummy change to DTR so that the set-control-line-state + # request is sent with the updated RTS state and the same DTR state + self._port.setDTR(self._port.dtr) + + def _get_pid(self): + if list_ports is None: + print( + "\nListing all serial ports is currently not available. " + "Can't get device PID." + ) + return + active_port = self._port.port + + # Pyserial only identifies regular ports, URL handlers are not supported + if not active_port.lower().startswith(("com", "/dev/")): + print( + "\nDevice PID identification is only supported on " + "COM and /dev/ serial ports." + ) + return + # Return the real path if the active port is a symlink + if active_port.startswith("/dev/") and os.path.islink(active_port): + active_port = os.path.realpath(active_port) + + # The "cu" (call-up) device has to be used for outgoing communication on MacOS + if sys.platform == "darwin" and "tty" in active_port: + active_port = [active_port, active_port.replace("tty", "cu")] + ports = list_ports.comports() + for p in ports: + if p.device in active_port: + return p.pid + print( + "\nFailed to get PID of a device on {}, " + "using standard reset sequence.".format(active_port) + ) + + def bootloader_reset(self, usb_jtag_serial=False, extra_delay=False): + """ + Issue a reset-to-bootloader, with USB-JTAG-Serial custom reset sequence option + """ + # RTS = either CH_PD/EN or nRESET (both active low = chip in reset) + # DTR = GPIO0 (active low = boot to flasher) + # + # DTR & RTS are active low signals, + # ie True = pin @ 0V, False = pin @ VCC. + if usb_jtag_serial: + # Custom reset sequence, which is required when the device + # is connecting via its USB-JTAG-Serial peripheral + self._setRTS(False) + self._setDTR(False) # Idle + time.sleep(0.1) + self._setDTR(True) # Set IO0 + self._setRTS(False) + time.sleep(0.1) + self._setRTS( + True + ) # Reset. Note dtr/rts calls inverted to go through (1,1) instead of (0,0) + self._setDTR(False) + self._setRTS( + True + ) # Extra RTS set for RTS as Windows only propagates DTR on RTS setting + time.sleep(0.1) + self._setDTR(False) + self._setRTS(False) + else: + # This fpga delay is for Espressif internal use + fpga_delay = ( + True + if self.FPGA_SLOW_BOOT + and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1" + else False + ) + delay = ( + 7 if fpga_delay else 0.5 if extra_delay else 0.05 + ) # 0.5 needed for ESP32 rev0 and rev1 + + self._setDTR(False) # IO0=HIGH + self._setRTS(True) # EN=LOW, chip in reset + time.sleep(0.1) + self._setDTR(True) # IO0=LOW + self._setRTS(False) # EN=HIGH, chip out of reset + time.sleep(delay) + self._setDTR(False) # IO0=HIGH, done + + def _connect_attempt( + self, mode="default_reset", usb_jtag_serial=False, extra_delay=False + ): + """A single connection attempt""" + last_error = None + boot_log_detected = False + download_mode = False + + # If we're doing no_sync, we're likely communicating as a pass through + # with an intermediate device to the ESP32 + if mode == "no_reset_no_sync": + return last_error + + if mode != "no_reset": + if not self.USES_RFC2217: # Might block on rfc2217 ports + # Empty serial buffer to isolate boot log + self._port.reset_input_buffer() + self.bootloader_reset(usb_jtag_serial, extra_delay) + + # Detect the ROM boot log and check actual boot mode (ESP32 and later only) + waiting = self._port.inWaiting() + read_bytes = self._port.read(waiting) + data = re.search( + b"boot:(0x[0-9a-fA-F]+)(.*waiting for download)?", read_bytes, re.DOTALL + ) + if data is not None: + boot_log_detected = True + boot_mode = data.group(1) + download_mode = data.group(2) is not None + + for _ in range(5): + try: + self.flush_input() + self._port.flushOutput() + self.sync() + return None + except FatalError as e: + print(".", end="") + sys.stdout.flush() + time.sleep(0.05) + last_error = e + + if boot_log_detected: + last_error = FatalError( + "Wrong boot mode detected ({})! " + "The chip needs to be in download mode.".format( + boot_mode.decode("utf-8") + ) + ) + if download_mode: + last_error = FatalError( + "Download mode successfully detected, but getting no sync reply: " + "The serial TX path seems to be down." + ) + return last_error + + def get_memory_region(self, name): + """ + Returns a tuple of (start, end) for the memory map entry with the given name, + or None if it doesn't exist + """ + try: + return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0] + except IndexError: + return None + + def connect( + self, + mode="default_reset", + attempts=DEFAULT_CONNECT_ATTEMPTS, + detecting=False, + warnings=True, + ): + """Try connecting repeatedly until successful, or giving up""" + if warnings and mode in ["no_reset", "no_reset_no_sync"]: + print( + 'WARNING: Pre-connection option "{}" was selected.'.format(mode), + "Connection may fail if the chip is not in bootloader " + "or flasher stub mode.", + ) + print("Connecting...", end="") + sys.stdout.flush() + last_error = None + + usb_jtag_serial = (mode == "usb_reset") or ( + self._get_pid() == self.USB_JTAG_SERIAL_PID + ) + + try: + for _, extra_delay in zip( + range(attempts) if attempts > 0 else itertools.count(), + itertools.cycle((False, True)), + ): + last_error = self._connect_attempt( + mode=mode, usb_jtag_serial=usb_jtag_serial, extra_delay=extra_delay + ) + if last_error is None: + break + finally: + print("") # end 'Connecting...' line + + if last_error is not None: + raise FatalError( + "Failed to connect to {}: {}" + "\nFor troubleshooting steps visit: " + "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html".format( # noqa E501 + self.CHIP_NAME, last_error + ) + ) + + if not detecting: + try: + from .targets import ROM_LIST + + # check the date code registers match what we expect to see + chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) + if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE: + actually = None + for cls in ROM_LIST: + if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: + actually = cls + break + if warnings and actually is None: + print( + "WARNING: This chip doesn't appear to be a %s " + "(chip magic value 0x%08x). " + "Probably it is unsupported by this version of esptool." + % (self.CHIP_NAME, chip_magic_value) + ) + else: + raise FatalError( + "This chip is %s not %s. Wrong --chip argument?" + % (actually.CHIP_NAME, self.CHIP_NAME) + ) + except UnsupportedCommandError: + self.secure_download_mode = True + self._post_connect() + self.check_chip_id() + + def _post_connect(self): + """ + Additional initialization hook, may be overridden by the chip-specific class. + Gets called after connect, and after auto-detection. + """ + pass + + def read_reg(self, addr, timeout=DEFAULT_TIMEOUT): + """Read memory address in target""" + # we don't call check_command here because read_reg() function is called + # when detecting chip type, and the way we check for success + # (STATUS_BYTES_LENGTH) is different for different chip types (!) + val, data = self.command( + self.ESP_READ_REG, struct.pack(" 0: + # add a dummy write to a date register as an excuse to have a delay + command += struct.pack( + " start: + raise FatalError( + "Software loader is resident at 0x%08x-0x%08x. " + "Can't load binary at overlapping address range 0x%08x-0x%08x. " + "Either change binary loading address, or use the --no-stub " + "option to disable the software loader." + % (start, end, load_start, load_end) + ) + + return self.check_command( + "enter RAM download mode", + self.ESP_MEM_BEGIN, + struct.pack(" length: + raise FatalError("Read more than expected") + + digest_frame = self.read() + if len(digest_frame) != 16: + raise FatalError("Expected digest, got: %s" % hexify(digest_frame)) + expected_digest = hexify(digest_frame).upper() + digest = hashlib.md5(data).hexdigest().upper() + if digest != expected_digest: + raise FatalError( + "Digest mismatch: expected %s, got %s" % (expected_digest, digest) + ) + return data + + def flash_spi_attach(self, hspi_arg): + """Send SPI attach command to enable the SPI flash pins + + ESP8266 ROM does this when you send flash_begin, ESP32 ROM + has it as a SPI command. + """ + # last 3 bytes in ESP_SPI_ATTACH argument are reserved values + arg = struct.pack(" 0: + self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) + if miso_bits > 0: + self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) + flags = 0 + if dummy_len > 0: + flags |= dummy_len - 1 + if addr_len > 0: + flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT + if flags: + self.write_reg(SPI_USR1_REG, flags) + + else: + + def set_data_lengths(mosi_bits, miso_bits): + SPI_DATA_LEN_REG = SPI_USR1_REG + SPI_MOSI_BITLEN_S = 17 + SPI_MISO_BITLEN_S = 8 + mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) + miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) + flags = (miso_mask << SPI_MISO_BITLEN_S) | ( + mosi_mask << SPI_MOSI_BITLEN_S + ) + if dummy_len > 0: + flags |= dummy_len - 1 + if addr_len > 0: + flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT + self.write_reg(SPI_DATA_LEN_REG, flags) + + # SPI peripheral "command" bitmasks for SPI_CMD_REG + SPI_CMD_USR = 1 << 18 + + # shift values + SPI_USR2_COMMAND_LEN_SHIFT = 28 + SPI_USR_ADDR_LEN_SHIFT = 26 + + if read_bits > 32: + raise FatalError( + "Reading more than 32 bits back from a SPI flash " + "operation is unsupported" + ) + if len(data) > 64: + raise FatalError( + "Writing more than 64 bytes of data with one SPI " + "command is unsupported" + ) + + data_bits = len(data) * 8 + old_spi_usr = self.read_reg(SPI_USR_REG) + old_spi_usr2 = self.read_reg(SPI_USR2_REG) + flags = SPI_USR_COMMAND + if read_bits > 0: + flags |= SPI_USR_MISO + if data_bits > 0: + flags |= SPI_USR_MOSI + if addr_len > 0: + flags |= SPI_USR_ADDR + if dummy_len > 0: + flags |= SPI_USR_DUMMY + set_data_lengths(data_bits, read_bits) + self.write_reg(SPI_USR_REG, flags) + self.write_reg( + SPI_USR2_REG, (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command + ) + if addr and addr_len > 0: + self.write_reg(SPI_ADDR_REG, addr) + if data_bits == 0: + self.write_reg(SPI_W0_REG, 0) # clear data register before we read it + else: + data = pad_to(data, 4, b"\00") # pad to 32-bit multiple + words = struct.unpack("I" * (len(data) // 4), data) + next_reg = SPI_W0_REG + for word in words: + self.write_reg(next_reg, word) + next_reg += 4 + self.write_reg(SPI_CMD_REG, SPI_CMD_USR) + + def wait_done(): + for _ in range(10): + if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: + return + raise FatalError("SPI command did not complete in time") + + wait_done() + + status = self.read_reg(SPI_W0_REG) + # restore some SPI controller registers + self.write_reg(SPI_USR_REG, old_spi_usr) + self.write_reg(SPI_USR2_REG, old_spi_usr2) + return status + + def read_spiflash_sfdp(self, addr, read_bits): + CMD_RDSFDP = 0x5A + return self.run_spiflash_command( + CMD_RDSFDP, read_bits=read_bits, addr=addr, addr_len=24, dummy_len=8 + ) + + def read_status(self, num_bytes=2): + """Read up to 24 bits (num_bytes) of SPI flash status register contents + via RDSR, RDSR2, RDSR3 commands + + Not all SPI flash supports all three commands. The upper 1 or 2 + bytes may be 0xFF. + """ + SPIFLASH_RDSR = 0x05 + SPIFLASH_RDSR2 = 0x35 + SPIFLASH_RDSR3 = 0x15 + + status = 0 + shift = 0 + for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: + status += self.run_spiflash_command(cmd, read_bits=8) << shift + shift += 8 + return status + + def write_status(self, new_status, num_bytes=2, set_non_volatile=False): + """Write up to 24 bits (num_bytes) of new status register + + num_bytes can be 1, 2 or 3. + + Not all flash supports the additional commands to write the + second and third byte of the status register. When writing 2 + bytes, esptool also sends a 16-byte WRSR command (as some + flash types use this instead of WRSR2.) + + If the set_non_volatile flag is set, non-volatile bits will + be set as well as volatile ones (WREN used instead of WEVSR). + + """ + SPIFLASH_WRSR = 0x01 + SPIFLASH_WRSR2 = 0x31 + SPIFLASH_WRSR3 = 0x11 + SPIFLASH_WEVSR = 0x50 + SPIFLASH_WREN = 0x06 + SPIFLASH_WRDI = 0x04 + + enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR + + # try using a 16-bit WRSR (not supported by all chips) + # this may be redundant, but shouldn't hurt + if num_bytes == 2: + self.run_spiflash_command(enable_cmd) + self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 + + self.run_spiflash_command(SPIFLASH_WRDI) + + def get_crystal_freq(self): + """ + Figure out the crystal frequency from the UART clock divider + + Returns a normalized value in integer MHz (only values 40 or 26 are supported) + """ + # The logic here is: + # - We know that our baud rate and the ESP UART baud rate are roughly the same, + # or we couldn't communicate + # - We can read the UART clock divider register to know how the ESP derives this + # from the APB bus frequency + # - Multiplying these two together gives us the bus frequency which is either + # the crystal frequency (ESP32) or double the crystal frequency (ESP8266). + # See the self.XTAL_CLK_DIVIDER parameter for this factor. + uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK + est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER + norm_xtal = 40 if est_xtal > 33 else 26 + if abs(norm_xtal - est_xtal) > 1: + print( + "WARNING: Detected crystal freq %.2fMHz is quite different to " + "normalized freq %dMHz. Unsupported crystal in use?" + % (est_xtal, norm_xtal) + ) + return norm_xtal + + def hard_reset(self): + print("Hard resetting via RTS pin...") + self._setRTS(True) # EN->LOW + time.sleep(0.1) + self._setRTS(False) + + def soft_reset(self, stay_in_bootloader): + if not self.IS_STUB: + if stay_in_bootloader: + return # ROM bootloader is already in bootloader! + else: + # 'run user code' is as close to a soft reset as we can do + self.flash_begin(0, 0) + self.flash_finish(False) + else: + if stay_in_bootloader: + # soft resetting from the stub loader + # will re-load the ROM bootloader + self.flash_begin(0, 0) + self.flash_finish(True) + elif self.CHIP_NAME != "ESP8266": + raise FatalError( + "Soft resetting is currently only supported on ESP8266" + ) + else: + # running user code from stub loader requires some hacks + # in the stub loader + self.command(self.ESP_RUN_USER_CODE, wait_response=False) + + def check_chip_id(self): + try: + chip_id = self.get_chip_id() + if chip_id != self.IMAGE_CHIP_ID: + print( + "WARNING: Chip ID {} ({}) doesn't match expected Chip ID {}. " + "esptool may not work correctly.".format( + chip_id, + self.UNSUPPORTED_CHIPS.get(chip_id, "Unknown"), + self.IMAGE_CHIP_ID, + ) + ) + # Try to flash anyways by disabling stub + self.stub_is_disabled = True + except NotImplementedInROMError: + pass + + +def slip_reader(port, trace_function): + """Generator to read SLIP packets from a serial port. + Yields one full SLIP packet at a time, raises exception on timeout or invalid data. + + Designed to avoid too many calls to serial.read(1), which can bog + down on slow systems. + """ + + def detect_panic_handler(input): + """ + Checks the input bytes for panic handler messages. + Raises a FatalError if Guru Meditation or Fatal Exception is found, as both + of these are used between different ROM versions. + Tries to also parse the error cause (e.g. IllegalInstruction). + """ + + guru_meditation = ( + rb"G?uru Meditation Error: (?:Core \d panic'ed \(([a-zA-Z]*)\))?" + ) + fatal_exception = rb"F?atal exception \(\d+\): (?:([a-zA-Z]*)?.*epc)?" + + # Search either for Guru Meditation or Fatal Exception + data = re.search( + rb"".join([rb"(?:", guru_meditation, rb"|", fatal_exception, rb")"]), + input, + re.DOTALL, + ) + if data is not None: + msg = "Guru Meditation Error detected {}".format( + " ".join( + [ + "({})".format(i.decode("utf-8")) + for i in [data.group(1), data.group(2)] + if i is not None + ] + ) + ) + raise FatalError(msg) + + partial_packet = None + in_escape = False + successful_slip = False + while True: + waiting = port.inWaiting() + read_bytes = port.read(1 if waiting == 0 else waiting) + if read_bytes == b"": + if partial_packet is None: # fail due to no data + msg = ( + "Serial data stream stopped: Possible serial noise or corruption." + if successful_slip + else "No serial data received." + ) + else: # fail during packet transfer + msg = "Packet content transfer stopped (received {} bytes)".format( + len(partial_packet) + ) + trace_function(msg) + raise FatalError(msg) + trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) + for b in read_bytes: + b = bytes([b]) + if partial_packet is None: # waiting for packet header + if b == b"\xc0": + partial_packet = b"" + else: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + remaining_data = port.read(port.inWaiting()) + trace_function( + "Remaining data in serial buffer: %s", + HexFormatter(remaining_data), + ) + detect_panic_handler(read_bytes + remaining_data) + raise FatalError( + "Invalid head of packet (0x%s): " + "Possible serial noise or corruption." % hexify(b) + ) + elif in_escape: # part-way through escape sequence + in_escape = False + if b == b"\xdc": + partial_packet += b"\xc0" + elif b == b"\xdd": + partial_packet += b"\xdb" + else: + trace_function("Read invalid data: %s", HexFormatter(read_bytes)) + remaining_data = port.read(port.inWaiting()) + trace_function( + "Remaining data in serial buffer: %s", + HexFormatter(remaining_data), + ) + detect_panic_handler(read_bytes + remaining_data) + raise FatalError("Invalid SLIP escape (0xdb, 0x%s)" % (hexify(b))) + elif b == b"\xdb": # start of escape sequence + in_escape = True + elif b == b"\xc0": # end of packet + trace_function("Received full packet: %s", HexFormatter(partial_packet)) + yield partial_packet + partial_packet = None + successful_slip = True + else: # normal byte in packet + partial_packet += b + + +class HexFormatter(object): + """ + Wrapper class which takes binary data in its constructor + and returns a hex string as it's __str__ method. + + This is intended for "lazy formatting" of trace() output + in hex format. Avoids overhead (significant on slow computers) + of generating long hex strings even if tracing is disabled. + + Note that this doesn't save any overhead if passed as an + argument to "%", only when passed to trace() + + If auto_split is set (default), any long line (> 16 bytes) will be + printed as separately indented lines, with ASCII decoding at the end + of each line. + """ + + def __init__(self, binary_string, auto_split=True): + self._s = binary_string + self._auto_split = auto_split + + def __str__(self): + if self._auto_split and len(self._s) > 16: + result = "" + s = self._s + while len(s) > 0: + line = s[:16] + ascii_line = "".join( + c + if ( + c == " " + or (c in string.printable and c not in string.whitespace) + ) + else "." + for c in line.decode("ascii", "replace") + ) + s = s[16:] + result += "\n %-16s %-16s | %s" % ( + hexify(line[:8], False), + hexify(line[8:], False), + ascii_line, + ) + return result + else: + return hexify(self._s, False) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py new file mode 100644 index 000000000..aae27f328 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py @@ -0,0 +1,29 @@ +from .esp32 import ESP32ROM +from .esp32c2 import ESP32C2ROM +from .esp32c3 import ESP32C3ROM +from .esp32c6 import ESP32C6ROM +from .esp32c6beta import ESP32C6BETAROM +from .esp32h2beta1 import ESP32H2BETA1ROM +from .esp32h2beta2 import ESP32H2BETA2ROM +from .esp32s2 import ESP32S2ROM +from .esp32s3 import ESP32S3ROM +from .esp32s3beta2 import ESP32S3BETA2ROM +from .esp8266 import ESP8266ROM + + +CHIP_DEFS = { + "esp8266": ESP8266ROM, + "esp32": ESP32ROM, + "esp32s2": ESP32S2ROM, + "esp32s3beta2": ESP32S3BETA2ROM, + "esp32s3": ESP32S3ROM, + "esp32c3": ESP32C3ROM, + "esp32c6beta": ESP32C6BETAROM, + "esp32h2beta1": ESP32H2BETA1ROM, + "esp32h2beta2": ESP32H2BETA2ROM, + "esp32c2": ESP32C2ROM, + "esp32c6": ESP32C6ROM, +} + +CHIP_LIST = list(CHIP_DEFS.keys()) +ROM_LIST = list(CHIP_DEFS.values()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py new file mode 100644 index 000000000..ea7deb5ad --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py @@ -0,0 +1,360 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import struct + +from ..loader import ESPLoader +from ..util import FatalError, NotSupportedError + + +class ESP32ROM(ESPLoader): + """Access class for ESP32 ROM bootloader""" + + CHIP_NAME = "ESP32" + IMAGE_CHIP_ID = 0 + IS_STUB = False + + FPGA_SLOW_BOOT = True + + CHIP_DETECT_MAGIC_VALUE = [0x00F01D83] + + IROM_MAP_START = 0x400D0000 + IROM_MAP_END = 0x40400000 + + DROM_MAP_START = 0x3F400000 + DROM_MAP_END = 0x3F800000 + + # ESP32 uses a 4 byte status reply + STATUS_BYTES_LENGTH = 4 + + SPI_REG_BASE = 0x3FF42000 + SPI_USR_OFFS = 0x1C + SPI_USR1_OFFS = 0x20 + SPI_USR2_OFFS = 0x24 + SPI_MOSI_DLEN_OFFS = 0x28 + SPI_MISO_DLEN_OFFS = 0x2C + EFUSE_RD_REG_BASE = 0x3FF5A000 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18 + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7 # EFUSE_RD_DISABLE_DL_ENCRYPT + + EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_RD_REG_BASE # EFUSE_BLK0_WDATA0_REG + EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7F << 20 # EFUSE_FLASH_CRYPT_CNT + + EFUSE_RD_ABS_DONE_REG = EFUSE_RD_REG_BASE + 0x018 + EFUSE_RD_ABS_DONE_0_MASK = 1 << 4 + EFUSE_RD_ABS_DONE_1_MASK = 1 << 5 + + DR_REG_SYSCON_BASE = 0x3FF66000 + APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C + APB_CTL_DATE_V = 0x1 + APB_CTL_DATE_S = 31 + + SPI_W0_OFFS = 0x80 + + UART_CLKDIV_REG = 0x3FF40014 + + XTAL_CLK_DIVIDER = 1 + + FLASH_SIZES = { + "1MB": 0x00, + "2MB": 0x10, + "4MB": 0x20, + "8MB": 0x30, + "16MB": 0x40, + "32MB": 0x50, + "64MB": 0x60, + "128MB": 0x70, + } + + FLASH_FREQUENCY = { + "80m": 0xF, + "40m": 0x0, + "26m": 0x1, + "20m": 0x2, + } + + BOOTLOADER_FLASH_OFFSET = 0x1000 + + OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"] + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x3F400000, 0x3F800000, "DROM"], + [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], + [0x3FF80000, 0x3FF82000, "RTC_DRAM"], + [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], + [0x3FFAE000, 0x40000000, "DRAM"], + [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"], + [0x40000000, 0x40070000, "IROM"], + [0x40070000, 0x40078000, "CACHE_PRO"], + [0x40078000, 0x40080000, "CACHE_APP"], + [0x40080000, 0x400A0000, "IRAM"], + [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"], + [0x400C0000, 0x400C2000, "RTC_IRAM"], + [0x400D0000, 0x40400000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"], + ] + + FLASH_ENCRYPTED_WRITE_ALIGN = 32 + + """ Try to read the BLOCK1 (encryption key) and check if it is valid """ + + def is_flash_encryption_key_valid(self): + """Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1 + this bit is at position 16 in EFUSE_BLK0_RDATA0_REG""" + word0 = self.read_efuse(0) + rd_disable = (word0 >> 16) & 0x1 + + # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed + if rd_disable: + return True + else: + # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero. + # When ESP32 has not generated AES/encryption key in BLOCK1, + # the contents will be readable and 0. + # If the flash encryption is enabled it is expected to have a valid + # non-zero key. We break out on first occurance of non-zero value + key_word = [0] * 7 + for i in range(len(key_word)): + key_word[i] = self.read_efuse(14 + i) + # key is non-zero so break & return + if key_word[i] != 0: + return True + return False + + def get_flash_crypt_config(self): + """For flash encryption related commands we need to make sure + user has programmed all the relevant efuse correctly so before + writing encrypted write_flash_encrypt esptool will verify the values + of flash_crypt_config to be non zero if they are not read + protected. If the values are zero a warning will be printed + + bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config + this bit is at position 19 in EFUSE_BLK0_RDATA0_REG""" + word0 = self.read_efuse(0) + rd_disable = (word0 >> 19) & 0x1 + + if rd_disable == 0: + """we can read the flash_crypt_config efuse value + so go & read it (EFUSE_BLK0_RDATA5_REG[31:28])""" + word5 = self.read_efuse(5) + word5 = (word5 >> 28) & 0xF + return word5 + else: + # if read of the efuse is disabled we assume it is set correctly + return 0xF + + def get_encrypted_download_disabled(self): + if ( + self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) + & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT + ): + return True + else: + return False + + def get_flash_encryption_enabled(self): + flash_crypt_cnt = ( + self.read_reg(self.EFUSE_SPI_BOOT_CRYPT_CNT_REG) + & self.EFUSE_SPI_BOOT_CRYPT_CNT_MASK + ) + # Flash encryption enabled when odd number of bits are set + return bin(flash_crypt_cnt).count("1") & 1 != 0 + + def get_secure_boot_enabled(self): + efuses = self.read_reg(self.EFUSE_RD_ABS_DONE_REG) + rev = self.get_chip_revision() + return efuses & self.EFUSE_RD_ABS_DONE_0_MASK or ( + rev >= 300 and efuses & self.EFUSE_RD_ABS_DONE_1_MASK + ) + + def get_pkg_version(self): + word3 = self.read_efuse(3) + pkg_version = (word3 >> 9) & 0x07 + pkg_version += ((word3 >> 2) & 0x1) << 3 + return pkg_version + + def get_chip_revision(self): + return self.get_major_chip_version() * 100 + self.get_minor_chip_version() + + def get_minor_chip_version(self): + return (self.read_efuse(5) >> 24) & 0x3 + + def get_major_chip_version(self): + rev_bit0 = (self.read_efuse(3) >> 15) & 0x1 + rev_bit1 = (self.read_efuse(5) >> 20) & 0x1 + apb_ctl_date = self.read_reg(self.APB_CTL_DATE_ADDR) + rev_bit2 = (apb_ctl_date >> self.APB_CTL_DATE_S) & self.APB_CTL_DATE_V + combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 + + revision = { + 0: 0, + 1: 1, + 3: 2, + 7: 3, + }.get(combine_value, 0) + return revision + + def get_chip_description(self): + pkg_version = self.get_pkg_version() + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + rev3 = major_rev == 3 + single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU + + chip_name = { + 0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6", + 1: "ESP32-S0WD" if single_core else "ESP32-D0WD", + 2: "ESP32-D2WD", + 4: "ESP32-U4WDH", + 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", + 6: "ESP32-PICO-V3-02", + 7: "ESP32-D0WDR2-V3", + }.get(pkg_version, "unknown ESP32") + + # ESP32-D0WD-V3, ESP32-D0WDQ6-V3 + if chip_name.startswith("ESP32-D0WD") and rev3: + chip_name += "-V3" + + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + features = ["WiFi"] + word3 = self.read_efuse(3) + + # names of variables in this section are lowercase + # versions of EFUSE names as documented in TRM and + # ESP-IDF efuse_reg.h + + chip_ver_dis_bt = word3 & (1 << 1) + if chip_ver_dis_bt == 0: + features += ["BT"] + + chip_ver_dis_app_cpu = word3 & (1 << 0) + if chip_ver_dis_app_cpu: + features += ["Single Core"] + else: + features += ["Dual Core"] + + chip_cpu_freq_rated = word3 & (1 << 13) + if chip_cpu_freq_rated: + chip_cpu_freq_low = word3 & (1 << 12) + if chip_cpu_freq_low: + features += ["160MHz"] + else: + features += ["240MHz"] + + pkg_version = self.get_pkg_version() + if pkg_version in [2, 4, 5, 6]: + features += ["Embedded Flash"] + + if pkg_version == 6: + features += ["Embedded PSRAM"] + + word4 = self.read_efuse(4) + adc_vref = (word4 >> 8) & 0x1F + if adc_vref: + features += ["VRef calibration in efuse"] + + blk3_part_res = word3 >> 14 & 0x1 + if blk3_part_res: + features += ["BLK3 partially reserved"] + + word6 = self.read_efuse(6) + coding_scheme = word6 & 0x3 + features += [ + "Coding Scheme %s" + % {0: "None", 1: "3/4", 2: "Repeat (UNSUPPORTED)", 3: "Invalid"}[ + coding_scheme + ] + ] + + return features + + def read_efuse(self, n): + """Read the nth word of the ESP3x EFUSE region.""" + return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n)) + + def chip_id(self): + raise NotSupportedError(self, "chip_id") + + def read_mac(self): + """Read MAC from EFUSE region""" + words = [self.read_efuse(2), self.read_efuse(1)] + bitstring = struct.pack(">II", *words) + bitstring = bitstring[2:8] # trim the 2 byte CRC + return tuple(bitstring) + + def get_erase_size(self, offset, size): + return size + + def override_vddsdio(self, new_voltage): + new_voltage = new_voltage.upper() + if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: + raise FatalError( + "The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'" + ) + RTC_CNTL_SDIO_CONF_REG = 0x3FF48074 + RTC_CNTL_XPD_SDIO_REG = 1 << 31 + RTC_CNTL_DREFH_SDIO_M = 3 << 29 + RTC_CNTL_DREFM_SDIO_M = 3 << 27 + RTC_CNTL_DREFL_SDIO_M = 3 << 25 + # RTC_CNTL_SDIO_TIEH = (1 << 23) + # not used here, setting TIEH=1 would set 3.3V output, + # not safe for esptool.py to do + RTC_CNTL_SDIO_FORCE = 1 << 22 + RTC_CNTL_SDIO_PD_EN = 1 << 21 + + reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting + reg_val |= RTC_CNTL_SDIO_PD_EN + if new_voltage != "OFF": + reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO + if new_voltage == "1.9V": + reg_val |= ( + RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M + ) # boost voltage + self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val) + print("VDDSDIO regulator set to %s" % new_voltage) + + def read_flash_slow(self, offset, length, progress_fn): + BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow) + + data = b"" + while len(data) < length: + block_len = min(BLOCK_LEN, length - len(data)) + r = self.check_command( + "read flash block", + self.ESP_READ_FLASH_SLOW, + struct.pack("> 22) & 0x07 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C2", + 1: "ESP32-C2", + }.get(self.get_pkg_version(), "unknown ESP32-C2") + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_minor_chip_version(self): + num_word = 1 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 16) & 0xF + + def get_major_chip_version(self): + num_word = 1 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3 + + def get_crystal_freq(self): + # The crystal detection algorithm of ESP32/ESP8266 works for ESP32-C2 as well. + return ESPLoader.get_crystal_freq(self) + + def change_baud(self, baud): + rom_with_26M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 26 + if rom_with_26M_XTAL: + # The code is copied over from ESPLoader.change_baud(). + # Probably this is just a temporary solution until the next chip revision. + + # The ROM code thinks it uses a 40 MHz XTAL. Recompute the baud rate + # in order to trick the ROM code to set the correct baud rate for + # a 26 MHz XTAL. + false_rom_baud = baud * 40 // 26 + + print(f"Changing baud rate to {baud}") + self.command( + self.ESP_CHANGE_BAUDRATE, struct.pack("> 21) & 0x07 + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C3", + }.get(self.get_pkg_version(), "unknown ESP32-C3") + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + return ["WiFi", "BLE"] + + def get_crystal_freq(self): + # ESP32C3 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-C3" + ) + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + return tuple(bitstring) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-C3 + + def get_secure_boot_enabled(self): + return ( + self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) + & self.EFUSE_SECURE_BOOT_EN_MASK + ) + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [ + (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), + ][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see an AES-128 key + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) + + +class ESP32C3StubLoader(ESP32C3ROM): + """Access class for ESP32C3 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py new file mode 100644 index 000000000..3cc9d4fdd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py @@ -0,0 +1,175 @@ +# SPDX-FileCopyrightText: 2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import struct + +from .esp32 import ESP32ROM +from ..util import FatalError, NotImplementedInROMError + + +class ESP32C6ROM(ESP32ROM): + CHIP_NAME = "ESP32-C6" + IMAGE_CHIP_ID = 13 + + FPGA_SLOW_BOOT = False + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x42800000 + DROM_MAP_START = 0x42800000 + DROM_MAP_END = 0x43000000 + + BOOTLOADER_FLASH_OFFSET = 0x0 + + # Magic value for ESP32C6 + CHIP_DETECT_MAGIC_VALUE = [0x2CE0806F] + + SPI_REG_BASE = 0x60003000 + SPI_USR_OFFS = 0x18 + SPI_USR1_OFFS = 0x1C + SPI_USR2_OFFS = 0x20 + SPI_MOSI_DLEN_OFFS = 0x24 + SPI_MISO_DLEN_OFFS = 0x28 + SPI_W0_OFFS = 0x58 + + UART_DATE_REG_ADDR = 0x60000000 + 0x7C + + EFUSE_BASE = 0x600B0800 + EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 + MAC_EFUSE_REG = EFUSE_BASE + 0x044 + + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + + EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY0_SHIFT = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 + + EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 + EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 + + EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 + EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 + + PURPOSE_VAL_XTS_AES128_KEY = 4 + + SUPPORTS_ENCRYPTED_FLASH = True + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x42800000, 0x43000000, "DROM"], + [0x40800000, 0x40880000, "DRAM"], + [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], + [0x4004AC00, 0x40050000, "DROM_MASK"], + [0x40000000, 0x4004AC00, "IROM_MASK"], + [0x42000000, 0x42800000, "IROM"], + [0x40800000, 0x40880000, "IRAM"], + [0x50000000, 0x50004000, "RTC_IRAM"], + [0x50000000, 0x50004000, "RTC_DRAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"], + ] + + def get_pkg_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C6", + }.get(self.get_pkg_version(), "unknown ESP32-C6") + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + return ["WiFi 6", "BT 5"] + + def get_crystal_freq(self): + # ESP32C6 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-C6" + ) + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + return tuple(bitstring) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-C6 + + def get_secure_boot_enabled(self): + return ( + self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) + & self.EFUSE_SECURE_BOOT_EN_MASK + ) + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [ + (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), + ][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see an AES-128 key + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) + + +class ESP32C6StubLoader(ESP32C6ROM): + """Access class for ESP32C6 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32C6ROM.STUB_CLASS = ESP32C6StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py new file mode 100644 index 000000000..d30e7b010 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from .esp32c3 import ESP32C3ROM + + +class ESP32C6BETAROM(ESP32C3ROM): + CHIP_NAME = "ESP32-C6(beta)" + IMAGE_CHIP_ID = 7 + + CHIP_DETECT_MAGIC_VALUE = [0x0DA1806F] + + UART_DATE_REG_ADDR = 0x00000500 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-C6", + }.get(self.get_pkg_version(), "unknown ESP32-C6") + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py new file mode 100644 index 000000000..529ba4c0a --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py @@ -0,0 +1,154 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import struct + +from .esp32 import ESP32ROM +from ..util import FatalError, NotImplementedInROMError + + +class ESP32H2BETA1ROM(ESP32ROM): + CHIP_NAME = "ESP32-H2(beta1)" + IMAGE_CHIP_ID = 10 + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x42800000 + DROM_MAP_START = 0x3C000000 + DROM_MAP_END = 0x3C800000 + + SPI_REG_BASE = 0x60002000 + SPI_USR_OFFS = 0x18 + SPI_USR1_OFFS = 0x1C + SPI_USR2_OFFS = 0x20 + SPI_MOSI_DLEN_OFFS = 0x24 + SPI_MISO_DLEN_OFFS = 0x28 + SPI_W0_OFFS = 0x58 + + BOOTLOADER_FLASH_OFFSET = 0x0 + + CHIP_DETECT_MAGIC_VALUE = [0xCA26CC22] + + UART_DATE_REG_ADDR = 0x60000000 + 0x7C + + EFUSE_BASE = 0x6001A000 + EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 + MAC_EFUSE_REG = EFUSE_BASE + 0x044 + + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + + EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY0_SHIFT = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 + + PURPOSE_VAL_XTS_AES128_KEY = 4 + + SUPPORTS_ENCRYPTED_FLASH = True + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + MEMORY_MAP = [] + + FLASH_FREQUENCY = { + "48m": 0xF, + "24m": 0x0, + "16m": 0x1, + "12m": 0x2, + } + + def get_pkg_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 5 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-H2", + }.get(self.get_pkg_version(), "unknown ESP32-H2") + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + return ["BLE/802.15.4"] + + def get_crystal_freq(self): + return 32 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-H2" + ) + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + return tuple(bitstring) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-H2 + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [ + (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), + ][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see an AES-128 key + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) + + +class ESP32H2BETA1StubLoader(ESP32H2BETA1ROM): + """Access class for ESP32H2BETA1 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32H2BETA1ROM.STUB_CLASS = ESP32H2BETA1StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py new file mode 100644 index 000000000..97a66a019 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py @@ -0,0 +1,42 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from .esp32h2beta1 import ESP32H2BETA1ROM + + +class ESP32H2BETA2ROM(ESP32H2BETA1ROM): + CHIP_NAME = "ESP32-H2(beta2)" + IMAGE_CHIP_ID = 14 + + CHIP_DETECT_MAGIC_VALUE = [0x6881B06F] + + def get_chip_description(self): + chip_name = { + 1: "ESP32-H2(beta2)", + }.get(self.get_pkg_version(), "unknown ESP32-H2") + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + +class ESP32H2BETA2StubLoader(ESP32H2BETA2ROM): + """Access class for ESP32H2BETA2 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32H2BETA2ROM.STUB_CLASS = ESP32H2BETA2StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py new file mode 100644 index 000000000..5bfefa6cf --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py @@ -0,0 +1,300 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import struct +import time + +from .esp32 import ESP32ROM +from ..util import FatalError, NotImplementedInROMError + + +class ESP32S2ROM(ESP32ROM): + CHIP_NAME = "ESP32-S2" + IMAGE_CHIP_ID = 2 + + FPGA_SLOW_BOOT = False + + IROM_MAP_START = 0x40080000 + IROM_MAP_END = 0x40B80000 + DROM_MAP_START = 0x3F000000 + DROM_MAP_END = 0x3F3F0000 + + CHIP_DETECT_MAGIC_VALUE = [0x000007C6] + + SPI_REG_BASE = 0x3F402000 + SPI_USR_OFFS = 0x18 + SPI_USR1_OFFS = 0x1C + SPI_USR2_OFFS = 0x20 + SPI_MOSI_DLEN_OFFS = 0x24 + SPI_MISO_DLEN_OFFS = 0x28 + SPI_W0_OFFS = 0x58 + + MAC_EFUSE_REG = 0x3F41A044 # ESP32-S2 has special block for MAC efuses + + UART_CLKDIV_REG = 0x3F400014 + + SUPPORTS_ENCRYPTED_FLASH = True + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + # todo: use espefuse APIs to get this info + EFUSE_BASE = 0x3F41A000 + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 + EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x05C + + EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY0_SHIFT = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19 + + EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 + EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 + + EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 + EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 + + PURPOSE_VAL_XTS_AES256_KEY_1 = 2 + PURPOSE_VAL_XTS_AES256_KEY_2 = 3 + PURPOSE_VAL_XTS_AES128_KEY = 4 + + UARTDEV_BUF_NO = 0x3FFFFD14 # Variable in ROM .bss which indicates the port in use + UARTDEV_BUF_NO_USB_OTG = 2 # Value of the above indicating that USB-OTG is in use + + USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used + + GPIO_STRAP_REG = 0x3F404038 + GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + RTC_CNTL_OPTION1_REG = 0x3F408128 + RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x3F000000, 0x3FF80000, "DROM"], + [0x3F500000, 0x3FF80000, "EXTRAM_DATA"], + [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"], + [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"], + [0x3FF9E000, 0x40072000, "MEM_INTERNAL"], + [0x3FFB0000, 0x40000000, "DRAM"], + [0x40000000, 0x4001A100, "IROM_MASK"], + [0x40020000, 0x40070000, "IRAM"], + [0x40070000, 0x40072000, "RTC_IRAM"], + [0x40080000, 0x40800000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"], + ] + + def get_pkg_version(self): + num_word = 4 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F + + def get_minor_chip_version(self): + hi_num_word = 3 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 20) & 0x01 + low_num_word = 4 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 4) & 0x07 + return (hi << 3) + low + + def get_major_chip_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x03 + + def get_flash_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F + + def get_psram_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 28) & 0x0F + + def get_block2_version(self): + # BLK_VERSION_MINOR + num_word = 4 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 4) & 0x07 + + def get_chip_description(self): + chip_name = { + 0: "ESP32-S2", + 1: "ESP32-S2FH2", + 2: "ESP32-S2FH4", + 102: "ESP32-S2FNR2", + 100: "ESP32-S2R2", + }.get( + self.get_flash_version() + self.get_psram_version() * 100, + "unknown ESP32-S2", + ) + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{chip_name} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + features = ["WiFi"] + + if self.secure_download_mode: + features += ["Secure Download Mode Enabled"] + + flash_version = { + 0: "No Embedded Flash", + 1: "Embedded Flash 2MB", + 2: "Embedded Flash 4MB", + }.get(self.get_flash_version(), "Unknown Embedded Flash") + features += [flash_version] + + psram_version = { + 0: "No Embedded PSRAM", + 1: "Embedded PSRAM 2MB", + 2: "Embedded PSRAM 4MB", + }.get(self.get_psram_version(), "Unknown Embedded PSRAM") + features += [psram_version] + + block2_version = { + 0: "No calibration in BLK2 of efuse", + 1: "ADC and temperature sensor calibration in BLK2 of efuse V1", + 2: "ADC and temperature sensor calibration in BLK2 of efuse V2", + }.get(self.get_block2_version(), "Unknown Calibration in BLK2") + features += [block2_version] + + return features + + def get_crystal_freq(self): + # ESP32-S2 XTAL is fixed to 40MHz + return 40 + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-S2" + ) + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + return tuple(bitstring) + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-S2 + + def get_secure_boot_enabled(self): + return ( + self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) + & self.EFUSE_SECURE_BOOT_EN_MASK + ) + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [ + (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), + ][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see either an AES-128 key or two AES-256 keys + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): + return True + + return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any( + p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes + ) + + def uses_usb_otg(self, _cache=[]): + """ + Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used + """ + if self.secure_download_mode: + return False # can't detect native USB in secure download mode + if not _cache: + buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xFF + _cache.append(buf_no == self.UARTDEV_BUF_NO_USB_OTG) + return _cache[0] + + def _post_connect(self): + if self.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + + def _check_if_can_reset(self): + """ + Check the strapping register to see if we can reset out of download mode. + """ + if os.getenv("ESPTOOL_TESTING") is not None: + print("ESPTOOL_TESTING is set, ignoring strapping mode check") + # Esptool tests over USB-OTG run with GPIO0 strapped low, + # don't complain in this case. + return + strap_reg = self.read_reg(self.GPIO_STRAP_REG) + force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) + if ( + strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 + and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 + ): + print( + "WARNING: {} chip was placed into download mode using GPIO0.\n" + "esptool.py can not exit the download mode over USB. " + "To run the app, reset the chip manually.\n" + "To suppress this note, set --after option to 'no_reset'.".format( + self.get_chip_description() + ) + ) + raise SystemExit(1) + + def hard_reset(self): + if self.uses_usb_otg(): + self._check_if_can_reset() + + print("Hard resetting via RTS pin...") + self._setRTS(True) # EN->LOW + if self.uses_usb_otg(): + # Give the chip some time to come out of reset, + # to be able to handle further DTR/RTS transitions + time.sleep(0.2) + self._setRTS(False) + time.sleep(0.2) + else: + time.sleep(0.1) + self._setRTS(False) + + +class ESP32S2StubLoader(ESP32S2ROM): + """Access class for ESP32-S2 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + if rom_loader.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK + + +ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py new file mode 100644 index 000000000..80469918e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py @@ -0,0 +1,275 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import struct +import time + +from .esp32 import ESP32ROM +from ..util import FatalError, NotImplementedInROMError + + +class ESP32S3ROM(ESP32ROM): + CHIP_NAME = "ESP32-S3" + + IMAGE_CHIP_ID = 9 + + CHIP_DETECT_MAGIC_VALUE = [0x9] + + FPGA_SLOW_BOOT = False + + IROM_MAP_START = 0x42000000 + IROM_MAP_END = 0x44000000 + DROM_MAP_START = 0x3C000000 + DROM_MAP_END = 0x3E000000 + + UART_DATE_REG_ADDR = 0x60000080 + + SPI_REG_BASE = 0x60002000 + SPI_USR_OFFS = 0x18 + SPI_USR1_OFFS = 0x1C + SPI_USR2_OFFS = 0x20 + SPI_MOSI_DLEN_OFFS = 0x24 + SPI_MISO_DLEN_OFFS = 0x28 + SPI_W0_OFFS = 0x58 + + BOOTLOADER_FLASH_OFFSET = 0x0 + + SUPPORTS_ENCRYPTED_FLASH = True + + FLASH_ENCRYPTED_WRITE_ALIGN = 16 + + # todo: use espefuse APIs to get this info + EFUSE_BASE = 0x60007000 # BLOCK0 read base address + EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x44 + EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x5C + MAC_EFUSE_REG = EFUSE_BASE + 0x044 + + EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address + + EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY0_SHIFT = 24 + EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 + EFUSE_PURPOSE_KEY1_SHIFT = 28 + EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY2_SHIFT = 0 + EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY3_SHIFT = 4 + EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY4_SHIFT = 8 + EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 + EFUSE_PURPOSE_KEY5_SHIFT = 12 + + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 + + EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 + EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 + + EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 + EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 + + PURPOSE_VAL_XTS_AES256_KEY_1 = 2 + PURPOSE_VAL_XTS_AES256_KEY_2 = 3 + PURPOSE_VAL_XTS_AES128_KEY = 4 + + UARTDEV_BUF_NO = 0x3FCEF14C # Variable in ROM .bss which indicates the port in use + UARTDEV_BUF_NO_USB_OTG = 3 # Value of the above indicating that USB-OTG is in use + + USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used + + GPIO_STRAP_REG = 0x60004038 + GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode + RTC_CNTL_OPTION1_REG = 0x6000812C + RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? + + UART_CLKDIV_REG = 0x60000014 + + MEMORY_MAP = [ + [0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3D000000, "DROM"], + [0x3D000000, 0x3E000000, "EXTRAM_DATA"], + [0x600FE000, 0x60100000, "RTC_DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FC88000, 0x403E2000, "MEM_INTERNAL"], + [0x3FC88000, 0x3FD00000, "DRAM"], + [0x40000000, 0x4001A100, "IROM_MASK"], + [0x40370000, 0x403E0000, "IRAM"], + [0x600FE000, 0x60100000, "RTC_IRAM"], + [0x42000000, 0x42800000, "IROM"], + [0x50000000, 0x50002000, "RTC_DATA"], + ] + + def get_pkg_version(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 + + def get_minor_chip_version(self): + hi_num_word = 5 + hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 + low_num_word = 3 + low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 + return (hi << 3) + low + + def get_blk_version_major(self): + num_word = 4 + return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 0) & 0x03 + + def get_blk_version_minor(self): + num_word = 3 + return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07 + + def get_major_chip_version(self): + num_word = 5 + rev = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 + + # Workaround: The major version field was allocated to other purposes + # when block version is v1.1. + # Luckily only chip v0.0 have this kind of block version and efuse usage. + if ( + self.get_minor_chip_version() == 0 + and self.get_blk_version_major() == 1 + and self.get_blk_version_minor() == 1 + ): + rev = 0 + return rev + + def get_chip_description(self): + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" + + def get_chip_features(self): + return ["WiFi", "BLE"] + + def get_crystal_freq(self): + # ESP32S3 XTAL is fixed to 40MHz + return 40 + + def get_flash_crypt_config(self): + return None # doesn't exist on ESP32-S3 + + def get_key_block_purpose(self, key_block): + if key_block < 0 or key_block > 5: + raise FatalError("Valid key block numbers must be in range 0-5") + + reg, shift = [ + (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), + (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), + (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), + (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), + (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), + (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), + ][key_block] + return (self.read_reg(reg) >> shift) & 0xF + + def is_flash_encryption_key_valid(self): + # Need to see either an AES-128 key or two AES-256 keys + purposes = [self.get_key_block_purpose(b) for b in range(6)] + + if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): + return True + + return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any( + p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes + ) + + def get_secure_boot_enabled(self): + return ( + self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) + & self.EFUSE_SECURE_BOOT_EN_MASK + ) + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "VDD_SDIO overrides are not supported for ESP32-S3" + ) + + def read_mac(self): + mac0 = self.read_reg(self.MAC_EFUSE_REG) + mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC + bitstring = struct.pack(">II", mac1, mac0)[2:] + return tuple(bitstring) + + def uses_usb_otg(self, _cache=[]): + """ + Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used + """ + if self.secure_download_mode: + return False # can't detect native USB in secure download mode + if not _cache: + buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xFF + _cache.append(buf_no == self.UARTDEV_BUF_NO_USB_OTG) + return _cache[0] + + def _post_connect(self): + if self.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + + def _check_if_can_reset(self): + """ + Check the strapping register to see if we can reset out of download mode. + """ + if os.getenv("ESPTOOL_TESTING") is not None: + print("ESPTOOL_TESTING is set, ignoring strapping mode check") + # Esptool tests over USB-OTG run with GPIO0 strapped low, + # don't complain in this case. + return + strap_reg = self.read_reg(self.GPIO_STRAP_REG) + force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) + if ( + strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 + and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 + ): + print( + "WARNING: {} chip was placed into download mode using GPIO0.\n" + "esptool.py can not exit the download mode over USB. " + "To run the app, reset the chip manually.\n" + "To suppress this note, set --after option to 'no_reset'.".format( + self.get_chip_description() + ) + ) + raise SystemExit(1) + + def hard_reset(self): + if self.uses_usb_otg(): + self._check_if_can_reset() + + print("Hard resetting via RTS pin...") + self._setRTS(True) # EN->LOW + if self.uses_usb_otg(): + # Give the chip some time to come out of reset, + # to be able to handle further DTR/RTS transitions + time.sleep(0.2) + self._setRTS(False) + time.sleep(0.2) + else: + time.sleep(0.1) + self._setRTS(False) + + +class ESP32S3StubLoader(ESP32S3ROM): + """Access class for ESP32S3 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + if rom_loader.uses_usb_otg(): + self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK + self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK + + +ESP32S3ROM.STUB_CLASS = ESP32S3StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py new file mode 100644 index 000000000..87c60dcce --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from .esp32s3 import ESP32S3ROM + + +class ESP32S3BETA2ROM(ESP32S3ROM): + CHIP_NAME = "ESP32-S3(beta2)" + IMAGE_CHIP_ID = 4 + + CHIP_DETECT_MAGIC_VALUE = [0xEB004136] + + EFUSE_BASE = 0x6001A000 # BLOCK0 read base address + + def get_chip_description(self): + major_rev = self.get_major_chip_version() + minor_rev = self.get_minor_chip_version() + return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" + + +class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM): + """Access class for ESP32S3 stub loader, runs on top of ROM. + + (Basically the same as ESP32StubLoader, but different base class. + Can possibly be made into a mixin.) + """ + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + +ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py new file mode 100644 index 000000000..cc5ba200f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py @@ -0,0 +1,190 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from ..loader import ESPLoader +from ..util import FatalError, NotImplementedInROMError + + +class ESP8266ROM(ESPLoader): + """Access class for ESP8266 ROM bootloader""" + + CHIP_NAME = "ESP8266" + IS_STUB = False + + CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101] + + # OTP ROM addresses + ESP_OTP_MAC0 = 0x3FF00050 + ESP_OTP_MAC1 = 0x3FF00054 + ESP_OTP_MAC3 = 0x3FF0005C + + SPI_REG_BASE = 0x60000200 + SPI_USR_OFFS = 0x1C + SPI_USR1_OFFS = 0x20 + SPI_USR2_OFFS = 0x24 + SPI_MOSI_DLEN_OFFS = None + SPI_MISO_DLEN_OFFS = None + SPI_W0_OFFS = 0x40 + + UART_CLKDIV_REG = 0x60000014 + + XTAL_CLK_DIVIDER = 2 + + FLASH_SIZES = { + "512KB": 0x00, + "256KB": 0x10, + "1MB": 0x20, + "2MB": 0x30, + "4MB": 0x40, + "2MB-c1": 0x50, + "4MB-c1": 0x60, + "8MB": 0x80, + "16MB": 0x90, + } + + FLASH_FREQUENCY = { + "80m": 0xF, + "40m": 0x0, + "26m": 0x1, + "20m": 0x2, + } + + BOOTLOADER_FLASH_OFFSET = 0 + + MEMORY_MAP = [ + [0x3FF00000, 0x3FF00010, "DPORT"], + [0x3FFE8000, 0x40000000, "DRAM"], + [0x40100000, 0x40108000, "IRAM"], + [0x40201010, 0x402E1010, "IROM"], + ] + + def get_efuses(self): + # Return the 128 bits of ESP8266 efuse as a single Python integer + result = self.read_reg(0x3FF0005C) << 96 + result |= self.read_reg(0x3FF00058) << 64 + result |= self.read_reg(0x3FF00054) << 32 + result |= self.read_reg(0x3FF00050) + return result + + def _get_flash_size(self, efuses): + # rX_Y = EFUSE_DATA_OUTX[Y] + r0_4 = (efuses & (1 << 4)) != 0 + r3_25 = (efuses & (1 << 121)) != 0 + r3_26 = (efuses & (1 << 122)) != 0 + r3_27 = (efuses & (1 << 123)) != 0 + + if r0_4 and not r3_25: + if not r3_27 and not r3_26: + return 1 + elif not r3_27 and r3_26: + return 2 + if not r0_4 and r3_25: + if not r3_27 and not r3_26: + return 2 + elif not r3_27 and r3_26: + return 4 + return -1 + + def get_chip_description(self): + efuses = self.get_efuses() + is_8285 = ( + efuses & ((1 << 4) | 1 << 80) + ) != 0 # One or the other efuse bit is set for ESP8285 + if is_8285: + flash_size = self._get_flash_size(efuses) + max_temp = ( + efuses & (1 << 5) + ) != 0 # This efuse bit identifies the max flash temperature + chip_name = { + 1: "ESP8285H08" if max_temp else "ESP8285N08", + 2: "ESP8285H16" if max_temp else "ESP8285N16", + }.get(flash_size, "ESP8285") + return chip_name + return "ESP8266EX" + + def get_chip_features(self): + features = ["WiFi"] + if "ESP8285" in self.get_chip_description(): + features += ["Embedded Flash"] + return features + + def flash_spi_attach(self, hspi_arg): + if self.IS_STUB: + super(ESP8266ROM, self).flash_spi_attach(hspi_arg) + else: + # ESP8266 ROM has no flash_spi_attach command in serial protocol, + # but flash_begin will do it + self.flash_begin(0, 0) + + def flash_set_parameters(self, size): + # not implemented in ROM, but OK to silently skip for ROM + if self.IS_STUB: + super(ESP8266ROM, self).flash_set_parameters(size) + + def chip_id(self): + """ + Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() func + """ + id0 = self.read_reg(self.ESP_OTP_MAC0) + id1 = self.read_reg(self.ESP_OTP_MAC1) + return (id0 >> 24) | ((id1 & 0xFFFFFF) << 8) + + def read_mac(self): + """Read MAC from OTP ROM""" + mac0 = self.read_reg(self.ESP_OTP_MAC0) + mac1 = self.read_reg(self.ESP_OTP_MAC1) + mac3 = self.read_reg(self.ESP_OTP_MAC3) + if mac3 != 0: + oui = ((mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF) + elif ((mac1 >> 16) & 0xFF) == 0: + oui = (0x18, 0xFE, 0x34) + elif ((mac1 >> 16) & 0xFF) == 1: + oui = (0xAC, 0xD0, 0x74) + else: + raise FatalError("Unknown OUI") + return oui + ((mac1 >> 8) & 0xFF, mac1 & 0xFF, (mac0 >> 24) & 0xFF) + + def get_erase_size(self, offset, size): + """Calculate an erase size given a specific size in bytes. + + Provides a workaround for the bootloader erase bug.""" + + sectors_per_block = 16 + sector_size = self.FLASH_SECTOR_SIZE + num_sectors = (size + sector_size - 1) // sector_size + start_sector = offset // sector_size + + head_sectors = sectors_per_block - (start_sector % sectors_per_block) + if num_sectors < head_sectors: + head_sectors = num_sectors + + if num_sectors < 2 * head_sectors: + return (num_sectors + 1) // 2 * sector_size + else: + return (num_sectors - head_sectors) * sector_size + + def override_vddsdio(self, new_voltage): + raise NotImplementedInROMError( + "Overriding VDDSDIO setting only applies to ESP32" + ) + + +class ESP8266StubLoader(ESP8266ROM): + """Access class for ESP8266 stub loader, runs on top of ROM.""" + + FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c + IS_STUB = True + + def __init__(self, rom_loader): + self.secure_download_mode = rom_loader.secure_download_mode + self._port = rom_loader._port + self._trace_enabled = rom_loader._trace_enabled + self.flush_input() # resets _slip_reader + + def get_erase_size(self, offset, size): + return size # stub doesn't have same size bug as ROM loader + + +ESP8266ROM.STUB_CLASS = ESP8266StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json new file mode 100644 index 000000000..37b9ae2ff --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json @@ -0,0 +1,7 @@ +{ + "entry": 1074521516, + "text": "CAD0PxwA9D8AAPQ/pOv9PxAA9D82QQAh+v/AIAA4AkH5/8AgACgEICB0nOIGBQAAAEH1/4H2/8AgAKgEiAigoHTgCAALImYC54b0/yHx/8AgADkCHfAAAPgg9D/4MPQ/NkEAkf3/wCAAiAmAgCRWSP+R+v/AIACICYCAJFZI/x3wAAAAECD0PwAg9D8AAAAINkEA5fz/Ifv/DAjAIACJApH7/4H5/8AgAJJoAMAgAJgIVnn/wCAAiAJ88oAiMCAgBB3wAAAAAEA2QQBl/P8Wmv+B7f+R/P/AIACZCMAgAJgIVnn/HfAAAAAAgAAAAAABmMD9P////wAEIPQ/NkEAIfz/OEIWIwal+P8WygWIQgz5DAOHqQyIIpCIEAwZgDmDMDB0Zfr/pfP/iCKR8v9AiBGHOR+R7f/ME5Hs/6Hv/8AgAIkKgdH/wCAAmQjAIACYCFZ5/xwJDBgwiZM9CIhCMIjAiUKIIjo4OSId8JDA/T8IQP0/gIAAAISAAABAQAAASID9P5TA/T82QQCx+P8goHSltwCW6gWB9v+R9v+goHSQmIDAIACyKQCR8/+QiIDAIACSGACQkPQbycDA9MAgAMJYAJqbwCAAokkAwCAAkhgAger/kJD0gID0h5lGgeT/keX/oej/mpjAIADICbHk/4ecGUYCAHzohxrhRgkAAADAIACJCsAgALkJRgIAwCAAuQrAIACJCZHY/5qIDAnAIACSWAAd8AAAUC0GQDZBAEGw/1g0UDNjFvMDWBRaU1BcQYYAAGXr/4hEphgEiCSHpfLl4/8Wmv+oFM0DvQKB8v/gCACgoHSMOiKgxClUKBQ6IikUKDQwMsA5NB3wCCD0PwAAQABw4vo/SCQGQPAiBkA2YQDl3P+tAYH8/+AIAD0KDBLs6ogBkqIAkIgQiQGl4f+R8v+h8//AIACICaCIIMAgAIJpALIhAKHv/4Hw/+AIAKAjgx3wAAD/DwAANkEAgYT/kqABkkgAMJxBkmgCkfr/MmgBKTgwMLSaIiozMDxBDAIpWDlIpfj/LQqMGiKgxR3wAAAskgBANkEAgqDArQKHkg6ioNuB+//gCACioNyGAwCCoNuHkgiB9//gCACioN2B9P/gCAAd8AAAADZBADoyBgIAAKICABsi5fv/N5L0HfAAAAAQAABYEAAAfNoFQNguBkCc2gVAHNsFQDYhIaLREIH6/+AIAIYKAAAAUfX/vQFQQ2PNBK0CgfX/4AgAoKB0/CrNBL0BotEQgfL/4AgASiJAM8BWM/2h6/+y0RAaqoHt/+AIAKHo/xwLGqrl9/8tAwYBAAAAIqBjHfAAAAA2QQCioMCBy//gCAAd8AAAbBAAAGgQAABwEAAAdBAAAHgQAAD8ZwBA0JIAQAhoAEA2QSFh+f+B+f8aZkkGGohi0RAMBCwKWQhCZhqB9v/gCABR8f+BzP8aVVgFV7gCBjgArQaByv/gCACB7f9x6f8aiHpRWQhGJgCB6P9Ac8AaiIgIvQFweGPNB60CgcH/4AgAoKB0jMpx3/8MBVJmFnpxBg0AAKX1/3C3IK0B5ev/JfX/zQcQsSBgpiCBtv/gCAB6InpEN7TOgdX/UHTAGoiICIc3o4bv/wAMCqJGbIHQ/xqIoigAgdD/4AgAVur+sab/ogZsGrtlgwD36gz2RQlat6JLABtVhvP/sq/+t5rIZkUIUiYaN7UCV7SooZv/YLYgEKqAgZ3/4AgAZe3/oZb/HAsaqmXj/6Xs/ywKgbz/4AgAHfAAwPw/T0hBSajr/T+I4QtAFOALQAwA9D84QPQ///8AAAAAAQCMgAAAEEAAAABAAAAAwPw/BMD8PxAnAAAUAPQ/8P//AKjr/T8IwPw/sMD9P3xoAEDsZwBAWIYAQGwqBkA4MgZAFCwGQMwsBkBMLAZANIUAQMyQAEB4LgZAMO8FQFiSAEBMggBANsEAId7/DAoiYQhCoACB7v/gCAAh2f8x2v8GAQBCYgBLIjcy9+Xg/wxLosEgJdf/JeD/MeT+IeT+QdL/KiPAIAA5ArHR/yGG/gwMDFpJAoHf/+AIAEHN/1KhAcAgACgELApQIiDAIAApBIF9/+AIAIHY/+AIACHG/8AgACgCzLocxEAiECLC+AwUIKSDDAuB0f/gCADxv//RSP/Bv/+xqP7ioQAMCoHM/+AIACG8/0Gl/iozYtQrDALAIABIAxZ0/8AgAFgDDBTAIAApA0JBEEIFAQwnQkERclEJKVEmlAccN3cUHgYIAEIFA3IFAoBEEXBEIGZEEUglwCAASARJUUYBAAAcJEJRCaXS/wyLosEQ5cj/QgUDcgUCgEQRcEQgcaD/cHD0R7cSoqDA5cP/oqDupcP/5c//Rt//AHIFAQzZl5cChq8AdzlWZmcCBugA9ncgZjcCxoEA9kcIZicCRmcABigAZkcCRpUAZlcCBsQARiQADJmXlwLGpwB3ORBmdwLGxQBmhwKGIADGHQAAAGaXAka3AAy5l5cCRpAABhkAHDmXlwIGUAB3OSpmtwLGXQAcCXc5DAz57QKXlwKGRADGEAAcGZeXAgZlABwkR5cCBnsAhgsAkqDSl5cCxkAAdzkQkqDQlxdbkqDRlxdpxgQAAACSoNOXlwKGVwGSoNSXlwKGVgDtAnKg/0bAACxJ7QJyoMCXFAIGvQApUUKgByCiIKW0/yCiICW0/2XA/2XA/7KgCKLBEAtEZbb/VvT9RiYAAAAMF1Y0LIFk/+AIAKB0g8atAAAAACaEBAwXBqsAQiUCciUDcJQgkJC0Vrn+Jaf/cESAnBoG+P8AoKxBgVj/4AgAVjr9ctfwcKTAzCcGgQAAoID0Vhj+RgQAoKD1gVH/4AgAVir7gTv/gHfAkTr/cKTAdznkxgMAAKCsQYFI/+AIAFY6+XLX8HCkwFan/sZwAHKgwCaEAoaMAO0CDAfGigAmtPXGYwByoAEmtAKGhgCyJQOiJQJlrf8GCQAAcqABJrQCBoEAkSb/QiUEIOIgcqDCR7kCBn0AuFWoJQwX5aD/oHKDxngADBlmtCxIRaEc/+0CcqDCR7oCBnQAeDW4VaglcHSCmeFlnv9B/f2Y4SlkQtQreSSgkoN9CQZrAJH4/e0CogkAcqDGFgoaeFmYJULE8ECZwKKgwJB6kwwKkqDvhgIAAKq1sgsYG6qwmTBHKvKiBQVCBQSAqhFAqiBCBQbtAgBEEaCkIEIFB4BEAaBEIECZwEKgwZB0k4ZTAEHg/e0CkgQAcqDGFgkUmDRyoMhWiROSRAB4VAZMAAAcie0CDBeXFALGSADoZfh12FXIRbg1qCWB+P7gCADtCqByg0ZCAAwXJkQCxj8AqCW9AoHw/uAIAAYfAABAoDTtAnKgwFaKDkC0QYuVTQp8/IYOAACoOZnhucHJ0YHr/uAIAJjhuMF4KagZ2AmgpxDCIQ0mBw7AIADiLQBwfDDgdxBwqiDAIACpDRtEkskQtzTCBpr/ZkQChpj/7QJyoMBGIwAMFya0AsYgAEHH/phVeCWZBEHG/nkEfQIGHACxwv4MF8gLQsTwnQJAl5PAcpNwmRDtAnKgxlZZBYG8/nKgydgIRz1KQKAUcqDAVhoEfQoMH0YCAHqVmGlLd5kKnQ9w7cB6rEc37RYp36kL6QjGev8MF2aEF0Gt/ngEjBdyoMgpBAwaQan+cKKDKQR9Cu0CcKB04mEMZYX/4iEM4KB05YT/JZH/Vge5QgUBcqAPdxRARzcUZkQCRnkAZmQCxn8AJjQChtz+hh8AHCd3lAKGcwBHNwscF3eUAgY6AEbW/gByoNJ3FE9yoNR3FHNG0v4AAACYNaGP/lglmeGBm/7gCABBjP6Bjf7AIABIBJjhQHQ1wEQRgEQQQEcgkESCrQJQtMKBkv7gCACio+iBj/7gCAAGwf4AANIlBcIlBLIlA6glJYr/Rrz+ALIFA0IFAoC7EUC7ILLL8KLFGGVq/wa2/kIFA3IFAoBEEXBEIHFW/ULE8Jg3kERjFuSrmBealJCcQQYCAJJhDqVU/5IhDqInBKYaBKgnp6nrpUz/Fpr/oicBQMQgssUYgXL+4AgAFkoAgqDEiVeIF0qIiReIN0BIwEk3xpz+ggUDcgUCgIgRcIggQsUYgsjwDBUGIAAAkVf+cVn9WAmJcVB3wHlheCYMGne4AQw6idGZ4anBZU3/qMFxUP6pAaFP/u0FvQTywRjdB8LBHIFY/uAIAF0KuCaocYjRmOGgu8C5JqCIwLgJqkSoYQweqrutAlCug7kJoKB0cLvAzHrS24DQroMW6gCtB4nRmeGlWv+Y4YjReQmRGf14OYyoUJ8xUJnA1ikAVsf21qUAURT9QqDHSVVGAACMNZwHxmz+FgebgQ/9QqDISVhGaf4AkQz9QqDJSVlGZv4ASCVWNJmtAoE0/uAIAKEg/oEu/uAIAIEx/uAIAEZe/gBINRY0l60CgSz+4AgAoqPogSb+4AgA4AQABlf+HfAAADZBAJ0CgqDAKAOHmQ/MMgwShgcADAIpA3zihg4AJhIHJiIWhgMAAACCoNuAKSOHmSYMIikDfPJGBwAioNwnmQgMEikDLQiGAwCCoN188oeZBgwSKQMioNsd8AAA", + "text_start": 1074520064, + "data": "CMD8Pw==", + "data_start": 1073605544 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json new file mode 100644 index 000000000..988dea7ce --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413328, + "text": "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", + "text_start": 1077411840, + "data": "DEDKPw==", + "data_start": 1070295976 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json new file mode 100644 index 000000000..c132a8e8e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413554, + "text": "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", + "text_start": 1077411840, + "data": "FEDIPw==", + "data_start": 1070164912 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json new file mode 100644 index 000000000..66a5e5b4f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json @@ -0,0 +1,7 @@ +{ + "entry": 1082131920, + "text": "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", + "text_start": 1082130432, + "data": "DACEQA==", + "data_start": 1082469288 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json new file mode 100644 index 000000000..730bd23f3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413328, + "text": "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", + "text_start": 1077411840, + "data": "DEDIPw==", + "data_start": 1070164904 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json new file mode 100644 index 000000000..bfb62c033 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413328, + "text": "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", + "text_start": 1077411840, + "data": "DEDIPw==", + "data_start": 1070164904 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json new file mode 100644 index 000000000..4ab15211f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json @@ -0,0 +1,7 @@ +{ + "entry": 1077413328, + "text": "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", + "text_start": 1077411840, + "data": "DEDIPw==", + "data_start": 1070164904 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json new file mode 100644 index 000000000..7f804348f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json @@ -0,0 +1,7 @@ +{ + "entry": 1073907652, + "text": "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", + "text_start": 1073905664, + "data": "EAD9Pw==", + "data_start": 1073622012 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json new file mode 100644 index 000000000..94ff2b1fc --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json @@ -0,0 +1,7 @@ +{ + "entry": 1077381644, + "text": "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", + "text_start": 1077379072, + "data": "EADKPw==", + "data_start": 1070279676 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json new file mode 100644 index 000000000..1c71662c9 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json @@ -0,0 +1,7 @@ +{ + "entry": 1077381644, + "text": "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", + "text_start": 1077379072, + "data": "EADKPw==", + "data_start": 1070279676 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json new file mode 100644 index 000000000..98223b800 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json @@ -0,0 +1,7 @@ +{ + "entry": 1074843652, + "text": "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", + "text_start": 1074843648, + "data": "CIH+PwUFBAACAwcAAwMLALnXEEDv1xBAHdgQQLrYEEBo5xBAHtkQQHTZEEDA2RBAaOcQQILaEED/2hBAwNsQQGjnEEBo5xBAWNwQQGjnEEA33xBAAOAQQDvgEEBo5xBAaOcQQNfgEEBo5xBAv+EQQGXiEECj4xBAY+QQQDTlEEBo5xBAaOcQQGjnEEBo5xBAYuYQQGjnEEBX5xBAkN0QQI/YEECm5RBAq9oQQPzZEEBo5xBA7OYQQDHnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQCLaEEBf2hBAvuUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", + "data_start": 1073720488 +} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py new file mode 100644 index 000000000..91637caf4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py @@ -0,0 +1,157 @@ +# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, +# Espressif Systems (Shanghai) CO LTD, other contributors as noted. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import struct +import sys + + +def byte(bitstr, index): + return bitstr[index] + + +def mask_to_shift(mask): + """Return the index of the least significant bit in the mask""" + shift = 0 + while mask & 0x1 == 0: + shift += 1 + mask >>= 1 + return shift + + +def div_roundup(a, b): + """Return a/b rounded up to nearest integer, + equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only + without possible floating point accuracy errors. + """ + return (int(a) + int(b) - 1) // int(b) + + +def flash_size_bytes(size): + """Given a flash size of the type passed in args.flash_size + (ie 512KB or 1MB) then return the size in bytes. + """ + if "MB" in size: + return int(size[: size.index("MB")]) * 1024 * 1024 + elif "KB" in size: + return int(size[: size.index("KB")]) * 1024 + else: + raise FatalError("Unknown size %s" % size) + + +def hexify(s, uppercase=True): + format_str = "%02X" if uppercase else "%02x" + return "".join(format_str % c for c in s) + + +def pad_to(data, alignment, pad_character=b"\xFF"): + """Pad to the next alignment boundary""" + pad_mod = len(data) % alignment + if pad_mod != 0: + data += pad_character * (alignment - pad_mod) + return data + + +def print_overwrite(message, last_line=False): + """Print a message, overwriting the currently printed line. + + If last_line is False, don't append a newline at the end + (expecting another subsequent call will overwrite this one.) + + After a sequence of calls with last_line=False, call once with last_line=True. + + If output is not a TTY (for example redirected a pipe), + no overwriting happens and this function is the same as print(). + """ + if sys.stdout.isatty(): + print("\r%s" % message, end="\n" if last_line else "") + else: + print(message) + + +class FatalError(RuntimeError): + """ + Wrapper class for runtime errors that aren't caused by internal bugs, but by + ESP ROM responses or input content. + """ + + def __init__(self, message): + RuntimeError.__init__(self, message) + + @staticmethod + def WithResult(message, result): + """ + Return a fatal error object that appends the hex values of + 'result' and its meaning as a string formatted argument. + """ + + err_defs = { + # ROM error codes + 0x101: "Out of memory", + 0x102: "Invalid argument", + 0x103: "Invalid state", + 0x104: "Invalid size", + 0x105: "Requested resource not found", + 0x106: "Operation or feature not supported", + 0x107: "Operation timed out", + 0x108: "Received response was invalid", + 0x109: "CRC or checksum was invalid", + 0x10A: "Version was invalid", + 0x10B: "MAC address was invalid", + # Flasher stub error codes + 0xC000: "Bad data length", + 0xC100: "Bad data checksum", + 0xC200: "Bad blocksize", + 0xC300: "Invalid command", + 0xC400: "Failed SPI operation", + 0xC500: "Failed SPI unlock", + 0xC600: "Not in flash mode", + 0xC700: "Inflate error", + 0xC800: "Not enough data", + 0xC900: "Too much data", + 0xFF00: "Command not implemented", + } + + err_code = struct.unpack(">H", result[:2]) + message += " (result was {}: {})".format( + hexify(result), err_defs.get(err_code[0], "Unknown result") + ) + return FatalError(message) + + +class NotImplementedInROMError(FatalError): + """ + Wrapper class for the error thrown when a particular ESP bootloader function + is not implemented in the ROM bootloader. + """ + + def __init__(self, bootloader, func): + FatalError.__init__( + self, + "%s ROM does not support function %s." + % (bootloader.CHIP_NAME, func.__name__), + ) + + +class NotSupportedError(FatalError): + def __init__(self, esp, function_name): + FatalError.__init__( + self, + "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME), + ) + + +class UnsupportedCommandError(RuntimeError): + """ + Wrapper class for when ROM loader returns an invalid command response. + + Usually this indicates the loader is running in Secure Download Mode. + """ + + def __init__(self, esp, op): + if esp.secure_download_mode: + msg = "This command (0x%x) is not supported in Secure Download Mode" % op + else: + msg = "Invalid (unsupported) command 0x%x" % op + RuntimeError.__init__(self, msg) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD index 993dc99e1..33e08b099 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD @@ -1,6 +1,6 @@ -../../Scripts/pip.exe,sha256=NGJ2cMtvSTPEJr45svh2fuHqXkvm-coBUSdJgVRKuZU,108451 -../../Scripts/pip3.10.exe,sha256=NGJ2cMtvSTPEJr45svh2fuHqXkvm-coBUSdJgVRKuZU,108451 -../../Scripts/pip3.exe,sha256=NGJ2cMtvSTPEJr45svh2fuHqXkvm-coBUSdJgVRKuZU,108451 +../../Scripts/pip.exe,sha256=C5XUgadRfynCyunDMP41Mc5O2O6AKV7DFTUBMae_n-s,108451 +../../Scripts/pip3.10.exe,sha256=C5XUgadRfynCyunDMP41Mc5O2O6AKV7DFTUBMae_n-s,108451 +../../Scripts/pip3.exe,sha256=C5XUgadRfynCyunDMP41Mc5O2O6AKV7DFTUBMae_n-s,108451 pip-22.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 pip-22.3.1.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093 pip-22.3.1.dist-info/METADATA,sha256=a9COYc5qzklDgbGlrKYkypMXon4A6IDgpeUTWLr7zzY,4072 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE new file mode 100644 index 000000000..ea215f2db --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE @@ -0,0 +1,27 @@ +pycparser -- A C parser in Python + +Copyright (c) 2008-2020, Eli Bendersky +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of Eli Bendersky nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA new file mode 100644 index 000000000..1d0fbd651 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA @@ -0,0 +1,31 @@ +Metadata-Version: 2.1 +Name: pycparser +Version: 2.21 +Summary: C parser in Python +Home-page: https://github.com/eliben/pycparser +Author: Eli Bendersky +Author-email: eliben@gmail.com +Maintainer: Eli Bendersky +License: BSD +Platform: Cross Platform +Classifier: Development Status :: 5 - Production/Stable +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* + + +pycparser is a complete parser of the C language, written in +pure Python using the PLY parsing library. +It parses C code into an AST and can serve as a front-end for +C compilers or analysis tools. + + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD new file mode 100644 index 000000000..2de17f9e2 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD @@ -0,0 +1,41 @@ +pycparser-2.21.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pycparser-2.21.dist-info/LICENSE,sha256=Pn3yW437ZYyakVAZMNTZQ7BQh6g0fH4rQyVhavU1BHs,1536 +pycparser-2.21.dist-info/METADATA,sha256=GvTEQA9yKj0nvP4mknfoGpMvjaJXCQjQANcQHrRrAxc,1108 +pycparser-2.21.dist-info/RECORD,, +pycparser-2.21.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 +pycparser-2.21.dist-info/top_level.txt,sha256=c-lPcS74L_8KoH7IE6PQF5ofyirRQNV4VhkbSFIPeWM,10 +pycparser/__init__.py,sha256=WUEp5D0fuHBH9Q8c1fYvR2eKWfj-CNghLf2MMlQLI1I,2815 +pycparser/__pycache__/__init__.cpython-310.pyc,, +pycparser/__pycache__/_ast_gen.cpython-310.pyc,, +pycparser/__pycache__/_build_tables.cpython-310.pyc,, +pycparser/__pycache__/ast_transforms.cpython-310.pyc,, +pycparser/__pycache__/c_ast.cpython-310.pyc,, +pycparser/__pycache__/c_generator.cpython-310.pyc,, +pycparser/__pycache__/c_lexer.cpython-310.pyc,, +pycparser/__pycache__/c_parser.cpython-310.pyc,, +pycparser/__pycache__/lextab.cpython-310.pyc,, +pycparser/__pycache__/plyparser.cpython-310.pyc,, +pycparser/__pycache__/yacctab.cpython-310.pyc,, +pycparser/_ast_gen.py,sha256=0JRVnDW-Jw-3IjVlo8je9rbAcp6Ko7toHAnB5zi7h0Q,10555 +pycparser/_build_tables.py,sha256=oZCd3Plhq-vkV-QuEsaahcf-jUI6-HgKsrAL9gvFzuU,1039 +pycparser/_c_ast.cfg,sha256=ld5ezE9yzIJFIVAUfw7ezJSlMi4nXKNCzfmqjOyQTNo,4255 +pycparser/ast_transforms.py,sha256=GTMYlUgWmXd5wJVyovXY1qzzAqjxzCpVVg0664dKGBs,5691 +pycparser/c_ast.py,sha256=HWeOrfYdCY0u5XaYhE1i60uVyE3yMWdcxzECUX-DqJw,31445 +pycparser/c_generator.py,sha256=yi6Mcqxv88J5ue8k5-mVGxh3iJ37iD4QyF-sWcGjC-8,17772 +pycparser/c_lexer.py,sha256=xCpjIb6vOUebBJpdifidb08y7XgAsO3T1gNGXJT93-w,17167 +pycparser/c_parser.py,sha256=_8y3i52bL6SUK21KmEEl0qzHxe-0eZRzjZGkWg8gQ4A,73680 +pycparser/lextab.py,sha256=fIxBAHYRC418oKF52M7xb8_KMj3K-tHx0TzZiKwxjPM,8504 +pycparser/ply/__init__.py,sha256=q4s86QwRsYRa20L9ueSxfh-hPihpftBjDOvYa2_SS2Y,102 +pycparser/ply/__pycache__/__init__.cpython-310.pyc,, +pycparser/ply/__pycache__/cpp.cpython-310.pyc,, +pycparser/ply/__pycache__/ctokens.cpython-310.pyc,, +pycparser/ply/__pycache__/lex.cpython-310.pyc,, +pycparser/ply/__pycache__/yacc.cpython-310.pyc,, +pycparser/ply/__pycache__/ygen.cpython-310.pyc,, +pycparser/ply/cpp.py,sha256=UtC3ylTWp5_1MKA-PLCuwKQR8zSOnlGuGGIdzj8xS98,33282 +pycparser/ply/ctokens.py,sha256=MKksnN40TehPhgVfxCJhjj_BjL943apreABKYz-bl0Y,3177 +pycparser/ply/lex.py,sha256=7Qol57x702HZwjA3ZLp-84CUEWq1EehW-N67Wzghi-M,42918 +pycparser/ply/yacc.py,sha256=eatSDkRLgRr6X3-hoDk_SQQv065R0BdL2K7fQ54CgVM,137323 +pycparser/ply/ygen.py,sha256=2JYNeYtrPz1JzLSLO3d4GsS8zJU8jY_I_CR1VI9gWrA,2251 +pycparser/plyparser.py,sha256=8tLOoEytcapvWrr1JfCf7Dog-wulBtS1YrDs8S7JfMo,4875 +pycparser/yacctab.py,sha256=j_fVNIyDWDRVk7eWMqQtlBw2AwUSV5JTrtT58l7zis0,205652 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL new file mode 100644 index 000000000..ef99c6cf3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.34.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt new file mode 100644 index 000000000..dc1c9e101 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt @@ -0,0 +1 @@ +pycparser diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py new file mode 100644 index 000000000..d82eb2d6f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py @@ -0,0 +1,90 @@ +#----------------------------------------------------------------- +# pycparser: __init__.py +# +# This package file exports some convenience functions for +# interacting with pycparser +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- +__all__ = ['c_lexer', 'c_parser', 'c_ast'] +__version__ = '2.21' + +import io +from subprocess import check_output +from .c_parser import CParser + + +def preprocess_file(filename, cpp_path='cpp', cpp_args=''): + """ Preprocess a file using cpp. + + filename: + Name of the file you want to preprocess. + + cpp_path: + cpp_args: + Refer to the documentation of parse_file for the meaning of these + arguments. + + When successful, returns the preprocessed file's contents. + Errors from cpp will be printed out. + """ + path_list = [cpp_path] + if isinstance(cpp_args, list): + path_list += cpp_args + elif cpp_args != '': + path_list += [cpp_args] + path_list += [filename] + + try: + # Note the use of universal_newlines to treat all newlines + # as \n for Python's purpose + text = check_output(path_list, universal_newlines=True) + except OSError as e: + raise RuntimeError("Unable to invoke 'cpp'. " + + 'Make sure its path was passed correctly\n' + + ('Original error: %s' % e)) + + return text + + +def parse_file(filename, use_cpp=False, cpp_path='cpp', cpp_args='', + parser=None): + """ Parse a C file using pycparser. + + filename: + Name of the file you want to parse. + + use_cpp: + Set to True if you want to execute the C pre-processor + on the file prior to parsing it. + + cpp_path: + If use_cpp is True, this is the path to 'cpp' on your + system. If no path is provided, it attempts to just + execute 'cpp', so it must be in your PATH. + + cpp_args: + If use_cpp is True, set this to the command line arguments strings + to cpp. Be careful with quotes - it's best to pass a raw string + (r'') here. For example: + r'-I../utils/fake_libc_include' + If several arguments are required, pass a list of strings. + + parser: + Optional parser object to be used instead of the default CParser + + When successful, an AST is returned. ParseError can be + thrown if the file doesn't parse successfully. + + Errors from cpp will be printed out. + """ + if use_cpp: + text = preprocess_file(filename, cpp_path, cpp_args) + else: + with io.open(filename) as f: + text = f.read() + + if parser is None: + parser = CParser() + return parser.parse(text, filename) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py new file mode 100644 index 000000000..0f7d330ba --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py @@ -0,0 +1,336 @@ +#----------------------------------------------------------------- +# _ast_gen.py +# +# Generates the AST Node classes from a specification given in +# a configuration file +# +# The design of this module was inspired by astgen.py from the +# Python 2.5 code-base. +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- +from string import Template + + +class ASTCodeGenerator(object): + def __init__(self, cfg_filename='_c_ast.cfg'): + """ Initialize the code generator from a configuration + file. + """ + self.cfg_filename = cfg_filename + self.node_cfg = [NodeCfg(name, contents) + for (name, contents) in self.parse_cfgfile(cfg_filename)] + + def generate(self, file=None): + """ Generates the code into file, an open file buffer. + """ + src = Template(_PROLOGUE_COMMENT).substitute( + cfg_filename=self.cfg_filename) + + src += _PROLOGUE_CODE + for node_cfg in self.node_cfg: + src += node_cfg.generate_source() + '\n\n' + + file.write(src) + + def parse_cfgfile(self, filename): + """ Parse the configuration file and yield pairs of + (name, contents) for each node. + """ + with open(filename, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith('#'): + continue + colon_i = line.find(':') + lbracket_i = line.find('[') + rbracket_i = line.find(']') + if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i: + raise RuntimeError("Invalid line in %s:\n%s\n" % (filename, line)) + + name = line[:colon_i] + val = line[lbracket_i + 1:rbracket_i] + vallist = [v.strip() for v in val.split(',')] if val else [] + yield name, vallist + + +class NodeCfg(object): + """ Node configuration. + + name: node name + contents: a list of contents - attributes and child nodes + See comment at the top of the configuration file for details. + """ + + def __init__(self, name, contents): + self.name = name + self.all_entries = [] + self.attr = [] + self.child = [] + self.seq_child = [] + + for entry in contents: + clean_entry = entry.rstrip('*') + self.all_entries.append(clean_entry) + + if entry.endswith('**'): + self.seq_child.append(clean_entry) + elif entry.endswith('*'): + self.child.append(clean_entry) + else: + self.attr.append(entry) + + def generate_source(self): + src = self._gen_init() + src += '\n' + self._gen_children() + src += '\n' + self._gen_iter() + src += '\n' + self._gen_attr_names() + return src + + def _gen_init(self): + src = "class %s(Node):\n" % self.name + + if self.all_entries: + args = ', '.join(self.all_entries) + slots = ', '.join("'{0}'".format(e) for e in self.all_entries) + slots += ", 'coord', '__weakref__'" + arglist = '(self, %s, coord=None)' % args + else: + slots = "'coord', '__weakref__'" + arglist = '(self, coord=None)' + + src += " __slots__ = (%s)\n" % slots + src += " def __init__%s:\n" % arglist + + for name in self.all_entries + ['coord']: + src += " self.%s = %s\n" % (name, name) + + return src + + def _gen_children(self): + src = ' def children(self):\n' + + if self.all_entries: + src += ' nodelist = []\n' + + for child in self.child: + src += ( + ' if self.%(child)s is not None:' + + ' nodelist.append(("%(child)s", self.%(child)s))\n') % ( + dict(child=child)) + + for seq_child in self.seq_child: + src += ( + ' for i, child in enumerate(self.%(child)s or []):\n' + ' nodelist.append(("%(child)s[%%d]" %% i, child))\n') % ( + dict(child=seq_child)) + + src += ' return tuple(nodelist)\n' + else: + src += ' return ()\n' + + return src + + def _gen_iter(self): + src = ' def __iter__(self):\n' + + if self.all_entries: + for child in self.child: + src += ( + ' if self.%(child)s is not None:\n' + + ' yield self.%(child)s\n') % (dict(child=child)) + + for seq_child in self.seq_child: + src += ( + ' for child in (self.%(child)s or []):\n' + ' yield child\n') % (dict(child=seq_child)) + + if not (self.child or self.seq_child): + # Empty generator + src += ( + ' return\n' + + ' yield\n') + else: + # Empty generator + src += ( + ' return\n' + + ' yield\n') + + return src + + def _gen_attr_names(self): + src = " attr_names = (" + ''.join("%r, " % nm for nm in self.attr) + ')' + return src + + +_PROLOGUE_COMMENT = \ +r'''#----------------------------------------------------------------- +# ** ATTENTION ** +# This code was automatically generated from the file: +# $cfg_filename +# +# Do not modify it directly. Modify the configuration file and +# run the generator again. +# ** ** *** ** ** +# +# pycparser: c_ast.py +# +# AST Node classes. +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- + +''' + +_PROLOGUE_CODE = r''' +import sys + +def _repr(obj): + """ + Get the representation of an object, with dedicated pprint-like format for lists. + """ + if isinstance(obj, list): + return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]' + else: + return repr(obj) + +class Node(object): + __slots__ = () + """ Abstract base class for AST nodes. + """ + def __repr__(self): + """ Generates a python representation of the current node + """ + result = self.__class__.__name__ + '(' + + indent = '' + separator = '' + for name in self.__slots__[:-2]: + result += separator + result += indent + result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__))))) + + separator = ',' + indent = '\n ' + (' ' * len(self.__class__.__name__)) + + result += indent + ')' + + return result + + def children(self): + """ A sequence of all children that are Nodes + """ + pass + + def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None): + """ Pretty print the Node and all its attributes and + children (recursively) to a buffer. + + buf: + Open IO buffer into which the Node is printed. + + offset: + Initial offset (amount of leading spaces) + + attrnames: + True if you want to see the attribute names in + name=value pairs. False to only see the values. + + nodenames: + True if you want to see the actual node names + within their parents. + + showcoord: + Do you want the coordinates of each Node to be + displayed. + """ + lead = ' ' * offset + if nodenames and _my_node_name is not None: + buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ') + else: + buf.write(lead + self.__class__.__name__+ ': ') + + if self.attr_names: + if attrnames: + nvlist = [(n, getattr(self,n)) for n in self.attr_names] + attrstr = ', '.join('%s=%s' % nv for nv in nvlist) + else: + vlist = [getattr(self, n) for n in self.attr_names] + attrstr = ', '.join('%s' % v for v in vlist) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for (child_name, child) in self.children(): + child.show( + buf, + offset=offset + 2, + attrnames=attrnames, + nodenames=nodenames, + showcoord=showcoord, + _my_node_name=child_name) + + +class NodeVisitor(object): + """ A base NodeVisitor class for visiting c_ast nodes. + Subclass it and define your own visit_XXX methods, where + XXX is the class name you want to visit with these + methods. + + For example: + + class ConstantVisitor(NodeVisitor): + def __init__(self): + self.values = [] + + def visit_Constant(self, node): + self.values.append(node.value) + + Creates a list of values of all the constant nodes + encountered below the given node. To use it: + + cv = ConstantVisitor() + cv.visit(node) + + Notes: + + * generic_visit() will be called for AST nodes for which + no visit_XXX method was defined. + * The children of nodes for which a visit_XXX was + defined will not be visited - if you need this, call + generic_visit() on the node. + You can use: + NodeVisitor.generic_visit(self, node) + * Modeled after Python's own AST visiting facilities + (the ast module of Python 3.0) + """ + + _method_cache = None + + def visit(self, node): + """ Visit a node. + """ + + if self._method_cache is None: + self._method_cache = {} + + visitor = self._method_cache.get(node.__class__.__name__, None) + if visitor is None: + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + self._method_cache[node.__class__.__name__] = visitor + + return visitor(node) + + def generic_visit(self, node): + """ Called if no explicit visitor function exists for a + node. Implements preorder visiting of the node. + """ + for c in node: + self.visit(c) + +''' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py new file mode 100644 index 000000000..958381ad0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py @@ -0,0 +1,37 @@ +#----------------------------------------------------------------- +# pycparser: _build_tables.py +# +# A dummy for generating the lexing/parsing tables and and +# compiling them into .pyc for faster execution in optimized mode. +# Also generates AST code from the configuration file. +# Should be called from the pycparser directory. +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- + +# Insert '.' and '..' as first entries to the search path for modules. +# Restricted environments like embeddable python do not include the +# current working directory on startup. +import sys +sys.path[0:0] = ['.', '..'] + +# Generate c_ast.py +from _ast_gen import ASTCodeGenerator +ast_gen = ASTCodeGenerator('_c_ast.cfg') +ast_gen.generate(open('c_ast.py', 'w')) + +from pycparser import c_parser + +# Generates the tables +# +c_parser.CParser( + lex_optimize=True, + yacc_debug=False, + yacc_optimize=True) + +# Load to compile into .pyc +# +import lextab +import yacctab +import c_ast diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg new file mode 100644 index 000000000..0626533e8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg @@ -0,0 +1,195 @@ +#----------------------------------------------------------------- +# pycparser: _c_ast.cfg +# +# Defines the AST Node classes used in pycparser. +# +# Each entry is a Node sub-class name, listing the attributes +# and child nodes of the class: +# * - a child node +# ** - a sequence of child nodes +# - an attribute +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- + +# ArrayDecl is a nested declaration of an array with the given type. +# dim: the dimension (for example, constant 42) +# dim_quals: list of dimension qualifiers, to support C99's allowing 'const' +# and 'static' within the array dimension in function declarations. +ArrayDecl: [type*, dim*, dim_quals] + +ArrayRef: [name*, subscript*] + +# op: =, +=, /= etc. +# +Assignment: [op, lvalue*, rvalue*] + +Alignas: [alignment*] + +BinaryOp: [op, left*, right*] + +Break: [] + +Case: [expr*, stmts**] + +Cast: [to_type*, expr*] + +# Compound statement in C99 is a list of block items (declarations or +# statements). +# +Compound: [block_items**] + +# Compound literal (anonymous aggregate) for C99. +# (type-name) {initializer_list} +# type: the typename +# init: InitList for the initializer list +# +CompoundLiteral: [type*, init*] + +# type: int, char, float, string, etc. +# +Constant: [type, value] + +Continue: [] + +# name: the variable being declared +# quals: list of qualifiers (const, volatile) +# funcspec: list function specifiers (i.e. inline in C99) +# storage: list of storage specifiers (extern, register, etc.) +# type: declaration type (probably nested with all the modifiers) +# init: initialization value, or None +# bitsize: bit field size, or None +# +Decl: [name, quals, align, storage, funcspec, type*, init*, bitsize*] + +DeclList: [decls**] + +Default: [stmts**] + +DoWhile: [cond*, stmt*] + +# Represents the ellipsis (...) parameter in a function +# declaration +# +EllipsisParam: [] + +# An empty statement (a semicolon ';' on its own) +# +EmptyStatement: [] + +# Enumeration type specifier +# name: an optional ID +# values: an EnumeratorList +# +Enum: [name, values*] + +# A name/value pair for enumeration values +# +Enumerator: [name, value*] + +# A list of enumerators +# +EnumeratorList: [enumerators**] + +# A list of expressions separated by the comma operator. +# +ExprList: [exprs**] + +# This is the top of the AST, representing a single C file (a +# translation unit in K&R jargon). It contains a list of +# "external-declaration"s, which is either declarations (Decl), +# Typedef or function definitions (FuncDef). +# +FileAST: [ext**] + +# for (init; cond; next) stmt +# +For: [init*, cond*, next*, stmt*] + +# name: Id +# args: ExprList +# +FuncCall: [name*, args*] + +# type (args) +# +FuncDecl: [args*, type*] + +# Function definition: a declarator for the function name and +# a body, which is a compound statement. +# There's an optional list of parameter declarations for old +# K&R-style definitions +# +FuncDef: [decl*, param_decls**, body*] + +Goto: [name] + +ID: [name] + +# Holder for types that are a simple identifier (e.g. the built +# ins void, char etc. and typedef-defined types) +# +IdentifierType: [names] + +If: [cond*, iftrue*, iffalse*] + +# An initialization list used for compound literals. +# +InitList: [exprs**] + +Label: [name, stmt*] + +# A named initializer for C99. +# The name of a NamedInitializer is a sequence of Nodes, because +# names can be hierarchical and contain constant expressions. +# +NamedInitializer: [name**, expr*] + +# a list of comma separated function parameter declarations +# +ParamList: [params**] + +PtrDecl: [quals, type*] + +Return: [expr*] + +StaticAssert: [cond*, message*] + +# name: struct tag name +# decls: declaration of members +# +Struct: [name, decls**] + +# type: . or -> +# name.field or name->field +# +StructRef: [name*, type, field*] + +Switch: [cond*, stmt*] + +# cond ? iftrue : iffalse +# +TernaryOp: [cond*, iftrue*, iffalse*] + +# A base type declaration +# +TypeDecl: [declname, quals, align, type*] + +# A typedef declaration. +# Very similar to Decl, but without some attributes +# +Typedef: [name, quals, storage, type*] + +Typename: [name, quals, align, type*] + +UnaryOp: [op, expr*] + +# name: union tag name +# decls: declaration of members +# +Union: [name, decls**] + +While: [cond*, stmt*] + +Pragma: [string] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py new file mode 100644 index 000000000..367dcf54c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py @@ -0,0 +1,164 @@ +#------------------------------------------------------------------------------ +# pycparser: ast_transforms.py +# +# Some utilities used by the parser to create a friendlier AST. +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#------------------------------------------------------------------------------ + +from . import c_ast + + +def fix_switch_cases(switch_node): + """ The 'case' statements in a 'switch' come out of parsing with one + child node, so subsequent statements are just tucked to the parent + Compound. Additionally, consecutive (fall-through) case statements + come out messy. This is a peculiarity of the C grammar. The following: + + switch (myvar) { + case 10: + k = 10; + p = k + 1; + return 10; + case 20: + case 30: + return 20; + default: + break; + } + + Creates this tree (pseudo-dump): + + Switch + ID: myvar + Compound: + Case 10: + k = 10 + p = k + 1 + return 10 + Case 20: + Case 30: + return 20 + Default: + break + + The goal of this transform is to fix this mess, turning it into the + following: + + Switch + ID: myvar + Compound: + Case 10: + k = 10 + p = k + 1 + return 10 + Case 20: + Case 30: + return 20 + Default: + break + + A fixed AST node is returned. The argument may be modified. + """ + assert isinstance(switch_node, c_ast.Switch) + if not isinstance(switch_node.stmt, c_ast.Compound): + return switch_node + + # The new Compound child for the Switch, which will collect children in the + # correct order + new_compound = c_ast.Compound([], switch_node.stmt.coord) + + # The last Case/Default node + last_case = None + + # Goes over the children of the Compound below the Switch, adding them + # either directly below new_compound or below the last Case as appropriate + # (for `switch(cond) {}`, block_items would have been None) + for child in (switch_node.stmt.block_items or []): + if isinstance(child, (c_ast.Case, c_ast.Default)): + # If it's a Case/Default: + # 1. Add it to the Compound and mark as "last case" + # 2. If its immediate child is also a Case or Default, promote it + # to a sibling. + new_compound.block_items.append(child) + _extract_nested_case(child, new_compound.block_items) + last_case = new_compound.block_items[-1] + else: + # Other statements are added as children to the last case, if it + # exists. + if last_case is None: + new_compound.block_items.append(child) + else: + last_case.stmts.append(child) + + switch_node.stmt = new_compound + return switch_node + + +def _extract_nested_case(case_node, stmts_list): + """ Recursively extract consecutive Case statements that are made nested + by the parser and add them to the stmts_list. + """ + if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)): + stmts_list.append(case_node.stmts.pop()) + _extract_nested_case(stmts_list[-1], stmts_list) + + +def fix_atomic_specifiers(decl): + """ Atomic specifiers like _Atomic(type) are unusually structured, + conferring a qualifier upon the contained type. + + This function fixes a decl with atomic specifiers to have a sane AST + structure, by removing spurious Typename->TypeDecl pairs and attaching + the _Atomic qualifier in the right place. + """ + # There can be multiple levels of _Atomic in a decl; fix them until a + # fixed point is reached. + while True: + decl, found = _fix_atomic_specifiers_once(decl) + if not found: + break + + # Make sure to add an _Atomic qual on the topmost decl if needed. Also + # restore the declname on the innermost TypeDecl (it gets placed in the + # wrong place during construction). + typ = decl + while not isinstance(typ, c_ast.TypeDecl): + try: + typ = typ.type + except AttributeError: + return decl + if '_Atomic' in typ.quals and '_Atomic' not in decl.quals: + decl.quals.append('_Atomic') + if typ.declname is None: + typ.declname = decl.name + + return decl + + +def _fix_atomic_specifiers_once(decl): + """ Performs one 'fix' round of atomic specifiers. + Returns (modified_decl, found) where found is True iff a fix was made. + """ + parent = decl + grandparent = None + node = decl.type + while node is not None: + if isinstance(node, c_ast.Typename) and '_Atomic' in node.quals: + break + try: + grandparent = parent + parent = node + node = node.type + except AttributeError: + # If we've reached a node without a `type` field, it means we won't + # find what we're looking for at this point; give up the search + # and return the original decl unmodified. + return decl, False + + assert isinstance(parent, c_ast.TypeDecl) + grandparent.type = node.type + if '_Atomic' not in node.type.quals: + node.type.quals.append('_Atomic') + return decl, True diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py new file mode 100644 index 000000000..6575a2ad3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py @@ -0,0 +1,1125 @@ +#----------------------------------------------------------------- +# ** ATTENTION ** +# This code was automatically generated from the file: +# _c_ast.cfg +# +# Do not modify it directly. Modify the configuration file and +# run the generator again. +# ** ** *** ** ** +# +# pycparser: c_ast.py +# +# AST Node classes. +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- + + +import sys + +def _repr(obj): + """ + Get the representation of an object, with dedicated pprint-like format for lists. + """ + if isinstance(obj, list): + return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]' + else: + return repr(obj) + +class Node(object): + __slots__ = () + """ Abstract base class for AST nodes. + """ + def __repr__(self): + """ Generates a python representation of the current node + """ + result = self.__class__.__name__ + '(' + + indent = '' + separator = '' + for name in self.__slots__[:-2]: + result += separator + result += indent + result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__))))) + + separator = ',' + indent = '\n ' + (' ' * len(self.__class__.__name__)) + + result += indent + ')' + + return result + + def children(self): + """ A sequence of all children that are Nodes + """ + pass + + def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None): + """ Pretty print the Node and all its attributes and + children (recursively) to a buffer. + + buf: + Open IO buffer into which the Node is printed. + + offset: + Initial offset (amount of leading spaces) + + attrnames: + True if you want to see the attribute names in + name=value pairs. False to only see the values. + + nodenames: + True if you want to see the actual node names + within their parents. + + showcoord: + Do you want the coordinates of each Node to be + displayed. + """ + lead = ' ' * offset + if nodenames and _my_node_name is not None: + buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ') + else: + buf.write(lead + self.__class__.__name__+ ': ') + + if self.attr_names: + if attrnames: + nvlist = [(n, getattr(self,n)) for n in self.attr_names] + attrstr = ', '.join('%s=%s' % nv for nv in nvlist) + else: + vlist = [getattr(self, n) for n in self.attr_names] + attrstr = ', '.join('%s' % v for v in vlist) + buf.write(attrstr) + + if showcoord: + buf.write(' (at %s)' % self.coord) + buf.write('\n') + + for (child_name, child) in self.children(): + child.show( + buf, + offset=offset + 2, + attrnames=attrnames, + nodenames=nodenames, + showcoord=showcoord, + _my_node_name=child_name) + + +class NodeVisitor(object): + """ A base NodeVisitor class for visiting c_ast nodes. + Subclass it and define your own visit_XXX methods, where + XXX is the class name you want to visit with these + methods. + + For example: + + class ConstantVisitor(NodeVisitor): + def __init__(self): + self.values = [] + + def visit_Constant(self, node): + self.values.append(node.value) + + Creates a list of values of all the constant nodes + encountered below the given node. To use it: + + cv = ConstantVisitor() + cv.visit(node) + + Notes: + + * generic_visit() will be called for AST nodes for which + no visit_XXX method was defined. + * The children of nodes for which a visit_XXX was + defined will not be visited - if you need this, call + generic_visit() on the node. + You can use: + NodeVisitor.generic_visit(self, node) + * Modeled after Python's own AST visiting facilities + (the ast module of Python 3.0) + """ + + _method_cache = None + + def visit(self, node): + """ Visit a node. + """ + + if self._method_cache is None: + self._method_cache = {} + + visitor = self._method_cache.get(node.__class__.__name__, None) + if visitor is None: + method = 'visit_' + node.__class__.__name__ + visitor = getattr(self, method, self.generic_visit) + self._method_cache[node.__class__.__name__] = visitor + + return visitor(node) + + def generic_visit(self, node): + """ Called if no explicit visitor function exists for a + node. Implements preorder visiting of the node. + """ + for c in node: + self.visit(c) + +class ArrayDecl(Node): + __slots__ = ('type', 'dim', 'dim_quals', 'coord', '__weakref__') + def __init__(self, type, dim, dim_quals, coord=None): + self.type = type + self.dim = dim + self.dim_quals = dim_quals + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + if self.dim is not None: nodelist.append(("dim", self.dim)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + if self.dim is not None: + yield self.dim + + attr_names = ('dim_quals', ) + +class ArrayRef(Node): + __slots__ = ('name', 'subscript', 'coord', '__weakref__') + def __init__(self, name, subscript, coord=None): + self.name = name + self.subscript = subscript + self.coord = coord + + def children(self): + nodelist = [] + if self.name is not None: nodelist.append(("name", self.name)) + if self.subscript is not None: nodelist.append(("subscript", self.subscript)) + return tuple(nodelist) + + def __iter__(self): + if self.name is not None: + yield self.name + if self.subscript is not None: + yield self.subscript + + attr_names = () + +class Assignment(Node): + __slots__ = ('op', 'lvalue', 'rvalue', 'coord', '__weakref__') + def __init__(self, op, lvalue, rvalue, coord=None): + self.op = op + self.lvalue = lvalue + self.rvalue = rvalue + self.coord = coord + + def children(self): + nodelist = [] + if self.lvalue is not None: nodelist.append(("lvalue", self.lvalue)) + if self.rvalue is not None: nodelist.append(("rvalue", self.rvalue)) + return tuple(nodelist) + + def __iter__(self): + if self.lvalue is not None: + yield self.lvalue + if self.rvalue is not None: + yield self.rvalue + + attr_names = ('op', ) + +class Alignas(Node): + __slots__ = ('alignment', 'coord', '__weakref__') + def __init__(self, alignment, coord=None): + self.alignment = alignment + self.coord = coord + + def children(self): + nodelist = [] + if self.alignment is not None: nodelist.append(("alignment", self.alignment)) + return tuple(nodelist) + + def __iter__(self): + if self.alignment is not None: + yield self.alignment + + attr_names = () + +class BinaryOp(Node): + __slots__ = ('op', 'left', 'right', 'coord', '__weakref__') + def __init__(self, op, left, right, coord=None): + self.op = op + self.left = left + self.right = right + self.coord = coord + + def children(self): + nodelist = [] + if self.left is not None: nodelist.append(("left", self.left)) + if self.right is not None: nodelist.append(("right", self.right)) + return tuple(nodelist) + + def __iter__(self): + if self.left is not None: + yield self.left + if self.right is not None: + yield self.right + + attr_names = ('op', ) + +class Break(Node): + __slots__ = ('coord', '__weakref__') + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def __iter__(self): + return + yield + + attr_names = () + +class Case(Node): + __slots__ = ('expr', 'stmts', 'coord', '__weakref__') + def __init__(self, expr, stmts, coord=None): + self.expr = expr + self.stmts = stmts + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(("expr", self.expr)) + for i, child in enumerate(self.stmts or []): + nodelist.append(("stmts[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + if self.expr is not None: + yield self.expr + for child in (self.stmts or []): + yield child + + attr_names = () + +class Cast(Node): + __slots__ = ('to_type', 'expr', 'coord', '__weakref__') + def __init__(self, to_type, expr, coord=None): + self.to_type = to_type + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.to_type is not None: nodelist.append(("to_type", self.to_type)) + if self.expr is not None: nodelist.append(("expr", self.expr)) + return tuple(nodelist) + + def __iter__(self): + if self.to_type is not None: + yield self.to_type + if self.expr is not None: + yield self.expr + + attr_names = () + +class Compound(Node): + __slots__ = ('block_items', 'coord', '__weakref__') + def __init__(self, block_items, coord=None): + self.block_items = block_items + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.block_items or []): + nodelist.append(("block_items[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.block_items or []): + yield child + + attr_names = () + +class CompoundLiteral(Node): + __slots__ = ('type', 'init', 'coord', '__weakref__') + def __init__(self, type, init, coord=None): + self.type = type + self.init = init + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + if self.init is not None: nodelist.append(("init", self.init)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + if self.init is not None: + yield self.init + + attr_names = () + +class Constant(Node): + __slots__ = ('type', 'value', 'coord', '__weakref__') + def __init__(self, type, value, coord=None): + self.type = type + self.value = value + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def __iter__(self): + return + yield + + attr_names = ('type', 'value', ) + +class Continue(Node): + __slots__ = ('coord', '__weakref__') + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def __iter__(self): + return + yield + + attr_names = () + +class Decl(Node): + __slots__ = ('name', 'quals', 'align', 'storage', 'funcspec', 'type', 'init', 'bitsize', 'coord', '__weakref__') + def __init__(self, name, quals, align, storage, funcspec, type, init, bitsize, coord=None): + self.name = name + self.quals = quals + self.align = align + self.storage = storage + self.funcspec = funcspec + self.type = type + self.init = init + self.bitsize = bitsize + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + if self.init is not None: nodelist.append(("init", self.init)) + if self.bitsize is not None: nodelist.append(("bitsize", self.bitsize)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + if self.init is not None: + yield self.init + if self.bitsize is not None: + yield self.bitsize + + attr_names = ('name', 'quals', 'align', 'storage', 'funcspec', ) + +class DeclList(Node): + __slots__ = ('decls', 'coord', '__weakref__') + def __init__(self, decls, coord=None): + self.decls = decls + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.decls or []): + nodelist.append(("decls[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.decls or []): + yield child + + attr_names = () + +class Default(Node): + __slots__ = ('stmts', 'coord', '__weakref__') + def __init__(self, stmts, coord=None): + self.stmts = stmts + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.stmts or []): + nodelist.append(("stmts[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.stmts or []): + yield child + + attr_names = () + +class DoWhile(Node): + __slots__ = ('cond', 'stmt', 'coord', '__weakref__') + def __init__(self, cond, stmt, coord=None): + self.cond = cond + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.stmt is not None: nodelist.append(("stmt", self.stmt)) + return tuple(nodelist) + + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.stmt is not None: + yield self.stmt + + attr_names = () + +class EllipsisParam(Node): + __slots__ = ('coord', '__weakref__') + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def __iter__(self): + return + yield + + attr_names = () + +class EmptyStatement(Node): + __slots__ = ('coord', '__weakref__') + def __init__(self, coord=None): + self.coord = coord + + def children(self): + return () + + def __iter__(self): + return + yield + + attr_names = () + +class Enum(Node): + __slots__ = ('name', 'values', 'coord', '__weakref__') + def __init__(self, name, values, coord=None): + self.name = name + self.values = values + self.coord = coord + + def children(self): + nodelist = [] + if self.values is not None: nodelist.append(("values", self.values)) + return tuple(nodelist) + + def __iter__(self): + if self.values is not None: + yield self.values + + attr_names = ('name', ) + +class Enumerator(Node): + __slots__ = ('name', 'value', 'coord', '__weakref__') + def __init__(self, name, value, coord=None): + self.name = name + self.value = value + self.coord = coord + + def children(self): + nodelist = [] + if self.value is not None: nodelist.append(("value", self.value)) + return tuple(nodelist) + + def __iter__(self): + if self.value is not None: + yield self.value + + attr_names = ('name', ) + +class EnumeratorList(Node): + __slots__ = ('enumerators', 'coord', '__weakref__') + def __init__(self, enumerators, coord=None): + self.enumerators = enumerators + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.enumerators or []): + nodelist.append(("enumerators[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.enumerators or []): + yield child + + attr_names = () + +class ExprList(Node): + __slots__ = ('exprs', 'coord', '__weakref__') + def __init__(self, exprs, coord=None): + self.exprs = exprs + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.exprs or []): + nodelist.append(("exprs[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.exprs or []): + yield child + + attr_names = () + +class FileAST(Node): + __slots__ = ('ext', 'coord', '__weakref__') + def __init__(self, ext, coord=None): + self.ext = ext + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.ext or []): + nodelist.append(("ext[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.ext or []): + yield child + + attr_names = () + +class For(Node): + __slots__ = ('init', 'cond', 'next', 'stmt', 'coord', '__weakref__') + def __init__(self, init, cond, next, stmt, coord=None): + self.init = init + self.cond = cond + self.next = next + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.init is not None: nodelist.append(("init", self.init)) + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.next is not None: nodelist.append(("next", self.next)) + if self.stmt is not None: nodelist.append(("stmt", self.stmt)) + return tuple(nodelist) + + def __iter__(self): + if self.init is not None: + yield self.init + if self.cond is not None: + yield self.cond + if self.next is not None: + yield self.next + if self.stmt is not None: + yield self.stmt + + attr_names = () + +class FuncCall(Node): + __slots__ = ('name', 'args', 'coord', '__weakref__') + def __init__(self, name, args, coord=None): + self.name = name + self.args = args + self.coord = coord + + def children(self): + nodelist = [] + if self.name is not None: nodelist.append(("name", self.name)) + if self.args is not None: nodelist.append(("args", self.args)) + return tuple(nodelist) + + def __iter__(self): + if self.name is not None: + yield self.name + if self.args is not None: + yield self.args + + attr_names = () + +class FuncDecl(Node): + __slots__ = ('args', 'type', 'coord', '__weakref__') + def __init__(self, args, type, coord=None): + self.args = args + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.args is not None: nodelist.append(("args", self.args)) + if self.type is not None: nodelist.append(("type", self.type)) + return tuple(nodelist) + + def __iter__(self): + if self.args is not None: + yield self.args + if self.type is not None: + yield self.type + + attr_names = () + +class FuncDef(Node): + __slots__ = ('decl', 'param_decls', 'body', 'coord', '__weakref__') + def __init__(self, decl, param_decls, body, coord=None): + self.decl = decl + self.param_decls = param_decls + self.body = body + self.coord = coord + + def children(self): + nodelist = [] + if self.decl is not None: nodelist.append(("decl", self.decl)) + if self.body is not None: nodelist.append(("body", self.body)) + for i, child in enumerate(self.param_decls or []): + nodelist.append(("param_decls[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + if self.decl is not None: + yield self.decl + if self.body is not None: + yield self.body + for child in (self.param_decls or []): + yield child + + attr_names = () + +class Goto(Node): + __slots__ = ('name', 'coord', '__weakref__') + def __init__(self, name, coord=None): + self.name = name + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def __iter__(self): + return + yield + + attr_names = ('name', ) + +class ID(Node): + __slots__ = ('name', 'coord', '__weakref__') + def __init__(self, name, coord=None): + self.name = name + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def __iter__(self): + return + yield + + attr_names = ('name', ) + +class IdentifierType(Node): + __slots__ = ('names', 'coord', '__weakref__') + def __init__(self, names, coord=None): + self.names = names + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def __iter__(self): + return + yield + + attr_names = ('names', ) + +class If(Node): + __slots__ = ('cond', 'iftrue', 'iffalse', 'coord', '__weakref__') + def __init__(self, cond, iftrue, iffalse, coord=None): + self.cond = cond + self.iftrue = iftrue + self.iffalse = iffalse + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.iftrue is not None: nodelist.append(("iftrue", self.iftrue)) + if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse)) + return tuple(nodelist) + + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.iftrue is not None: + yield self.iftrue + if self.iffalse is not None: + yield self.iffalse + + attr_names = () + +class InitList(Node): + __slots__ = ('exprs', 'coord', '__weakref__') + def __init__(self, exprs, coord=None): + self.exprs = exprs + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.exprs or []): + nodelist.append(("exprs[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.exprs or []): + yield child + + attr_names = () + +class Label(Node): + __slots__ = ('name', 'stmt', 'coord', '__weakref__') + def __init__(self, name, stmt, coord=None): + self.name = name + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.stmt is not None: nodelist.append(("stmt", self.stmt)) + return tuple(nodelist) + + def __iter__(self): + if self.stmt is not None: + yield self.stmt + + attr_names = ('name', ) + +class NamedInitializer(Node): + __slots__ = ('name', 'expr', 'coord', '__weakref__') + def __init__(self, name, expr, coord=None): + self.name = name + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(("expr", self.expr)) + for i, child in enumerate(self.name or []): + nodelist.append(("name[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + if self.expr is not None: + yield self.expr + for child in (self.name or []): + yield child + + attr_names = () + +class ParamList(Node): + __slots__ = ('params', 'coord', '__weakref__') + def __init__(self, params, coord=None): + self.params = params + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.params or []): + nodelist.append(("params[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.params or []): + yield child + + attr_names = () + +class PtrDecl(Node): + __slots__ = ('quals', 'type', 'coord', '__weakref__') + def __init__(self, quals, type, coord=None): + self.quals = quals + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + + attr_names = ('quals', ) + +class Return(Node): + __slots__ = ('expr', 'coord', '__weakref__') + def __init__(self, expr, coord=None): + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(("expr", self.expr)) + return tuple(nodelist) + + def __iter__(self): + if self.expr is not None: + yield self.expr + + attr_names = () + +class StaticAssert(Node): + __slots__ = ('cond', 'message', 'coord', '__weakref__') + def __init__(self, cond, message, coord=None): + self.cond = cond + self.message = message + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.message is not None: nodelist.append(("message", self.message)) + return tuple(nodelist) + + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.message is not None: + yield self.message + + attr_names = () + +class Struct(Node): + __slots__ = ('name', 'decls', 'coord', '__weakref__') + def __init__(self, name, decls, coord=None): + self.name = name + self.decls = decls + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.decls or []): + nodelist.append(("decls[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.decls or []): + yield child + + attr_names = ('name', ) + +class StructRef(Node): + __slots__ = ('name', 'type', 'field', 'coord', '__weakref__') + def __init__(self, name, type, field, coord=None): + self.name = name + self.type = type + self.field = field + self.coord = coord + + def children(self): + nodelist = [] + if self.name is not None: nodelist.append(("name", self.name)) + if self.field is not None: nodelist.append(("field", self.field)) + return tuple(nodelist) + + def __iter__(self): + if self.name is not None: + yield self.name + if self.field is not None: + yield self.field + + attr_names = ('type', ) + +class Switch(Node): + __slots__ = ('cond', 'stmt', 'coord', '__weakref__') + def __init__(self, cond, stmt, coord=None): + self.cond = cond + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.stmt is not None: nodelist.append(("stmt", self.stmt)) + return tuple(nodelist) + + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.stmt is not None: + yield self.stmt + + attr_names = () + +class TernaryOp(Node): + __slots__ = ('cond', 'iftrue', 'iffalse', 'coord', '__weakref__') + def __init__(self, cond, iftrue, iffalse, coord=None): + self.cond = cond + self.iftrue = iftrue + self.iffalse = iffalse + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.iftrue is not None: nodelist.append(("iftrue", self.iftrue)) + if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse)) + return tuple(nodelist) + + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.iftrue is not None: + yield self.iftrue + if self.iffalse is not None: + yield self.iffalse + + attr_names = () + +class TypeDecl(Node): + __slots__ = ('declname', 'quals', 'align', 'type', 'coord', '__weakref__') + def __init__(self, declname, quals, align, type, coord=None): + self.declname = declname + self.quals = quals + self.align = align + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + + attr_names = ('declname', 'quals', 'align', ) + +class Typedef(Node): + __slots__ = ('name', 'quals', 'storage', 'type', 'coord', '__weakref__') + def __init__(self, name, quals, storage, type, coord=None): + self.name = name + self.quals = quals + self.storage = storage + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + + attr_names = ('name', 'quals', 'storage', ) + +class Typename(Node): + __slots__ = ('name', 'quals', 'align', 'type', 'coord', '__weakref__') + def __init__(self, name, quals, align, type, coord=None): + self.name = name + self.quals = quals + self.align = align + self.type = type + self.coord = coord + + def children(self): + nodelist = [] + if self.type is not None: nodelist.append(("type", self.type)) + return tuple(nodelist) + + def __iter__(self): + if self.type is not None: + yield self.type + + attr_names = ('name', 'quals', 'align', ) + +class UnaryOp(Node): + __slots__ = ('op', 'expr', 'coord', '__weakref__') + def __init__(self, op, expr, coord=None): + self.op = op + self.expr = expr + self.coord = coord + + def children(self): + nodelist = [] + if self.expr is not None: nodelist.append(("expr", self.expr)) + return tuple(nodelist) + + def __iter__(self): + if self.expr is not None: + yield self.expr + + attr_names = ('op', ) + +class Union(Node): + __slots__ = ('name', 'decls', 'coord', '__weakref__') + def __init__(self, name, decls, coord=None): + self.name = name + self.decls = decls + self.coord = coord + + def children(self): + nodelist = [] + for i, child in enumerate(self.decls or []): + nodelist.append(("decls[%d]" % i, child)) + return tuple(nodelist) + + def __iter__(self): + for child in (self.decls or []): + yield child + + attr_names = ('name', ) + +class While(Node): + __slots__ = ('cond', 'stmt', 'coord', '__weakref__') + def __init__(self, cond, stmt, coord=None): + self.cond = cond + self.stmt = stmt + self.coord = coord + + def children(self): + nodelist = [] + if self.cond is not None: nodelist.append(("cond", self.cond)) + if self.stmt is not None: nodelist.append(("stmt", self.stmt)) + return tuple(nodelist) + + def __iter__(self): + if self.cond is not None: + yield self.cond + if self.stmt is not None: + yield self.stmt + + attr_names = () + +class Pragma(Node): + __slots__ = ('string', 'coord', '__weakref__') + def __init__(self, string, coord=None): + self.string = string + self.coord = coord + + def children(self): + nodelist = [] + return tuple(nodelist) + + def __iter__(self): + return + yield + + attr_names = ('string', ) + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py new file mode 100644 index 000000000..1057b2c62 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py @@ -0,0 +1,502 @@ +#------------------------------------------------------------------------------ +# pycparser: c_generator.py +# +# C code generator from pycparser AST nodes. +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#------------------------------------------------------------------------------ +from . import c_ast + + +class CGenerator(object): + """ Uses the same visitor pattern as c_ast.NodeVisitor, but modified to + return a value from each visit method, using string accumulation in + generic_visit. + """ + def __init__(self, reduce_parentheses=False): + """ Constructs C-code generator + + reduce_parentheses: + if True, eliminates needless parentheses on binary operators + """ + # Statements start with indentation of self.indent_level spaces, using + # the _make_indent method. + self.indent_level = 0 + self.reduce_parentheses = reduce_parentheses + + def _make_indent(self): + return ' ' * self.indent_level + + def visit(self, node): + method = 'visit_' + node.__class__.__name__ + return getattr(self, method, self.generic_visit)(node) + + def generic_visit(self, node): + if node is None: + return '' + else: + return ''.join(self.visit(c) for c_name, c in node.children()) + + def visit_Constant(self, n): + return n.value + + def visit_ID(self, n): + return n.name + + def visit_Pragma(self, n): + ret = '#pragma' + if n.string: + ret += ' ' + n.string + return ret + + def visit_ArrayRef(self, n): + arrref = self._parenthesize_unless_simple(n.name) + return arrref + '[' + self.visit(n.subscript) + ']' + + def visit_StructRef(self, n): + sref = self._parenthesize_unless_simple(n.name) + return sref + n.type + self.visit(n.field) + + def visit_FuncCall(self, n): + fref = self._parenthesize_unless_simple(n.name) + return fref + '(' + self.visit(n.args) + ')' + + def visit_UnaryOp(self, n): + if n.op == 'sizeof': + # Always parenthesize the argument of sizeof since it can be + # a name. + return 'sizeof(%s)' % self.visit(n.expr) + else: + operand = self._parenthesize_unless_simple(n.expr) + if n.op == 'p++': + return '%s++' % operand + elif n.op == 'p--': + return '%s--' % operand + else: + return '%s%s' % (n.op, operand) + + # Precedence map of binary operators: + precedence_map = { + # Should be in sync with c_parser.CParser.precedence + # Higher numbers are stronger binding + '||': 0, # weakest binding + '&&': 1, + '|': 2, + '^': 3, + '&': 4, + '==': 5, '!=': 5, + '>': 6, '>=': 6, '<': 6, '<=': 6, + '>>': 7, '<<': 7, + '+': 8, '-': 8, + '*': 9, '/': 9, '%': 9 # strongest binding + } + + def visit_BinaryOp(self, n): + # Note: all binary operators are left-to-right associative + # + # If `n.left.op` has a stronger or equally binding precedence in + # comparison to `n.op`, no parenthesis are needed for the left: + # e.g., `(a*b) + c` is equivalent to `a*b + c`, as well as + # `(a+b) - c` is equivalent to `a+b - c` (same precedence). + # If the left operator is weaker binding than the current, then + # parentheses are necessary: + # e.g., `(a+b) * c` is NOT equivalent to `a+b * c`. + lval_str = self._parenthesize_if( + n.left, + lambda d: not (self._is_simple_node(d) or + self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and + self.precedence_map[d.op] >= self.precedence_map[n.op])) + # If `n.right.op` has a stronger -but not equal- binding precedence, + # parenthesis can be omitted on the right: + # e.g., `a + (b*c)` is equivalent to `a + b*c`. + # If the right operator is weaker or equally binding, then parentheses + # are necessary: + # e.g., `a * (b+c)` is NOT equivalent to `a * b+c` and + # `a - (b+c)` is NOT equivalent to `a - b+c` (same precedence). + rval_str = self._parenthesize_if( + n.right, + lambda d: not (self._is_simple_node(d) or + self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and + self.precedence_map[d.op] > self.precedence_map[n.op])) + return '%s %s %s' % (lval_str, n.op, rval_str) + + def visit_Assignment(self, n): + rval_str = self._parenthesize_if( + n.rvalue, + lambda n: isinstance(n, c_ast.Assignment)) + return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str) + + def visit_IdentifierType(self, n): + return ' '.join(n.names) + + def _visit_expr(self, n): + if isinstance(n, c_ast.InitList): + return '{' + self.visit(n) + '}' + elif isinstance(n, c_ast.ExprList): + return '(' + self.visit(n) + ')' + else: + return self.visit(n) + + def visit_Decl(self, n, no_type=False): + # no_type is used when a Decl is part of a DeclList, where the type is + # explicitly only for the first declaration in a list. + # + s = n.name if no_type else self._generate_decl(n) + if n.bitsize: s += ' : ' + self.visit(n.bitsize) + if n.init: + s += ' = ' + self._visit_expr(n.init) + return s + + def visit_DeclList(self, n): + s = self.visit(n.decls[0]) + if len(n.decls) > 1: + s += ', ' + ', '.join(self.visit_Decl(decl, no_type=True) + for decl in n.decls[1:]) + return s + + def visit_Typedef(self, n): + s = '' + if n.storage: s += ' '.join(n.storage) + ' ' + s += self._generate_type(n.type) + return s + + def visit_Cast(self, n): + s = '(' + self._generate_type(n.to_type, emit_declname=False) + ')' + return s + ' ' + self._parenthesize_unless_simple(n.expr) + + def visit_ExprList(self, n): + visited_subexprs = [] + for expr in n.exprs: + visited_subexprs.append(self._visit_expr(expr)) + return ', '.join(visited_subexprs) + + def visit_InitList(self, n): + visited_subexprs = [] + for expr in n.exprs: + visited_subexprs.append(self._visit_expr(expr)) + return ', '.join(visited_subexprs) + + def visit_Enum(self, n): + return self._generate_struct_union_enum(n, name='enum') + + def visit_Alignas(self, n): + return '_Alignas({})'.format(self.visit(n.alignment)) + + def visit_Enumerator(self, n): + if not n.value: + return '{indent}{name},\n'.format( + indent=self._make_indent(), + name=n.name, + ) + else: + return '{indent}{name} = {value},\n'.format( + indent=self._make_indent(), + name=n.name, + value=self.visit(n.value), + ) + + def visit_FuncDef(self, n): + decl = self.visit(n.decl) + self.indent_level = 0 + body = self.visit(n.body) + if n.param_decls: + knrdecls = ';\n'.join(self.visit(p) for p in n.param_decls) + return decl + '\n' + knrdecls + ';\n' + body + '\n' + else: + return decl + '\n' + body + '\n' + + def visit_FileAST(self, n): + s = '' + for ext in n.ext: + if isinstance(ext, c_ast.FuncDef): + s += self.visit(ext) + elif isinstance(ext, c_ast.Pragma): + s += self.visit(ext) + '\n' + else: + s += self.visit(ext) + ';\n' + return s + + def visit_Compound(self, n): + s = self._make_indent() + '{\n' + self.indent_level += 2 + if n.block_items: + s += ''.join(self._generate_stmt(stmt) for stmt in n.block_items) + self.indent_level -= 2 + s += self._make_indent() + '}\n' + return s + + def visit_CompoundLiteral(self, n): + return '(' + self.visit(n.type) + '){' + self.visit(n.init) + '}' + + + def visit_EmptyStatement(self, n): + return ';' + + def visit_ParamList(self, n): + return ', '.join(self.visit(param) for param in n.params) + + def visit_Return(self, n): + s = 'return' + if n.expr: s += ' ' + self.visit(n.expr) + return s + ';' + + def visit_Break(self, n): + return 'break;' + + def visit_Continue(self, n): + return 'continue;' + + def visit_TernaryOp(self, n): + s = '(' + self._visit_expr(n.cond) + ') ? ' + s += '(' + self._visit_expr(n.iftrue) + ') : ' + s += '(' + self._visit_expr(n.iffalse) + ')' + return s + + def visit_If(self, n): + s = 'if (' + if n.cond: s += self.visit(n.cond) + s += ')\n' + s += self._generate_stmt(n.iftrue, add_indent=True) + if n.iffalse: + s += self._make_indent() + 'else\n' + s += self._generate_stmt(n.iffalse, add_indent=True) + return s + + def visit_For(self, n): + s = 'for (' + if n.init: s += self.visit(n.init) + s += ';' + if n.cond: s += ' ' + self.visit(n.cond) + s += ';' + if n.next: s += ' ' + self.visit(n.next) + s += ')\n' + s += self._generate_stmt(n.stmt, add_indent=True) + return s + + def visit_While(self, n): + s = 'while (' + if n.cond: s += self.visit(n.cond) + s += ')\n' + s += self._generate_stmt(n.stmt, add_indent=True) + return s + + def visit_DoWhile(self, n): + s = 'do\n' + s += self._generate_stmt(n.stmt, add_indent=True) + s += self._make_indent() + 'while (' + if n.cond: s += self.visit(n.cond) + s += ');' + return s + + def visit_StaticAssert(self, n): + s = '_Static_assert(' + s += self.visit(n.cond) + if n.message: + s += ',' + s += self.visit(n.message) + s += ')' + return s + + def visit_Switch(self, n): + s = 'switch (' + self.visit(n.cond) + ')\n' + s += self._generate_stmt(n.stmt, add_indent=True) + return s + + def visit_Case(self, n): + s = 'case ' + self.visit(n.expr) + ':\n' + for stmt in n.stmts: + s += self._generate_stmt(stmt, add_indent=True) + return s + + def visit_Default(self, n): + s = 'default:\n' + for stmt in n.stmts: + s += self._generate_stmt(stmt, add_indent=True) + return s + + def visit_Label(self, n): + return n.name + ':\n' + self._generate_stmt(n.stmt) + + def visit_Goto(self, n): + return 'goto ' + n.name + ';' + + def visit_EllipsisParam(self, n): + return '...' + + def visit_Struct(self, n): + return self._generate_struct_union_enum(n, 'struct') + + def visit_Typename(self, n): + return self._generate_type(n.type) + + def visit_Union(self, n): + return self._generate_struct_union_enum(n, 'union') + + def visit_NamedInitializer(self, n): + s = '' + for name in n.name: + if isinstance(name, c_ast.ID): + s += '.' + name.name + else: + s += '[' + self.visit(name) + ']' + s += ' = ' + self._visit_expr(n.expr) + return s + + def visit_FuncDecl(self, n): + return self._generate_type(n) + + def visit_ArrayDecl(self, n): + return self._generate_type(n, emit_declname=False) + + def visit_TypeDecl(self, n): + return self._generate_type(n, emit_declname=False) + + def visit_PtrDecl(self, n): + return self._generate_type(n, emit_declname=False) + + def _generate_struct_union_enum(self, n, name): + """ Generates code for structs, unions, and enums. name should be + 'struct', 'union', or 'enum'. + """ + if name in ('struct', 'union'): + members = n.decls + body_function = self._generate_struct_union_body + else: + assert name == 'enum' + members = None if n.values is None else n.values.enumerators + body_function = self._generate_enum_body + s = name + ' ' + (n.name or '') + if members is not None: + # None means no members + # Empty sequence means an empty list of members + s += '\n' + s += self._make_indent() + self.indent_level += 2 + s += '{\n' + s += body_function(members) + self.indent_level -= 2 + s += self._make_indent() + '}' + return s + + def _generate_struct_union_body(self, members): + return ''.join(self._generate_stmt(decl) for decl in members) + + def _generate_enum_body(self, members): + # `[:-2] + '\n'` removes the final `,` from the enumerator list + return ''.join(self.visit(value) for value in members)[:-2] + '\n' + + def _generate_stmt(self, n, add_indent=False): + """ Generation from a statement node. This method exists as a wrapper + for individual visit_* methods to handle different treatment of + some statements in this context. + """ + typ = type(n) + if add_indent: self.indent_level += 2 + indent = self._make_indent() + if add_indent: self.indent_level -= 2 + + if typ in ( + c_ast.Decl, c_ast.Assignment, c_ast.Cast, c_ast.UnaryOp, + c_ast.BinaryOp, c_ast.TernaryOp, c_ast.FuncCall, c_ast.ArrayRef, + c_ast.StructRef, c_ast.Constant, c_ast.ID, c_ast.Typedef, + c_ast.ExprList): + # These can also appear in an expression context so no semicolon + # is added to them automatically + # + return indent + self.visit(n) + ';\n' + elif typ in (c_ast.Compound,): + # No extra indentation required before the opening brace of a + # compound - because it consists of multiple lines it has to + # compute its own indentation. + # + return self.visit(n) + elif typ in (c_ast.If,): + return indent + self.visit(n) + else: + return indent + self.visit(n) + '\n' + + def _generate_decl(self, n): + """ Generation from a Decl node. + """ + s = '' + if n.funcspec: s = ' '.join(n.funcspec) + ' ' + if n.storage: s += ' '.join(n.storage) + ' ' + if n.align: s += self.visit(n.align[0]) + ' ' + s += self._generate_type(n.type) + return s + + def _generate_type(self, n, modifiers=[], emit_declname = True): + """ Recursive generation from a type node. n is the type node. + modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers + encountered on the way down to a TypeDecl, to allow proper + generation from it. + """ + typ = type(n) + #~ print(n, modifiers) + + if typ == c_ast.TypeDecl: + s = '' + if n.quals: s += ' '.join(n.quals) + ' ' + s += self.visit(n.type) + + nstr = n.declname if n.declname and emit_declname else '' + # Resolve modifiers. + # Wrap in parens to distinguish pointer to array and pointer to + # function syntax. + # + for i, modifier in enumerate(modifiers): + if isinstance(modifier, c_ast.ArrayDecl): + if (i != 0 and + isinstance(modifiers[i - 1], c_ast.PtrDecl)): + nstr = '(' + nstr + ')' + nstr += '[' + if modifier.dim_quals: + nstr += ' '.join(modifier.dim_quals) + ' ' + nstr += self.visit(modifier.dim) + ']' + elif isinstance(modifier, c_ast.FuncDecl): + if (i != 0 and + isinstance(modifiers[i - 1], c_ast.PtrDecl)): + nstr = '(' + nstr + ')' + nstr += '(' + self.visit(modifier.args) + ')' + elif isinstance(modifier, c_ast.PtrDecl): + if modifier.quals: + nstr = '* %s%s' % (' '.join(modifier.quals), + ' ' + nstr if nstr else '') + else: + nstr = '*' + nstr + if nstr: s += ' ' + nstr + return s + elif typ == c_ast.Decl: + return self._generate_decl(n.type) + elif typ == c_ast.Typename: + return self._generate_type(n.type, emit_declname = emit_declname) + elif typ == c_ast.IdentifierType: + return ' '.join(n.names) + ' ' + elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl): + return self._generate_type(n.type, modifiers + [n], + emit_declname = emit_declname) + else: + return self.visit(n) + + def _parenthesize_if(self, n, condition): + """ Visits 'n' and returns its string representation, parenthesized + if the condition function applied to the node returns True. + """ + s = self._visit_expr(n) + if condition(n): + return '(' + s + ')' + else: + return s + + def _parenthesize_unless_simple(self, n): + """ Common use case for _parenthesize_if + """ + return self._parenthesize_if(n, lambda d: not self._is_simple_node(d)) + + def _is_simple_node(self, n): + """ Returns True for nodes that are "simple" - i.e. nodes that always + have higher precedence than operators. + """ + return isinstance(n, (c_ast.Constant, c_ast.ID, c_ast.ArrayRef, + c_ast.StructRef, c_ast.FuncCall)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py new file mode 100644 index 000000000..d68d8ebfa --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py @@ -0,0 +1,554 @@ +#------------------------------------------------------------------------------ +# pycparser: c_lexer.py +# +# CLexer class: lexer for the C language +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#------------------------------------------------------------------------------ +import re + +from .ply import lex +from .ply.lex import TOKEN + + +class CLexer(object): + """ A lexer for the C language. After building it, set the + input text with input(), and call token() to get new + tokens. + + The public attribute filename can be set to an initial + filename, but the lexer will update it upon #line + directives. + """ + def __init__(self, error_func, on_lbrace_func, on_rbrace_func, + type_lookup_func): + """ Create a new Lexer. + + error_func: + An error function. Will be called with an error + message, line and column as arguments, in case of + an error during lexing. + + on_lbrace_func, on_rbrace_func: + Called when an LBRACE or RBRACE is encountered + (likely to push/pop type_lookup_func's scope) + + type_lookup_func: + A type lookup function. Given a string, it must + return True IFF this string is a name of a type + that was defined with a typedef earlier. + """ + self.error_func = error_func + self.on_lbrace_func = on_lbrace_func + self.on_rbrace_func = on_rbrace_func + self.type_lookup_func = type_lookup_func + self.filename = '' + + # Keeps track of the last token returned from self.token() + self.last_token = None + + # Allow either "# line" or "# " to support GCC's + # cpp output + # + self.line_pattern = re.compile(r'([ \t]*line\W)|([ \t]*\d+)') + self.pragma_pattern = re.compile(r'[ \t]*pragma\W') + + def build(self, **kwargs): + """ Builds the lexer from the specification. Must be + called after the lexer object is created. + + This method exists separately, because the PLY + manual warns against calling lex.lex inside + __init__ + """ + self.lexer = lex.lex(object=self, **kwargs) + + def reset_lineno(self): + """ Resets the internal line number counter of the lexer. + """ + self.lexer.lineno = 1 + + def input(self, text): + self.lexer.input(text) + + def token(self): + self.last_token = self.lexer.token() + return self.last_token + + def find_tok_column(self, token): + """ Find the column of the token in its line. + """ + last_cr = self.lexer.lexdata.rfind('\n', 0, token.lexpos) + return token.lexpos - last_cr + + ######################-- PRIVATE --###################### + + ## + ## Internal auxiliary methods + ## + def _error(self, msg, token): + location = self._make_tok_location(token) + self.error_func(msg, location[0], location[1]) + self.lexer.skip(1) + + def _make_tok_location(self, token): + return (token.lineno, self.find_tok_column(token)) + + ## + ## Reserved keywords + ## + keywords = ( + 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST', + 'CONTINUE', 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN', + 'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', + 'REGISTER', 'OFFSETOF', + 'RESTRICT', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT', + 'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID', + 'VOLATILE', 'WHILE', '__INT128', + ) + + keywords_new = ( + '_BOOL', '_COMPLEX', + '_NORETURN', '_THREAD_LOCAL', '_STATIC_ASSERT', + '_ATOMIC', '_ALIGNOF', '_ALIGNAS', + ) + + keyword_map = {} + + for keyword in keywords: + keyword_map[keyword.lower()] = keyword + + for keyword in keywords_new: + keyword_map[keyword[:2].upper() + keyword[2:].lower()] = keyword + + ## + ## All the tokens recognized by the lexer + ## + tokens = keywords + keywords_new + ( + # Identifiers + 'ID', + + # Type identifiers (identifiers previously defined as + # types with typedef) + 'TYPEID', + + # constants + 'INT_CONST_DEC', 'INT_CONST_OCT', 'INT_CONST_HEX', 'INT_CONST_BIN', 'INT_CONST_CHAR', + 'FLOAT_CONST', 'HEX_FLOAT_CONST', + 'CHAR_CONST', + 'WCHAR_CONST', + 'U8CHAR_CONST', + 'U16CHAR_CONST', + 'U32CHAR_CONST', + + # String literals + 'STRING_LITERAL', + 'WSTRING_LITERAL', + 'U8STRING_LITERAL', + 'U16STRING_LITERAL', + 'U32STRING_LITERAL', + + # Operators + 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD', + 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', + 'LOR', 'LAND', 'LNOT', + 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', + + # Assignment + 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', + 'PLUSEQUAL', 'MINUSEQUAL', + 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', + 'OREQUAL', + + # Increment/decrement + 'PLUSPLUS', 'MINUSMINUS', + + # Structure dereference (->) + 'ARROW', + + # Conditional operator (?) + 'CONDOP', + + # Delimiters + 'LPAREN', 'RPAREN', # ( ) + 'LBRACKET', 'RBRACKET', # [ ] + 'LBRACE', 'RBRACE', # { } + 'COMMA', 'PERIOD', # . , + 'SEMI', 'COLON', # ; : + + # Ellipsis (...) + 'ELLIPSIS', + + # pre-processor + 'PPHASH', # '#' + 'PPPRAGMA', # 'pragma' + 'PPPRAGMASTR', + ) + + ## + ## Regexes for use in tokens + ## + ## + + # valid C identifiers (K&R2: A.2.3), plus '$' (supported by some compilers) + identifier = r'[a-zA-Z_$][0-9a-zA-Z_$]*' + + hex_prefix = '0[xX]' + hex_digits = '[0-9a-fA-F]+' + bin_prefix = '0[bB]' + bin_digits = '[01]+' + + # integer constants (K&R2: A.2.5.1) + integer_suffix_opt = r'(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?' + decimal_constant = '(0'+integer_suffix_opt+')|([1-9][0-9]*'+integer_suffix_opt+')' + octal_constant = '0[0-7]*'+integer_suffix_opt + hex_constant = hex_prefix+hex_digits+integer_suffix_opt + bin_constant = bin_prefix+bin_digits+integer_suffix_opt + + bad_octal_constant = '0[0-7]*[89]' + + # character constants (K&R2: A.2.5.2) + # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line + # directives with Windows paths as filenames (..\..\dir\file) + # For the same reason, decimal_escape allows all digit sequences. We want to + # parse all correct code, even if it means to sometimes parse incorrect + # code. + # + # The original regexes were taken verbatim from the C syntax definition, + # and were later modified to avoid worst-case exponential running time. + # + # simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])""" + # decimal_escape = r"""(\d+)""" + # hex_escape = r"""(x[0-9a-fA-F]+)""" + # bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])""" + # + # The following modifications were made to avoid the ambiguity that allowed backtracking: + # (https://github.com/eliben/pycparser/issues/61) + # + # - \x was removed from simple_escape, unless it was not followed by a hex digit, to avoid ambiguity with hex_escape. + # - hex_escape allows one or more hex characters, but requires that the next character(if any) is not hex + # - decimal_escape allows one or more decimal characters, but requires that the next character(if any) is not a decimal + # - bad_escape does not allow any decimals (8-9), to avoid conflicting with the permissive decimal_escape. + # + # Without this change, python's `re` module would recursively try parsing each ambiguous escape sequence in multiple ways. + # e.g. `\123` could be parsed as `\1`+`23`, `\12`+`3`, and `\123`. + + simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))""" + decimal_escape = r"""(\d+)(?!\d)""" + hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])""" + bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])""" + + escape_sequence = r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))' + + # This complicated regex with lookahead might be slow for strings, so because all of the valid escapes (including \x) allowed + # 0 or more non-escaped characters after the first character, simple_escape+decimal_escape+hex_escape got simplified to + + escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])""" + + cconst_char = r"""([^'\\\n]|"""+escape_sequence+')' + char_const = "'"+cconst_char+"'" + wchar_const = 'L'+char_const + u8char_const = 'u8'+char_const + u16char_const = 'u'+char_const + u32char_const = 'U'+char_const + multicharacter_constant = "'"+cconst_char+"{2,4}'" + unmatched_quote = "('"+cconst_char+"*\\n)|('"+cconst_char+"*$)" + bad_char_const = r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+bad_escape+r"""[^'\n]*')""" + + # string literals (K&R2: A.2.6) + string_char = r"""([^"\\\n]|"""+escape_sequence_start_in_string+')' + string_literal = '"'+string_char+'*"' + wstring_literal = 'L'+string_literal + u8string_literal = 'u8'+string_literal + u16string_literal = 'u'+string_literal + u32string_literal = 'U'+string_literal + bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"' + + # floating constants (K&R2: A.2.5.3) + exponent_part = r"""([eE][-+]?[0-9]+)""" + fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)""" + floating_constant = '(((('+fractional_constant+')'+exponent_part+'?)|([0-9]+'+exponent_part+'))[FfLl]?)' + binary_exponent_part = r'''([pP][+-]?[0-9]+)''' + hex_fractional_constant = '((('+hex_digits+r""")?\."""+hex_digits+')|('+hex_digits+r"""\.))""" + hex_floating_constant = '('+hex_prefix+'('+hex_digits+'|'+hex_fractional_constant+')'+binary_exponent_part+'[FfLl]?)' + + ## + ## Lexer states: used for preprocessor \n-terminated directives + ## + states = ( + # ppline: preprocessor line directives + # + ('ppline', 'exclusive'), + + # pppragma: pragma + # + ('pppragma', 'exclusive'), + ) + + def t_PPHASH(self, t): + r'[ \t]*\#' + if self.line_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos): + t.lexer.begin('ppline') + self.pp_line = self.pp_filename = None + elif self.pragma_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos): + t.lexer.begin('pppragma') + else: + t.type = 'PPHASH' + return t + + ## + ## Rules for the ppline state + ## + @TOKEN(string_literal) + def t_ppline_FILENAME(self, t): + if self.pp_line is None: + self._error('filename before line number in #line', t) + else: + self.pp_filename = t.value.lstrip('"').rstrip('"') + + @TOKEN(decimal_constant) + def t_ppline_LINE_NUMBER(self, t): + if self.pp_line is None: + self.pp_line = t.value + else: + # Ignore: GCC's cpp sometimes inserts a numeric flag + # after the file name + pass + + def t_ppline_NEWLINE(self, t): + r'\n' + if self.pp_line is None: + self._error('line number missing in #line', t) + else: + self.lexer.lineno = int(self.pp_line) + + if self.pp_filename is not None: + self.filename = self.pp_filename + + t.lexer.begin('INITIAL') + + def t_ppline_PPLINE(self, t): + r'line' + pass + + t_ppline_ignore = ' \t' + + def t_ppline_error(self, t): + self._error('invalid #line directive', t) + + ## + ## Rules for the pppragma state + ## + def t_pppragma_NEWLINE(self, t): + r'\n' + t.lexer.lineno += 1 + t.lexer.begin('INITIAL') + + def t_pppragma_PPPRAGMA(self, t): + r'pragma' + return t + + t_pppragma_ignore = ' \t' + + def t_pppragma_STR(self, t): + '.+' + t.type = 'PPPRAGMASTR' + return t + + def t_pppragma_error(self, t): + self._error('invalid #pragma directive', t) + + ## + ## Rules for the normal state + ## + t_ignore = ' \t' + + # Newlines + def t_NEWLINE(self, t): + r'\n+' + t.lexer.lineno += t.value.count("\n") + + # Operators + t_PLUS = r'\+' + t_MINUS = r'-' + t_TIMES = r'\*' + t_DIVIDE = r'/' + t_MOD = r'%' + t_OR = r'\|' + t_AND = r'&' + t_NOT = r'~' + t_XOR = r'\^' + t_LSHIFT = r'<<' + t_RSHIFT = r'>>' + t_LOR = r'\|\|' + t_LAND = r'&&' + t_LNOT = r'!' + t_LT = r'<' + t_GT = r'>' + t_LE = r'<=' + t_GE = r'>=' + t_EQ = r'==' + t_NE = r'!=' + + # Assignment operators + t_EQUALS = r'=' + t_TIMESEQUAL = r'\*=' + t_DIVEQUAL = r'/=' + t_MODEQUAL = r'%=' + t_PLUSEQUAL = r'\+=' + t_MINUSEQUAL = r'-=' + t_LSHIFTEQUAL = r'<<=' + t_RSHIFTEQUAL = r'>>=' + t_ANDEQUAL = r'&=' + t_OREQUAL = r'\|=' + t_XOREQUAL = r'\^=' + + # Increment/decrement + t_PLUSPLUS = r'\+\+' + t_MINUSMINUS = r'--' + + # -> + t_ARROW = r'->' + + # ? + t_CONDOP = r'\?' + + # Delimiters + t_LPAREN = r'\(' + t_RPAREN = r'\)' + t_LBRACKET = r'\[' + t_RBRACKET = r'\]' + t_COMMA = r',' + t_PERIOD = r'\.' + t_SEMI = r';' + t_COLON = r':' + t_ELLIPSIS = r'\.\.\.' + + # Scope delimiters + # To see why on_lbrace_func is needed, consider: + # typedef char TT; + # void foo(int TT) { TT = 10; } + # TT x = 5; + # Outside the function, TT is a typedef, but inside (starting and ending + # with the braces) it's a parameter. The trouble begins with yacc's + # lookahead token. If we open a new scope in brace_open, then TT has + # already been read and incorrectly interpreted as TYPEID. So, we need + # to open and close scopes from within the lexer. + # Similar for the TT immediately outside the end of the function. + # + @TOKEN(r'\{') + def t_LBRACE(self, t): + self.on_lbrace_func() + return t + @TOKEN(r'\}') + def t_RBRACE(self, t): + self.on_rbrace_func() + return t + + t_STRING_LITERAL = string_literal + + # The following floating and integer constants are defined as + # functions to impose a strict order (otherwise, decimal + # is placed before the others because its regex is longer, + # and this is bad) + # + @TOKEN(floating_constant) + def t_FLOAT_CONST(self, t): + return t + + @TOKEN(hex_floating_constant) + def t_HEX_FLOAT_CONST(self, t): + return t + + @TOKEN(hex_constant) + def t_INT_CONST_HEX(self, t): + return t + + @TOKEN(bin_constant) + def t_INT_CONST_BIN(self, t): + return t + + @TOKEN(bad_octal_constant) + def t_BAD_CONST_OCT(self, t): + msg = "Invalid octal constant" + self._error(msg, t) + + @TOKEN(octal_constant) + def t_INT_CONST_OCT(self, t): + return t + + @TOKEN(decimal_constant) + def t_INT_CONST_DEC(self, t): + return t + + # Must come before bad_char_const, to prevent it from + # catching valid char constants as invalid + # + @TOKEN(multicharacter_constant) + def t_INT_CONST_CHAR(self, t): + return t + + @TOKEN(char_const) + def t_CHAR_CONST(self, t): + return t + + @TOKEN(wchar_const) + def t_WCHAR_CONST(self, t): + return t + + @TOKEN(u8char_const) + def t_U8CHAR_CONST(self, t): + return t + + @TOKEN(u16char_const) + def t_U16CHAR_CONST(self, t): + return t + + @TOKEN(u32char_const) + def t_U32CHAR_CONST(self, t): + return t + + @TOKEN(unmatched_quote) + def t_UNMATCHED_QUOTE(self, t): + msg = "Unmatched '" + self._error(msg, t) + + @TOKEN(bad_char_const) + def t_BAD_CHAR_CONST(self, t): + msg = "Invalid char constant %s" % t.value + self._error(msg, t) + + @TOKEN(wstring_literal) + def t_WSTRING_LITERAL(self, t): + return t + + @TOKEN(u8string_literal) + def t_U8STRING_LITERAL(self, t): + return t + + @TOKEN(u16string_literal) + def t_U16STRING_LITERAL(self, t): + return t + + @TOKEN(u32string_literal) + def t_U32STRING_LITERAL(self, t): + return t + + # unmatched string literals are caught by the preprocessor + + @TOKEN(bad_string_literal) + def t_BAD_STRING_LITERAL(self, t): + msg = "String contains invalid escape code" + self._error(msg, t) + + @TOKEN(identifier) + def t_ID(self, t): + t.type = self.keyword_map.get(t.value, "ID") + if t.type == 'ID' and self.type_lookup_func(t.value): + t.type = "TYPEID" + return t + + def t_error(self, t): + msg = 'Illegal character %s' % repr(t.value[0]) + self._error(msg, t) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py new file mode 100644 index 000000000..640a75940 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py @@ -0,0 +1,1936 @@ +#------------------------------------------------------------------------------ +# pycparser: c_parser.py +# +# CParser class: Parser and AST builder for the C language +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#------------------------------------------------------------------------------ +from .ply import yacc + +from . import c_ast +from .c_lexer import CLexer +from .plyparser import PLYParser, ParseError, parameterized, template +from .ast_transforms import fix_switch_cases, fix_atomic_specifiers + + +@template +class CParser(PLYParser): + def __init__( + self, + lex_optimize=True, + lexer=CLexer, + lextab='pycparser.lextab', + yacc_optimize=True, + yacctab='pycparser.yacctab', + yacc_debug=False, + taboutputdir=''): + """ Create a new CParser. + + Some arguments for controlling the debug/optimization + level of the parser are provided. The defaults are + tuned for release/performance mode. + The simple rules for using them are: + *) When tweaking CParser/CLexer, set these to False + *) When releasing a stable parser, set to True + + lex_optimize: + Set to False when you're modifying the lexer. + Otherwise, changes in the lexer won't be used, if + some lextab.py file exists. + When releasing with a stable lexer, set to True + to save the re-generation of the lexer table on + each run. + + lexer: + Set this parameter to define the lexer to use if + you're not using the default CLexer. + + lextab: + Points to the lex table that's used for optimized + mode. Only if you're modifying the lexer and want + some tests to avoid re-generating the table, make + this point to a local lex table file (that's been + earlier generated with lex_optimize=True) + + yacc_optimize: + Set to False when you're modifying the parser. + Otherwise, changes in the parser won't be used, if + some parsetab.py file exists. + When releasing with a stable parser, set to True + to save the re-generation of the parser table on + each run. + + yacctab: + Points to the yacc table that's used for optimized + mode. Only if you're modifying the parser, make + this point to a local yacc table file + + yacc_debug: + Generate a parser.out file that explains how yacc + built the parsing table from the grammar. + + taboutputdir: + Set this parameter to control the location of generated + lextab and yacctab files. + """ + self.clex = lexer( + error_func=self._lex_error_func, + on_lbrace_func=self._lex_on_lbrace_func, + on_rbrace_func=self._lex_on_rbrace_func, + type_lookup_func=self._lex_type_lookup_func) + + self.clex.build( + optimize=lex_optimize, + lextab=lextab, + outputdir=taboutputdir) + self.tokens = self.clex.tokens + + rules_with_opt = [ + 'abstract_declarator', + 'assignment_expression', + 'declaration_list', + 'declaration_specifiers_no_type', + 'designation', + 'expression', + 'identifier_list', + 'init_declarator_list', + 'id_init_declarator_list', + 'initializer_list', + 'parameter_type_list', + 'block_item_list', + 'type_qualifier_list', + 'struct_declarator_list' + ] + + for rule in rules_with_opt: + self._create_opt_rule(rule) + + self.cparser = yacc.yacc( + module=self, + start='translation_unit_or_empty', + debug=yacc_debug, + optimize=yacc_optimize, + tabmodule=yacctab, + outputdir=taboutputdir) + + # Stack of scopes for keeping track of symbols. _scope_stack[-1] is + # the current (topmost) scope. Each scope is a dictionary that + # specifies whether a name is a type. If _scope_stack[n][name] is + # True, 'name' is currently a type in the scope. If it's False, + # 'name' is used in the scope but not as a type (for instance, if we + # saw: int name; + # If 'name' is not a key in _scope_stack[n] then 'name' was not defined + # in this scope at all. + self._scope_stack = [dict()] + + # Keeps track of the last token given to yacc (the lookahead token) + self._last_yielded_token = None + + def parse(self, text, filename='', debug=False): + """ Parses C code and returns an AST. + + text: + A string containing the C source code + + filename: + Name of the file being parsed (for meaningful + error messages) + + debug: + Debug flag to YACC + """ + self.clex.filename = filename + self.clex.reset_lineno() + self._scope_stack = [dict()] + self._last_yielded_token = None + return self.cparser.parse( + input=text, + lexer=self.clex, + debug=debug) + + ######################-- PRIVATE --###################### + + def _push_scope(self): + self._scope_stack.append(dict()) + + def _pop_scope(self): + assert len(self._scope_stack) > 1 + self._scope_stack.pop() + + def _add_typedef_name(self, name, coord): + """ Add a new typedef name (ie a TYPEID) to the current scope + """ + if not self._scope_stack[-1].get(name, True): + self._parse_error( + "Typedef %r previously declared as non-typedef " + "in this scope" % name, coord) + self._scope_stack[-1][name] = True + + def _add_identifier(self, name, coord): + """ Add a new object, function, or enum member name (ie an ID) to the + current scope + """ + if self._scope_stack[-1].get(name, False): + self._parse_error( + "Non-typedef %r previously declared as typedef " + "in this scope" % name, coord) + self._scope_stack[-1][name] = False + + def _is_type_in_scope(self, name): + """ Is *name* a typedef-name in the current scope? + """ + for scope in reversed(self._scope_stack): + # If name is an identifier in this scope it shadows typedefs in + # higher scopes. + in_scope = scope.get(name) + if in_scope is not None: return in_scope + return False + + def _lex_error_func(self, msg, line, column): + self._parse_error(msg, self._coord(line, column)) + + def _lex_on_lbrace_func(self): + self._push_scope() + + def _lex_on_rbrace_func(self): + self._pop_scope() + + def _lex_type_lookup_func(self, name): + """ Looks up types that were previously defined with + typedef. + Passed to the lexer for recognizing identifiers that + are types. + """ + is_type = self._is_type_in_scope(name) + return is_type + + def _get_yacc_lookahead_token(self): + """ We need access to yacc's lookahead token in certain cases. + This is the last token yacc requested from the lexer, so we + ask the lexer. + """ + return self.clex.last_token + + # To understand what's going on here, read sections A.8.5 and + # A.8.6 of K&R2 very carefully. + # + # A C type consists of a basic type declaration, with a list + # of modifiers. For example: + # + # int *c[5]; + # + # The basic declaration here is 'int c', and the pointer and + # the array are the modifiers. + # + # Basic declarations are represented by TypeDecl (from module c_ast) and the + # modifiers are FuncDecl, PtrDecl and ArrayDecl. + # + # The standard states that whenever a new modifier is parsed, it should be + # added to the end of the list of modifiers. For example: + # + # K&R2 A.8.6.2: Array Declarators + # + # In a declaration T D where D has the form + # D1 [constant-expression-opt] + # and the type of the identifier in the declaration T D1 is + # "type-modifier T", the type of the + # identifier of D is "type-modifier array of T" + # + # This is what this method does. The declarator it receives + # can be a list of declarators ending with TypeDecl. It + # tacks the modifier to the end of this list, just before + # the TypeDecl. + # + # Additionally, the modifier may be a list itself. This is + # useful for pointers, that can come as a chain from the rule + # p_pointer. In this case, the whole modifier list is spliced + # into the new location. + def _type_modify_decl(self, decl, modifier): + """ Tacks a type modifier on a declarator, and returns + the modified declarator. + + Note: the declarator and modifier may be modified + """ + #~ print '****' + #~ decl.show(offset=3) + #~ modifier.show(offset=3) + #~ print '****' + + modifier_head = modifier + modifier_tail = modifier + + # The modifier may be a nested list. Reach its tail. + while modifier_tail.type: + modifier_tail = modifier_tail.type + + # If the decl is a basic type, just tack the modifier onto it. + if isinstance(decl, c_ast.TypeDecl): + modifier_tail.type = decl + return modifier + else: + # Otherwise, the decl is a list of modifiers. Reach + # its tail and splice the modifier onto the tail, + # pointing to the underlying basic type. + decl_tail = decl + + while not isinstance(decl_tail.type, c_ast.TypeDecl): + decl_tail = decl_tail.type + + modifier_tail.type = decl_tail.type + decl_tail.type = modifier_head + return decl + + # Due to the order in which declarators are constructed, + # they have to be fixed in order to look like a normal AST. + # + # When a declaration arrives from syntax construction, it has + # these problems: + # * The innermost TypeDecl has no type (because the basic + # type is only known at the uppermost declaration level) + # * The declaration has no variable name, since that is saved + # in the innermost TypeDecl + # * The typename of the declaration is a list of type + # specifiers, and not a node. Here, basic identifier types + # should be separated from more complex types like enums + # and structs. + # + # This method fixes these problems. + def _fix_decl_name_type(self, decl, typename): + """ Fixes a declaration. Modifies decl. + """ + # Reach the underlying basic type + # + type = decl + while not isinstance(type, c_ast.TypeDecl): + type = type.type + + decl.name = type.declname + type.quals = decl.quals[:] + + # The typename is a list of types. If any type in this + # list isn't an IdentifierType, it must be the only + # type in the list (it's illegal to declare "int enum ..") + # If all the types are basic, they're collected in the + # IdentifierType holder. + for tn in typename: + if not isinstance(tn, c_ast.IdentifierType): + if len(typename) > 1: + self._parse_error( + "Invalid multiple types specified", tn.coord) + else: + type.type = tn + return decl + + if not typename: + # Functions default to returning int + # + if not isinstance(decl.type, c_ast.FuncDecl): + self._parse_error( + "Missing type in declaration", decl.coord) + type.type = c_ast.IdentifierType( + ['int'], + coord=decl.coord) + else: + # At this point, we know that typename is a list of IdentifierType + # nodes. Concatenate all the names into a single list. + # + type.type = c_ast.IdentifierType( + [name for id in typename for name in id.names], + coord=typename[0].coord) + return decl + + def _add_declaration_specifier(self, declspec, newspec, kind, append=False): + """ Declaration specifiers are represented by a dictionary + with the entries: + * qual: a list of type qualifiers + * storage: a list of storage type qualifiers + * type: a list of type specifiers + * function: a list of function specifiers + * alignment: a list of alignment specifiers + + This method is given a declaration specifier, and a + new specifier of a given kind. + If `append` is True, the new specifier is added to the end of + the specifiers list, otherwise it's added at the beginning. + Returns the declaration specifier, with the new + specifier incorporated. + """ + spec = declspec or dict(qual=[], storage=[], type=[], function=[], alignment=[]) + + if append: + spec[kind].append(newspec) + else: + spec[kind].insert(0, newspec) + + return spec + + def _build_declarations(self, spec, decls, typedef_namespace=False): + """ Builds a list of declarations all sharing the given specifiers. + If typedef_namespace is true, each declared name is added + to the "typedef namespace", which also includes objects, + functions, and enum constants. + """ + is_typedef = 'typedef' in spec['storage'] + declarations = [] + + # Bit-fields are allowed to be unnamed. + if decls[0].get('bitsize') is not None: + pass + + # When redeclaring typedef names as identifiers in inner scopes, a + # problem can occur where the identifier gets grouped into + # spec['type'], leaving decl as None. This can only occur for the + # first declarator. + elif decls[0]['decl'] is None: + if len(spec['type']) < 2 or len(spec['type'][-1].names) != 1 or \ + not self._is_type_in_scope(spec['type'][-1].names[0]): + coord = '?' + for t in spec['type']: + if hasattr(t, 'coord'): + coord = t.coord + break + self._parse_error('Invalid declaration', coord) + + # Make this look as if it came from "direct_declarator:ID" + decls[0]['decl'] = c_ast.TypeDecl( + declname=spec['type'][-1].names[0], + type=None, + quals=None, + align=spec['alignment'], + coord=spec['type'][-1].coord) + # Remove the "new" type's name from the end of spec['type'] + del spec['type'][-1] + + # A similar problem can occur where the declaration ends up looking + # like an abstract declarator. Give it a name if this is the case. + elif not isinstance(decls[0]['decl'], ( + c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType)): + decls_0_tail = decls[0]['decl'] + while not isinstance(decls_0_tail, c_ast.TypeDecl): + decls_0_tail = decls_0_tail.type + if decls_0_tail.declname is None: + decls_0_tail.declname = spec['type'][-1].names[0] + del spec['type'][-1] + + for decl in decls: + assert decl['decl'] is not None + if is_typedef: + declaration = c_ast.Typedef( + name=None, + quals=spec['qual'], + storage=spec['storage'], + type=decl['decl'], + coord=decl['decl'].coord) + else: + declaration = c_ast.Decl( + name=None, + quals=spec['qual'], + align=spec['alignment'], + storage=spec['storage'], + funcspec=spec['function'], + type=decl['decl'], + init=decl.get('init'), + bitsize=decl.get('bitsize'), + coord=decl['decl'].coord) + + if isinstance(declaration.type, ( + c_ast.Enum, c_ast.Struct, c_ast.Union, + c_ast.IdentifierType)): + fixed_decl = declaration + else: + fixed_decl = self._fix_decl_name_type(declaration, spec['type']) + + # Add the type name defined by typedef to a + # symbol table (for usage in the lexer) + if typedef_namespace: + if is_typedef: + self._add_typedef_name(fixed_decl.name, fixed_decl.coord) + else: + self._add_identifier(fixed_decl.name, fixed_decl.coord) + + fixed_decl = fix_atomic_specifiers(fixed_decl) + declarations.append(fixed_decl) + + return declarations + + def _build_function_definition(self, spec, decl, param_decls, body): + """ Builds a function definition. + """ + if 'typedef' in spec['storage']: + self._parse_error("Invalid typedef", decl.coord) + + declaration = self._build_declarations( + spec=spec, + decls=[dict(decl=decl, init=None)], + typedef_namespace=True)[0] + + return c_ast.FuncDef( + decl=declaration, + param_decls=param_decls, + body=body, + coord=decl.coord) + + def _select_struct_union_class(self, token): + """ Given a token (either STRUCT or UNION), selects the + appropriate AST class. + """ + if token == 'struct': + return c_ast.Struct + else: + return c_ast.Union + + ## + ## Precedence and associativity of operators + ## + # If this changes, c_generator.CGenerator.precedence_map needs to change as + # well + precedence = ( + ('left', 'LOR'), + ('left', 'LAND'), + ('left', 'OR'), + ('left', 'XOR'), + ('left', 'AND'), + ('left', 'EQ', 'NE'), + ('left', 'GT', 'GE', 'LT', 'LE'), + ('left', 'RSHIFT', 'LSHIFT'), + ('left', 'PLUS', 'MINUS'), + ('left', 'TIMES', 'DIVIDE', 'MOD') + ) + + ## + ## Grammar productions + ## Implementation of the BNF defined in K&R2 A.13 + ## + + # Wrapper around a translation unit, to allow for empty input. + # Not strictly part of the C99 Grammar, but useful in practice. + def p_translation_unit_or_empty(self, p): + """ translation_unit_or_empty : translation_unit + | empty + """ + if p[1] is None: + p[0] = c_ast.FileAST([]) + else: + p[0] = c_ast.FileAST(p[1]) + + def p_translation_unit_1(self, p): + """ translation_unit : external_declaration + """ + # Note: external_declaration is already a list + p[0] = p[1] + + def p_translation_unit_2(self, p): + """ translation_unit : translation_unit external_declaration + """ + p[1].extend(p[2]) + p[0] = p[1] + + # Declarations always come as lists (because they can be + # several in one line), so we wrap the function definition + # into a list as well, to make the return value of + # external_declaration homogeneous. + def p_external_declaration_1(self, p): + """ external_declaration : function_definition + """ + p[0] = [p[1]] + + def p_external_declaration_2(self, p): + """ external_declaration : declaration + """ + p[0] = p[1] + + def p_external_declaration_3(self, p): + """ external_declaration : pp_directive + | pppragma_directive + """ + p[0] = [p[1]] + + def p_external_declaration_4(self, p): + """ external_declaration : SEMI + """ + p[0] = [] + + def p_external_declaration_5(self, p): + """ external_declaration : static_assert + """ + p[0] = p[1] + + def p_static_assert_declaration(self, p): + """ static_assert : _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN + | _STATIC_ASSERT LPAREN constant_expression RPAREN + """ + if len(p) == 5: + p[0] = [c_ast.StaticAssert(p[3], None, self._token_coord(p, 1))] + else: + p[0] = [c_ast.StaticAssert(p[3], p[5], self._token_coord(p, 1))] + + def p_pp_directive(self, p): + """ pp_directive : PPHASH + """ + self._parse_error('Directives not supported yet', + self._token_coord(p, 1)) + + def p_pppragma_directive(self, p): + """ pppragma_directive : PPPRAGMA + | PPPRAGMA PPPRAGMASTR + """ + if len(p) == 3: + p[0] = c_ast.Pragma(p[2], self._token_coord(p, 2)) + else: + p[0] = c_ast.Pragma("", self._token_coord(p, 1)) + + # In function definitions, the declarator can be followed by + # a declaration list, for old "K&R style" function definitios. + def p_function_definition_1(self, p): + """ function_definition : id_declarator declaration_list_opt compound_statement + """ + # no declaration specifiers - 'int' becomes the default type + spec = dict( + qual=[], + alignment=[], + storage=[], + type=[c_ast.IdentifierType(['int'], + coord=self._token_coord(p, 1))], + function=[]) + + p[0] = self._build_function_definition( + spec=spec, + decl=p[1], + param_decls=p[2], + body=p[3]) + + def p_function_definition_2(self, p): + """ function_definition : declaration_specifiers id_declarator declaration_list_opt compound_statement + """ + spec = p[1] + + p[0] = self._build_function_definition( + spec=spec, + decl=p[2], + param_decls=p[3], + body=p[4]) + + # Note, according to C18 A.2.2 6.7.10 static_assert-declaration _Static_assert + # is a declaration, not a statement. We additionally recognise it as a statement + # to fix parsing of _Static_assert inside the functions. + # + def p_statement(self, p): + """ statement : labeled_statement + | expression_statement + | compound_statement + | selection_statement + | iteration_statement + | jump_statement + | pppragma_directive + | static_assert + """ + p[0] = p[1] + + # A pragma is generally considered a decorator rather than an actual + # statement. Still, for the purposes of analyzing an abstract syntax tree of + # C code, pragma's should not be ignored and were previously treated as a + # statement. This presents a problem for constructs that take a statement + # such as labeled_statements, selection_statements, and + # iteration_statements, causing a misleading structure in the AST. For + # example, consider the following C code. + # + # for (int i = 0; i < 3; i++) + # #pragma omp critical + # sum += 1; + # + # This code will compile and execute "sum += 1;" as the body of the for + # loop. Previous implementations of PyCParser would render the AST for this + # block of code as follows: + # + # For: + # DeclList: + # Decl: i, [], [], [] + # TypeDecl: i, [] + # IdentifierType: ['int'] + # Constant: int, 0 + # BinaryOp: < + # ID: i + # Constant: int, 3 + # UnaryOp: p++ + # ID: i + # Pragma: omp critical + # Assignment: += + # ID: sum + # Constant: int, 1 + # + # This AST misleadingly takes the Pragma as the body of the loop and the + # assignment then becomes a sibling of the loop. + # + # To solve edge cases like these, the pragmacomp_or_statement rule groups + # a pragma and its following statement (which would otherwise be orphaned) + # using a compound block, effectively turning the above code into: + # + # for (int i = 0; i < 3; i++) { + # #pragma omp critical + # sum += 1; + # } + def p_pragmacomp_or_statement(self, p): + """ pragmacomp_or_statement : pppragma_directive statement + | statement + """ + if isinstance(p[1], c_ast.Pragma) and len(p) == 3: + p[0] = c_ast.Compound( + block_items=[p[1], p[2]], + coord=self._token_coord(p, 1)) + else: + p[0] = p[1] + + # In C, declarations can come several in a line: + # int x, *px, romulo = 5; + # + # However, for the AST, we will split them to separate Decl + # nodes. + # + # This rule splits its declarations and always returns a list + # of Decl nodes, even if it's one element long. + # + def p_decl_body(self, p): + """ decl_body : declaration_specifiers init_declarator_list_opt + | declaration_specifiers_no_type id_init_declarator_list_opt + """ + spec = p[1] + + # p[2] (init_declarator_list_opt) is either a list or None + # + if p[2] is None: + # By the standard, you must have at least one declarator unless + # declaring a structure tag, a union tag, or the members of an + # enumeration. + # + ty = spec['type'] + s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum) + if len(ty) == 1 and isinstance(ty[0], s_u_or_e): + decls = [c_ast.Decl( + name=None, + quals=spec['qual'], + align=spec['alignment'], + storage=spec['storage'], + funcspec=spec['function'], + type=ty[0], + init=None, + bitsize=None, + coord=ty[0].coord)] + + # However, this case can also occur on redeclared identifiers in + # an inner scope. The trouble is that the redeclared type's name + # gets grouped into declaration_specifiers; _build_declarations + # compensates for this. + # + else: + decls = self._build_declarations( + spec=spec, + decls=[dict(decl=None, init=None)], + typedef_namespace=True) + + else: + decls = self._build_declarations( + spec=spec, + decls=p[2], + typedef_namespace=True) + + p[0] = decls + + # The declaration has been split to a decl_body sub-rule and + # SEMI, because having them in a single rule created a problem + # for defining typedefs. + # + # If a typedef line was directly followed by a line using the + # type defined with the typedef, the type would not be + # recognized. This is because to reduce the declaration rule, + # the parser's lookahead asked for the token after SEMI, which + # was the type from the next line, and the lexer had no chance + # to see the updated type symbol table. + # + # Splitting solves this problem, because after seeing SEMI, + # the parser reduces decl_body, which actually adds the new + # type into the table to be seen by the lexer before the next + # line is reached. + def p_declaration(self, p): + """ declaration : decl_body SEMI + """ + p[0] = p[1] + + # Since each declaration is a list of declarations, this + # rule will combine all the declarations and return a single + # list + # + def p_declaration_list(self, p): + """ declaration_list : declaration + | declaration_list declaration + """ + p[0] = p[1] if len(p) == 2 else p[1] + p[2] + + # To know when declaration-specifiers end and declarators begin, + # we require declaration-specifiers to have at least one + # type-specifier, and disallow typedef-names after we've seen any + # type-specifier. These are both required by the spec. + # + def p_declaration_specifiers_no_type_1(self, p): + """ declaration_specifiers_no_type : type_qualifier declaration_specifiers_no_type_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'qual') + + def p_declaration_specifiers_no_type_2(self, p): + """ declaration_specifiers_no_type : storage_class_specifier declaration_specifiers_no_type_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'storage') + + def p_declaration_specifiers_no_type_3(self, p): + """ declaration_specifiers_no_type : function_specifier declaration_specifiers_no_type_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'function') + + # Without this, `typedef _Atomic(T) U` will parse incorrectly because the + # _Atomic qualifier will match, instead of the specifier. + def p_declaration_specifiers_no_type_4(self, p): + """ declaration_specifiers_no_type : atomic_specifier declaration_specifiers_no_type_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'type') + + def p_declaration_specifiers_no_type_5(self, p): + """ declaration_specifiers_no_type : alignment_specifier declaration_specifiers_no_type_opt + """ + p[0] = self._add_declaration_specifier(p[2], p[1], 'alignment') + + def p_declaration_specifiers_1(self, p): + """ declaration_specifiers : declaration_specifiers type_qualifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'qual', append=True) + + def p_declaration_specifiers_2(self, p): + """ declaration_specifiers : declaration_specifiers storage_class_specifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'storage', append=True) + + def p_declaration_specifiers_3(self, p): + """ declaration_specifiers : declaration_specifiers function_specifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'function', append=True) + + def p_declaration_specifiers_4(self, p): + """ declaration_specifiers : declaration_specifiers type_specifier_no_typeid + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True) + + def p_declaration_specifiers_5(self, p): + """ declaration_specifiers : type_specifier + """ + p[0] = self._add_declaration_specifier(None, p[1], 'type') + + def p_declaration_specifiers_6(self, p): + """ declaration_specifiers : declaration_specifiers_no_type type_specifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True) + + def p_declaration_specifiers_7(self, p): + """ declaration_specifiers : declaration_specifiers alignment_specifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'alignment', append=True) + + def p_storage_class_specifier(self, p): + """ storage_class_specifier : AUTO + | REGISTER + | STATIC + | EXTERN + | TYPEDEF + | _THREAD_LOCAL + """ + p[0] = p[1] + + def p_function_specifier(self, p): + """ function_specifier : INLINE + | _NORETURN + """ + p[0] = p[1] + + def p_type_specifier_no_typeid(self, p): + """ type_specifier_no_typeid : VOID + | _BOOL + | CHAR + | SHORT + | INT + | LONG + | FLOAT + | DOUBLE + | _COMPLEX + | SIGNED + | UNSIGNED + | __INT128 + """ + p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1)) + + def p_type_specifier(self, p): + """ type_specifier : typedef_name + | enum_specifier + | struct_or_union_specifier + | type_specifier_no_typeid + | atomic_specifier + """ + p[0] = p[1] + + # See section 6.7.2.4 of the C11 standard. + def p_atomic_specifier(self, p): + """ atomic_specifier : _ATOMIC LPAREN type_name RPAREN + """ + typ = p[3] + typ.quals.append('_Atomic') + p[0] = typ + + def p_type_qualifier(self, p): + """ type_qualifier : CONST + | RESTRICT + | VOLATILE + | _ATOMIC + """ + p[0] = p[1] + + def p_init_declarator_list(self, p): + """ init_declarator_list : init_declarator + | init_declarator_list COMMA init_declarator + """ + p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] + + # Returns a {decl= : init=} dictionary + # If there's no initializer, uses None + # + def p_init_declarator(self, p): + """ init_declarator : declarator + | declarator EQUALS initializer + """ + p[0] = dict(decl=p[1], init=(p[3] if len(p) > 2 else None)) + + def p_id_init_declarator_list(self, p): + """ id_init_declarator_list : id_init_declarator + | id_init_declarator_list COMMA init_declarator + """ + p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] + + def p_id_init_declarator(self, p): + """ id_init_declarator : id_declarator + | id_declarator EQUALS initializer + """ + p[0] = dict(decl=p[1], init=(p[3] if len(p) > 2 else None)) + + # Require at least one type specifier in a specifier-qualifier-list + # + def p_specifier_qualifier_list_1(self, p): + """ specifier_qualifier_list : specifier_qualifier_list type_specifier_no_typeid + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True) + + def p_specifier_qualifier_list_2(self, p): + """ specifier_qualifier_list : specifier_qualifier_list type_qualifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'qual', append=True) + + def p_specifier_qualifier_list_3(self, p): + """ specifier_qualifier_list : type_specifier + """ + p[0] = self._add_declaration_specifier(None, p[1], 'type') + + def p_specifier_qualifier_list_4(self, p): + """ specifier_qualifier_list : type_qualifier_list type_specifier + """ + p[0] = dict(qual=p[1], alignment=[], storage=[], type=[p[2]], function=[]) + + def p_specifier_qualifier_list_5(self, p): + """ specifier_qualifier_list : alignment_specifier + """ + p[0] = dict(qual=[], alignment=[p[1]], storage=[], type=[], function=[]) + + def p_specifier_qualifier_list_6(self, p): + """ specifier_qualifier_list : specifier_qualifier_list alignment_specifier + """ + p[0] = self._add_declaration_specifier(p[1], p[2], 'alignment') + + # TYPEID is allowed here (and in other struct/enum related tag names), because + # struct/enum tags reside in their own namespace and can be named the same as types + # + def p_struct_or_union_specifier_1(self, p): + """ struct_or_union_specifier : struct_or_union ID + | struct_or_union TYPEID + """ + klass = self._select_struct_union_class(p[1]) + # None means no list of members + p[0] = klass( + name=p[2], + decls=None, + coord=self._token_coord(p, 2)) + + def p_struct_or_union_specifier_2(self, p): + """ struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close + | struct_or_union brace_open brace_close + """ + klass = self._select_struct_union_class(p[1]) + if len(p) == 4: + # Empty sequence means an empty list of members + p[0] = klass( + name=None, + decls=[], + coord=self._token_coord(p, 2)) + else: + p[0] = klass( + name=None, + decls=p[3], + coord=self._token_coord(p, 2)) + + + def p_struct_or_union_specifier_3(self, p): + """ struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close + | struct_or_union ID brace_open brace_close + | struct_or_union TYPEID brace_open struct_declaration_list brace_close + | struct_or_union TYPEID brace_open brace_close + """ + klass = self._select_struct_union_class(p[1]) + if len(p) == 5: + # Empty sequence means an empty list of members + p[0] = klass( + name=p[2], + decls=[], + coord=self._token_coord(p, 2)) + else: + p[0] = klass( + name=p[2], + decls=p[4], + coord=self._token_coord(p, 2)) + + def p_struct_or_union(self, p): + """ struct_or_union : STRUCT + | UNION + """ + p[0] = p[1] + + # Combine all declarations into a single list + # + def p_struct_declaration_list(self, p): + """ struct_declaration_list : struct_declaration + | struct_declaration_list struct_declaration + """ + if len(p) == 2: + p[0] = p[1] or [] + else: + p[0] = p[1] + (p[2] or []) + + def p_struct_declaration_1(self, p): + """ struct_declaration : specifier_qualifier_list struct_declarator_list_opt SEMI + """ + spec = p[1] + assert 'typedef' not in spec['storage'] + + if p[2] is not None: + decls = self._build_declarations( + spec=spec, + decls=p[2]) + + elif len(spec['type']) == 1: + # Anonymous struct/union, gcc extension, C1x feature. + # Although the standard only allows structs/unions here, I see no + # reason to disallow other types since some compilers have typedefs + # here, and pycparser isn't about rejecting all invalid code. + # + node = spec['type'][0] + if isinstance(node, c_ast.Node): + decl_type = node + else: + decl_type = c_ast.IdentifierType(node) + + decls = self._build_declarations( + spec=spec, + decls=[dict(decl=decl_type)]) + + else: + # Structure/union members can have the same names as typedefs. + # The trouble is that the member's name gets grouped into + # specifier_qualifier_list; _build_declarations compensates. + # + decls = self._build_declarations( + spec=spec, + decls=[dict(decl=None, init=None)]) + + p[0] = decls + + def p_struct_declaration_2(self, p): + """ struct_declaration : SEMI + """ + p[0] = None + + def p_struct_declaration_3(self, p): + """ struct_declaration : pppragma_directive + """ + p[0] = [p[1]] + + def p_struct_declarator_list(self, p): + """ struct_declarator_list : struct_declarator + | struct_declarator_list COMMA struct_declarator + """ + p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] + + # struct_declarator passes up a dict with the keys: decl (for + # the underlying declarator) and bitsize (for the bitsize) + # + def p_struct_declarator_1(self, p): + """ struct_declarator : declarator + """ + p[0] = {'decl': p[1], 'bitsize': None} + + def p_struct_declarator_2(self, p): + """ struct_declarator : declarator COLON constant_expression + | COLON constant_expression + """ + if len(p) > 3: + p[0] = {'decl': p[1], 'bitsize': p[3]} + else: + p[0] = {'decl': c_ast.TypeDecl(None, None, None, None), 'bitsize': p[2]} + + def p_enum_specifier_1(self, p): + """ enum_specifier : ENUM ID + | ENUM TYPEID + """ + p[0] = c_ast.Enum(p[2], None, self._token_coord(p, 1)) + + def p_enum_specifier_2(self, p): + """ enum_specifier : ENUM brace_open enumerator_list brace_close + """ + p[0] = c_ast.Enum(None, p[3], self._token_coord(p, 1)) + + def p_enum_specifier_3(self, p): + """ enum_specifier : ENUM ID brace_open enumerator_list brace_close + | ENUM TYPEID brace_open enumerator_list brace_close + """ + p[0] = c_ast.Enum(p[2], p[4], self._token_coord(p, 1)) + + def p_enumerator_list(self, p): + """ enumerator_list : enumerator + | enumerator_list COMMA + | enumerator_list COMMA enumerator + """ + if len(p) == 2: + p[0] = c_ast.EnumeratorList([p[1]], p[1].coord) + elif len(p) == 3: + p[0] = p[1] + else: + p[1].enumerators.append(p[3]) + p[0] = p[1] + + def p_alignment_specifier(self, p): + """ alignment_specifier : _ALIGNAS LPAREN type_name RPAREN + | _ALIGNAS LPAREN constant_expression RPAREN + """ + p[0] = c_ast.Alignas(p[3], self._token_coord(p, 1)) + + def p_enumerator(self, p): + """ enumerator : ID + | ID EQUALS constant_expression + """ + if len(p) == 2: + enumerator = c_ast.Enumerator( + p[1], None, + self._token_coord(p, 1)) + else: + enumerator = c_ast.Enumerator( + p[1], p[3], + self._token_coord(p, 1)) + self._add_identifier(enumerator.name, enumerator.coord) + + p[0] = enumerator + + def p_declarator(self, p): + """ declarator : id_declarator + | typeid_declarator + """ + p[0] = p[1] + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_xxx_declarator_1(self, p): + """ xxx_declarator : direct_xxx_declarator + """ + p[0] = p[1] + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_xxx_declarator_2(self, p): + """ xxx_declarator : pointer direct_xxx_declarator + """ + p[0] = self._type_modify_decl(p[2], p[1]) + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_direct_xxx_declarator_1(self, p): + """ direct_xxx_declarator : yyy + """ + p[0] = c_ast.TypeDecl( + declname=p[1], + type=None, + quals=None, + align=None, + coord=self._token_coord(p, 1)) + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID')) + def p_direct_xxx_declarator_2(self, p): + """ direct_xxx_declarator : LPAREN xxx_declarator RPAREN + """ + p[0] = p[2] + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_direct_xxx_declarator_3(self, p): + """ direct_xxx_declarator : direct_xxx_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET + """ + quals = (p[3] if len(p) > 5 else []) or [] + # Accept dimension qualifiers + # Per C99 6.7.5.3 p7 + arr = c_ast.ArrayDecl( + type=None, + dim=p[4] if len(p) > 5 else p[3], + dim_quals=quals, + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_direct_xxx_declarator_4(self, p): + """ direct_xxx_declarator : direct_xxx_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET + | direct_xxx_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET + """ + # Using slice notation for PLY objects doesn't work in Python 3 for the + # version of PLY embedded with pycparser; see PLY Google Code issue 30. + # Work around that here by listing the two elements separately. + listed_quals = [item if isinstance(item, list) else [item] + for item in [p[3],p[4]]] + dim_quals = [qual for sublist in listed_quals for qual in sublist + if qual is not None] + arr = c_ast.ArrayDecl( + type=None, + dim=p[5], + dim_quals=dim_quals, + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + # Special for VLAs + # + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_direct_xxx_declarator_5(self, p): + """ direct_xxx_declarator : direct_xxx_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET + """ + arr = c_ast.ArrayDecl( + type=None, + dim=c_ast.ID(p[4], self._token_coord(p, 4)), + dim_quals=p[3] if p[3] is not None else [], + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) + def p_direct_xxx_declarator_6(self, p): + """ direct_xxx_declarator : direct_xxx_declarator LPAREN parameter_type_list RPAREN + | direct_xxx_declarator LPAREN identifier_list_opt RPAREN + """ + func = c_ast.FuncDecl( + args=p[3], + type=None, + coord=p[1].coord) + + # To see why _get_yacc_lookahead_token is needed, consider: + # typedef char TT; + # void foo(int TT) { TT = 10; } + # Outside the function, TT is a typedef, but inside (starting and + # ending with the braces) it's a parameter. The trouble begins with + # yacc's lookahead token. We don't know if we're declaring or + # defining a function until we see LBRACE, but if we wait for yacc to + # trigger a rule on that token, then TT will have already been read + # and incorrectly interpreted as TYPEID. We need to add the + # parameters to the scope the moment the lexer sees LBRACE. + # + if self._get_yacc_lookahead_token().type == "LBRACE": + if func.args is not None: + for param in func.args.params: + if isinstance(param, c_ast.EllipsisParam): break + self._add_identifier(param.name, param.coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=func) + + def p_pointer(self, p): + """ pointer : TIMES type_qualifier_list_opt + | TIMES type_qualifier_list_opt pointer + """ + coord = self._token_coord(p, 1) + # Pointer decls nest from inside out. This is important when different + # levels have different qualifiers. For example: + # + # char * const * p; + # + # Means "pointer to const pointer to char" + # + # While: + # + # char ** const p; + # + # Means "const pointer to pointer to char" + # + # So when we construct PtrDecl nestings, the leftmost pointer goes in + # as the most nested type. + nested_type = c_ast.PtrDecl(quals=p[2] or [], type=None, coord=coord) + if len(p) > 3: + tail_type = p[3] + while tail_type.type is not None: + tail_type = tail_type.type + tail_type.type = nested_type + p[0] = p[3] + else: + p[0] = nested_type + + def p_type_qualifier_list(self, p): + """ type_qualifier_list : type_qualifier + | type_qualifier_list type_qualifier + """ + p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]] + + def p_parameter_type_list(self, p): + """ parameter_type_list : parameter_list + | parameter_list COMMA ELLIPSIS + """ + if len(p) > 2: + p[1].params.append(c_ast.EllipsisParam(self._token_coord(p, 3))) + + p[0] = p[1] + + def p_parameter_list(self, p): + """ parameter_list : parameter_declaration + | parameter_list COMMA parameter_declaration + """ + if len(p) == 2: # single parameter + p[0] = c_ast.ParamList([p[1]], p[1].coord) + else: + p[1].params.append(p[3]) + p[0] = p[1] + + # From ISO/IEC 9899:TC2, 6.7.5.3.11: + # "If, in a parameter declaration, an identifier can be treated either + # as a typedef name or as a parameter name, it shall be taken as a + # typedef name." + # + # Inside a parameter declaration, once we've reduced declaration specifiers, + # if we shift in an LPAREN and see a TYPEID, it could be either an abstract + # declarator or a declarator nested inside parens. This rule tells us to + # always treat it as an abstract declarator. Therefore, we only accept + # `id_declarator`s and `typeid_noparen_declarator`s. + def p_parameter_declaration_1(self, p): + """ parameter_declaration : declaration_specifiers id_declarator + | declaration_specifiers typeid_noparen_declarator + """ + spec = p[1] + if not spec['type']: + spec['type'] = [c_ast.IdentifierType(['int'], + coord=self._token_coord(p, 1))] + p[0] = self._build_declarations( + spec=spec, + decls=[dict(decl=p[2])])[0] + + def p_parameter_declaration_2(self, p): + """ parameter_declaration : declaration_specifiers abstract_declarator_opt + """ + spec = p[1] + if not spec['type']: + spec['type'] = [c_ast.IdentifierType(['int'], + coord=self._token_coord(p, 1))] + + # Parameters can have the same names as typedefs. The trouble is that + # the parameter's name gets grouped into declaration_specifiers, making + # it look like an old-style declaration; compensate. + # + if len(spec['type']) > 1 and len(spec['type'][-1].names) == 1 and \ + self._is_type_in_scope(spec['type'][-1].names[0]): + decl = self._build_declarations( + spec=spec, + decls=[dict(decl=p[2], init=None)])[0] + + # This truly is an old-style parameter declaration + # + else: + decl = c_ast.Typename( + name='', + quals=spec['qual'], + align=None, + type=p[2] or c_ast.TypeDecl(None, None, None, None), + coord=self._token_coord(p, 2)) + typename = spec['type'] + decl = self._fix_decl_name_type(decl, typename) + + p[0] = decl + + def p_identifier_list(self, p): + """ identifier_list : identifier + | identifier_list COMMA identifier + """ + if len(p) == 2: # single parameter + p[0] = c_ast.ParamList([p[1]], p[1].coord) + else: + p[1].params.append(p[3]) + p[0] = p[1] + + def p_initializer_1(self, p): + """ initializer : assignment_expression + """ + p[0] = p[1] + + def p_initializer_2(self, p): + """ initializer : brace_open initializer_list_opt brace_close + | brace_open initializer_list COMMA brace_close + """ + if p[2] is None: + p[0] = c_ast.InitList([], self._token_coord(p, 1)) + else: + p[0] = p[2] + + def p_initializer_list(self, p): + """ initializer_list : designation_opt initializer + | initializer_list COMMA designation_opt initializer + """ + if len(p) == 3: # single initializer + init = p[2] if p[1] is None else c_ast.NamedInitializer(p[1], p[2]) + p[0] = c_ast.InitList([init], p[2].coord) + else: + init = p[4] if p[3] is None else c_ast.NamedInitializer(p[3], p[4]) + p[1].exprs.append(init) + p[0] = p[1] + + def p_designation(self, p): + """ designation : designator_list EQUALS + """ + p[0] = p[1] + + # Designators are represented as a list of nodes, in the order in which + # they're written in the code. + # + def p_designator_list(self, p): + """ designator_list : designator + | designator_list designator + """ + p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]] + + def p_designator(self, p): + """ designator : LBRACKET constant_expression RBRACKET + | PERIOD identifier + """ + p[0] = p[2] + + def p_type_name(self, p): + """ type_name : specifier_qualifier_list abstract_declarator_opt + """ + typename = c_ast.Typename( + name='', + quals=p[1]['qual'][:], + align=None, + type=p[2] or c_ast.TypeDecl(None, None, None, None), + coord=self._token_coord(p, 2)) + + p[0] = self._fix_decl_name_type(typename, p[1]['type']) + + def p_abstract_declarator_1(self, p): + """ abstract_declarator : pointer + """ + dummytype = c_ast.TypeDecl(None, None, None, None) + p[0] = self._type_modify_decl( + decl=dummytype, + modifier=p[1]) + + def p_abstract_declarator_2(self, p): + """ abstract_declarator : pointer direct_abstract_declarator + """ + p[0] = self._type_modify_decl(p[2], p[1]) + + def p_abstract_declarator_3(self, p): + """ abstract_declarator : direct_abstract_declarator + """ + p[0] = p[1] + + # Creating and using direct_abstract_declarator_opt here + # instead of listing both direct_abstract_declarator and the + # lack of it in the beginning of _1 and _2 caused two + # shift/reduce errors. + # + def p_direct_abstract_declarator_1(self, p): + """ direct_abstract_declarator : LPAREN abstract_declarator RPAREN """ + p[0] = p[2] + + def p_direct_abstract_declarator_2(self, p): + """ direct_abstract_declarator : direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET + """ + arr = c_ast.ArrayDecl( + type=None, + dim=p[3], + dim_quals=[], + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + def p_direct_abstract_declarator_3(self, p): + """ direct_abstract_declarator : LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET + """ + quals = (p[2] if len(p) > 4 else []) or [] + p[0] = c_ast.ArrayDecl( + type=c_ast.TypeDecl(None, None, None, None), + dim=p[3] if len(p) > 4 else p[2], + dim_quals=quals, + coord=self._token_coord(p, 1)) + + def p_direct_abstract_declarator_4(self, p): + """ direct_abstract_declarator : direct_abstract_declarator LBRACKET TIMES RBRACKET + """ + arr = c_ast.ArrayDecl( + type=None, + dim=c_ast.ID(p[3], self._token_coord(p, 3)), + dim_quals=[], + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=arr) + + def p_direct_abstract_declarator_5(self, p): + """ direct_abstract_declarator : LBRACKET TIMES RBRACKET + """ + p[0] = c_ast.ArrayDecl( + type=c_ast.TypeDecl(None, None, None, None), + dim=c_ast.ID(p[3], self._token_coord(p, 3)), + dim_quals=[], + coord=self._token_coord(p, 1)) + + def p_direct_abstract_declarator_6(self, p): + """ direct_abstract_declarator : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN + """ + func = c_ast.FuncDecl( + args=p[3], + type=None, + coord=p[1].coord) + + p[0] = self._type_modify_decl(decl=p[1], modifier=func) + + def p_direct_abstract_declarator_7(self, p): + """ direct_abstract_declarator : LPAREN parameter_type_list_opt RPAREN + """ + p[0] = c_ast.FuncDecl( + args=p[2], + type=c_ast.TypeDecl(None, None, None, None), + coord=self._token_coord(p, 1)) + + # declaration is a list, statement isn't. To make it consistent, block_item + # will always be a list + # + def p_block_item(self, p): + """ block_item : declaration + | statement + """ + p[0] = p[1] if isinstance(p[1], list) else [p[1]] + + # Since we made block_item a list, this just combines lists + # + def p_block_item_list(self, p): + """ block_item_list : block_item + | block_item_list block_item + """ + # Empty block items (plain ';') produce [None], so ignore them + p[0] = p[1] if (len(p) == 2 or p[2] == [None]) else p[1] + p[2] + + def p_compound_statement_1(self, p): + """ compound_statement : brace_open block_item_list_opt brace_close """ + p[0] = c_ast.Compound( + block_items=p[2], + coord=self._token_coord(p, 1)) + + def p_labeled_statement_1(self, p): + """ labeled_statement : ID COLON pragmacomp_or_statement """ + p[0] = c_ast.Label(p[1], p[3], self._token_coord(p, 1)) + + def p_labeled_statement_2(self, p): + """ labeled_statement : CASE constant_expression COLON pragmacomp_or_statement """ + p[0] = c_ast.Case(p[2], [p[4]], self._token_coord(p, 1)) + + def p_labeled_statement_3(self, p): + """ labeled_statement : DEFAULT COLON pragmacomp_or_statement """ + p[0] = c_ast.Default([p[3]], self._token_coord(p, 1)) + + def p_selection_statement_1(self, p): + """ selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement """ + p[0] = c_ast.If(p[3], p[5], None, self._token_coord(p, 1)) + + def p_selection_statement_2(self, p): + """ selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement """ + p[0] = c_ast.If(p[3], p[5], p[7], self._token_coord(p, 1)) + + def p_selection_statement_3(self, p): + """ selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement """ + p[0] = fix_switch_cases( + c_ast.Switch(p[3], p[5], self._token_coord(p, 1))) + + def p_iteration_statement_1(self, p): + """ iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement """ + p[0] = c_ast.While(p[3], p[5], self._token_coord(p, 1)) + + def p_iteration_statement_2(self, p): + """ iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI """ + p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1)) + + def p_iteration_statement_3(self, p): + """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ + p[0] = c_ast.For(p[3], p[5], p[7], p[9], self._token_coord(p, 1)) + + def p_iteration_statement_4(self, p): + """ iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ + p[0] = c_ast.For(c_ast.DeclList(p[3], self._token_coord(p, 1)), + p[4], p[6], p[8], self._token_coord(p, 1)) + + def p_jump_statement_1(self, p): + """ jump_statement : GOTO ID SEMI """ + p[0] = c_ast.Goto(p[2], self._token_coord(p, 1)) + + def p_jump_statement_2(self, p): + """ jump_statement : BREAK SEMI """ + p[0] = c_ast.Break(self._token_coord(p, 1)) + + def p_jump_statement_3(self, p): + """ jump_statement : CONTINUE SEMI """ + p[0] = c_ast.Continue(self._token_coord(p, 1)) + + def p_jump_statement_4(self, p): + """ jump_statement : RETURN expression SEMI + | RETURN SEMI + """ + p[0] = c_ast.Return(p[2] if len(p) == 4 else None, self._token_coord(p, 1)) + + def p_expression_statement(self, p): + """ expression_statement : expression_opt SEMI """ + if p[1] is None: + p[0] = c_ast.EmptyStatement(self._token_coord(p, 2)) + else: + p[0] = p[1] + + def p_expression(self, p): + """ expression : assignment_expression + | expression COMMA assignment_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + if not isinstance(p[1], c_ast.ExprList): + p[1] = c_ast.ExprList([p[1]], p[1].coord) + + p[1].exprs.append(p[3]) + p[0] = p[1] + + def p_parenthesized_compound_expression(self, p): + """ assignment_expression : LPAREN compound_statement RPAREN """ + p[0] = p[2] + + def p_typedef_name(self, p): + """ typedef_name : TYPEID """ + p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1)) + + def p_assignment_expression(self, p): + """ assignment_expression : conditional_expression + | unary_expression assignment_operator assignment_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = c_ast.Assignment(p[2], p[1], p[3], p[1].coord) + + # K&R2 defines these as many separate rules, to encode + # precedence and associativity. Why work hard ? I'll just use + # the built in precedence/associativity specification feature + # of PLY. (see precedence declaration above) + # + def p_assignment_operator(self, p): + """ assignment_operator : EQUALS + | XOREQUAL + | TIMESEQUAL + | DIVEQUAL + | MODEQUAL + | PLUSEQUAL + | MINUSEQUAL + | LSHIFTEQUAL + | RSHIFTEQUAL + | ANDEQUAL + | OREQUAL + """ + p[0] = p[1] + + def p_constant_expression(self, p): + """ constant_expression : conditional_expression """ + p[0] = p[1] + + def p_conditional_expression(self, p): + """ conditional_expression : binary_expression + | binary_expression CONDOP expression COLON conditional_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = c_ast.TernaryOp(p[1], p[3], p[5], p[1].coord) + + def p_binary_expression(self, p): + """ binary_expression : cast_expression + | binary_expression TIMES binary_expression + | binary_expression DIVIDE binary_expression + | binary_expression MOD binary_expression + | binary_expression PLUS binary_expression + | binary_expression MINUS binary_expression + | binary_expression RSHIFT binary_expression + | binary_expression LSHIFT binary_expression + | binary_expression LT binary_expression + | binary_expression LE binary_expression + | binary_expression GE binary_expression + | binary_expression GT binary_expression + | binary_expression EQ binary_expression + | binary_expression NE binary_expression + | binary_expression AND binary_expression + | binary_expression OR binary_expression + | binary_expression XOR binary_expression + | binary_expression LAND binary_expression + | binary_expression LOR binary_expression + """ + if len(p) == 2: + p[0] = p[1] + else: + p[0] = c_ast.BinaryOp(p[2], p[1], p[3], p[1].coord) + + def p_cast_expression_1(self, p): + """ cast_expression : unary_expression """ + p[0] = p[1] + + def p_cast_expression_2(self, p): + """ cast_expression : LPAREN type_name RPAREN cast_expression """ + p[0] = c_ast.Cast(p[2], p[4], self._token_coord(p, 1)) + + def p_unary_expression_1(self, p): + """ unary_expression : postfix_expression """ + p[0] = p[1] + + def p_unary_expression_2(self, p): + """ unary_expression : PLUSPLUS unary_expression + | MINUSMINUS unary_expression + | unary_operator cast_expression + """ + p[0] = c_ast.UnaryOp(p[1], p[2], p[2].coord) + + def p_unary_expression_3(self, p): + """ unary_expression : SIZEOF unary_expression + | SIZEOF LPAREN type_name RPAREN + | _ALIGNOF LPAREN type_name RPAREN + """ + p[0] = c_ast.UnaryOp( + p[1], + p[2] if len(p) == 3 else p[3], + self._token_coord(p, 1)) + + def p_unary_operator(self, p): + """ unary_operator : AND + | TIMES + | PLUS + | MINUS + | NOT + | LNOT + """ + p[0] = p[1] + + def p_postfix_expression_1(self, p): + """ postfix_expression : primary_expression """ + p[0] = p[1] + + def p_postfix_expression_2(self, p): + """ postfix_expression : postfix_expression LBRACKET expression RBRACKET """ + p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord) + + def p_postfix_expression_3(self, p): + """ postfix_expression : postfix_expression LPAREN argument_expression_list RPAREN + | postfix_expression LPAREN RPAREN + """ + p[0] = c_ast.FuncCall(p[1], p[3] if len(p) == 5 else None, p[1].coord) + + def p_postfix_expression_4(self, p): + """ postfix_expression : postfix_expression PERIOD ID + | postfix_expression PERIOD TYPEID + | postfix_expression ARROW ID + | postfix_expression ARROW TYPEID + """ + field = c_ast.ID(p[3], self._token_coord(p, 3)) + p[0] = c_ast.StructRef(p[1], p[2], field, p[1].coord) + + def p_postfix_expression_5(self, p): + """ postfix_expression : postfix_expression PLUSPLUS + | postfix_expression MINUSMINUS + """ + p[0] = c_ast.UnaryOp('p' + p[2], p[1], p[1].coord) + + def p_postfix_expression_6(self, p): + """ postfix_expression : LPAREN type_name RPAREN brace_open initializer_list brace_close + | LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close + """ + p[0] = c_ast.CompoundLiteral(p[2], p[5]) + + def p_primary_expression_1(self, p): + """ primary_expression : identifier """ + p[0] = p[1] + + def p_primary_expression_2(self, p): + """ primary_expression : constant """ + p[0] = p[1] + + def p_primary_expression_3(self, p): + """ primary_expression : unified_string_literal + | unified_wstring_literal + """ + p[0] = p[1] + + def p_primary_expression_4(self, p): + """ primary_expression : LPAREN expression RPAREN """ + p[0] = p[2] + + def p_primary_expression_5(self, p): + """ primary_expression : OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN + """ + coord = self._token_coord(p, 1) + p[0] = c_ast.FuncCall(c_ast.ID(p[1], coord), + c_ast.ExprList([p[3], p[5]], coord), + coord) + + def p_offsetof_member_designator(self, p): + """ offsetof_member_designator : identifier + | offsetof_member_designator PERIOD identifier + | offsetof_member_designator LBRACKET expression RBRACKET + """ + if len(p) == 2: + p[0] = p[1] + elif len(p) == 4: + p[0] = c_ast.StructRef(p[1], p[2], p[3], p[1].coord) + elif len(p) == 5: + p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord) + else: + raise NotImplementedError("Unexpected parsing state. len(p): %u" % len(p)) + + def p_argument_expression_list(self, p): + """ argument_expression_list : assignment_expression + | argument_expression_list COMMA assignment_expression + """ + if len(p) == 2: # single expr + p[0] = c_ast.ExprList([p[1]], p[1].coord) + else: + p[1].exprs.append(p[3]) + p[0] = p[1] + + def p_identifier(self, p): + """ identifier : ID """ + p[0] = c_ast.ID(p[1], self._token_coord(p, 1)) + + def p_constant_1(self, p): + """ constant : INT_CONST_DEC + | INT_CONST_OCT + | INT_CONST_HEX + | INT_CONST_BIN + | INT_CONST_CHAR + """ + uCount = 0 + lCount = 0 + for x in p[1][-3:]: + if x in ('l', 'L'): + lCount += 1 + elif x in ('u', 'U'): + uCount += 1 + t = '' + if uCount > 1: + raise ValueError('Constant cannot have more than one u/U suffix.') + elif lCount > 2: + raise ValueError('Constant cannot have more than two l/L suffix.') + prefix = 'unsigned ' * uCount + 'long ' * lCount + p[0] = c_ast.Constant( + prefix + 'int', p[1], self._token_coord(p, 1)) + + def p_constant_2(self, p): + """ constant : FLOAT_CONST + | HEX_FLOAT_CONST + """ + if 'x' in p[1].lower(): + t = 'float' + else: + if p[1][-1] in ('f', 'F'): + t = 'float' + elif p[1][-1] in ('l', 'L'): + t = 'long double' + else: + t = 'double' + + p[0] = c_ast.Constant( + t, p[1], self._token_coord(p, 1)) + + def p_constant_3(self, p): + """ constant : CHAR_CONST + | WCHAR_CONST + | U8CHAR_CONST + | U16CHAR_CONST + | U32CHAR_CONST + """ + p[0] = c_ast.Constant( + 'char', p[1], self._token_coord(p, 1)) + + # The "unified" string and wstring literal rules are for supporting + # concatenation of adjacent string literals. + # I.e. "hello " "world" is seen by the C compiler as a single string literal + # with the value "hello world" + # + def p_unified_string_literal(self, p): + """ unified_string_literal : STRING_LITERAL + | unified_string_literal STRING_LITERAL + """ + if len(p) == 2: # single literal + p[0] = c_ast.Constant( + 'string', p[1], self._token_coord(p, 1)) + else: + p[1].value = p[1].value[:-1] + p[2][1:] + p[0] = p[1] + + def p_unified_wstring_literal(self, p): + """ unified_wstring_literal : WSTRING_LITERAL + | U8STRING_LITERAL + | U16STRING_LITERAL + | U32STRING_LITERAL + | unified_wstring_literal WSTRING_LITERAL + | unified_wstring_literal U8STRING_LITERAL + | unified_wstring_literal U16STRING_LITERAL + | unified_wstring_literal U32STRING_LITERAL + """ + if len(p) == 2: # single literal + p[0] = c_ast.Constant( + 'string', p[1], self._token_coord(p, 1)) + else: + p[1].value = p[1].value.rstrip()[:-1] + p[2][2:] + p[0] = p[1] + + def p_brace_open(self, p): + """ brace_open : LBRACE + """ + p[0] = p[1] + p.set_lineno(0, p.lineno(1)) + + def p_brace_close(self, p): + """ brace_close : RBRACE + """ + p[0] = p[1] + p.set_lineno(0, p.lineno(1)) + + def p_empty(self, p): + 'empty : ' + p[0] = None + + def p_error(self, p): + # If error recovery is added here in the future, make sure + # _get_yacc_lookahead_token still works! + # + if p: + self._parse_error( + 'before: %s' % p.value, + self._coord(lineno=p.lineno, + column=self.clex.find_tok_column(p))) + else: + self._parse_error('At end of input', self.clex.filename) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py new file mode 100644 index 000000000..444b4656d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py @@ -0,0 +1,10 @@ +# lextab.py. This file automatically created by PLY (version 3.10). Don't edit! +_tabversion = '3.10' +_lextokens = set(('INT_CONST_CHAR', 'VOID', 'LBRACKET', 'WCHAR_CONST', 'FLOAT_CONST', 'MINUS', 'RPAREN', 'STRUCT', 'LONG', 'PLUS', 'ELLIPSIS', 'U32STRING_LITERAL', 'GT', 'GOTO', 'ENUM', 'PERIOD', 'GE', 'INT_CONST_DEC', 'ARROW', '_STATIC_ASSERT', '__INT128', 'HEX_FLOAT_CONST', 'DOUBLE', 'MINUSEQUAL', 'INT_CONST_OCT', 'TIMESEQUAL', 'OR', 'SHORT', 'RETURN', 'RSHIFTEQUAL', '_ALIGNAS', 'RESTRICT', 'STATIC', 'SIZEOF', 'UNSIGNED', 'PLUSPLUS', 'COLON', 'WSTRING_LITERAL', 'DIVIDE', 'FOR', 'UNION', 'EQUALS', 'ELSE', 'ANDEQUAL', 'EQ', 'AND', 'TYPEID', 'LBRACE', 'PPHASH', 'INT', 'SIGNED', 'CONTINUE', 'NOT', 'OREQUAL', 'MOD', 'RSHIFT', 'DEFAULT', '_NORETURN', 'CHAR', 'WHILE', 'DIVEQUAL', '_ALIGNOF', 'EXTERN', 'LNOT', 'CASE', 'LAND', 'REGISTER', 'MODEQUAL', 'NE', 'SWITCH', 'INT_CONST_HEX', '_COMPLEX', 'PPPRAGMASTR', 'PLUSEQUAL', 'U32CHAR_CONST', 'CONDOP', 'U8STRING_LITERAL', 'BREAK', 'VOLATILE', 'PPPRAGMA', 'INLINE', 'INT_CONST_BIN', 'DO', 'U8CHAR_CONST', 'CONST', 'U16STRING_LITERAL', 'LOR', 'CHAR_CONST', 'LSHIFT', 'RBRACE', '_BOOL', 'LE', 'SEMI', '_THREAD_LOCAL', 'LT', 'COMMA', 'U16CHAR_CONST', 'OFFSETOF', '_ATOMIC', 'TYPEDEF', 'XOR', 'AUTO', 'TIMES', 'LPAREN', 'MINUSMINUS', 'ID', 'IF', 'STRING_LITERAL', 'FLOAT', 'XOREQUAL', 'LSHIFTEQUAL', 'RBRACKET')) +_lexreflags = 64 +_lexliterals = '' +_lexstateinfo = {'ppline': 'exclusive', 'pppragma': 'exclusive', 'INITIAL': 'inclusive'} +_lexstatere = {'ppline': [('(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P(0(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|([1-9][0-9]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?))|(?P\\n)|(?Pline)', [None, ('t_ppline_FILENAME', 'FILENAME'), None, None, ('t_ppline_LINE_NUMBER', 'LINE_NUMBER'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_ppline_NEWLINE', 'NEWLINE'), ('t_ppline_PPLINE', 'PPLINE')])], 'pppragma': [('(?P\\n)|(?Ppragma)|(?P.+)', [None, ('t_pppragma_NEWLINE', 'NEWLINE'), ('t_pppragma_PPPRAGMA', 'PPPRAGMA'), ('t_pppragma_STR', 'STR')])], 'INITIAL': [('(?P[ \\t]*\\#)|(?P\\n+)|(?P\\{)|(?P\\})|(?P((((([0-9]*\\.[0-9]+)|([0-9]+\\.))([eE][-+]?[0-9]+)?)|([0-9]+([eE][-+]?[0-9]+)))[FfLl]?))|(?P(0[xX]([0-9a-fA-F]+|((([0-9a-fA-F]+)?\\.[0-9a-fA-F]+)|([0-9a-fA-F]+\\.)))([pP][+-]?[0-9]+)[FfLl]?))|(?P0[xX][0-9a-fA-F]+(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P0[bB][01]+(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)', [None, ('t_PPHASH', 'PPHASH'), ('t_NEWLINE', 'NEWLINE'), ('t_LBRACE', 'LBRACE'), ('t_RBRACE', 'RBRACE'), ('t_FLOAT_CONST', 'FLOAT_CONST'), None, None, None, None, None, None, None, None, None, ('t_HEX_FLOAT_CONST', 'HEX_FLOAT_CONST'), None, None, None, None, None, None, None, ('t_INT_CONST_HEX', 'INT_CONST_HEX'), None, None, None, None, None, None, None, ('t_INT_CONST_BIN', 'INT_CONST_BIN')]), ('(?P0[0-7]*[89])|(?P0[0-7]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P(0(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|([1-9][0-9]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?))|(?P\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F])))){2,4}\')|(?P\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?PL\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?Pu8\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?Pu\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?PU\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')', [None, ('t_BAD_CONST_OCT', 'BAD_CONST_OCT'), ('t_INT_CONST_OCT', 'INT_CONST_OCT'), None, None, None, None, None, None, None, ('t_INT_CONST_DEC', 'INT_CONST_DEC'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_INT_CONST_CHAR', 'INT_CONST_CHAR'), None, None, None, None, None, None, ('t_CHAR_CONST', 'CHAR_CONST'), None, None, None, None, None, None, ('t_WCHAR_CONST', 'WCHAR_CONST'), None, None, None, None, None, None, ('t_U8CHAR_CONST', 'U8CHAR_CONST'), None, None, None, None, None, None, ('t_U16CHAR_CONST', 'U16CHAR_CONST'), None, None, None, None, None, None, ('t_U32CHAR_CONST', 'U32CHAR_CONST')]), ('(?P(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))*\\n)|(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))*$))|(?P(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))[^\'\n]+\')|(\'\')|(\'([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?\'"x0-9])[^\'\\n]*\'))|(?PL"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?Pu8"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?Pu"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?PU"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?\'"x0-9])([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P[a-zA-Z_$][0-9a-zA-Z_$]*)|(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P\\.\\.\\.)|(?P\\+\\+)|(?P\\|\\|)|(?P\\^=)|(?P\\|=)|(?P<<=)|(?P>>=)|(?P\\+=)|(?P\\*=)', [None, ('t_UNMATCHED_QUOTE', 'UNMATCHED_QUOTE'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_BAD_CHAR_CONST', 'BAD_CHAR_CONST'), None, None, None, None, None, None, None, None, None, None, ('t_WSTRING_LITERAL', 'WSTRING_LITERAL'), None, None, ('t_U8STRING_LITERAL', 'U8STRING_LITERAL'), None, None, ('t_U16STRING_LITERAL', 'U16STRING_LITERAL'), None, None, ('t_U32STRING_LITERAL', 'U32STRING_LITERAL'), None, None, ('t_BAD_STRING_LITERAL', 'BAD_STRING_LITERAL'), None, None, None, None, None, ('t_ID', 'ID'), (None, 'STRING_LITERAL'), None, None, (None, 'ELLIPSIS'), (None, 'PLUSPLUS'), (None, 'LOR'), (None, 'XOREQUAL'), (None, 'OREQUAL'), (None, 'LSHIFTEQUAL'), (None, 'RSHIFTEQUAL'), (None, 'PLUSEQUAL'), (None, 'TIMESEQUAL')]), ('(?P\\+)|(?P%=)|(?P/=)|(?P\\])|(?P\\?)|(?P\\^)|(?P<<)|(?P<=)|(?P\\()|(?P->)|(?P==)|(?P!=)|(?P--)|(?P\\|)|(?P\\*)|(?P\\[)|(?P>=)|(?P\\))|(?P&&)|(?P>>)|(?P-=)|(?P\\.)|(?P&=)|(?P=)|(?P<)|(?P,)|(?P/)|(?P&)|(?P%)|(?P;)|(?P-)|(?P>)|(?P:)|(?P~)|(?P!)', [None, (None, 'PLUS'), (None, 'MODEQUAL'), (None, 'DIVEQUAL'), (None, 'RBRACKET'), (None, 'CONDOP'), (None, 'XOR'), (None, 'LSHIFT'), (None, 'LE'), (None, 'LPAREN'), (None, 'ARROW'), (None, 'EQ'), (None, 'NE'), (None, 'MINUSMINUS'), (None, 'OR'), (None, 'TIMES'), (None, 'LBRACKET'), (None, 'GE'), (None, 'RPAREN'), (None, 'LAND'), (None, 'RSHIFT'), (None, 'MINUSEQUAL'), (None, 'PERIOD'), (None, 'ANDEQUAL'), (None, 'EQUALS'), (None, 'LT'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'AND'), (None, 'MOD'), (None, 'SEMI'), (None, 'MINUS'), (None, 'GT'), (None, 'COLON'), (None, 'NOT'), (None, 'LNOT')])]} +_lexstateignore = {'ppline': ' \t', 'pppragma': ' \t', 'INITIAL': ' \t'} +_lexstateerrorf = {'ppline': 't_ppline_error', 'pppragma': 't_pppragma_error', 'INITIAL': 't_error'} +_lexstateeoff = {} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py new file mode 100644 index 000000000..6e53cddcf --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py @@ -0,0 +1,5 @@ +# PLY package +# Author: David Beazley (dave@dabeaz.com) + +__version__ = '3.9' +__all__ = ['lex','yacc'] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py new file mode 100644 index 000000000..86273eac7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py @@ -0,0 +1,905 @@ +# ----------------------------------------------------------------------------- +# cpp.py +# +# Author: David Beazley (http://www.dabeaz.com) +# Copyright (C) 2017 +# All rights reserved +# +# This module implements an ANSI-C style lexical preprocessor for PLY. +# ----------------------------------------------------------------------------- +import sys + +# Some Python 3 compatibility shims +if sys.version_info.major < 3: + STRING_TYPES = (str, unicode) +else: + STRING_TYPES = str + xrange = range + +# ----------------------------------------------------------------------------- +# Default preprocessor lexer definitions. These tokens are enough to get +# a basic preprocessor working. Other modules may import these if they want +# ----------------------------------------------------------------------------- + +tokens = ( + 'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND' +) + +literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\"" + +# Whitespace +def t_CPP_WS(t): + r'\s+' + t.lexer.lineno += t.value.count("\n") + return t + +t_CPP_POUND = r'\#' +t_CPP_DPOUND = r'\#\#' + +# Identifier +t_CPP_ID = r'[A-Za-z_][\w_]*' + +# Integer literal +def CPP_INTEGER(t): + r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)' + return t + +t_CPP_INTEGER = CPP_INTEGER + +# Floating literal +t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' + +# String literal +def t_CPP_STRING(t): + r'\"([^\\\n]|(\\(.|\n)))*?\"' + t.lexer.lineno += t.value.count("\n") + return t + +# Character constant 'c' or L'c' +def t_CPP_CHAR(t): + r'(L)?\'([^\\\n]|(\\(.|\n)))*?\'' + t.lexer.lineno += t.value.count("\n") + return t + +# Comment +def t_CPP_COMMENT1(t): + r'(/\*(.|\n)*?\*/)' + ncr = t.value.count("\n") + t.lexer.lineno += ncr + # replace with one space or a number of '\n' + t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' ' + return t + +# Line comment +def t_CPP_COMMENT2(t): + r'(//.*?(\n|$))' + # replace with '/n' + t.type = 'CPP_WS'; t.value = '\n' + return t + +def t_error(t): + t.type = t.value[0] + t.value = t.value[0] + t.lexer.skip(1) + return t + +import re +import copy +import time +import os.path + +# ----------------------------------------------------------------------------- +# trigraph() +# +# Given an input string, this function replaces all trigraph sequences. +# The following mapping is used: +# +# ??= # +# ??/ \ +# ??' ^ +# ??( [ +# ??) ] +# ??! | +# ??< { +# ??> } +# ??- ~ +# ----------------------------------------------------------------------------- + +_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''') +_trigraph_rep = { + '=':'#', + '/':'\\', + "'":'^', + '(':'[', + ')':']', + '!':'|', + '<':'{', + '>':'}', + '-':'~' +} + +def trigraph(input): + return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input) + +# ------------------------------------------------------------------ +# Macro object +# +# This object holds information about preprocessor macros +# +# .name - Macro name (string) +# .value - Macro value (a list of tokens) +# .arglist - List of argument names +# .variadic - Boolean indicating whether or not variadic macro +# .vararg - Name of the variadic parameter +# +# When a macro is created, the macro replacement token sequence is +# pre-scanned and used to create patch lists that are later used +# during macro expansion +# ------------------------------------------------------------------ + +class Macro(object): + def __init__(self,name,value,arglist=None,variadic=False): + self.name = name + self.value = value + self.arglist = arglist + self.variadic = variadic + if variadic: + self.vararg = arglist[-1] + self.source = None + +# ------------------------------------------------------------------ +# Preprocessor object +# +# Object representing a preprocessor. Contains macro definitions, +# include directories, and other information +# ------------------------------------------------------------------ + +class Preprocessor(object): + def __init__(self,lexer=None): + if lexer is None: + lexer = lex.lexer + self.lexer = lexer + self.macros = { } + self.path = [] + self.temp_path = [] + + # Probe the lexer for selected tokens + self.lexprobe() + + tm = time.localtime() + self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm)) + self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm)) + self.parser = None + + # ----------------------------------------------------------------------------- + # tokenize() + # + # Utility function. Given a string of text, tokenize into a list of tokens + # ----------------------------------------------------------------------------- + + def tokenize(self,text): + tokens = [] + self.lexer.input(text) + while True: + tok = self.lexer.token() + if not tok: break + tokens.append(tok) + return tokens + + # --------------------------------------------------------------------- + # error() + # + # Report a preprocessor error/warning of some kind + # ---------------------------------------------------------------------- + + def error(self,file,line,msg): + print("%s:%d %s" % (file,line,msg)) + + # ---------------------------------------------------------------------- + # lexprobe() + # + # This method probes the preprocessor lexer object to discover + # the token types of symbols that are important to the preprocessor. + # If this works right, the preprocessor will simply "work" + # with any suitable lexer regardless of how tokens have been named. + # ---------------------------------------------------------------------- + + def lexprobe(self): + + # Determine the token type for identifiers + self.lexer.input("identifier") + tok = self.lexer.token() + if not tok or tok.value != "identifier": + print("Couldn't determine identifier type") + else: + self.t_ID = tok.type + + # Determine the token type for integers + self.lexer.input("12345") + tok = self.lexer.token() + if not tok or int(tok.value) != 12345: + print("Couldn't determine integer type") + else: + self.t_INTEGER = tok.type + self.t_INTEGER_TYPE = type(tok.value) + + # Determine the token type for strings enclosed in double quotes + self.lexer.input("\"filename\"") + tok = self.lexer.token() + if not tok or tok.value != "\"filename\"": + print("Couldn't determine string type") + else: + self.t_STRING = tok.type + + # Determine the token type for whitespace--if any + self.lexer.input(" ") + tok = self.lexer.token() + if not tok or tok.value != " ": + self.t_SPACE = None + else: + self.t_SPACE = tok.type + + # Determine the token type for newlines + self.lexer.input("\n") + tok = self.lexer.token() + if not tok or tok.value != "\n": + self.t_NEWLINE = None + print("Couldn't determine token for newlines") + else: + self.t_NEWLINE = tok.type + + self.t_WS = (self.t_SPACE, self.t_NEWLINE) + + # Check for other characters used by the preprocessor + chars = [ '<','>','#','##','\\','(',')',',','.'] + for c in chars: + self.lexer.input(c) + tok = self.lexer.token() + if not tok or tok.value != c: + print("Unable to lex '%s' required for preprocessor" % c) + + # ---------------------------------------------------------------------- + # add_path() + # + # Adds a search path to the preprocessor. + # ---------------------------------------------------------------------- + + def add_path(self,path): + self.path.append(path) + + # ---------------------------------------------------------------------- + # group_lines() + # + # Given an input string, this function splits it into lines. Trailing whitespace + # is removed. Any line ending with \ is grouped with the next line. This + # function forms the lowest level of the preprocessor---grouping into text into + # a line-by-line format. + # ---------------------------------------------------------------------- + + def group_lines(self,input): + lex = self.lexer.clone() + lines = [x.rstrip() for x in input.splitlines()] + for i in xrange(len(lines)): + j = i+1 + while lines[i].endswith('\\') and (j < len(lines)): + lines[i] = lines[i][:-1]+lines[j] + lines[j] = "" + j += 1 + + input = "\n".join(lines) + lex.input(input) + lex.lineno = 1 + + current_line = [] + while True: + tok = lex.token() + if not tok: + break + current_line.append(tok) + if tok.type in self.t_WS and '\n' in tok.value: + yield current_line + current_line = [] + + if current_line: + yield current_line + + # ---------------------------------------------------------------------- + # tokenstrip() + # + # Remove leading/trailing whitespace tokens from a token list + # ---------------------------------------------------------------------- + + def tokenstrip(self,tokens): + i = 0 + while i < len(tokens) and tokens[i].type in self.t_WS: + i += 1 + del tokens[:i] + i = len(tokens)-1 + while i >= 0 and tokens[i].type in self.t_WS: + i -= 1 + del tokens[i+1:] + return tokens + + + # ---------------------------------------------------------------------- + # collect_args() + # + # Collects comma separated arguments from a list of tokens. The arguments + # must be enclosed in parenthesis. Returns a tuple (tokencount,args,positions) + # where tokencount is the number of tokens consumed, args is a list of arguments, + # and positions is a list of integers containing the starting index of each + # argument. Each argument is represented by a list of tokens. + # + # When collecting arguments, leading and trailing whitespace is removed + # from each argument. + # + # This function properly handles nested parenthesis and commas---these do not + # define new arguments. + # ---------------------------------------------------------------------- + + def collect_args(self,tokenlist): + args = [] + positions = [] + current_arg = [] + nesting = 1 + tokenlen = len(tokenlist) + + # Search for the opening '('. + i = 0 + while (i < tokenlen) and (tokenlist[i].type in self.t_WS): + i += 1 + + if (i < tokenlen) and (tokenlist[i].value == '('): + positions.append(i+1) + else: + self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments") + return 0, [], [] + + i += 1 + + while i < tokenlen: + t = tokenlist[i] + if t.value == '(': + current_arg.append(t) + nesting += 1 + elif t.value == ')': + nesting -= 1 + if nesting == 0: + if current_arg: + args.append(self.tokenstrip(current_arg)) + positions.append(i) + return i+1,args,positions + current_arg.append(t) + elif t.value == ',' and nesting == 1: + args.append(self.tokenstrip(current_arg)) + positions.append(i+1) + current_arg = [] + else: + current_arg.append(t) + i += 1 + + # Missing end argument + self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments") + return 0, [],[] + + # ---------------------------------------------------------------------- + # macro_prescan() + # + # Examine the macro value (token sequence) and identify patch points + # This is used to speed up macro expansion later on---we'll know + # right away where to apply patches to the value to form the expansion + # ---------------------------------------------------------------------- + + def macro_prescan(self,macro): + macro.patch = [] # Standard macro arguments + macro.str_patch = [] # String conversion expansion + macro.var_comma_patch = [] # Variadic macro comma patch + i = 0 + while i < len(macro.value): + if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist: + argnum = macro.arglist.index(macro.value[i].value) + # Conversion of argument to a string + if i > 0 and macro.value[i-1].value == '#': + macro.value[i] = copy.copy(macro.value[i]) + macro.value[i].type = self.t_STRING + del macro.value[i-1] + macro.str_patch.append((argnum,i-1)) + continue + # Concatenation + elif (i > 0 and macro.value[i-1].value == '##'): + macro.patch.append(('c',argnum,i-1)) + del macro.value[i-1] + continue + elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'): + macro.patch.append(('c',argnum,i)) + i += 1 + continue + # Standard expansion + else: + macro.patch.append(('e',argnum,i)) + elif macro.value[i].value == '##': + if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \ + ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \ + (macro.value[i+1].value == macro.vararg): + macro.var_comma_patch.append(i-1) + i += 1 + macro.patch.sort(key=lambda x: x[2],reverse=True) + + # ---------------------------------------------------------------------- + # macro_expand_args() + # + # Given a Macro and list of arguments (each a token list), this method + # returns an expanded version of a macro. The return value is a token sequence + # representing the replacement macro tokens + # ---------------------------------------------------------------------- + + def macro_expand_args(self,macro,args): + # Make a copy of the macro token sequence + rep = [copy.copy(_x) for _x in macro.value] + + # Make string expansion patches. These do not alter the length of the replacement sequence + + str_expansion = {} + for argnum, i in macro.str_patch: + if argnum not in str_expansion: + str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\") + rep[i] = copy.copy(rep[i]) + rep[i].value = str_expansion[argnum] + + # Make the variadic macro comma patch. If the variadic macro argument is empty, we get rid + comma_patch = False + if macro.variadic and not args[-1]: + for i in macro.var_comma_patch: + rep[i] = None + comma_patch = True + + # Make all other patches. The order of these matters. It is assumed that the patch list + # has been sorted in reverse order of patch location since replacements will cause the + # size of the replacement sequence to expand from the patch point. + + expanded = { } + for ptype, argnum, i in macro.patch: + # Concatenation. Argument is left unexpanded + if ptype == 'c': + rep[i:i+1] = args[argnum] + # Normal expansion. Argument is macro expanded first + elif ptype == 'e': + if argnum not in expanded: + expanded[argnum] = self.expand_macros(args[argnum]) + rep[i:i+1] = expanded[argnum] + + # Get rid of removed comma if necessary + if comma_patch: + rep = [_i for _i in rep if _i] + + return rep + + + # ---------------------------------------------------------------------- + # expand_macros() + # + # Given a list of tokens, this function performs macro expansion. + # The expanded argument is a dictionary that contains macros already + # expanded. This is used to prevent infinite recursion. + # ---------------------------------------------------------------------- + + def expand_macros(self,tokens,expanded=None): + if expanded is None: + expanded = {} + i = 0 + while i < len(tokens): + t = tokens[i] + if t.type == self.t_ID: + if t.value in self.macros and t.value not in expanded: + # Yes, we found a macro match + expanded[t.value] = True + + m = self.macros[t.value] + if not m.arglist: + # A simple macro + ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded) + for e in ex: + e.lineno = t.lineno + tokens[i:i+1] = ex + i += len(ex) + else: + # A macro with arguments + j = i + 1 + while j < len(tokens) and tokens[j].type in self.t_WS: + j += 1 + if tokens[j].value == '(': + tokcount,args,positions = self.collect_args(tokens[j:]) + if not m.variadic and len(args) != len(m.arglist): + self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist))) + i = j + tokcount + elif m.variadic and len(args) < len(m.arglist)-1: + if len(m.arglist) > 2: + self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1)) + else: + self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1)) + i = j + tokcount + else: + if m.variadic: + if len(args) == len(m.arglist)-1: + args.append([]) + else: + args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1] + del args[len(m.arglist):] + + # Get macro replacement text + rep = self.macro_expand_args(m,args) + rep = self.expand_macros(rep,expanded) + for r in rep: + r.lineno = t.lineno + tokens[i:j+tokcount] = rep + i += len(rep) + del expanded[t.value] + continue + elif t.value == '__LINE__': + t.type = self.t_INTEGER + t.value = self.t_INTEGER_TYPE(t.lineno) + + i += 1 + return tokens + + # ---------------------------------------------------------------------- + # evalexpr() + # + # Evaluate an expression token sequence for the purposes of evaluating + # integral expressions. + # ---------------------------------------------------------------------- + + def evalexpr(self,tokens): + # tokens = tokenize(line) + # Search for defined macros + i = 0 + while i < len(tokens): + if tokens[i].type == self.t_ID and tokens[i].value == 'defined': + j = i + 1 + needparen = False + result = "0L" + while j < len(tokens): + if tokens[j].type in self.t_WS: + j += 1 + continue + elif tokens[j].type == self.t_ID: + if tokens[j].value in self.macros: + result = "1L" + else: + result = "0L" + if not needparen: break + elif tokens[j].value == '(': + needparen = True + elif tokens[j].value == ')': + break + else: + self.error(self.source,tokens[i].lineno,"Malformed defined()") + j += 1 + tokens[i].type = self.t_INTEGER + tokens[i].value = self.t_INTEGER_TYPE(result) + del tokens[i+1:j+1] + i += 1 + tokens = self.expand_macros(tokens) + for i,t in enumerate(tokens): + if t.type == self.t_ID: + tokens[i] = copy.copy(t) + tokens[i].type = self.t_INTEGER + tokens[i].value = self.t_INTEGER_TYPE("0L") + elif t.type == self.t_INTEGER: + tokens[i] = copy.copy(t) + # Strip off any trailing suffixes + tokens[i].value = str(tokens[i].value) + while tokens[i].value[-1] not in "0123456789abcdefABCDEF": + tokens[i].value = tokens[i].value[:-1] + + expr = "".join([str(x.value) for x in tokens]) + expr = expr.replace("&&"," and ") + expr = expr.replace("||"," or ") + expr = expr.replace("!"," not ") + try: + result = eval(expr) + except Exception: + self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression") + result = 0 + return result + + # ---------------------------------------------------------------------- + # parsegen() + # + # Parse an input string/ + # ---------------------------------------------------------------------- + def parsegen(self,input,source=None): + + # Replace trigraph sequences + t = trigraph(input) + lines = self.group_lines(t) + + if not source: + source = "" + + self.define("__FILE__ \"%s\"" % source) + + self.source = source + chunk = [] + enable = True + iftrigger = False + ifstack = [] + + for x in lines: + for i,tok in enumerate(x): + if tok.type not in self.t_WS: break + if tok.value == '#': + # Preprocessor directive + + # insert necessary whitespace instead of eaten tokens + for tok in x: + if tok.type in self.t_WS and '\n' in tok.value: + chunk.append(tok) + + dirtokens = self.tokenstrip(x[i+1:]) + if dirtokens: + name = dirtokens[0].value + args = self.tokenstrip(dirtokens[1:]) + else: + name = "" + args = [] + + if name == 'define': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + self.define(args) + elif name == 'include': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + oldfile = self.macros['__FILE__'] + for tok in self.include(args): + yield tok + self.macros['__FILE__'] = oldfile + self.source = source + elif name == 'undef': + if enable: + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + self.undef(args) + elif name == 'ifdef': + ifstack.append((enable,iftrigger)) + if enable: + if not args[0].value in self.macros: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'ifndef': + ifstack.append((enable,iftrigger)) + if enable: + if args[0].value in self.macros: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'if': + ifstack.append((enable,iftrigger)) + if enable: + result = self.evalexpr(args) + if not result: + enable = False + iftrigger = False + else: + iftrigger = True + elif name == 'elif': + if ifstack: + if ifstack[-1][0]: # We only pay attention if outer "if" allows this + if enable: # If already true, we flip enable False + enable = False + elif not iftrigger: # If False, but not triggered yet, we'll check expression + result = self.evalexpr(args) + if result: + enable = True + iftrigger = True + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #elif") + + elif name == 'else': + if ifstack: + if ifstack[-1][0]: + if enable: + enable = False + elif not iftrigger: + enable = True + iftrigger = True + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #else") + + elif name == 'endif': + if ifstack: + enable,iftrigger = ifstack.pop() + else: + self.error(self.source,dirtokens[0].lineno,"Misplaced #endif") + else: + # Unknown preprocessor directive + pass + + else: + # Normal text + if enable: + chunk.extend(x) + + for tok in self.expand_macros(chunk): + yield tok + chunk = [] + + # ---------------------------------------------------------------------- + # include() + # + # Implementation of file-inclusion + # ---------------------------------------------------------------------- + + def include(self,tokens): + # Try to extract the filename and then process an include file + if not tokens: + return + if tokens: + if tokens[0].value != '<' and tokens[0].type != self.t_STRING: + tokens = self.expand_macros(tokens) + + if tokens[0].value == '<': + # Include <...> + i = 1 + while i < len(tokens): + if tokens[i].value == '>': + break + i += 1 + else: + print("Malformed #include <...>") + return + filename = "".join([x.value for x in tokens[1:i]]) + path = self.path + [""] + self.temp_path + elif tokens[0].type == self.t_STRING: + filename = tokens[0].value[1:-1] + path = self.temp_path + [""] + self.path + else: + print("Malformed #include statement") + return + for p in path: + iname = os.path.join(p,filename) + try: + data = open(iname,"r").read() + dname = os.path.dirname(iname) + if dname: + self.temp_path.insert(0,dname) + for tok in self.parsegen(data,filename): + yield tok + if dname: + del self.temp_path[0] + break + except IOError: + pass + else: + print("Couldn't find '%s'" % filename) + + # ---------------------------------------------------------------------- + # define() + # + # Define a new macro + # ---------------------------------------------------------------------- + + def define(self,tokens): + if isinstance(tokens,STRING_TYPES): + tokens = self.tokenize(tokens) + + linetok = tokens + try: + name = linetok[0] + if len(linetok) > 1: + mtype = linetok[1] + else: + mtype = None + if not mtype: + m = Macro(name.value,[]) + self.macros[name.value] = m + elif mtype.type in self.t_WS: + # A normal macro + m = Macro(name.value,self.tokenstrip(linetok[2:])) + self.macros[name.value] = m + elif mtype.value == '(': + # A macro with arguments + tokcount, args, positions = self.collect_args(linetok[1:]) + variadic = False + for a in args: + if variadic: + print("No more arguments may follow a variadic argument") + break + astr = "".join([str(_i.value) for _i in a]) + if astr == "...": + variadic = True + a[0].type = self.t_ID + a[0].value = '__VA_ARGS__' + variadic = True + del a[1:] + continue + elif astr[-3:] == "..." and a[0].type == self.t_ID: + variadic = True + del a[1:] + # If, for some reason, "." is part of the identifier, strip off the name for the purposes + # of macro expansion + if a[0].value[-3:] == '...': + a[0].value = a[0].value[:-3] + continue + if len(a) > 1 or a[0].type != self.t_ID: + print("Invalid macro argument") + break + else: + mvalue = self.tokenstrip(linetok[1+tokcount:]) + i = 0 + while i < len(mvalue): + if i+1 < len(mvalue): + if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##': + del mvalue[i] + continue + elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS: + del mvalue[i+1] + i += 1 + m = Macro(name.value,mvalue,[x[0].value for x in args],variadic) + self.macro_prescan(m) + self.macros[name.value] = m + else: + print("Bad macro definition") + except LookupError: + print("Bad macro definition") + + # ---------------------------------------------------------------------- + # undef() + # + # Undefine a macro + # ---------------------------------------------------------------------- + + def undef(self,tokens): + id = tokens[0].value + try: + del self.macros[id] + except LookupError: + pass + + # ---------------------------------------------------------------------- + # parse() + # + # Parse input text. + # ---------------------------------------------------------------------- + def parse(self,input,source=None,ignore={}): + self.ignore = ignore + self.parser = self.parsegen(input,source) + + # ---------------------------------------------------------------------- + # token() + # + # Method to return individual tokens + # ---------------------------------------------------------------------- + def token(self): + try: + while True: + tok = next(self.parser) + if tok.type not in self.ignore: return tok + except StopIteration: + self.parser = None + return None + +if __name__ == '__main__': + import ply.lex as lex + lexer = lex.lex() + + # Run a preprocessor + import sys + f = open(sys.argv[1]) + input = f.read() + + p = Preprocessor(lexer) + p.parse(input,sys.argv[1]) + while True: + tok = p.token() + if not tok: break + print(p.source, tok) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py new file mode 100644 index 000000000..f6f6952d6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py @@ -0,0 +1,133 @@ +# ---------------------------------------------------------------------- +# ctokens.py +# +# Token specifications for symbols in ANSI C and C++. This file is +# meant to be used as a library in other tokenizers. +# ---------------------------------------------------------------------- + +# Reserved words + +tokens = [ + # Literals (identifier, integer constant, float constant, string constant, char const) + 'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER', + + # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=) + 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO', + 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', + 'LOR', 'LAND', 'LNOT', + 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', + + # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=) + 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL', + 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL', + + # Increment/decrement (++,--) + 'INCREMENT', 'DECREMENT', + + # Structure dereference (->) + 'ARROW', + + # Ternary operator (?) + 'TERNARY', + + # Delimeters ( ) [ ] { } , . ; : + 'LPAREN', 'RPAREN', + 'LBRACKET', 'RBRACKET', + 'LBRACE', 'RBRACE', + 'COMMA', 'PERIOD', 'SEMI', 'COLON', + + # Ellipsis (...) + 'ELLIPSIS', +] + +# Operators +t_PLUS = r'\+' +t_MINUS = r'-' +t_TIMES = r'\*' +t_DIVIDE = r'/' +t_MODULO = r'%' +t_OR = r'\|' +t_AND = r'&' +t_NOT = r'~' +t_XOR = r'\^' +t_LSHIFT = r'<<' +t_RSHIFT = r'>>' +t_LOR = r'\|\|' +t_LAND = r'&&' +t_LNOT = r'!' +t_LT = r'<' +t_GT = r'>' +t_LE = r'<=' +t_GE = r'>=' +t_EQ = r'==' +t_NE = r'!=' + +# Assignment operators + +t_EQUALS = r'=' +t_TIMESEQUAL = r'\*=' +t_DIVEQUAL = r'/=' +t_MODEQUAL = r'%=' +t_PLUSEQUAL = r'\+=' +t_MINUSEQUAL = r'-=' +t_LSHIFTEQUAL = r'<<=' +t_RSHIFTEQUAL = r'>>=' +t_ANDEQUAL = r'&=' +t_OREQUAL = r'\|=' +t_XOREQUAL = r'\^=' + +# Increment/decrement +t_INCREMENT = r'\+\+' +t_DECREMENT = r'--' + +# -> +t_ARROW = r'->' + +# ? +t_TERNARY = r'\?' + +# Delimeters +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LBRACKET = r'\[' +t_RBRACKET = r'\]' +t_LBRACE = r'\{' +t_RBRACE = r'\}' +t_COMMA = r',' +t_PERIOD = r'\.' +t_SEMI = r';' +t_COLON = r':' +t_ELLIPSIS = r'\.\.\.' + +# Identifiers +t_ID = r'[A-Za-z_][A-Za-z0-9_]*' + +# Integer literal +t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?' + +# Floating literal +t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' + +# String literal +t_STRING = r'\"([^\\\n]|(\\.))*?\"' + +# Character constant 'c' or L'c' +t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\'' + +# Comment (C-Style) +def t_COMMENT(t): + r'/\*(.|\n)*?\*/' + t.lexer.lineno += t.value.count('\n') + return t + +# Comment (C++-Style) +def t_CPPCOMMENT(t): + r'//.*\n' + t.lexer.lineno += 1 + return t + + + + + + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py new file mode 100644 index 000000000..4bdd76ca0 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py @@ -0,0 +1,1099 @@ +# ----------------------------------------------------------------------------- +# ply: lex.py +# +# Copyright (C) 2001-2017 +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- + +__version__ = '3.10' +__tabversion__ = '3.10' + +import re +import sys +import types +import copy +import os +import inspect + +# This tuple contains known string types +try: + # Python 2.6 + StringTypes = (types.StringType, types.UnicodeType) +except AttributeError: + # Python 3.0 + StringTypes = (str, bytes) + +# This regular expression is used to match valid token names +_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') + +# Exception thrown when invalid token encountered and no default error +# handler is defined. +class LexError(Exception): + def __init__(self, message, s): + self.args = (message,) + self.text = s + + +# Token class. This class is used to represent the tokens produced. +class LexToken(object): + def __str__(self): + return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos) + + def __repr__(self): + return str(self) + + +# This object is a stand-in for a logging object created by the +# logging module. + +class PlyLogger(object): + def __init__(self, f): + self.f = f + + def critical(self, msg, *args, **kwargs): + self.f.write((msg % args) + '\n') + + def warning(self, msg, *args, **kwargs): + self.f.write('WARNING: ' + (msg % args) + '\n') + + def error(self, msg, *args, **kwargs): + self.f.write('ERROR: ' + (msg % args) + '\n') + + info = critical + debug = critical + + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self, name): + return self + + def __call__(self, *args, **kwargs): + return self + + +# ----------------------------------------------------------------------------- +# === Lexing Engine === +# +# The following Lexer class implements the lexer runtime. There are only +# a few public methods and attributes: +# +# input() - Store a new string in the lexer +# token() - Get the next token +# clone() - Clone the lexer +# +# lineno - Current line number +# lexpos - Current position in the input string +# ----------------------------------------------------------------------------- + +class Lexer: + def __init__(self): + self.lexre = None # Master regular expression. This is a list of + # tuples (re, findex) where re is a compiled + # regular expression and findex is a list + # mapping regex group numbers to rules + self.lexretext = None # Current regular expression strings + self.lexstatere = {} # Dictionary mapping lexer states to master regexs + self.lexstateretext = {} # Dictionary mapping lexer states to regex strings + self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names + self.lexstate = 'INITIAL' # Current lexer state + self.lexstatestack = [] # Stack of lexer states + self.lexstateinfo = None # State information + self.lexstateignore = {} # Dictionary of ignored characters for each state + self.lexstateerrorf = {} # Dictionary of error functions for each state + self.lexstateeoff = {} # Dictionary of eof functions for each state + self.lexreflags = 0 # Optional re compile flags + self.lexdata = None # Actual input data (as a string) + self.lexpos = 0 # Current position in input text + self.lexlen = 0 # Length of the input text + self.lexerrorf = None # Error rule (if any) + self.lexeoff = None # EOF rule (if any) + self.lextokens = None # List of valid tokens + self.lexignore = '' # Ignored characters + self.lexliterals = '' # Literal characters that can be passed through + self.lexmodule = None # Module + self.lineno = 1 # Current line number + self.lexoptimize = False # Optimized mode + + def clone(self, object=None): + c = copy.copy(self) + + # If the object parameter has been supplied, it means we are attaching the + # lexer to a new object. In this case, we have to rebind all methods in + # the lexstatere and lexstateerrorf tables. + + if object: + newtab = {} + for key, ritem in self.lexstatere.items(): + newre = [] + for cre, findex in ritem: + newfindex = [] + for f in findex: + if not f or not f[0]: + newfindex.append(f) + continue + newfindex.append((getattr(object, f[0].__name__), f[1])) + newre.append((cre, newfindex)) + newtab[key] = newre + c.lexstatere = newtab + c.lexstateerrorf = {} + for key, ef in self.lexstateerrorf.items(): + c.lexstateerrorf[key] = getattr(object, ef.__name__) + c.lexmodule = object + return c + + # ------------------------------------------------------------ + # writetab() - Write lexer information to a table file + # ------------------------------------------------------------ + def writetab(self, lextab, outputdir=''): + if isinstance(lextab, types.ModuleType): + raise IOError("Won't overwrite existing lextab module") + basetabmodule = lextab.split('.')[-1] + filename = os.path.join(outputdir, basetabmodule) + '.py' + with open(filename, 'w') as tf: + tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__)) + tf.write('_tabversion = %s\n' % repr(__tabversion__)) + tf.write('_lextokens = set(%s)\n' % repr(tuple(self.lextokens))) + tf.write('_lexreflags = %s\n' % repr(self.lexreflags)) + tf.write('_lexliterals = %s\n' % repr(self.lexliterals)) + tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo)) + + # Rewrite the lexstatere table, replacing function objects with function names + tabre = {} + for statename, lre in self.lexstatere.items(): + titem = [] + for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]): + titem.append((retext, _funcs_to_names(func, renames))) + tabre[statename] = titem + + tf.write('_lexstatere = %s\n' % repr(tabre)) + tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore)) + + taberr = {} + for statename, ef in self.lexstateerrorf.items(): + taberr[statename] = ef.__name__ if ef else None + tf.write('_lexstateerrorf = %s\n' % repr(taberr)) + + tabeof = {} + for statename, ef in self.lexstateeoff.items(): + tabeof[statename] = ef.__name__ if ef else None + tf.write('_lexstateeoff = %s\n' % repr(tabeof)) + + # ------------------------------------------------------------ + # readtab() - Read lexer information from a tab file + # ------------------------------------------------------------ + def readtab(self, tabfile, fdict): + if isinstance(tabfile, types.ModuleType): + lextab = tabfile + else: + exec('import %s' % tabfile) + lextab = sys.modules[tabfile] + + if getattr(lextab, '_tabversion', '0.0') != __tabversion__: + raise ImportError('Inconsistent PLY version') + + self.lextokens = lextab._lextokens + self.lexreflags = lextab._lexreflags + self.lexliterals = lextab._lexliterals + self.lextokens_all = self.lextokens | set(self.lexliterals) + self.lexstateinfo = lextab._lexstateinfo + self.lexstateignore = lextab._lexstateignore + self.lexstatere = {} + self.lexstateretext = {} + for statename, lre in lextab._lexstatere.items(): + titem = [] + txtitem = [] + for pat, func_name in lre: + titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict))) + + self.lexstatere[statename] = titem + self.lexstateretext[statename] = txtitem + + self.lexstateerrorf = {} + for statename, ef in lextab._lexstateerrorf.items(): + self.lexstateerrorf[statename] = fdict[ef] + + self.lexstateeoff = {} + for statename, ef in lextab._lexstateeoff.items(): + self.lexstateeoff[statename] = fdict[ef] + + self.begin('INITIAL') + + # ------------------------------------------------------------ + # input() - Push a new string into the lexer + # ------------------------------------------------------------ + def input(self, s): + # Pull off the first character to see if s looks like a string + c = s[:1] + if not isinstance(c, StringTypes): + raise ValueError('Expected a string') + self.lexdata = s + self.lexpos = 0 + self.lexlen = len(s) + + # ------------------------------------------------------------ + # begin() - Changes the lexing state + # ------------------------------------------------------------ + def begin(self, state): + if state not in self.lexstatere: + raise ValueError('Undefined state') + self.lexre = self.lexstatere[state] + self.lexretext = self.lexstateretext[state] + self.lexignore = self.lexstateignore.get(state, '') + self.lexerrorf = self.lexstateerrorf.get(state, None) + self.lexeoff = self.lexstateeoff.get(state, None) + self.lexstate = state + + # ------------------------------------------------------------ + # push_state() - Changes the lexing state and saves old on stack + # ------------------------------------------------------------ + def push_state(self, state): + self.lexstatestack.append(self.lexstate) + self.begin(state) + + # ------------------------------------------------------------ + # pop_state() - Restores the previous state + # ------------------------------------------------------------ + def pop_state(self): + self.begin(self.lexstatestack.pop()) + + # ------------------------------------------------------------ + # current_state() - Returns the current lexing state + # ------------------------------------------------------------ + def current_state(self): + return self.lexstate + + # ------------------------------------------------------------ + # skip() - Skip ahead n characters + # ------------------------------------------------------------ + def skip(self, n): + self.lexpos += n + + # ------------------------------------------------------------ + # opttoken() - Return the next token from the Lexer + # + # Note: This function has been carefully implemented to be as fast + # as possible. Don't make changes unless you really know what + # you are doing + # ------------------------------------------------------------ + def token(self): + # Make local copies of frequently referenced attributes + lexpos = self.lexpos + lexlen = self.lexlen + lexignore = self.lexignore + lexdata = self.lexdata + + while lexpos < lexlen: + # This code provides some short-circuit code for whitespace, tabs, and other ignored characters + if lexdata[lexpos] in lexignore: + lexpos += 1 + continue + + # Look for a regular expression match + for lexre, lexindexfunc in self.lexre: + m = lexre.match(lexdata, lexpos) + if not m: + continue + + # Create a token for return + tok = LexToken() + tok.value = m.group() + tok.lineno = self.lineno + tok.lexpos = lexpos + + i = m.lastindex + func, tok.type = lexindexfunc[i] + + if not func: + # If no token type was set, it's an ignored token + if tok.type: + self.lexpos = m.end() + return tok + else: + lexpos = m.end() + break + + lexpos = m.end() + + # If token is processed by a function, call it + + tok.lexer = self # Set additional attributes useful in token rules + self.lexmatch = m + self.lexpos = lexpos + + newtok = func(tok) + + # Every function must return a token, if nothing, we just move to next token + if not newtok: + lexpos = self.lexpos # This is here in case user has updated lexpos. + lexignore = self.lexignore # This is here in case there was a state change + break + + # Verify type of the token. If not in the token map, raise an error + if not self.lexoptimize: + if newtok.type not in self.lextokens_all: + raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( + func.__code__.co_filename, func.__code__.co_firstlineno, + func.__name__, newtok.type), lexdata[lexpos:]) + + return newtok + else: + # No match, see if in literals + if lexdata[lexpos] in self.lexliterals: + tok = LexToken() + tok.value = lexdata[lexpos] + tok.lineno = self.lineno + tok.type = tok.value + tok.lexpos = lexpos + self.lexpos = lexpos + 1 + return tok + + # No match. Call t_error() if defined. + if self.lexerrorf: + tok = LexToken() + tok.value = self.lexdata[lexpos:] + tok.lineno = self.lineno + tok.type = 'error' + tok.lexer = self + tok.lexpos = lexpos + self.lexpos = lexpos + newtok = self.lexerrorf(tok) + if lexpos == self.lexpos: + # Error method didn't change text position at all. This is an error. + raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) + lexpos = self.lexpos + if not newtok: + continue + return newtok + + self.lexpos = lexpos + raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) + + if self.lexeoff: + tok = LexToken() + tok.type = 'eof' + tok.value = '' + tok.lineno = self.lineno + tok.lexpos = lexpos + tok.lexer = self + self.lexpos = lexpos + newtok = self.lexeoff(tok) + return newtok + + self.lexpos = lexpos + 1 + if self.lexdata is None: + raise RuntimeError('No input string given with input()') + return None + + # Iterator interface + def __iter__(self): + return self + + def next(self): + t = self.token() + if t is None: + raise StopIteration + return t + + __next__ = next + +# ----------------------------------------------------------------------------- +# ==== Lex Builder === +# +# The functions and classes below are used to collect lexing information +# and build a Lexer object from it. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# _get_regex(func) +# +# Returns the regular expression assigned to a function either as a doc string +# or as a .regex attribute attached by the @TOKEN decorator. +# ----------------------------------------------------------------------------- +def _get_regex(func): + return getattr(func, 'regex', func.__doc__) + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- +def get_caller_module_dict(levels): + f = sys._getframe(levels) + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + return ldict + +# ----------------------------------------------------------------------------- +# _funcs_to_names() +# +# Given a list of regular expression functions, this converts it to a list +# suitable for output to a table file +# ----------------------------------------------------------------------------- +def _funcs_to_names(funclist, namelist): + result = [] + for f, name in zip(funclist, namelist): + if f and f[0]: + result.append((name, f[1])) + else: + result.append(f) + return result + +# ----------------------------------------------------------------------------- +# _names_to_funcs() +# +# Given a list of regular expression function names, this converts it back to +# functions. +# ----------------------------------------------------------------------------- +def _names_to_funcs(namelist, fdict): + result = [] + for n in namelist: + if n and n[0]: + result.append((fdict[n[0]], n[1])) + else: + result.append(n) + return result + +# ----------------------------------------------------------------------------- +# _form_master_re() +# +# This function takes a list of all of the regex components and attempts to +# form the master regular expression. Given limitations in the Python re +# module, it may be necessary to break the master regex into separate expressions. +# ----------------------------------------------------------------------------- +def _form_master_re(relist, reflags, ldict, toknames): + if not relist: + return [] + regex = '|'.join(relist) + try: + lexre = re.compile(regex, reflags) + + # Build the index to function map for the matching engine + lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1) + lexindexnames = lexindexfunc[:] + + for f, i in lexre.groupindex.items(): + handle = ldict.get(f, None) + if type(handle) in (types.FunctionType, types.MethodType): + lexindexfunc[i] = (handle, toknames[f]) + lexindexnames[i] = f + elif handle is not None: + lexindexnames[i] = f + if f.find('ignore_') > 0: + lexindexfunc[i] = (None, None) + else: + lexindexfunc[i] = (None, toknames[f]) + + return [(lexre, lexindexfunc)], [regex], [lexindexnames] + except Exception: + m = int(len(relist)/2) + if m == 0: + m = 1 + llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames) + rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames) + return (llist+rlist), (lre+rre), (lnames+rnames) + +# ----------------------------------------------------------------------------- +# def _statetoken(s,names) +# +# Given a declaration name s of the form "t_" and a dictionary whose keys are +# state names, this function returns a tuple (states,tokenname) where states +# is a tuple of state names and tokenname is the name of the token. For example, +# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') +# ----------------------------------------------------------------------------- +def _statetoken(s, names): + nonstate = 1 + parts = s.split('_') + for i, part in enumerate(parts[1:], 1): + if part not in names and part != 'ANY': + break + + if i > 1: + states = tuple(parts[1:i]) + else: + states = ('INITIAL',) + + if 'ANY' in states: + states = tuple(names) + + tokenname = '_'.join(parts[i:]) + return (states, tokenname) + + +# ----------------------------------------------------------------------------- +# LexerReflect() +# +# This class represents information needed to build a lexer as extracted from a +# user's input file. +# ----------------------------------------------------------------------------- +class LexerReflect(object): + def __init__(self, ldict, log=None, reflags=0): + self.ldict = ldict + self.error_func = None + self.tokens = [] + self.reflags = reflags + self.stateinfo = {'INITIAL': 'inclusive'} + self.modules = set() + self.error = False + self.log = PlyLogger(sys.stderr) if log is None else log + + # Get all of the basic information + def get_all(self): + self.get_tokens() + self.get_literals() + self.get_states() + self.get_rules() + + # Validate all of the information + def validate_all(self): + self.validate_tokens() + self.validate_literals() + self.validate_rules() + return self.error + + # Get the tokens map + def get_tokens(self): + tokens = self.ldict.get('tokens', None) + if not tokens: + self.log.error('No token list is defined') + self.error = True + return + + if not isinstance(tokens, (list, tuple)): + self.log.error('tokens must be a list or tuple') + self.error = True + return + + if not tokens: + self.log.error('tokens is empty') + self.error = True + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + terminals = {} + for n in self.tokens: + if not _is_identifier.match(n): + self.log.error("Bad token name '%s'", n) + self.error = True + if n in terminals: + self.log.warning("Token '%s' multiply defined", n) + terminals[n] = 1 + + # Get the literals specifier + def get_literals(self): + self.literals = self.ldict.get('literals', '') + if not self.literals: + self.literals = '' + + # Validate literals + def validate_literals(self): + try: + for c in self.literals: + if not isinstance(c, StringTypes) or len(c) > 1: + self.log.error('Invalid literal %s. Must be a single character', repr(c)) + self.error = True + + except TypeError: + self.log.error('Invalid literals specification. literals must be a sequence of characters') + self.error = True + + def get_states(self): + self.states = self.ldict.get('states', None) + # Build statemap + if self.states: + if not isinstance(self.states, (tuple, list)): + self.log.error('states must be defined as a tuple or list') + self.error = True + else: + for s in self.states: + if not isinstance(s, tuple) or len(s) != 2: + self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s)) + self.error = True + continue + name, statetype = s + if not isinstance(name, StringTypes): + self.log.error('State name %s must be a string', repr(name)) + self.error = True + continue + if not (statetype == 'inclusive' or statetype == 'exclusive'): + self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name) + self.error = True + continue + if name in self.stateinfo: + self.log.error("State '%s' already defined", name) + self.error = True + continue + self.stateinfo[name] = statetype + + # Get all of the symbols with a t_ prefix and sort them into various + # categories (functions, strings, error functions, and ignore characters) + + def get_rules(self): + tsymbols = [f for f in self.ldict if f[:2] == 't_'] + + # Now build up a list of functions and a list of strings + self.toknames = {} # Mapping of symbols to token names + self.funcsym = {} # Symbols defined as functions + self.strsym = {} # Symbols defined as strings + self.ignore = {} # Ignore strings by state + self.errorf = {} # Error functions by state + self.eoff = {} # EOF functions by state + + for s in self.stateinfo: + self.funcsym[s] = [] + self.strsym[s] = [] + + if len(tsymbols) == 0: + self.log.error('No rules of the form t_rulename are defined') + self.error = True + return + + for f in tsymbols: + t = self.ldict[f] + states, tokname = _statetoken(f, self.stateinfo) + self.toknames[f] = tokname + + if hasattr(t, '__call__'): + if tokname == 'error': + for s in states: + self.errorf[s] = t + elif tokname == 'eof': + for s in states: + self.eoff[s] = t + elif tokname == 'ignore': + line = t.__code__.co_firstlineno + file = t.__code__.co_filename + self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__) + self.error = True + else: + for s in states: + self.funcsym[s].append((f, t)) + elif isinstance(t, StringTypes): + if tokname == 'ignore': + for s in states: + self.ignore[s] = t + if '\\' in t: + self.log.warning("%s contains a literal backslash '\\'", f) + + elif tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", f) + self.error = True + else: + for s in states: + self.strsym[s].append((f, t)) + else: + self.log.error('%s not defined as a function or string', f) + self.error = True + + # Sort the functions by line number + for f in self.funcsym.values(): + f.sort(key=lambda x: x[1].__code__.co_firstlineno) + + # Sort the strings by regular expression length + for s in self.strsym.values(): + s.sort(key=lambda x: len(x[1]), reverse=True) + + # Validate all of the t_rules collected + def validate_rules(self): + for state in self.stateinfo: + # Validate all rules defined by functions + + for fname, f in self.funcsym[state]: + line = f.__code__.co_firstlineno + file = f.__code__.co_filename + module = inspect.getmodule(f) + self.modules.add(module) + + tokname = self.toknames[fname] + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = f.__code__.co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) + self.error = True + continue + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) + self.error = True + continue + + if not _get_regex(f): + self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__) + self.error = True + continue + + try: + c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags) + if c.match(''): + self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__) + self.error = True + except re.error as e: + self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e) + if '#' in _get_regex(f): + self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__) + self.error = True + + # Validate all rules defined by strings + for name, r in self.strsym[state]: + tokname = self.toknames[name] + if tokname == 'error': + self.log.error("Rule '%s' must be defined as a function", name) + self.error = True + continue + + if tokname not in self.tokens and tokname.find('ignore_') < 0: + self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname) + self.error = True + continue + + try: + c = re.compile('(?P<%s>%s)' % (name, r), self.reflags) + if (c.match('')): + self.log.error("Regular expression for rule '%s' matches empty string", name) + self.error = True + except re.error as e: + self.log.error("Invalid regular expression for rule '%s'. %s", name, e) + if '#' in r: + self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name) + self.error = True + + if not self.funcsym[state] and not self.strsym[state]: + self.log.error("No rules defined for state '%s'", state) + self.error = True + + # Validate the error function + efunc = self.errorf.get(state, None) + if efunc: + f = efunc + line = f.__code__.co_firstlineno + file = f.__code__.co_filename + module = inspect.getmodule(f) + self.modules.add(module) + + if isinstance(f, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + nargs = f.__code__.co_argcount + if nargs > reqargs: + self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) + self.error = True + + if nargs < reqargs: + self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) + self.error = True + + for module in self.modules: + self.validate_module(module) + + # ----------------------------------------------------------------------------- + # validate_module() + # + # This checks to see if there are duplicated t_rulename() functions or strings + # in the parser input file. This is done using a simple regular expression + # match on each line in the source code of the given module. + # ----------------------------------------------------------------------------- + + def validate_module(self, module): + try: + lines, linen = inspect.getsourcelines(module) + except IOError: + return + + fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') + sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') + + counthash = {} + linen += 1 + for line in lines: + m = fre.match(line) + if not m: + m = sre.match(line) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + filename = inspect.getsourcefile(module) + self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev) + self.error = True + linen += 1 + +# ----------------------------------------------------------------------------- +# lex(module) +# +# Build all of the regular expression rules from definitions in the supplied module +# ----------------------------------------------------------------------------- +def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab', + reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None): + + if lextab is None: + lextab = 'lextab' + + global lexer + + ldict = None + stateinfo = {'INITIAL': 'inclusive'} + lexobj = Lexer() + lexobj.lexoptimize = optimize + global token, input + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + if debug: + if debuglog is None: + debuglog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the lexer + if object: + module = object + + # Get the module dictionary used for the parser + if module: + _items = [(k, getattr(module, k)) for k in dir(module)] + ldict = dict(_items) + # If no __file__ attribute is available, try to obtain it from the __module__ instead + if '__file__' not in ldict: + ldict['__file__'] = sys.modules[ldict['__module__']].__file__ + else: + ldict = get_caller_module_dict(2) + + # Determine if the module is package of a package or not. + # If so, fix the tabmodule setting so that tables load correctly + pkg = ldict.get('__package__') + if pkg and isinstance(lextab, str): + if '.' not in lextab: + lextab = pkg + '.' + lextab + + # Collect parser information from the dictionary + linfo = LexerReflect(ldict, log=errorlog, reflags=reflags) + linfo.get_all() + if not optimize: + if linfo.validate_all(): + raise SyntaxError("Can't build lexer") + + if optimize and lextab: + try: + lexobj.readtab(lextab, ldict) + token = lexobj.token + input = lexobj.input + lexer = lexobj + return lexobj + + except ImportError: + pass + + # Dump some basic debugging information + if debug: + debuglog.info('lex: tokens = %r', linfo.tokens) + debuglog.info('lex: literals = %r', linfo.literals) + debuglog.info('lex: states = %r', linfo.stateinfo) + + # Build a dictionary of valid token names + lexobj.lextokens = set() + for n in linfo.tokens: + lexobj.lextokens.add(n) + + # Get literals specification + if isinstance(linfo.literals, (list, tuple)): + lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) + else: + lexobj.lexliterals = linfo.literals + + lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals) + + # Get the stateinfo dictionary + stateinfo = linfo.stateinfo + + regexs = {} + # Build the master regular expressions + for state in stateinfo: + regex_list = [] + + # Add rules defined by functions first + for fname, f in linfo.funcsym[state]: + line = f.__code__.co_firstlineno + file = f.__code__.co_filename + regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f))) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state) + + # Now add all of the simple rules + for name, r in linfo.strsym[state]: + regex_list.append('(?P<%s>%s)' % (name, r)) + if debug: + debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state) + + regexs[state] = regex_list + + # Build the master regular expressions + + if debug: + debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====') + + for state in regexs: + lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames) + lexobj.lexstatere[state] = lexre + lexobj.lexstateretext[state] = re_text + lexobj.lexstaterenames[state] = re_names + if debug: + for i, text in enumerate(re_text): + debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text) + + # For inclusive states, we need to add the regular expressions from the INITIAL state + for state, stype in stateinfo.items(): + if state != 'INITIAL' and stype == 'inclusive': + lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) + lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) + lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) + + lexobj.lexstateinfo = stateinfo + lexobj.lexre = lexobj.lexstatere['INITIAL'] + lexobj.lexretext = lexobj.lexstateretext['INITIAL'] + lexobj.lexreflags = reflags + + # Set up ignore variables + lexobj.lexstateignore = linfo.ignore + lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '') + + # Set up error functions + lexobj.lexstateerrorf = linfo.errorf + lexobj.lexerrorf = linfo.errorf.get('INITIAL', None) + if not lexobj.lexerrorf: + errorlog.warning('No t_error rule is defined') + + # Set up eof functions + lexobj.lexstateeoff = linfo.eoff + lexobj.lexeoff = linfo.eoff.get('INITIAL', None) + + # Check state information for ignore and error rules + for s, stype in stateinfo.items(): + if stype == 'exclusive': + if s not in linfo.errorf: + errorlog.warning("No error rule is defined for exclusive state '%s'", s) + if s not in linfo.ignore and lexobj.lexignore: + errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) + elif stype == 'inclusive': + if s not in linfo.errorf: + linfo.errorf[s] = linfo.errorf.get('INITIAL', None) + if s not in linfo.ignore: + linfo.ignore[s] = linfo.ignore.get('INITIAL', '') + + # Create global versions of the token() and input() functions + token = lexobj.token + input = lexobj.input + lexer = lexobj + + # If in optimize mode, we write the lextab + if lextab and optimize: + if outputdir is None: + # If no output directory is set, the location of the output files + # is determined according to the following rules: + # - If lextab specifies a package, files go into that package directory + # - Otherwise, files go in the same directory as the specifying module + if isinstance(lextab, types.ModuleType): + srcfile = lextab.__file__ + else: + if '.' not in lextab: + srcfile = ldict['__file__'] + else: + parts = lextab.split('.') + pkgname = '.'.join(parts[:-1]) + exec('import %s' % pkgname) + srcfile = getattr(sys.modules[pkgname], '__file__', '') + outputdir = os.path.dirname(srcfile) + try: + lexobj.writetab(lextab, outputdir) + except IOError as e: + errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e)) + + return lexobj + +# ----------------------------------------------------------------------------- +# runmain() +# +# This runs the lexer as a main program +# ----------------------------------------------------------------------------- + +def runmain(lexer=None, data=None): + if not data: + try: + filename = sys.argv[1] + f = open(filename) + data = f.read() + f.close() + except IndexError: + sys.stdout.write('Reading from standard input (type EOF to end):\n') + data = sys.stdin.read() + + if lexer: + _input = lexer.input + else: + _input = input + _input(data) + if lexer: + _token = lexer.token + else: + _token = token + + while True: + tok = _token() + if not tok: + break + sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos)) + +# ----------------------------------------------------------------------------- +# @TOKEN(regex) +# +# This decorator function can be used to set the regex expression on a function +# when its docstring might need to be set in an alternative way +# ----------------------------------------------------------------------------- + +def TOKEN(r): + def set_regex(f): + if hasattr(r, '__call__'): + f.regex = _get_regex(r) + else: + f.regex = r + return f + return set_regex + +# Alternative spelling of the TOKEN decorator +Token = TOKEN diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py new file mode 100644 index 000000000..20b4f2863 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py @@ -0,0 +1,3494 @@ +# ----------------------------------------------------------------------------- +# ply: yacc.py +# +# Copyright (C) 2001-2017 +# David M. Beazley (Dabeaz LLC) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the David Beazley or Dabeaz LLC may be used to +# endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ----------------------------------------------------------------------------- +# +# This implements an LR parser that is constructed from grammar rules defined +# as Python functions. The grammer is specified by supplying the BNF inside +# Python documentation strings. The inspiration for this technique was borrowed +# from John Aycock's Spark parsing system. PLY might be viewed as cross between +# Spark and the GNU bison utility. +# +# The current implementation is only somewhat object-oriented. The +# LR parser itself is defined in terms of an object (which allows multiple +# parsers to co-exist). However, most of the variables used during table +# construction are defined in terms of global variables. Users shouldn't +# notice unless they are trying to define multiple parsers at the same +# time using threads (in which case they should have their head examined). +# +# This implementation supports both SLR and LALR(1) parsing. LALR(1) +# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), +# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, +# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced +# by the more efficient DeRemer and Pennello algorithm. +# +# :::::::: WARNING ::::::: +# +# Construction of LR parsing tables is fairly complicated and expensive. +# To make this module run fast, a *LOT* of work has been put into +# optimization---often at the expensive of readability and what might +# consider to be good Python "coding style." Modify the code at your +# own risk! +# ---------------------------------------------------------------------------- + +import re +import types +import sys +import os.path +import inspect +import base64 +import warnings + +__version__ = '3.10' +__tabversion__ = '3.10' + +#----------------------------------------------------------------------------- +# === User configurable parameters === +# +# Change these to modify the default behavior of yacc (if you wish) +#----------------------------------------------------------------------------- + +yaccdebug = True # Debugging mode. If set, yacc generates a + # a 'parser.out' file in the current directory + +debug_file = 'parser.out' # Default name of the debugging file +tab_module = 'parsetab' # Default name of the table module +default_lr = 'LALR' # Default LR table generation method + +error_count = 3 # Number of symbols that must be shifted to leave recovery mode + +yaccdevel = False # Set to True if developing yacc. This turns off optimized + # implementations of certain functions. + +resultlimit = 40 # Size limit of results when running in debug mode. + +pickle_protocol = 0 # Protocol to use when writing pickle files + +# String type-checking compatibility +if sys.version_info[0] < 3: + string_types = basestring +else: + string_types = str + +MAXINT = sys.maxsize + +# This object is a stand-in for a logging object created by the +# logging module. PLY will use this by default to create things +# such as the parser.out file. If a user wants more detailed +# information, they can create their own logging object and pass +# it into PLY. + +class PlyLogger(object): + def __init__(self, f): + self.f = f + + def debug(self, msg, *args, **kwargs): + self.f.write((msg % args) + '\n') + + info = debug + + def warning(self, msg, *args, **kwargs): + self.f.write('WARNING: ' + (msg % args) + '\n') + + def error(self, msg, *args, **kwargs): + self.f.write('ERROR: ' + (msg % args) + '\n') + + critical = debug + +# Null logger is used when no output is generated. Does nothing. +class NullLogger(object): + def __getattribute__(self, name): + return self + + def __call__(self, *args, **kwargs): + return self + +# Exception raised for yacc-related errors +class YaccError(Exception): + pass + +# Format the result message that the parser produces when running in debug mode. +def format_result(r): + repr_str = repr(r) + if '\n' in repr_str: + repr_str = repr(repr_str) + if len(repr_str) > resultlimit: + repr_str = repr_str[:resultlimit] + ' ...' + result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str) + return result + +# Format stack entries when the parser is running in debug mode +def format_stack_entry(r): + repr_str = repr(r) + if '\n' in repr_str: + repr_str = repr(repr_str) + if len(repr_str) < 16: + return repr_str + else: + return '<%s @ 0x%x>' % (type(r).__name__, id(r)) + +# Panic mode error recovery support. This feature is being reworked--much of the +# code here is to offer a deprecation/backwards compatible transition + +_errok = None +_token = None +_restart = None +_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error(). +Instead, invoke the methods on the associated parser instance: + + def p_error(p): + ... + # Use parser.errok(), parser.token(), parser.restart() + ... + + parser = yacc.yacc() +''' + +def errok(): + warnings.warn(_warnmsg) + return _errok() + +def restart(): + warnings.warn(_warnmsg) + return _restart() + +def token(): + warnings.warn(_warnmsg) + return _token() + +# Utility function to call the p_error() function with some deprecation hacks +def call_errorfunc(errorfunc, token, parser): + global _errok, _token, _restart + _errok = parser.errok + _token = parser.token + _restart = parser.restart + r = errorfunc(token) + try: + del _errok, _token, _restart + except NameError: + pass + return r + +#----------------------------------------------------------------------------- +# === LR Parsing Engine === +# +# The following classes are used for the LR parser itself. These are not +# used during table construction and are independent of the actual LR +# table generation algorithm +#----------------------------------------------------------------------------- + +# This class is used to hold non-terminal grammar symbols during parsing. +# It normally has the following attributes set: +# .type = Grammar symbol type +# .value = Symbol value +# .lineno = Starting line number +# .endlineno = Ending line number (optional, set automatically) +# .lexpos = Starting lex position +# .endlexpos = Ending lex position (optional, set automatically) + +class YaccSymbol: + def __str__(self): + return self.type + + def __repr__(self): + return str(self) + +# This class is a wrapper around the objects actually passed to each +# grammar rule. Index lookup and assignment actually assign the +# .value attribute of the underlying YaccSymbol object. +# The lineno() method returns the line number of a given +# item (or 0 if not defined). The linespan() method returns +# a tuple of (startline,endline) representing the range of lines +# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) +# representing the range of positional information for a symbol. + +class YaccProduction: + def __init__(self, s, stack=None): + self.slice = s + self.stack = stack + self.lexer = None + self.parser = None + + def __getitem__(self, n): + if isinstance(n, slice): + return [s.value for s in self.slice[n]] + elif n >= 0: + return self.slice[n].value + else: + return self.stack[n].value + + def __setitem__(self, n, v): + self.slice[n].value = v + + def __getslice__(self, i, j): + return [s.value for s in self.slice[i:j]] + + def __len__(self): + return len(self.slice) + + def lineno(self, n): + return getattr(self.slice[n], 'lineno', 0) + + def set_lineno(self, n, lineno): + self.slice[n].lineno = lineno + + def linespan(self, n): + startline = getattr(self.slice[n], 'lineno', 0) + endline = getattr(self.slice[n], 'endlineno', startline) + return startline, endline + + def lexpos(self, n): + return getattr(self.slice[n], 'lexpos', 0) + + def lexspan(self, n): + startpos = getattr(self.slice[n], 'lexpos', 0) + endpos = getattr(self.slice[n], 'endlexpos', startpos) + return startpos, endpos + + def error(self): + raise SyntaxError + +# ----------------------------------------------------------------------------- +# == LRParser == +# +# The LR Parsing engine. +# ----------------------------------------------------------------------------- + +class LRParser: + def __init__(self, lrtab, errorf): + self.productions = lrtab.lr_productions + self.action = lrtab.lr_action + self.goto = lrtab.lr_goto + self.errorfunc = errorf + self.set_defaulted_states() + self.errorok = True + + def errok(self): + self.errorok = True + + def restart(self): + del self.statestack[:] + del self.symstack[:] + sym = YaccSymbol() + sym.type = '$end' + self.symstack.append(sym) + self.statestack.append(0) + + # Defaulted state support. + # This method identifies parser states where there is only one possible reduction action. + # For such states, the parser can make a choose to make a rule reduction without consuming + # the next look-ahead token. This delayed invocation of the tokenizer can be useful in + # certain kinds of advanced parsing situations where the lexer and parser interact with + # each other or change states (i.e., manipulation of scope, lexer states, etc.). + # + # See: https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions + def set_defaulted_states(self): + self.defaulted_states = {} + for state, actions in self.action.items(): + rules = list(actions.values()) + if len(rules) == 1 and rules[0] < 0: + self.defaulted_states[state] = rules[0] + + def disable_defaulted_states(self): + self.defaulted_states = {} + + def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + if debug or yaccdevel: + if isinstance(debug, int): + debug = PlyLogger(sys.stderr) + return self.parsedebug(input, lexer, debug, tracking, tokenfunc) + elif tracking: + return self.parseopt(input, lexer, debug, tracking, tokenfunc) + else: + return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parsedebug(). + # + # This is the debugging enabled version of parse(). All changes made to the + # parsing engine should be made here. Optimized versions of this function + # are automatically created by the ply/ygen.py script. This script cuts out + # sections enclosed in markers such as this: + # + # #--! DEBUG + # statements + # #--! DEBUG + # + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + #--! parsedebug-start + lookahead = None # Current lookahead symbol + lookaheadstack = [] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + defaulted_states = self.defaulted_states # Local reference to defaulted states + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + #--! DEBUG + debug.info('PLY: PARSE DEBUG START') + #--! DEBUG + + # If no lexer was given, we will try to use the lex module + if not lexer: + from . import lex + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set the parser() token method (sometimes used in error recovery) + self.token = get_token + + # Set up the state and symbol stacks + + statestack = [] # Stack of parsing states + self.statestack = statestack + symstack = [] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while True: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + #--! DEBUG + debug.debug('') + debug.debug('State : %s', state) + #--! DEBUG + + if state not in defaulted_states: + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + else: + t = defaulted_states[state] + #--! DEBUG + debug.debug('Defaulted state %s: Reduce using %d', state, -t) + #--! DEBUG + + #--! DEBUG + debug.debug('Stack : %s', + ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + #--! DEBUG + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + #--! DEBUG + debug.debug('Action : Shift and goto state %s', t) + #--! DEBUG + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: + errorcount -= 1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + #--! DEBUG + if plen: + debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, + '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']', + goto[statestack[-1-plen]][pname]) + else: + debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [], + goto[statestack[-1]][pname]) + + #--! DEBUG + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + #--! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1, 'endlineno', t1.lineno) + sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) + #--! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + self.state = state + p.callable(pslice) + del statestack[-plen:] + #--! DEBUG + debug.info('Result : %s', format_result(pslice[0])) + #--! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + symstack.extend(targ[1:-1]) # Put the production slice back on the stack + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + #--! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + #--! TRACKING + + targ = [sym] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + self.state = state + p.callable(pslice) + #--! DEBUG + debug.info('Result : %s', format_result(pslice[0])) + #--! DEBUG + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n, 'value', None) + #--! DEBUG + debug.info('Done : Returning %s', format_result(result)) + debug.info('PLY: PARSE DEBUG END') + #--! DEBUG + return result + + if t is None: + + #--! DEBUG + debug.error('Error : %s', + ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) + #--! DEBUG + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = False + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + if errtoken and not hasattr(errtoken, 'lexer'): + errtoken.lexer = lexer + self.state = state + tok = call_errorfunc(self.errorfunc, errtoken, self) + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken, 'lineno'): + lineno = lookahead.lineno + else: + lineno = 0 + if lineno: + sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) + else: + sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) + else: + sys.stderr.write('yacc: Parse error in input. EOF\n') + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + #--! TRACKING + if tracking: + sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) + sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) + #--! TRACKING + lookahead = None + continue + + # Create the error symbol for the first time and make it the new lookahead symbol + t = YaccSymbol() + t.type = 'error' + + if hasattr(lookahead, 'lineno'): + t.lineno = t.endlineno = lookahead.lineno + if hasattr(lookahead, 'lexpos'): + t.lexpos = t.endlexpos = lookahead.lexpos + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + sym = symstack.pop() + #--! TRACKING + if tracking: + lookahead.lineno = sym.lineno + lookahead.lexpos = sym.lexpos + #--! TRACKING + statestack.pop() + state = statestack[-1] + + continue + + # Call an error function here + raise RuntimeError('yacc: internal parser error!!!\n') + + #--! parsedebug-end + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt(). + # + # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY! + # This code is automatically generated by the ply/ygen.py script. Make + # changes to the parsedebug() method instead. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + #--! parseopt-start + lookahead = None # Current lookahead symbol + lookaheadstack = [] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + defaulted_states = self.defaulted_states # Local reference to defaulted states + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + + # If no lexer was given, we will try to use the lex module + if not lexer: + from . import lex + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set the parser() token method (sometimes used in error recovery) + self.token = get_token + + # Set up the state and symbol stacks + + statestack = [] # Stack of parsing states + self.statestack = statestack + symstack = [] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while True: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + + if state not in defaulted_states: + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + else: + t = defaulted_states[state] + + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: + errorcount -= 1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + #--! TRACKING + if tracking: + t1 = targ[1] + sym.lineno = t1.lineno + sym.lexpos = t1.lexpos + t1 = targ[-1] + sym.endlineno = getattr(t1, 'endlineno', t1.lineno) + sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) + #--! TRACKING + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + self.state = state + p.callable(pslice) + del statestack[-plen:] + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + symstack.extend(targ[1:-1]) # Put the production slice back on the stack + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + #--! TRACKING + if tracking: + sym.lineno = lexer.lineno + sym.lexpos = lexer.lexpos + #--! TRACKING + + targ = [sym] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + self.state = state + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n, 'value', None) + return result + + if t is None: + + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = False + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + if errtoken and not hasattr(errtoken, 'lexer'): + errtoken.lexer = lexer + self.state = state + tok = call_errorfunc(self.errorfunc, errtoken, self) + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken, 'lineno'): + lineno = lookahead.lineno + else: + lineno = 0 + if lineno: + sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) + else: + sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) + else: + sys.stderr.write('yacc: Parse error in input. EOF\n') + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + #--! TRACKING + if tracking: + sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) + sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) + #--! TRACKING + lookahead = None + continue + + # Create the error symbol for the first time and make it the new lookahead symbol + t = YaccSymbol() + t.type = 'error' + + if hasattr(lookahead, 'lineno'): + t.lineno = t.endlineno = lookahead.lineno + if hasattr(lookahead, 'lexpos'): + t.lexpos = t.endlexpos = lookahead.lexpos + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + sym = symstack.pop() + #--! TRACKING + if tracking: + lookahead.lineno = sym.lineno + lookahead.lexpos = sym.lexpos + #--! TRACKING + statestack.pop() + state = statestack[-1] + + continue + + # Call an error function here + raise RuntimeError('yacc: internal parser error!!!\n') + + #--! parseopt-end + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # parseopt_notrack(). + # + # Optimized version of parseopt() with line number tracking removed. + # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated + # by the ply/ygen.py script. Make changes to the parsedebug() method instead. + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): + #--! parseopt-notrack-start + lookahead = None # Current lookahead symbol + lookaheadstack = [] # Stack of lookahead symbols + actions = self.action # Local reference to action table (to avoid lookup on self.) + goto = self.goto # Local reference to goto table (to avoid lookup on self.) + prod = self.productions # Local reference to production list (to avoid lookup on self.) + defaulted_states = self.defaulted_states # Local reference to defaulted states + pslice = YaccProduction(None) # Production object passed to grammar rules + errorcount = 0 # Used during error recovery + + + # If no lexer was given, we will try to use the lex module + if not lexer: + from . import lex + lexer = lex.lexer + + # Set up the lexer and parser objects on pslice + pslice.lexer = lexer + pslice.parser = self + + # If input was supplied, pass to lexer + if input is not None: + lexer.input(input) + + if tokenfunc is None: + # Tokenize function + get_token = lexer.token + else: + get_token = tokenfunc + + # Set the parser() token method (sometimes used in error recovery) + self.token = get_token + + # Set up the state and symbol stacks + + statestack = [] # Stack of parsing states + self.statestack = statestack + symstack = [] # Stack of grammar symbols + self.symstack = symstack + + pslice.stack = symstack # Put in the production + errtoken = None # Err token + + # The start state is assumed to be (0,$end) + + statestack.append(0) + sym = YaccSymbol() + sym.type = '$end' + symstack.append(sym) + state = 0 + while True: + # Get the next symbol on the input. If a lookahead symbol + # is already set, we just use that. Otherwise, we'll pull + # the next token off of the lookaheadstack or from the lexer + + + if state not in defaulted_states: + if not lookahead: + if not lookaheadstack: + lookahead = get_token() # Get the next token + else: + lookahead = lookaheadstack.pop() + if not lookahead: + lookahead = YaccSymbol() + lookahead.type = '$end' + + # Check the action table + ltype = lookahead.type + t = actions[state].get(ltype) + else: + t = defaulted_states[state] + + + if t is not None: + if t > 0: + # shift a symbol on the stack + statestack.append(t) + state = t + + + symstack.append(lookahead) + lookahead = None + + # Decrease error count on successful shift + if errorcount: + errorcount -= 1 + continue + + if t < 0: + # reduce a symbol on the stack, emit a production + p = prod[-t] + pname = p.name + plen = p.len + + # Get production function + sym = YaccSymbol() + sym.type = pname # Production name + sym.value = None + + + if plen: + targ = symstack[-plen-1:] + targ[0] = sym + + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # below as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + del symstack[-plen:] + self.state = state + p.callable(pslice) + del statestack[-plen:] + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + symstack.extend(targ[1:-1]) # Put the production slice back on the stack + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + else: + + + targ = [sym] + + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # The code enclosed in this section is duplicated + # above as a performance optimization. Make sure + # changes get made in both locations. + + pslice.slice = targ + + try: + # Call the grammar rule with our special slice object + self.state = state + p.callable(pslice) + symstack.append(sym) + state = goto[statestack[-1]][pname] + statestack.append(state) + except SyntaxError: + # If an error was set. Enter error recovery state + lookaheadstack.append(lookahead) # Save the current lookahead token + statestack.pop() # Pop back one state (before the reduce) + state = statestack[-1] + sym.type = 'error' + sym.value = 'error' + lookahead = sym + errorcount = error_count + self.errorok = False + + continue + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + if t == 0: + n = symstack[-1] + result = getattr(n, 'value', None) + return result + + if t is None: + + + # We have some kind of parsing error here. To handle + # this, we are going to push the current token onto + # the tokenstack and replace it with an 'error' token. + # If there are any synchronization rules, they may + # catch it. + # + # In addition to pushing the error token, we call call + # the user defined p_error() function if this is the + # first syntax error. This function is only called if + # errorcount == 0. + if errorcount == 0 or self.errorok: + errorcount = error_count + self.errorok = False + errtoken = lookahead + if errtoken.type == '$end': + errtoken = None # End of file! + if self.errorfunc: + if errtoken and not hasattr(errtoken, 'lexer'): + errtoken.lexer = lexer + self.state = state + tok = call_errorfunc(self.errorfunc, errtoken, self) + if self.errorok: + # User must have done some kind of panic + # mode recovery on their own. The + # returned token is the next lookahead + lookahead = tok + errtoken = None + continue + else: + if errtoken: + if hasattr(errtoken, 'lineno'): + lineno = lookahead.lineno + else: + lineno = 0 + if lineno: + sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) + else: + sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) + else: + sys.stderr.write('yacc: Parse error in input. EOF\n') + return + + else: + errorcount = error_count + + # case 1: the statestack only has 1 entry on it. If we're in this state, the + # entire parse has been rolled back and we're completely hosed. The token is + # discarded and we just keep going. + + if len(statestack) <= 1 and lookahead.type != '$end': + lookahead = None + errtoken = None + state = 0 + # Nuke the pushback stack + del lookaheadstack[:] + continue + + # case 2: the statestack has a couple of entries on it, but we're + # at the end of the file. nuke the top entry and generate an error token + + # Start nuking entries on the stack + if lookahead.type == '$end': + # Whoa. We're really hosed here. Bail out + return + + if lookahead.type != 'error': + sym = symstack[-1] + if sym.type == 'error': + # Hmmm. Error is on top of stack, we'll just nuke input + # symbol and continue + lookahead = None + continue + + # Create the error symbol for the first time and make it the new lookahead symbol + t = YaccSymbol() + t.type = 'error' + + if hasattr(lookahead, 'lineno'): + t.lineno = t.endlineno = lookahead.lineno + if hasattr(lookahead, 'lexpos'): + t.lexpos = t.endlexpos = lookahead.lexpos + t.value = lookahead + lookaheadstack.append(lookahead) + lookahead = t + else: + sym = symstack.pop() + statestack.pop() + state = statestack[-1] + + continue + + # Call an error function here + raise RuntimeError('yacc: internal parser error!!!\n') + + #--! parseopt-notrack-end + +# ----------------------------------------------------------------------------- +# === Grammar Representation === +# +# The following functions, classes, and variables are used to represent and +# manipulate the rules that make up a grammar. +# ----------------------------------------------------------------------------- + +# regex matching identifiers +_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') + +# ----------------------------------------------------------------------------- +# class Production: +# +# This class stores the raw information about a single production or grammar rule. +# A grammar rule refers to a specification such as this: +# +# expr : expr PLUS term +# +# Here are the basic attributes defined on all productions +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','PLUS','term'] +# prec - Production precedence level +# number - Production number. +# func - Function that executes on reduce +# file - File where production function is defined +# lineno - Line number where production function is defined +# +# The following attributes are defined or optional. +# +# len - Length of the production (number of symbols on right hand side) +# usyms - Set of unique symbols found in the production +# ----------------------------------------------------------------------------- + +class Production(object): + reduced = 0 + def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0): + self.name = name + self.prod = tuple(prod) + self.number = number + self.func = func + self.callable = None + self.file = file + self.line = line + self.prec = precedence + + # Internal settings used during table construction + + self.len = len(self.prod) # Length of the production + + # Create a list of unique production symbols used in the production + self.usyms = [] + for s in self.prod: + if s not in self.usyms: + self.usyms.append(s) + + # List of all LR items for the production + self.lr_items = [] + self.lr_next = None + + # Create a string representation + if self.prod: + self.str = '%s -> %s' % (self.name, ' '.join(self.prod)) + else: + self.str = '%s -> ' % self.name + + def __str__(self): + return self.str + + def __repr__(self): + return 'Production(' + str(self) + ')' + + def __len__(self): + return len(self.prod) + + def __nonzero__(self): + return 1 + + def __getitem__(self, index): + return self.prod[index] + + # Return the nth lr_item from the production (or None if at the end) + def lr_item(self, n): + if n > len(self.prod): + return None + p = LRItem(self, n) + # Precompute the list of productions immediately following. + try: + p.lr_after = Prodnames[p.prod[n+1]] + except (IndexError, KeyError): + p.lr_after = [] + try: + p.lr_before = p.prod[n-1] + except IndexError: + p.lr_before = None + return p + + # Bind the production function name to a callable + def bind(self, pdict): + if self.func: + self.callable = pdict[self.func] + +# This class serves as a minimal standin for Production objects when +# reading table data from files. It only contains information +# actually used by the LR parsing engine, plus some additional +# debugging information. +class MiniProduction(object): + def __init__(self, str, name, len, func, file, line): + self.name = name + self.len = len + self.func = func + self.callable = None + self.file = file + self.line = line + self.str = str + + def __str__(self): + return self.str + + def __repr__(self): + return 'MiniProduction(%s)' % self.str + + # Bind the production function name to a callable + def bind(self, pdict): + if self.func: + self.callable = pdict[self.func] + + +# ----------------------------------------------------------------------------- +# class LRItem +# +# This class represents a specific stage of parsing a production rule. For +# example: +# +# expr : expr . PLUS term +# +# In the above, the "." represents the current location of the parse. Here +# basic attributes: +# +# name - Name of the production. For example 'expr' +# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] +# number - Production number. +# +# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' +# then lr_next refers to 'expr -> expr PLUS . term' +# lr_index - LR item index (location of the ".") in the prod list. +# lookaheads - LALR lookahead symbols for this item +# len - Length of the production (number of symbols on right hand side) +# lr_after - List of all productions that immediately follow +# lr_before - Grammar symbol immediately before +# ----------------------------------------------------------------------------- + +class LRItem(object): + def __init__(self, p, n): + self.name = p.name + self.prod = list(p.prod) + self.number = p.number + self.lr_index = n + self.lookaheads = {} + self.prod.insert(n, '.') + self.prod = tuple(self.prod) + self.len = len(self.prod) + self.usyms = p.usyms + + def __str__(self): + if self.prod: + s = '%s -> %s' % (self.name, ' '.join(self.prod)) + else: + s = '%s -> ' % self.name + return s + + def __repr__(self): + return 'LRItem(' + str(self) + ')' + +# ----------------------------------------------------------------------------- +# rightmost_terminal() +# +# Return the rightmost terminal from a list of symbols. Used in add_production() +# ----------------------------------------------------------------------------- +def rightmost_terminal(symbols, terminals): + i = len(symbols) - 1 + while i >= 0: + if symbols[i] in terminals: + return symbols[i] + i -= 1 + return None + +# ----------------------------------------------------------------------------- +# === GRAMMAR CLASS === +# +# The following class represents the contents of the specified grammar along +# with various computed properties such as first sets, follow sets, LR items, etc. +# This data is used for critical parts of the table generation process later. +# ----------------------------------------------------------------------------- + +class GrammarError(YaccError): + pass + +class Grammar(object): + def __init__(self, terminals): + self.Productions = [None] # A list of all of the productions. The first + # entry is always reserved for the purpose of + # building an augmented grammar + + self.Prodnames = {} # A dictionary mapping the names of nonterminals to a list of all + # productions of that nonterminal. + + self.Prodmap = {} # A dictionary that is only used to detect duplicate + # productions. + + self.Terminals = {} # A dictionary mapping the names of terminal symbols to a + # list of the rules where they are used. + + for term in terminals: + self.Terminals[term] = [] + + self.Terminals['error'] = [] + + self.Nonterminals = {} # A dictionary mapping names of nonterminals to a list + # of rule numbers where they are used. + + self.First = {} # A dictionary of precomputed FIRST(x) symbols + + self.Follow = {} # A dictionary of precomputed FOLLOW(x) symbols + + self.Precedence = {} # Precedence rules for each terminal. Contains tuples of the + # form ('right',level) or ('nonassoc', level) or ('left',level) + + self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer. + # This is only used to provide error checking and to generate + # a warning about unused precedence rules. + + self.Start = None # Starting symbol for the grammar + + + def __len__(self): + return len(self.Productions) + + def __getitem__(self, index): + return self.Productions[index] + + # ----------------------------------------------------------------------------- + # set_precedence() + # + # Sets the precedence for a given terminal. assoc is the associativity such as + # 'left','right', or 'nonassoc'. level is a numeric level. + # + # ----------------------------------------------------------------------------- + + def set_precedence(self, term, assoc, level): + assert self.Productions == [None], 'Must call set_precedence() before add_production()' + if term in self.Precedence: + raise GrammarError('Precedence already specified for terminal %r' % term) + if assoc not in ['left', 'right', 'nonassoc']: + raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") + self.Precedence[term] = (assoc, level) + + # ----------------------------------------------------------------------------- + # add_production() + # + # Given an action function, this function assembles a production rule and + # computes its precedence level. + # + # The production rule is supplied as a list of symbols. For example, + # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and + # symbols ['expr','PLUS','term']. + # + # Precedence is determined by the precedence of the right-most non-terminal + # or the precedence of a terminal specified by %prec. + # + # A variety of error checks are performed to make sure production symbols + # are valid and that %prec is used correctly. + # ----------------------------------------------------------------------------- + + def add_production(self, prodname, syms, func=None, file='', line=0): + + if prodname in self.Terminals: + raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname)) + if prodname == 'error': + raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname)) + if not _is_identifier.match(prodname): + raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname)) + + # Look for literal tokens + for n, s in enumerate(syms): + if s[0] in "'\"": + try: + c = eval(s) + if (len(c) > 1): + raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' % + (file, line, s, prodname)) + if c not in self.Terminals: + self.Terminals[c] = [] + syms[n] = c + continue + except SyntaxError: + pass + if not _is_identifier.match(s) and s != '%prec': + raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname)) + + # Determine the precedence level + if '%prec' in syms: + if syms[-1] == '%prec': + raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line)) + if syms[-2] != '%prec': + raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' % + (file, line)) + precname = syms[-1] + prodprec = self.Precedence.get(precname) + if not prodprec: + raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname)) + else: + self.UsedPrecedence.add(precname) + del syms[-2:] # Drop %prec from the rule + else: + # If no %prec, precedence is determined by the rightmost terminal symbol + precname = rightmost_terminal(syms, self.Terminals) + prodprec = self.Precedence.get(precname, ('right', 0)) + + # See if the rule is already in the rulemap + map = '%s -> %s' % (prodname, syms) + if map in self.Prodmap: + m = self.Prodmap[map] + raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) + + 'Previous definition at %s:%d' % (m.file, m.line)) + + # From this point on, everything is valid. Create a new Production instance + pnumber = len(self.Productions) + if prodname not in self.Nonterminals: + self.Nonterminals[prodname] = [] + + # Add the production number to Terminals and Nonterminals + for t in syms: + if t in self.Terminals: + self.Terminals[t].append(pnumber) + else: + if t not in self.Nonterminals: + self.Nonterminals[t] = [] + self.Nonterminals[t].append(pnumber) + + # Create a production and add it to the list of productions + p = Production(pnumber, prodname, syms, prodprec, func, file, line) + self.Productions.append(p) + self.Prodmap[map] = p + + # Add to the global productions list + try: + self.Prodnames[prodname].append(p) + except KeyError: + self.Prodnames[prodname] = [p] + + # ----------------------------------------------------------------------------- + # set_start() + # + # Sets the starting symbol and creates the augmented grammar. Production + # rule 0 is S' -> start where start is the start symbol. + # ----------------------------------------------------------------------------- + + def set_start(self, start=None): + if not start: + start = self.Productions[1].name + if start not in self.Nonterminals: + raise GrammarError('start symbol %s undefined' % start) + self.Productions[0] = Production(0, "S'", [start]) + self.Nonterminals[start].append(0) + self.Start = start + + # ----------------------------------------------------------------------------- + # find_unreachable() + # + # Find all of the nonterminal symbols that can't be reached from the starting + # symbol. Returns a list of nonterminals that can't be reached. + # ----------------------------------------------------------------------------- + + def find_unreachable(self): + + # Mark all symbols that are reachable from a symbol s + def mark_reachable_from(s): + if s in reachable: + return + reachable.add(s) + for p in self.Prodnames.get(s, []): + for r in p.prod: + mark_reachable_from(r) + + reachable = set() + mark_reachable_from(self.Productions[0].prod[0]) + return [s for s in self.Nonterminals if s not in reachable] + + # ----------------------------------------------------------------------------- + # infinite_cycles() + # + # This function looks at the various parsing rules and tries to detect + # infinite recursion cycles (grammar rules where there is no possible way + # to derive a string of only terminals). + # ----------------------------------------------------------------------------- + + def infinite_cycles(self): + terminates = {} + + # Terminals: + for t in self.Terminals: + terminates[t] = True + + terminates['$end'] = True + + # Nonterminals: + + # Initialize to false: + for n in self.Nonterminals: + terminates[n] = False + + # Then propagate termination until no change: + while True: + some_change = False + for (n, pl) in self.Prodnames.items(): + # Nonterminal n terminates iff any of its productions terminates. + for p in pl: + # Production p terminates iff all of its rhs symbols terminate. + for s in p.prod: + if not terminates[s]: + # The symbol s does not terminate, + # so production p does not terminate. + p_terminates = False + break + else: + # didn't break from the loop, + # so every symbol s terminates + # so production p terminates. + p_terminates = True + + if p_terminates: + # symbol n terminates! + if not terminates[n]: + terminates[n] = True + some_change = True + # Don't need to consider any more productions for this n. + break + + if not some_change: + break + + infinite = [] + for (s, term) in terminates.items(): + if not term: + if s not in self.Prodnames and s not in self.Terminals and s != 'error': + # s is used-but-not-defined, and we've already warned of that, + # so it would be overkill to say that it's also non-terminating. + pass + else: + infinite.append(s) + + return infinite + + # ----------------------------------------------------------------------------- + # undefined_symbols() + # + # Find all symbols that were used the grammar, but not defined as tokens or + # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol + # and prod is the production where the symbol was used. + # ----------------------------------------------------------------------------- + def undefined_symbols(self): + result = [] + for p in self.Productions: + if not p: + continue + + for s in p.prod: + if s not in self.Prodnames and s not in self.Terminals and s != 'error': + result.append((s, p)) + return result + + # ----------------------------------------------------------------------------- + # unused_terminals() + # + # Find all terminals that were defined, but not used by the grammar. Returns + # a list of all symbols. + # ----------------------------------------------------------------------------- + def unused_terminals(self): + unused_tok = [] + for s, v in self.Terminals.items(): + if s != 'error' and not v: + unused_tok.append(s) + + return unused_tok + + # ------------------------------------------------------------------------------ + # unused_rules() + # + # Find all grammar rules that were defined, but not used (maybe not reachable) + # Returns a list of productions. + # ------------------------------------------------------------------------------ + + def unused_rules(self): + unused_prod = [] + for s, v in self.Nonterminals.items(): + if not v: + p = self.Prodnames[s][0] + unused_prod.append(p) + return unused_prod + + # ----------------------------------------------------------------------------- + # unused_precedence() + # + # Returns a list of tuples (term,precedence) corresponding to precedence + # rules that were never used by the grammar. term is the name of the terminal + # on which precedence was applied and precedence is a string such as 'left' or + # 'right' corresponding to the type of precedence. + # ----------------------------------------------------------------------------- + + def unused_precedence(self): + unused = [] + for termname in self.Precedence: + if not (termname in self.Terminals or termname in self.UsedPrecedence): + unused.append((termname, self.Precedence[termname][0])) + + return unused + + # ------------------------------------------------------------------------- + # _first() + # + # Compute the value of FIRST1(beta) where beta is a tuple of symbols. + # + # During execution of compute_first1, the result may be incomplete. + # Afterward (e.g., when called from compute_follow()), it will be complete. + # ------------------------------------------------------------------------- + def _first(self, beta): + + # We are computing First(x1,x2,x3,...,xn) + result = [] + for x in beta: + x_produces_empty = False + + # Add all the non- symbols of First[x] to the result. + for f in self.First[x]: + if f == '': + x_produces_empty = True + else: + if f not in result: + result.append(f) + + if x_produces_empty: + # We have to consider the next x in beta, + # i.e. stay in the loop. + pass + else: + # We don't have to consider any further symbols in beta. + break + else: + # There was no 'break' from the loop, + # so x_produces_empty was true for all x in beta, + # so beta produces empty as well. + result.append('') + + return result + + # ------------------------------------------------------------------------- + # compute_first() + # + # Compute the value of FIRST1(X) for all symbols + # ------------------------------------------------------------------------- + def compute_first(self): + if self.First: + return self.First + + # Terminals: + for t in self.Terminals: + self.First[t] = [t] + + self.First['$end'] = ['$end'] + + # Nonterminals: + + # Initialize to the empty set: + for n in self.Nonterminals: + self.First[n] = [] + + # Then propagate symbols until no change: + while True: + some_change = False + for n in self.Nonterminals: + for p in self.Prodnames[n]: + for f in self._first(p.prod): + if f not in self.First[n]: + self.First[n].append(f) + some_change = True + if not some_change: + break + + return self.First + + # --------------------------------------------------------------------- + # compute_follow() + # + # Computes all of the follow sets for every non-terminal symbol. The + # follow set is the set of all symbols that might follow a given + # non-terminal. See the Dragon book, 2nd Ed. p. 189. + # --------------------------------------------------------------------- + def compute_follow(self, start=None): + # If already computed, return the result + if self.Follow: + return self.Follow + + # If first sets not computed yet, do that first. + if not self.First: + self.compute_first() + + # Add '$end' to the follow list of the start symbol + for k in self.Nonterminals: + self.Follow[k] = [] + + if not start: + start = self.Productions[1].name + + self.Follow[start] = ['$end'] + + while True: + didadd = False + for p in self.Productions[1:]: + # Here is the production set + for i, B in enumerate(p.prod): + if B in self.Nonterminals: + # Okay. We got a non-terminal in a production + fst = self._first(p.prod[i+1:]) + hasempty = False + for f in fst: + if f != '' and f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = True + if f == '': + hasempty = True + if hasempty or i == (len(p.prod)-1): + # Add elements of follow(a) to follow(b) + for f in self.Follow[p.name]: + if f not in self.Follow[B]: + self.Follow[B].append(f) + didadd = True + if not didadd: + break + return self.Follow + + + # ----------------------------------------------------------------------------- + # build_lritems() + # + # This function walks the list of productions and builds a complete set of the + # LR items. The LR items are stored in two ways: First, they are uniquely + # numbered and placed in the list _lritems. Second, a linked list of LR items + # is built for each production. For example: + # + # E -> E PLUS E + # + # Creates the list + # + # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] + # ----------------------------------------------------------------------------- + + def build_lritems(self): + for p in self.Productions: + lastlri = p + i = 0 + lr_items = [] + while True: + if i > len(p): + lri = None + else: + lri = LRItem(p, i) + # Precompute the list of productions immediately following + try: + lri.lr_after = self.Prodnames[lri.prod[i+1]] + except (IndexError, KeyError): + lri.lr_after = [] + try: + lri.lr_before = lri.prod[i-1] + except IndexError: + lri.lr_before = None + + lastlri.lr_next = lri + if not lri: + break + lr_items.append(lri) + lastlri = lri + i += 1 + p.lr_items = lr_items + +# ----------------------------------------------------------------------------- +# == Class LRTable == +# +# This basic class represents a basic table of LR parsing information. +# Methods for generating the tables are not defined here. They are defined +# in the derived class LRGeneratedTable. +# ----------------------------------------------------------------------------- + +class VersionError(YaccError): + pass + +class LRTable(object): + def __init__(self): + self.lr_action = None + self.lr_goto = None + self.lr_productions = None + self.lr_method = None + + def read_table(self, module): + if isinstance(module, types.ModuleType): + parsetab = module + else: + exec('import %s' % module) + parsetab = sys.modules[module] + + if parsetab._tabversion != __tabversion__: + raise VersionError('yacc table file version is out of date') + + self.lr_action = parsetab._lr_action + self.lr_goto = parsetab._lr_goto + + self.lr_productions = [] + for p in parsetab._lr_productions: + self.lr_productions.append(MiniProduction(*p)) + + self.lr_method = parsetab._lr_method + return parsetab._lr_signature + + def read_pickle(self, filename): + try: + import cPickle as pickle + except ImportError: + import pickle + + if not os.path.exists(filename): + raise ImportError + + in_f = open(filename, 'rb') + + tabversion = pickle.load(in_f) + if tabversion != __tabversion__: + raise VersionError('yacc table file version is out of date') + self.lr_method = pickle.load(in_f) + signature = pickle.load(in_f) + self.lr_action = pickle.load(in_f) + self.lr_goto = pickle.load(in_f) + productions = pickle.load(in_f) + + self.lr_productions = [] + for p in productions: + self.lr_productions.append(MiniProduction(*p)) + + in_f.close() + return signature + + # Bind all production function names to callable objects in pdict + def bind_callables(self, pdict): + for p in self.lr_productions: + p.bind(pdict) + + +# ----------------------------------------------------------------------------- +# === LR Generator === +# +# The following classes and functions are used to generate LR parsing tables on +# a grammar. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# digraph() +# traverse() +# +# The following two functions are used to compute set valued functions +# of the form: +# +# F(x) = F'(x) U U{F(y) | x R y} +# +# This is used to compute the values of Read() sets as well as FOLLOW sets +# in LALR(1) generation. +# +# Inputs: X - An input set +# R - A relation +# FP - Set-valued function +# ------------------------------------------------------------------------------ + +def digraph(X, R, FP): + N = {} + for x in X: + N[x] = 0 + stack = [] + F = {} + for x in X: + if N[x] == 0: + traverse(x, N, stack, F, X, R, FP) + return F + +def traverse(x, N, stack, F, X, R, FP): + stack.append(x) + d = len(stack) + N[x] = d + F[x] = FP(x) # F(X) <- F'(x) + + rel = R(x) # Get y's related to x + for y in rel: + if N[y] == 0: + traverse(y, N, stack, F, X, R, FP) + N[x] = min(N[x], N[y]) + for a in F.get(y, []): + if a not in F[x]: + F[x].append(a) + if N[x] == d: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + while element != x: + N[stack[-1]] = MAXINT + F[stack[-1]] = F[x] + element = stack.pop() + +class LALRError(YaccError): + pass + +# ----------------------------------------------------------------------------- +# == LRGeneratedTable == +# +# This class implements the LR table generation algorithm. There are no +# public methods except for write() +# ----------------------------------------------------------------------------- + +class LRGeneratedTable(LRTable): + def __init__(self, grammar, method='LALR', log=None): + if method not in ['SLR', 'LALR']: + raise LALRError('Unsupported method %s' % method) + + self.grammar = grammar + self.lr_method = method + + # Set up the logger + if not log: + log = NullLogger() + self.log = log + + # Internal attributes + self.lr_action = {} # Action table + self.lr_goto = {} # Goto table + self.lr_productions = grammar.Productions # Copy of grammar Production array + self.lr_goto_cache = {} # Cache of computed gotos + self.lr0_cidhash = {} # Cache of closures + + self._add_count = 0 # Internal counter used to detect cycles + + # Diagonistic information filled in by the table generator + self.sr_conflict = 0 + self.rr_conflict = 0 + self.conflicts = [] # List of conflicts + + self.sr_conflicts = [] + self.rr_conflicts = [] + + # Build the tables + self.grammar.build_lritems() + self.grammar.compute_first() + self.grammar.compute_follow() + self.lr_parse_table() + + # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. + + def lr0_closure(self, I): + self._add_count += 1 + + # Add everything in I to J + J = I[:] + didadd = True + while didadd: + didadd = False + for j in J: + for x in j.lr_after: + if getattr(x, 'lr0_added', 0) == self._add_count: + continue + # Add B --> .G to J + J.append(x.lr_next) + x.lr0_added = self._add_count + didadd = True + + return J + + # Compute the LR(0) goto function goto(I,X) where I is a set + # of LR(0) items and X is a grammar symbol. This function is written + # in a way that guarantees uniqueness of the generated goto sets + # (i.e. the same goto set will never be returned as two different Python + # objects). With uniqueness, we can later do fast set comparisons using + # id(obj) instead of element-wise comparison. + + def lr0_goto(self, I, x): + # First we look for a previously cached entry + g = self.lr_goto_cache.get((id(I), x)) + if g: + return g + + # Now we generate the goto set in a way that guarantees uniqueness + # of the result + + s = self.lr_goto_cache.get(x) + if not s: + s = {} + self.lr_goto_cache[x] = s + + gs = [] + for p in I: + n = p.lr_next + if n and n.lr_before == x: + s1 = s.get(id(n)) + if not s1: + s1 = {} + s[id(n)] = s1 + gs.append(n) + s = s1 + g = s.get('$end') + if not g: + if gs: + g = self.lr0_closure(gs) + s['$end'] = g + else: + s['$end'] = gs + self.lr_goto_cache[(id(I), x)] = g + return g + + # Compute the LR(0) sets of item function + def lr0_items(self): + C = [self.lr0_closure([self.grammar.Productions[0].lr_next])] + i = 0 + for I in C: + self.lr0_cidhash[id(I)] = i + i += 1 + + # Loop over the items in C and each grammar symbols + i = 0 + while i < len(C): + I = C[i] + i += 1 + + # Collect all of the symbols that could possibly be in the goto(I,X) sets + asyms = {} + for ii in I: + for s in ii.usyms: + asyms[s] = None + + for x in asyms: + g = self.lr0_goto(I, x) + if not g or id(g) in self.lr0_cidhash: + continue + self.lr0_cidhash[id(g)] = len(C) + C.append(g) + + return C + + # ----------------------------------------------------------------------------- + # ==== LALR(1) Parsing ==== + # + # LALR(1) parsing is almost exactly the same as SLR except that instead of + # relying upon Follow() sets when performing reductions, a more selective + # lookahead set that incorporates the state of the LR(0) machine is utilized. + # Thus, we mainly just have to focus on calculating the lookahead sets. + # + # The method used here is due to DeRemer and Pennelo (1982). + # + # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) + # Lookahead Sets", ACM Transactions on Programming Languages and Systems, + # Vol. 4, No. 4, Oct. 1982, pp. 615-649 + # + # Further details can also be found in: + # + # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", + # McGraw-Hill Book Company, (1985). + # + # ----------------------------------------------------------------------------- + + # ----------------------------------------------------------------------------- + # compute_nullable_nonterminals() + # + # Creates a dictionary containing all of the non-terminals that might produce + # an empty production. + # ----------------------------------------------------------------------------- + + def compute_nullable_nonterminals(self): + nullable = set() + num_nullable = 0 + while True: + for p in self.grammar.Productions[1:]: + if p.len == 0: + nullable.add(p.name) + continue + for t in p.prod: + if t not in nullable: + break + else: + nullable.add(p.name) + if len(nullable) == num_nullable: + break + num_nullable = len(nullable) + return nullable + + # ----------------------------------------------------------------------------- + # find_nonterminal_trans(C) + # + # Given a set of LR(0) items, this functions finds all of the non-terminal + # transitions. These are transitions in which a dot appears immediately before + # a non-terminal. Returns a list of tuples of the form (state,N) where state + # is the state number and N is the nonterminal symbol. + # + # The input C is the set of LR(0) items. + # ----------------------------------------------------------------------------- + + def find_nonterminal_transitions(self, C): + trans = [] + for stateno, state in enumerate(C): + for p in state: + if p.lr_index < p.len - 1: + t = (stateno, p.prod[p.lr_index+1]) + if t[1] in self.grammar.Nonterminals: + if t not in trans: + trans.append(t) + return trans + + # ----------------------------------------------------------------------------- + # dr_relation() + # + # Computes the DR(p,A) relationships for non-terminal transitions. The input + # is a tuple (state,N) where state is a number and N is a nonterminal symbol. + # + # Returns a list of terminals. + # ----------------------------------------------------------------------------- + + def dr_relation(self, C, trans, nullable): + dr_set = {} + state, N = trans + terms = [] + + g = self.lr0_goto(C[state], N) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index+1] + if a in self.grammar.Terminals: + if a not in terms: + terms.append(a) + + # This extra bit is to handle the start state + if state == 0 and N == self.grammar.Productions[0].prod[0]: + terms.append('$end') + + return terms + + # ----------------------------------------------------------------------------- + # reads_relation() + # + # Computes the READS() relation (p,A) READS (t,C). + # ----------------------------------------------------------------------------- + + def reads_relation(self, C, trans, empty): + # Look for empty transitions + rel = [] + state, N = trans + + g = self.lr0_goto(C[state], N) + j = self.lr0_cidhash.get(id(g), -1) + for p in g: + if p.lr_index < p.len - 1: + a = p.prod[p.lr_index + 1] + if a in empty: + rel.append((j, a)) + + return rel + + # ----------------------------------------------------------------------------- + # compute_lookback_includes() + # + # Determines the lookback and includes relations + # + # LOOKBACK: + # + # This relation is determined by running the LR(0) state machine forward. + # For example, starting with a production "N : . A B C", we run it forward + # to obtain "N : A B C ." We then build a relationship between this final + # state and the starting state. These relationships are stored in a dictionary + # lookdict. + # + # INCLUDES: + # + # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). + # + # This relation is used to determine non-terminal transitions that occur + # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) + # if the following holds: + # + # B -> LAT, where T -> epsilon and p' -L-> p + # + # L is essentially a prefix (which may be empty), T is a suffix that must be + # able to derive an empty string. State p' must lead to state p with the string L. + # + # ----------------------------------------------------------------------------- + + def compute_lookback_includes(self, C, trans, nullable): + lookdict = {} # Dictionary of lookback relations + includedict = {} # Dictionary of include relations + + # Make a dictionary of non-terminal transitions + dtrans = {} + for t in trans: + dtrans[t] = 1 + + # Loop over all transitions and compute lookbacks and includes + for state, N in trans: + lookb = [] + includes = [] + for p in C[state]: + if p.name != N: + continue + + # Okay, we have a name match. We now follow the production all the way + # through the state machine until we get the . on the right hand side + + lr_index = p.lr_index + j = state + while lr_index < p.len - 1: + lr_index = lr_index + 1 + t = p.prod[lr_index] + + # Check to see if this symbol and state are a non-terminal transition + if (j, t) in dtrans: + # Yes. Okay, there is some chance that this is an includes relation + # the only way to know for certain is whether the rest of the + # production derives empty + + li = lr_index + 1 + while li < p.len: + if p.prod[li] in self.grammar.Terminals: + break # No forget it + if p.prod[li] not in nullable: + break + li = li + 1 + else: + # Appears to be a relation between (j,t) and (state,N) + includes.append((j, t)) + + g = self.lr0_goto(C[j], t) # Go to next set + j = self.lr0_cidhash.get(id(g), -1) # Go to next state + + # When we get here, j is the final state, now we have to locate the production + for r in C[j]: + if r.name != p.name: + continue + if r.len != p.len: + continue + i = 0 + # This look is comparing a production ". A B C" with "A B C ." + while i < r.lr_index: + if r.prod[i] != p.prod[i+1]: + break + i = i + 1 + else: + lookb.append((j, r)) + for i in includes: + if i not in includedict: + includedict[i] = [] + includedict[i].append((state, N)) + lookdict[(state, N)] = lookb + + return lookdict, includedict + + # ----------------------------------------------------------------------------- + # compute_read_sets() + # + # Given a set of LR(0) items, this function computes the read sets. + # + # Inputs: C = Set of LR(0) items + # ntrans = Set of nonterminal transitions + # nullable = Set of empty transitions + # + # Returns a set containing the read sets + # ----------------------------------------------------------------------------- + + def compute_read_sets(self, C, ntrans, nullable): + FP = lambda x: self.dr_relation(C, x, nullable) + R = lambda x: self.reads_relation(C, x, nullable) + F = digraph(ntrans, R, FP) + return F + + # ----------------------------------------------------------------------------- + # compute_follow_sets() + # + # Given a set of LR(0) items, a set of non-terminal transitions, a readset, + # and an include set, this function computes the follow sets + # + # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} + # + # Inputs: + # ntrans = Set of nonterminal transitions + # readsets = Readset (previously computed) + # inclsets = Include sets (previously computed) + # + # Returns a set containing the follow sets + # ----------------------------------------------------------------------------- + + def compute_follow_sets(self, ntrans, readsets, inclsets): + FP = lambda x: readsets[x] + R = lambda x: inclsets.get(x, []) + F = digraph(ntrans, R, FP) + return F + + # ----------------------------------------------------------------------------- + # add_lookaheads() + # + # Attaches the lookahead symbols to grammar rules. + # + # Inputs: lookbacks - Set of lookback relations + # followset - Computed follow set + # + # This function directly attaches the lookaheads to productions contained + # in the lookbacks set + # ----------------------------------------------------------------------------- + + def add_lookaheads(self, lookbacks, followset): + for trans, lb in lookbacks.items(): + # Loop over productions in lookback + for state, p in lb: + if state not in p.lookaheads: + p.lookaheads[state] = [] + f = followset.get(trans, []) + for a in f: + if a not in p.lookaheads[state]: + p.lookaheads[state].append(a) + + # ----------------------------------------------------------------------------- + # add_lalr_lookaheads() + # + # This function does all of the work of adding lookahead information for use + # with LALR parsing + # ----------------------------------------------------------------------------- + + def add_lalr_lookaheads(self, C): + # Determine all of the nullable nonterminals + nullable = self.compute_nullable_nonterminals() + + # Find all non-terminal transitions + trans = self.find_nonterminal_transitions(C) + + # Compute read sets + readsets = self.compute_read_sets(C, trans, nullable) + + # Compute lookback/includes relations + lookd, included = self.compute_lookback_includes(C, trans, nullable) + + # Compute LALR FOLLOW sets + followsets = self.compute_follow_sets(trans, readsets, included) + + # Add all of the lookaheads + self.add_lookaheads(lookd, followsets) + + # ----------------------------------------------------------------------------- + # lr_parse_table() + # + # This function constructs the parse tables for SLR or LALR + # ----------------------------------------------------------------------------- + def lr_parse_table(self): + Productions = self.grammar.Productions + Precedence = self.grammar.Precedence + goto = self.lr_goto # Goto array + action = self.lr_action # Action array + log = self.log # Logger for output + + actionp = {} # Action production array (temporary) + + log.info('Parsing method: %s', self.lr_method) + + # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items + # This determines the number of states + + C = self.lr0_items() + + if self.lr_method == 'LALR': + self.add_lalr_lookaheads(C) + + # Build the parser table, state by state + st = 0 + for I in C: + # Loop over each production in I + actlist = [] # List of actions + st_action = {} + st_actionp = {} + st_goto = {} + log.info('') + log.info('state %d', st) + log.info('') + for p in I: + log.info(' (%d) %s', p.number, p) + log.info('') + + for p in I: + if p.len == p.lr_index + 1: + if p.name == "S'": + # Start symbol. Accept! + st_action['$end'] = 0 + st_actionp['$end'] = p + else: + # We are at the end of a production. Reduce! + if self.lr_method == 'LALR': + laheads = p.lookaheads[st] + else: + laheads = self.grammar.Follow[p.name] + for a in laheads: + actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p))) + r = st_action.get(a) + if r is not None: + # Whoa. Have a shift/reduce or reduce/reduce conflict + if r > 0: + # Need to decide on shift or reduce here + # By default we favor shifting. Need to add + # some precedence rules here. + + # Shift precedence comes from the token + sprec, slevel = Precedence.get(a, ('right', 0)) + + # Reduce precedence comes from rule being reduced (p) + rprec, rlevel = Productions[p.number].prec + + if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): + # We really need to reduce here. + st_action[a] = -p.number + st_actionp[a] = p + if not slevel and not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as reduce', a) + self.sr_conflicts.append((st, a, 'reduce')) + Productions[p.number].reduced += 1 + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the shift + if not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as shift', a) + self.sr_conflicts.append((st, a, 'shift')) + elif r < 0: + # Reduce/reduce conflict. In this case, we favor the rule + # that was defined first in the grammar file + oldp = Productions[-r] + pp = Productions[p.number] + if oldp.line > pp.line: + st_action[a] = -p.number + st_actionp[a] = p + chosenp, rejectp = pp, oldp + Productions[p.number].reduced += 1 + Productions[oldp.number].reduced -= 1 + else: + chosenp, rejectp = oldp, pp + self.rr_conflicts.append((st, chosenp, rejectp)) + log.info(' ! reduce/reduce conflict for %s resolved using rule %d (%s)', + a, st_actionp[a].number, st_actionp[a]) + else: + raise LALRError('Unknown conflict in state %d' % st) + else: + st_action[a] = -p.number + st_actionp[a] = p + Productions[p.number].reduced += 1 + else: + i = p.lr_index + a = p.prod[i+1] # Get symbol right after the "." + if a in self.grammar.Terminals: + g = self.lr0_goto(I, a) + j = self.lr0_cidhash.get(id(g), -1) + if j >= 0: + # We are in a shift state + actlist.append((a, p, 'shift and go to state %d' % j)) + r = st_action.get(a) + if r is not None: + # Whoa have a shift/reduce or shift/shift conflict + if r > 0: + if r != j: + raise LALRError('Shift/shift conflict in state %d' % st) + elif r < 0: + # Do a precedence check. + # - if precedence of reduce rule is higher, we reduce. + # - if precedence of reduce is same and left assoc, we reduce. + # - otherwise we shift + + # Shift precedence comes from the token + sprec, slevel = Precedence.get(a, ('right', 0)) + + # Reduce precedence comes from the rule that could have been reduced + rprec, rlevel = Productions[st_actionp[a].number].prec + + if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): + # We decide to shift here... highest precedence to shift + Productions[st_actionp[a].number].reduced -= 1 + st_action[a] = j + st_actionp[a] = p + if not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as shift', a) + self.sr_conflicts.append((st, a, 'shift')) + elif (slevel == rlevel) and (rprec == 'nonassoc'): + st_action[a] = None + else: + # Hmmm. Guess we'll keep the reduce + if not slevel and not rlevel: + log.info(' ! shift/reduce conflict for %s resolved as reduce', a) + self.sr_conflicts.append((st, a, 'reduce')) + + else: + raise LALRError('Unknown conflict in state %d' % st) + else: + st_action[a] = j + st_actionp[a] = p + + # Print the actions associated with each terminal + _actprint = {} + for a, p, m in actlist: + if a in st_action: + if p is st_actionp[a]: + log.info(' %-15s %s', a, m) + _actprint[(a, m)] = 1 + log.info('') + # Print the actions that were not used. (debugging) + not_used = 0 + for a, p, m in actlist: + if a in st_action: + if p is not st_actionp[a]: + if not (a, m) in _actprint: + log.debug(' ! %-15s [ %s ]', a, m) + not_used = 1 + _actprint[(a, m)] = 1 + if not_used: + log.debug('') + + # Construct the goto table for this state + + nkeys = {} + for ii in I: + for s in ii.usyms: + if s in self.grammar.Nonterminals: + nkeys[s] = None + for n in nkeys: + g = self.lr0_goto(I, n) + j = self.lr0_cidhash.get(id(g), -1) + if j >= 0: + st_goto[n] = j + log.info(' %-30s shift and go to state %d', n, j) + + action[st] = st_action + actionp[st] = st_actionp + goto[st] = st_goto + st += 1 + + # ----------------------------------------------------------------------------- + # write() + # + # This function writes the LR parsing tables to a file + # ----------------------------------------------------------------------------- + + def write_table(self, tabmodule, outputdir='', signature=''): + if isinstance(tabmodule, types.ModuleType): + raise IOError("Won't overwrite existing tabmodule") + + basemodulename = tabmodule.split('.')[-1] + filename = os.path.join(outputdir, basemodulename) + '.py' + try: + f = open(filename, 'w') + + f.write(''' +# %s +# This file is automatically generated. Do not edit. +_tabversion = %r + +_lr_method = %r + +_lr_signature = %r + ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature)) + + # Change smaller to 0 to go back to original tables + smaller = 1 + + # Factor out names to try and make smaller + if smaller: + items = {} + + for s, nd in self.lr_action.items(): + for name, v in nd.items(): + i = items.get(name) + if not i: + i = ([], []) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write('\n_lr_action_items = {') + for k, v in items.items(): + f.write('%r:([' % k) + for i in v[0]: + f.write('%r,' % i) + f.write('],[') + for i in v[1]: + f.write('%r,' % i) + + f.write(']),') + f.write('}\n') + + f.write(''' +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items +''') + + else: + f.write('\n_lr_action = { ') + for k, v in self.lr_action.items(): + f.write('(%r,%r):%r,' % (k[0], k[1], v)) + f.write('}\n') + + if smaller: + # Factor out names to try and make smaller + items = {} + + for s, nd in self.lr_goto.items(): + for name, v in nd.items(): + i = items.get(name) + if not i: + i = ([], []) + items[name] = i + i[0].append(s) + i[1].append(v) + + f.write('\n_lr_goto_items = {') + for k, v in items.items(): + f.write('%r:([' % k) + for i in v[0]: + f.write('%r,' % i) + f.write('],[') + for i in v[1]: + f.write('%r,' % i) + + f.write(']),') + f.write('}\n') + + f.write(''' +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +''') + else: + f.write('\n_lr_goto = { ') + for k, v in self.lr_goto.items(): + f.write('(%r,%r):%r,' % (k[0], k[1], v)) + f.write('}\n') + + # Write production table + f.write('_lr_productions = [\n') + for p in self.lr_productions: + if p.func: + f.write(' (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len, + p.func, os.path.basename(p.file), p.line)) + else: + f.write(' (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len)) + f.write(']\n') + f.close() + + except IOError as e: + raise + + + # ----------------------------------------------------------------------------- + # pickle_table() + # + # This function pickles the LR parsing tables to a supplied file object + # ----------------------------------------------------------------------------- + + def pickle_table(self, filename, signature=''): + try: + import cPickle as pickle + except ImportError: + import pickle + with open(filename, 'wb') as outf: + pickle.dump(__tabversion__, outf, pickle_protocol) + pickle.dump(self.lr_method, outf, pickle_protocol) + pickle.dump(signature, outf, pickle_protocol) + pickle.dump(self.lr_action, outf, pickle_protocol) + pickle.dump(self.lr_goto, outf, pickle_protocol) + + outp = [] + for p in self.lr_productions: + if p.func: + outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line)) + else: + outp.append((str(p), p.name, p.len, None, None, None)) + pickle.dump(outp, outf, pickle_protocol) + +# ----------------------------------------------------------------------------- +# === INTROSPECTION === +# +# The following functions and classes are used to implement the PLY +# introspection features followed by the yacc() function itself. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# get_caller_module_dict() +# +# This function returns a dictionary containing all of the symbols defined within +# a caller further down the call stack. This is used to get the environment +# associated with the yacc() call if none was provided. +# ----------------------------------------------------------------------------- + +def get_caller_module_dict(levels): + f = sys._getframe(levels) + ldict = f.f_globals.copy() + if f.f_globals != f.f_locals: + ldict.update(f.f_locals) + return ldict + +# ----------------------------------------------------------------------------- +# parse_grammar() +# +# This takes a raw grammar rule string and parses it into production data +# ----------------------------------------------------------------------------- +def parse_grammar(doc, file, line): + grammar = [] + # Split the doc string into lines + pstrings = doc.splitlines() + lastp = None + dline = line + for ps in pstrings: + dline += 1 + p = ps.split() + if not p: + continue + try: + if p[0] == '|': + # This is a continuation of a previous rule + if not lastp: + raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline)) + prodname = lastp + syms = p[1:] + else: + prodname = p[0] + lastp = prodname + syms = p[2:] + assign = p[1] + if assign != ':' and assign != '::=': + raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline)) + + grammar.append((file, dline, prodname, syms)) + except SyntaxError: + raise + except Exception: + raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())) + + return grammar + +# ----------------------------------------------------------------------------- +# ParserReflect() +# +# This class represents information extracted for building a parser including +# start symbol, error function, tokens, precedence list, action functions, +# etc. +# ----------------------------------------------------------------------------- +class ParserReflect(object): + def __init__(self, pdict, log=None): + self.pdict = pdict + self.start = None + self.error_func = None + self.tokens = None + self.modules = set() + self.grammar = [] + self.error = False + + if log is None: + self.log = PlyLogger(sys.stderr) + else: + self.log = log + + # Get all of the basic information + def get_all(self): + self.get_start() + self.get_error_func() + self.get_tokens() + self.get_precedence() + self.get_pfunctions() + + # Validate all of the information + def validate_all(self): + self.validate_start() + self.validate_error_func() + self.validate_tokens() + self.validate_precedence() + self.validate_pfunctions() + self.validate_modules() + return self.error + + # Compute a signature over the grammar + def signature(self): + parts = [] + try: + if self.start: + parts.append(self.start) + if self.prec: + parts.append(''.join([''.join(p) for p in self.prec])) + if self.tokens: + parts.append(' '.join(self.tokens)) + for f in self.pfuncs: + if f[3]: + parts.append(f[3]) + except (TypeError, ValueError): + pass + return ''.join(parts) + + # ----------------------------------------------------------------------------- + # validate_modules() + # + # This method checks to see if there are duplicated p_rulename() functions + # in the parser module file. Without this function, it is really easy for + # users to make mistakes by cutting and pasting code fragments (and it's a real + # bugger to try and figure out why the resulting parser doesn't work). Therefore, + # we just do a little regular expression pattern matching of def statements + # to try and detect duplicates. + # ----------------------------------------------------------------------------- + + def validate_modules(self): + # Match def p_funcname( + fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') + + for module in self.modules: + try: + lines, linen = inspect.getsourcelines(module) + except IOError: + continue + + counthash = {} + for linen, line in enumerate(lines): + linen += 1 + m = fre.match(line) + if m: + name = m.group(1) + prev = counthash.get(name) + if not prev: + counthash[name] = linen + else: + filename = inspect.getsourcefile(module) + self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d', + filename, linen, name, prev) + + # Get the start symbol + def get_start(self): + self.start = self.pdict.get('start') + + # Validate the start symbol + def validate_start(self): + if self.start is not None: + if not isinstance(self.start, string_types): + self.log.error("'start' must be a string") + + # Look for error handler + def get_error_func(self): + self.error_func = self.pdict.get('p_error') + + # Validate the error function + def validate_error_func(self): + if self.error_func: + if isinstance(self.error_func, types.FunctionType): + ismethod = 0 + elif isinstance(self.error_func, types.MethodType): + ismethod = 1 + else: + self.log.error("'p_error' defined, but is not a function or method") + self.error = True + return + + eline = self.error_func.__code__.co_firstlineno + efile = self.error_func.__code__.co_filename + module = inspect.getmodule(self.error_func) + self.modules.add(module) + + argcount = self.error_func.__code__.co_argcount - ismethod + if argcount != 1: + self.log.error('%s:%d: p_error() requires 1 argument', efile, eline) + self.error = True + + # Get the tokens map + def get_tokens(self): + tokens = self.pdict.get('tokens') + if not tokens: + self.log.error('No token list is defined') + self.error = True + return + + if not isinstance(tokens, (list, tuple)): + self.log.error('tokens must be a list or tuple') + self.error = True + return + + if not tokens: + self.log.error('tokens is empty') + self.error = True + return + + self.tokens = tokens + + # Validate the tokens + def validate_tokens(self): + # Validate the tokens. + if 'error' in self.tokens: + self.log.error("Illegal token name 'error'. Is a reserved word") + self.error = True + return + + terminals = set() + for n in self.tokens: + if n in terminals: + self.log.warning('Token %r multiply defined', n) + terminals.add(n) + + # Get the precedence map (if any) + def get_precedence(self): + self.prec = self.pdict.get('precedence') + + # Validate and parse the precedence map + def validate_precedence(self): + preclist = [] + if self.prec: + if not isinstance(self.prec, (list, tuple)): + self.log.error('precedence must be a list or tuple') + self.error = True + return + for level, p in enumerate(self.prec): + if not isinstance(p, (list, tuple)): + self.log.error('Bad precedence table') + self.error = True + return + + if len(p) < 2: + self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p) + self.error = True + return + assoc = p[0] + if not isinstance(assoc, string_types): + self.log.error('precedence associativity must be a string') + self.error = True + return + for term in p[1:]: + if not isinstance(term, string_types): + self.log.error('precedence items must be strings') + self.error = True + return + preclist.append((term, assoc, level+1)) + self.preclist = preclist + + # Get all p_functions from the grammar + def get_pfunctions(self): + p_functions = [] + for name, item in self.pdict.items(): + if not name.startswith('p_') or name == 'p_error': + continue + if isinstance(item, (types.FunctionType, types.MethodType)): + line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno) + module = inspect.getmodule(item) + p_functions.append((line, module, name, item.__doc__)) + + # Sort all of the actions by line number; make sure to stringify + # modules to make them sortable, since `line` may not uniquely sort all + # p functions + p_functions.sort(key=lambda p_function: ( + p_function[0], + str(p_function[1]), + p_function[2], + p_function[3])) + self.pfuncs = p_functions + + # Validate all of the p_functions + def validate_pfunctions(self): + grammar = [] + # Check for non-empty symbols + if len(self.pfuncs) == 0: + self.log.error('no rules of the form p_rulename are defined') + self.error = True + return + + for line, module, name, doc in self.pfuncs: + file = inspect.getsourcefile(module) + func = self.pdict[name] + if isinstance(func, types.MethodType): + reqargs = 2 + else: + reqargs = 1 + if func.__code__.co_argcount > reqargs: + self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__) + self.error = True + elif func.__code__.co_argcount < reqargs: + self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__) + self.error = True + elif not func.__doc__: + self.log.warning('%s:%d: No documentation string specified in function %r (ignored)', + file, line, func.__name__) + else: + try: + parsed_g = parse_grammar(doc, file, line) + for g in parsed_g: + grammar.append((name, g)) + except SyntaxError as e: + self.log.error(str(e)) + self.error = True + + # Looks like a valid grammar rule + # Mark the file in which defined. + self.modules.add(module) + + # Secondary validation step that looks for p_ definitions that are not functions + # or functions that look like they might be grammar rules. + + for n, v in self.pdict.items(): + if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)): + continue + if n.startswith('t_'): + continue + if n.startswith('p_') and n != 'p_error': + self.log.warning('%r not defined as a function', n) + if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or + (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)): + if v.__doc__: + try: + doc = v.__doc__.split(' ') + if doc[1] == ':': + self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix', + v.__code__.co_filename, v.__code__.co_firstlineno, n) + except IndexError: + pass + + self.grammar = grammar + +# ----------------------------------------------------------------------------- +# yacc(module) +# +# Build a parser +# ----------------------------------------------------------------------------- + +def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, + check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file, + outputdir=None, debuglog=None, errorlog=None, picklefile=None): + + if tabmodule is None: + tabmodule = tab_module + + # Reference to the parsing method of the last built parser + global parse + + # If pickling is enabled, table files are not created + if picklefile: + write_tables = 0 + + if errorlog is None: + errorlog = PlyLogger(sys.stderr) + + # Get the module dictionary used for the parser + if module: + _items = [(k, getattr(module, k)) for k in dir(module)] + pdict = dict(_items) + # If no __file__ attribute is available, try to obtain it from the __module__ instead + if '__file__' not in pdict: + pdict['__file__'] = sys.modules[pdict['__module__']].__file__ + else: + pdict = get_caller_module_dict(2) + + if outputdir is None: + # If no output directory is set, the location of the output files + # is determined according to the following rules: + # - If tabmodule specifies a package, files go into that package directory + # - Otherwise, files go in the same directory as the specifying module + if isinstance(tabmodule, types.ModuleType): + srcfile = tabmodule.__file__ + else: + if '.' not in tabmodule: + srcfile = pdict['__file__'] + else: + parts = tabmodule.split('.') + pkgname = '.'.join(parts[:-1]) + exec('import %s' % pkgname) + srcfile = getattr(sys.modules[pkgname], '__file__', '') + outputdir = os.path.dirname(srcfile) + + # Determine if the module is package of a package or not. + # If so, fix the tabmodule setting so that tables load correctly + pkg = pdict.get('__package__') + if pkg and isinstance(tabmodule, str): + if '.' not in tabmodule: + tabmodule = pkg + '.' + tabmodule + + + + # Set start symbol if it's specified directly using an argument + if start is not None: + pdict['start'] = start + + # Collect parser information from the dictionary + pinfo = ParserReflect(pdict, log=errorlog) + pinfo.get_all() + + if pinfo.error: + raise YaccError('Unable to build parser') + + # Check signature against table files (if any) + signature = pinfo.signature() + + # Read the tables + try: + lr = LRTable() + if picklefile: + read_signature = lr.read_pickle(picklefile) + else: + read_signature = lr.read_table(tabmodule) + if optimize or (read_signature == signature): + try: + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr, pinfo.error_func) + parse = parser.parse + return parser + except Exception as e: + errorlog.warning('There was a problem loading the table file: %r', e) + except VersionError as e: + errorlog.warning(str(e)) + except ImportError: + pass + + if debuglog is None: + if debug: + try: + debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w')) + except IOError as e: + errorlog.warning("Couldn't open %r. %s" % (debugfile, e)) + debuglog = NullLogger() + else: + debuglog = NullLogger() + + debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__) + + errors = False + + # Validate the parser information + if pinfo.validate_all(): + raise YaccError('Unable to build parser') + + if not pinfo.error_func: + errorlog.warning('no p_error() function is defined') + + # Create a grammar object + grammar = Grammar(pinfo.tokens) + + # Set precedence level for terminals + for term, assoc, level in pinfo.preclist: + try: + grammar.set_precedence(term, assoc, level) + except GrammarError as e: + errorlog.warning('%s', e) + + # Add productions to the grammar + for funcname, gram in pinfo.grammar: + file, line, prodname, syms = gram + try: + grammar.add_production(prodname, syms, funcname, file, line) + except GrammarError as e: + errorlog.error('%s', e) + errors = True + + # Set the grammar start symbols + try: + if start is None: + grammar.set_start(pinfo.start) + else: + grammar.set_start(start) + except GrammarError as e: + errorlog.error(str(e)) + errors = True + + if errors: + raise YaccError('Unable to build parser') + + # Verify the grammar structure + undefined_symbols = grammar.undefined_symbols() + for sym, prod in undefined_symbols: + errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym) + errors = True + + unused_terminals = grammar.unused_terminals() + if unused_terminals: + debuglog.info('') + debuglog.info('Unused terminals:') + debuglog.info('') + for term in unused_terminals: + errorlog.warning('Token %r defined, but not used', term) + debuglog.info(' %s', term) + + # Print out all productions to the debug log + if debug: + debuglog.info('') + debuglog.info('Grammar') + debuglog.info('') + for n, p in enumerate(grammar.Productions): + debuglog.info('Rule %-5d %s', n, p) + + # Find unused non-terminals + unused_rules = grammar.unused_rules() + for prod in unused_rules: + errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name) + + if len(unused_terminals) == 1: + errorlog.warning('There is 1 unused token') + if len(unused_terminals) > 1: + errorlog.warning('There are %d unused tokens', len(unused_terminals)) + + if len(unused_rules) == 1: + errorlog.warning('There is 1 unused rule') + if len(unused_rules) > 1: + errorlog.warning('There are %d unused rules', len(unused_rules)) + + if debug: + debuglog.info('') + debuglog.info('Terminals, with rules where they appear') + debuglog.info('') + terms = list(grammar.Terminals) + terms.sort() + for term in terms: + debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]])) + + debuglog.info('') + debuglog.info('Nonterminals, with rules where they appear') + debuglog.info('') + nonterms = list(grammar.Nonterminals) + nonterms.sort() + for nonterm in nonterms: + debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]])) + debuglog.info('') + + if check_recursion: + unreachable = grammar.find_unreachable() + for u in unreachable: + errorlog.warning('Symbol %r is unreachable', u) + + infinite = grammar.infinite_cycles() + for inf in infinite: + errorlog.error('Infinite recursion detected for symbol %r', inf) + errors = True + + unused_prec = grammar.unused_precedence() + for term, assoc in unused_prec: + errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term) + errors = True + + if errors: + raise YaccError('Unable to build parser') + + # Run the LRGeneratedTable on the grammar + if debug: + errorlog.debug('Generating %s tables', method) + + lr = LRGeneratedTable(grammar, method, debuglog) + + if debug: + num_sr = len(lr.sr_conflicts) + + # Report shift/reduce and reduce/reduce conflicts + if num_sr == 1: + errorlog.warning('1 shift/reduce conflict') + elif num_sr > 1: + errorlog.warning('%d shift/reduce conflicts', num_sr) + + num_rr = len(lr.rr_conflicts) + if num_rr == 1: + errorlog.warning('1 reduce/reduce conflict') + elif num_rr > 1: + errorlog.warning('%d reduce/reduce conflicts', num_rr) + + # Write out conflicts to the output file + if debug and (lr.sr_conflicts or lr.rr_conflicts): + debuglog.warning('') + debuglog.warning('Conflicts:') + debuglog.warning('') + + for state, tok, resolution in lr.sr_conflicts: + debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s', tok, state, resolution) + + already_reported = set() + for state, rule, rejected in lr.rr_conflicts: + if (state, id(rule), id(rejected)) in already_reported: + continue + debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) + debuglog.warning('rejected rule (%s) in state %d', rejected, state) + errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) + errorlog.warning('rejected rule (%s) in state %d', rejected, state) + already_reported.add((state, id(rule), id(rejected))) + + warned_never = [] + for state, rule, rejected in lr.rr_conflicts: + if not rejected.reduced and (rejected not in warned_never): + debuglog.warning('Rule (%s) is never reduced', rejected) + errorlog.warning('Rule (%s) is never reduced', rejected) + warned_never.append(rejected) + + # Write the table file if requested + if write_tables: + try: + lr.write_table(tabmodule, outputdir, signature) + except IOError as e: + errorlog.warning("Couldn't create %r. %s" % (tabmodule, e)) + + # Write a pickled version of the tables + if picklefile: + try: + lr.pickle_table(picklefile, signature) + except IOError as e: + errorlog.warning("Couldn't create %r. %s" % (picklefile, e)) + + # Build the parser + lr.bind_callables(pinfo.pdict) + parser = LRParser(lr, pinfo.error_func) + + parse = parser.parse + return parser diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py new file mode 100644 index 000000000..acf5ca1a3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py @@ -0,0 +1,74 @@ +# ply: ygen.py +# +# This is a support program that auto-generates different versions of the YACC parsing +# function with different features removed for the purposes of performance. +# +# Users should edit the method LParser.parsedebug() in yacc.py. The source code +# for that method is then used to create the other methods. See the comments in +# yacc.py for further details. + +import os.path +import shutil + +def get_source_range(lines, tag): + srclines = enumerate(lines) + start_tag = '#--! %s-start' % tag + end_tag = '#--! %s-end' % tag + + for start_index, line in srclines: + if line.strip().startswith(start_tag): + break + + for end_index, line in srclines: + if line.strip().endswith(end_tag): + break + + return (start_index + 1, end_index) + +def filter_section(lines, tag): + filtered_lines = [] + include = True + tag_text = '#--! %s' % tag + for line in lines: + if line.strip().startswith(tag_text): + include = not include + elif include: + filtered_lines.append(line) + return filtered_lines + +def main(): + dirname = os.path.dirname(__file__) + shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak')) + with open(os.path.join(dirname, 'yacc.py'), 'r') as f: + lines = f.readlines() + + parse_start, parse_end = get_source_range(lines, 'parsedebug') + parseopt_start, parseopt_end = get_source_range(lines, 'parseopt') + parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack') + + # Get the original source + orig_lines = lines[parse_start:parse_end] + + # Filter the DEBUG sections out + parseopt_lines = filter_section(orig_lines, 'DEBUG') + + # Filter the TRACKING sections out + parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING') + + # Replace the parser source sections with updated versions + lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines + lines[parseopt_start:parseopt_end] = parseopt_lines + + lines = [line.rstrip()+'\n' for line in lines] + with open(os.path.join(dirname, 'yacc.py'), 'w') as f: + f.writelines(lines) + + print('Updated yacc.py') + +if __name__ == '__main__': + main() + + + + + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py new file mode 100644 index 000000000..b8f4c4395 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py @@ -0,0 +1,133 @@ +#----------------------------------------------------------------- +# plyparser.py +# +# PLYParser class and other utilities for simplifying programming +# parsers with PLY +# +# Eli Bendersky [https://eli.thegreenplace.net/] +# License: BSD +#----------------------------------------------------------------- + +import warnings + +class Coord(object): + """ Coordinates of a syntactic element. Consists of: + - File name + - Line number + - (optional) column number, for the Lexer + """ + __slots__ = ('file', 'line', 'column', '__weakref__') + def __init__(self, file, line, column=None): + self.file = file + self.line = line + self.column = column + + def __str__(self): + str = "%s:%s" % (self.file, self.line) + if self.column: str += ":%s" % self.column + return str + + +class ParseError(Exception): pass + + +class PLYParser(object): + def _create_opt_rule(self, rulename): + """ Given a rule name, creates an optional ply.yacc rule + for it. The name of the optional rule is + _opt + """ + optname = rulename + '_opt' + + def optrule(self, p): + p[0] = p[1] + + optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename) + optrule.__name__ = 'p_%s' % optname + setattr(self.__class__, optrule.__name__, optrule) + + def _coord(self, lineno, column=None): + return Coord( + file=self.clex.filename, + line=lineno, + column=column) + + def _token_coord(self, p, token_idx): + """ Returns the coordinates for the YaccProduction object 'p' indexed + with 'token_idx'. The coordinate includes the 'lineno' and + 'column'. Both follow the lex semantic, starting from 1. + """ + last_cr = p.lexer.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx)) + if last_cr < 0: + last_cr = -1 + column = (p.lexpos(token_idx) - (last_cr)) + return self._coord(p.lineno(token_idx), column) + + def _parse_error(self, msg, coord): + raise ParseError("%s: %s" % (coord, msg)) + + +def parameterized(*params): + """ Decorator to create parameterized rules. + + Parameterized rule methods must be named starting with 'p_' and contain + 'xxx', and their docstrings may contain 'xxx' and 'yyy'. These will be + replaced by the given parameter tuples. For example, ``p_xxx_rule()`` with + docstring 'xxx_rule : yyy' when decorated with + ``@parameterized(('id', 'ID'))`` produces ``p_id_rule()`` with the docstring + 'id_rule : ID'. Using multiple tuples produces multiple rules. + """ + def decorate(rule_func): + rule_func._params = params + return rule_func + return decorate + + +def template(cls): + """ Class decorator to generate rules from parameterized rule templates. + + See `parameterized` for more information on parameterized rules. + """ + issued_nodoc_warning = False + for attr_name in dir(cls): + if attr_name.startswith('p_'): + method = getattr(cls, attr_name) + if hasattr(method, '_params'): + # Remove the template method + delattr(cls, attr_name) + # Create parameterized rules from this method; only run this if + # the method has a docstring. This is to address an issue when + # pycparser's users are installed in -OO mode which strips + # docstrings away. + # See: https://github.com/eliben/pycparser/pull/198/ and + # https://github.com/eliben/pycparser/issues/197 + # for discussion. + if method.__doc__ is not None: + _create_param_rules(cls, method) + elif not issued_nodoc_warning: + warnings.warn( + 'parsing methods must have __doc__ for pycparser to work properly', + RuntimeWarning, + stacklevel=2) + issued_nodoc_warning = True + return cls + + +def _create_param_rules(cls, func): + """ Create ply.yacc rules based on a parameterized rule function + + Generates new methods (one per each pair of parameters) based on the + template rule function `func`, and attaches them to `cls`. The rule + function's parameters must be accessible via its `_params` attribute. + """ + for xxx, yyy in func._params: + # Use the template method's body for each new method + def param_rule(self, p): + func(self, p) + + # Substitute in the params for the grammar rule and function name + param_rule.__doc__ = func.__doc__.replace('xxx', xxx).replace('yyy', yyy) + param_rule.__name__ = func.__name__.replace('xxx', xxx) + + # Attach the new method to the class + setattr(cls, param_rule.__name__, param_rule) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py new file mode 100644 index 000000000..0622c3660 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py @@ -0,0 +1,366 @@ + +# yacctab.py +# This file is automatically generated. Do not edit. +_tabversion = '3.10' + +_lr_method = 'LALR' + +_lr_signature = 'translation_unit_or_emptyleftLORleftLANDleftORleftXORleftANDleftEQNEleftGTGELTLEleftRSHIFTLSHIFTleftPLUSMINUSleftTIMESDIVIDEMODAUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN FLOAT FOR GOTO IF INLINE INT LONG REGISTER OFFSETOF RESTRICT RETURN SHORT SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE __INT128 _BOOL _COMPLEX _NORETURN _THREAD_LOCAL _STATIC_ASSERT _ATOMIC _ALIGNOF _ALIGNAS ID TYPEID INT_CONST_DEC INT_CONST_OCT INT_CONST_HEX INT_CONST_BIN INT_CONST_CHAR FLOAT_CONST HEX_FLOAT_CONST CHAR_CONST WCHAR_CONST U8CHAR_CONST U16CHAR_CONST U32CHAR_CONST STRING_LITERAL WSTRING_LITERAL U8STRING_LITERAL U16STRING_LITERAL U32STRING_LITERAL PLUS MINUS TIMES DIVIDE MOD OR AND NOT XOR LSHIFT RSHIFT LOR LAND LNOT LT LE GT GE EQ NE EQUALS TIMESEQUAL DIVEQUAL MODEQUAL PLUSEQUAL MINUSEQUAL LSHIFTEQUAL RSHIFTEQUAL ANDEQUAL XOREQUAL OREQUAL PLUSPLUS MINUSMINUS ARROW CONDOP LPAREN RPAREN LBRACKET RBRACKET LBRACE RBRACE COMMA PERIOD SEMI COLON ELLIPSIS PPHASH PPPRAGMA PPPRAGMASTRabstract_declarator_opt : empty\n| abstract_declaratorassignment_expression_opt : empty\n| assignment_expressionblock_item_list_opt : empty\n| block_item_listdeclaration_list_opt : empty\n| declaration_listdeclaration_specifiers_no_type_opt : empty\n| declaration_specifiers_no_typedesignation_opt : empty\n| designationexpression_opt : empty\n| expressionid_init_declarator_list_opt : empty\n| id_init_declarator_listidentifier_list_opt : empty\n| identifier_listinit_declarator_list_opt : empty\n| init_declarator_listinitializer_list_opt : empty\n| initializer_listparameter_type_list_opt : empty\n| parameter_type_liststruct_declarator_list_opt : empty\n| struct_declarator_listtype_qualifier_list_opt : empty\n| type_qualifier_list direct_id_declarator : ID\n direct_id_declarator : LPAREN id_declarator RPAREN\n direct_id_declarator : direct_id_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_id_declarator : direct_id_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n | direct_id_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n direct_id_declarator : direct_id_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n direct_id_declarator : direct_id_declarator LPAREN parameter_type_list RPAREN\n | direct_id_declarator LPAREN identifier_list_opt RPAREN\n direct_typeid_declarator : TYPEID\n direct_typeid_declarator : LPAREN typeid_declarator RPAREN\n direct_typeid_declarator : direct_typeid_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_typeid_declarator : direct_typeid_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n | direct_typeid_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n direct_typeid_declarator : direct_typeid_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n direct_typeid_declarator : direct_typeid_declarator LPAREN parameter_type_list RPAREN\n | direct_typeid_declarator LPAREN identifier_list_opt RPAREN\n direct_typeid_noparen_declarator : TYPEID\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n | direct_typeid_noparen_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LPAREN parameter_type_list RPAREN\n | direct_typeid_noparen_declarator LPAREN identifier_list_opt RPAREN\n id_declarator : direct_id_declarator\n id_declarator : pointer direct_id_declarator\n typeid_declarator : direct_typeid_declarator\n typeid_declarator : pointer direct_typeid_declarator\n typeid_noparen_declarator : direct_typeid_noparen_declarator\n typeid_noparen_declarator : pointer direct_typeid_noparen_declarator\n translation_unit_or_empty : translation_unit\n | empty\n translation_unit : external_declaration\n translation_unit : translation_unit external_declaration\n external_declaration : function_definition\n external_declaration : declaration\n external_declaration : pp_directive\n | pppragma_directive\n external_declaration : SEMI\n external_declaration : static_assert\n static_assert : _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN\n | _STATIC_ASSERT LPAREN constant_expression RPAREN\n pp_directive : PPHASH\n pppragma_directive : PPPRAGMA\n | PPPRAGMA PPPRAGMASTR\n function_definition : id_declarator declaration_list_opt compound_statement\n function_definition : declaration_specifiers id_declarator declaration_list_opt compound_statement\n statement : labeled_statement\n | expression_statement\n | compound_statement\n | selection_statement\n | iteration_statement\n | jump_statement\n | pppragma_directive\n | static_assert\n pragmacomp_or_statement : pppragma_directive statement\n | statement\n decl_body : declaration_specifiers init_declarator_list_opt\n | declaration_specifiers_no_type id_init_declarator_list_opt\n declaration : decl_body SEMI\n declaration_list : declaration\n | declaration_list declaration\n declaration_specifiers_no_type : type_qualifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : storage_class_specifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : function_specifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : atomic_specifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : alignment_specifier declaration_specifiers_no_type_opt\n declaration_specifiers : declaration_specifiers type_qualifier\n declaration_specifiers : declaration_specifiers storage_class_specifier\n declaration_specifiers : declaration_specifiers function_specifier\n declaration_specifiers : declaration_specifiers type_specifier_no_typeid\n declaration_specifiers : type_specifier\n declaration_specifiers : declaration_specifiers_no_type type_specifier\n declaration_specifiers : declaration_specifiers alignment_specifier\n storage_class_specifier : AUTO\n | REGISTER\n | STATIC\n | EXTERN\n | TYPEDEF\n | _THREAD_LOCAL\n function_specifier : INLINE\n | _NORETURN\n type_specifier_no_typeid : VOID\n | _BOOL\n | CHAR\n | SHORT\n | INT\n | LONG\n | FLOAT\n | DOUBLE\n | _COMPLEX\n | SIGNED\n | UNSIGNED\n | __INT128\n type_specifier : typedef_name\n | enum_specifier\n | struct_or_union_specifier\n | type_specifier_no_typeid\n | atomic_specifier\n atomic_specifier : _ATOMIC LPAREN type_name RPAREN\n type_qualifier : CONST\n | RESTRICT\n | VOLATILE\n | _ATOMIC\n init_declarator_list : init_declarator\n | init_declarator_list COMMA init_declarator\n init_declarator : declarator\n | declarator EQUALS initializer\n id_init_declarator_list : id_init_declarator\n | id_init_declarator_list COMMA init_declarator\n id_init_declarator : id_declarator\n | id_declarator EQUALS initializer\n specifier_qualifier_list : specifier_qualifier_list type_specifier_no_typeid\n specifier_qualifier_list : specifier_qualifier_list type_qualifier\n specifier_qualifier_list : type_specifier\n specifier_qualifier_list : type_qualifier_list type_specifier\n specifier_qualifier_list : alignment_specifier\n specifier_qualifier_list : specifier_qualifier_list alignment_specifier\n struct_or_union_specifier : struct_or_union ID\n | struct_or_union TYPEID\n struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close\n | struct_or_union brace_open brace_close\n struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close\n | struct_or_union ID brace_open brace_close\n | struct_or_union TYPEID brace_open struct_declaration_list brace_close\n | struct_or_union TYPEID brace_open brace_close\n struct_or_union : STRUCT\n | UNION\n struct_declaration_list : struct_declaration\n | struct_declaration_list struct_declaration\n struct_declaration : specifier_qualifier_list struct_declarator_list_opt SEMI\n struct_declaration : SEMI\n struct_declaration : pppragma_directive\n struct_declarator_list : struct_declarator\n | struct_declarator_list COMMA struct_declarator\n struct_declarator : declarator\n struct_declarator : declarator COLON constant_expression\n | COLON constant_expression\n enum_specifier : ENUM ID\n | ENUM TYPEID\n enum_specifier : ENUM brace_open enumerator_list brace_close\n enum_specifier : ENUM ID brace_open enumerator_list brace_close\n | ENUM TYPEID brace_open enumerator_list brace_close\n enumerator_list : enumerator\n | enumerator_list COMMA\n | enumerator_list COMMA enumerator\n alignment_specifier : _ALIGNAS LPAREN type_name RPAREN\n | _ALIGNAS LPAREN constant_expression RPAREN\n enumerator : ID\n | ID EQUALS constant_expression\n declarator : id_declarator\n | typeid_declarator\n pointer : TIMES type_qualifier_list_opt\n | TIMES type_qualifier_list_opt pointer\n type_qualifier_list : type_qualifier\n | type_qualifier_list type_qualifier\n parameter_type_list : parameter_list\n | parameter_list COMMA ELLIPSIS\n parameter_list : parameter_declaration\n | parameter_list COMMA parameter_declaration\n parameter_declaration : declaration_specifiers id_declarator\n | declaration_specifiers typeid_noparen_declarator\n parameter_declaration : declaration_specifiers abstract_declarator_opt\n identifier_list : identifier\n | identifier_list COMMA identifier\n initializer : assignment_expression\n initializer : brace_open initializer_list_opt brace_close\n | brace_open initializer_list COMMA brace_close\n initializer_list : designation_opt initializer\n | initializer_list COMMA designation_opt initializer\n designation : designator_list EQUALS\n designator_list : designator\n | designator_list designator\n designator : LBRACKET constant_expression RBRACKET\n | PERIOD identifier\n type_name : specifier_qualifier_list abstract_declarator_opt\n abstract_declarator : pointer\n abstract_declarator : pointer direct_abstract_declarator\n abstract_declarator : direct_abstract_declarator\n direct_abstract_declarator : LPAREN abstract_declarator RPAREN direct_abstract_declarator : direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET\n direct_abstract_declarator : LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_abstract_declarator : direct_abstract_declarator LBRACKET TIMES RBRACKET\n direct_abstract_declarator : LBRACKET TIMES RBRACKET\n direct_abstract_declarator : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN\n direct_abstract_declarator : LPAREN parameter_type_list_opt RPAREN\n block_item : declaration\n | statement\n block_item_list : block_item\n | block_item_list block_item\n compound_statement : brace_open block_item_list_opt brace_close labeled_statement : ID COLON pragmacomp_or_statement labeled_statement : CASE constant_expression COLON pragmacomp_or_statement labeled_statement : DEFAULT COLON pragmacomp_or_statement selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement jump_statement : GOTO ID SEMI jump_statement : BREAK SEMI jump_statement : CONTINUE SEMI jump_statement : RETURN expression SEMI\n | RETURN SEMI\n expression_statement : expression_opt SEMI expression : assignment_expression\n | expression COMMA assignment_expression\n assignment_expression : LPAREN compound_statement RPAREN typedef_name : TYPEID assignment_expression : conditional_expression\n | unary_expression assignment_operator assignment_expression\n assignment_operator : EQUALS\n | XOREQUAL\n | TIMESEQUAL\n | DIVEQUAL\n | MODEQUAL\n | PLUSEQUAL\n | MINUSEQUAL\n | LSHIFTEQUAL\n | RSHIFTEQUAL\n | ANDEQUAL\n | OREQUAL\n constant_expression : conditional_expression conditional_expression : binary_expression\n | binary_expression CONDOP expression COLON conditional_expression\n binary_expression : cast_expression\n | binary_expression TIMES binary_expression\n | binary_expression DIVIDE binary_expression\n | binary_expression MOD binary_expression\n | binary_expression PLUS binary_expression\n | binary_expression MINUS binary_expression\n | binary_expression RSHIFT binary_expression\n | binary_expression LSHIFT binary_expression\n | binary_expression LT binary_expression\n | binary_expression LE binary_expression\n | binary_expression GE binary_expression\n | binary_expression GT binary_expression\n | binary_expression EQ binary_expression\n | binary_expression NE binary_expression\n | binary_expression AND binary_expression\n | binary_expression OR binary_expression\n | binary_expression XOR binary_expression\n | binary_expression LAND binary_expression\n | binary_expression LOR binary_expression\n cast_expression : unary_expression cast_expression : LPAREN type_name RPAREN cast_expression unary_expression : postfix_expression unary_expression : PLUSPLUS unary_expression\n | MINUSMINUS unary_expression\n | unary_operator cast_expression\n unary_expression : SIZEOF unary_expression\n | SIZEOF LPAREN type_name RPAREN\n | _ALIGNOF LPAREN type_name RPAREN\n unary_operator : AND\n | TIMES\n | PLUS\n | MINUS\n | NOT\n | LNOT\n postfix_expression : primary_expression postfix_expression : postfix_expression LBRACKET expression RBRACKET postfix_expression : postfix_expression LPAREN argument_expression_list RPAREN\n | postfix_expression LPAREN RPAREN\n postfix_expression : postfix_expression PERIOD ID\n | postfix_expression PERIOD TYPEID\n | postfix_expression ARROW ID\n | postfix_expression ARROW TYPEID\n postfix_expression : postfix_expression PLUSPLUS\n | postfix_expression MINUSMINUS\n postfix_expression : LPAREN type_name RPAREN brace_open initializer_list brace_close\n | LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close\n primary_expression : identifier primary_expression : constant primary_expression : unified_string_literal\n | unified_wstring_literal\n primary_expression : LPAREN expression RPAREN primary_expression : OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN\n offsetof_member_designator : identifier\n | offsetof_member_designator PERIOD identifier\n | offsetof_member_designator LBRACKET expression RBRACKET\n argument_expression_list : assignment_expression\n | argument_expression_list COMMA assignment_expression\n identifier : ID constant : INT_CONST_DEC\n | INT_CONST_OCT\n | INT_CONST_HEX\n | INT_CONST_BIN\n | INT_CONST_CHAR\n constant : FLOAT_CONST\n | HEX_FLOAT_CONST\n constant : CHAR_CONST\n | WCHAR_CONST\n | U8CHAR_CONST\n | U16CHAR_CONST\n | U32CHAR_CONST\n unified_string_literal : STRING_LITERAL\n | unified_string_literal STRING_LITERAL\n unified_wstring_literal : WSTRING_LITERAL\n | U8STRING_LITERAL\n | U16STRING_LITERAL\n | U32STRING_LITERAL\n | unified_wstring_literal WSTRING_LITERAL\n | unified_wstring_literal U8STRING_LITERAL\n | unified_wstring_literal U16STRING_LITERAL\n | unified_wstring_literal U32STRING_LITERAL\n brace_open : LBRACE\n brace_close : RBRACE\n empty : ' + +_lr_action_items = {'INT_CONST_CHAR':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,132,-335,-28,-182,-27,132,-337,-87,-72,-337,132,-286,-285,132,132,-283,-287,-288,132,-284,132,132,132,-336,-183,132,132,-28,-337,132,-28,-337,-337,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,-337,-76,-79,-82,-75,132,-77,132,132,-81,-215,-214,-80,-216,132,-78,132,132,-69,-284,132,132,-284,132,132,-244,-247,-245,-241,-242,-246,-248,132,-250,-251,-243,-249,-12,132,132,-11,132,132,132,132,-234,-233,132,-231,132,132,-217,132,-230,132,-84,-218,132,132,132,-337,-337,-198,132,132,132,-337,-284,-229,-232,132,-221,132,-83,-219,-68,132,-28,-337,132,-11,132,132,-220,132,132,132,-284,132,132,132,-337,132,-225,-224,-222,-84,132,132,132,-226,-223,132,-228,-227,]),'VOID':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[6,-337,-113,-128,6,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,6,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,6,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,6,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,6,-131,-95,-101,-97,6,-53,-126,6,-88,6,6,-93,6,-147,-335,-146,6,-167,-166,-182,-100,-126,6,-87,-90,-94,-92,-61,-72,6,-144,-142,6,6,6,-73,6,-89,6,6,6,-149,-159,-160,-156,-336,6,-183,-30,6,6,-74,6,6,6,6,-174,-175,6,-143,-140,6,-141,-145,-76,-79,-82,-75,-77,6,-81,-215,-214,-80,-216,-78,-127,6,-153,6,-151,-148,-157,-168,-69,-36,-35,6,6,6,-234,-233,6,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,6,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LBRACKET':([2,3,5,6,7,10,11,12,13,18,20,22,23,26,27,30,33,34,35,36,39,42,43,44,46,48,49,50,54,56,58,60,62,68,71,73,76,77,80,81,82,86,96,97,98,100,101,103,104,105,106,109,111,127,132,133,134,136,138,139,140,141,142,143,145,147,148,152,153,154,156,160,161,163,164,166,167,168,169,176,177,187,191,198,199,200,211,216,227,230,235,236,237,238,240,241,261,263,269,275,276,278,279,280,283,310,312,314,316,317,328,340,341,342,344,345,347,355,356,371,376,402,403,404,405,407,411,414,442,443,448,449,453,454,457,458,464,465,470,472,474,482,483,488,489,490,492,511,512,518,519,520,526,527,529,530,531,532,544,545,547,550,551,559,560,563,565,570,571,572,],[-113,-128,-124,-110,-106,-104,-107,-125,-105,-99,-109,-120,-115,-102,-126,-108,-238,-111,-337,-122,-129,-29,-121,-116,-112,117,-123,-117,-119,-114,-130,-118,-103,-96,-98,128,-131,-37,-95,-101,-97,117,-147,-335,-146,-167,-166,-28,-180,-182,-27,-100,-126,128,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-314,-142,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,265,-323,-312,282,-149,-336,-183,-181,-30,282,-38,373,-326,-334,-332,-331,-333,-174,-175,-298,-297,-143,-140,282,282,-141,-145,421,-312,-127,-153,-151,-148,-168,-36,-35,282,282,459,-45,-44,-43,-199,373,-296,-295,-294,-293,-292,-305,421,-152,-150,-170,-169,-31,-34,282,459,-39,-42,-202,373,-200,-290,-291,373,-213,-207,-211,-33,-32,-41,-40,-201,549,-307,-209,-208,-210,-212,-51,-50,-306,373,-299,-46,-49,-308,-300,-48,-47,-309,]),'WCHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,133,-335,-28,-182,-27,133,-337,-87,-72,-337,133,-286,-285,133,133,-283,-287,-288,133,-284,133,133,133,-336,-183,133,133,-28,-337,133,-28,-337,-337,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,-337,-76,-79,-82,-75,133,-77,133,133,-81,-215,-214,-80,-216,133,-78,133,133,-69,-284,133,133,-284,133,133,-244,-247,-245,-241,-242,-246,-248,133,-250,-251,-243,-249,-12,133,133,-11,133,133,133,133,-234,-233,133,-231,133,133,-217,133,-230,133,-84,-218,133,133,133,-337,-337,-198,133,133,133,-337,-284,-229,-232,133,-221,133,-83,-219,-68,133,-28,-337,133,-11,133,133,-220,133,133,133,-284,133,133,133,-337,133,-225,-224,-222,-84,133,133,133,-226,-223,133,-228,-227,]),'FLOAT_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,134,-335,-28,-182,-27,134,-337,-87,-72,-337,134,-286,-285,134,134,-283,-287,-288,134,-284,134,134,134,-336,-183,134,134,-28,-337,134,-28,-337,-337,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,-337,-76,-79,-82,-75,134,-77,134,134,-81,-215,-214,-80,-216,134,-78,134,134,-69,-284,134,134,-284,134,134,-244,-247,-245,-241,-242,-246,-248,134,-250,-251,-243,-249,-12,134,134,-11,134,134,134,134,-234,-233,134,-231,134,134,-217,134,-230,134,-84,-218,134,134,134,-337,-337,-198,134,134,134,-337,-284,-229,-232,134,-221,134,-83,-219,-68,134,-28,-337,134,-11,134,134,-220,134,134,134,-284,134,134,134,-337,134,-225,-224,-222,-84,134,134,134,-226,-223,134,-228,-227,]),'MINUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,224,227,229,230,231,232,233,234,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,273,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,135,-335,-28,-182,-27,135,-337,-87,-72,-337,135,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-274,-314,135,-327,135,-283,-287,-325,-304,-322,-302,-255,-315,-289,245,-328,-316,-288,-329,-320,-276,-323,135,-284,135,135,-312,135,-336,-183,135,135,-28,-337,135,-28,-337,-274,-337,135,-326,135,-280,135,-277,-334,-332,-331,-333,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,-298,-297,135,135,-279,-278,-337,-76,-79,-82,-75,135,-77,135,135,-81,-215,-214,-80,-216,135,-78,-312,135,135,-69,-284,135,135,-284,135,135,-244,-247,-245,-241,-242,-246,-248,135,-250,-251,-243,-249,-12,135,135,-11,245,245,245,-260,245,245,245,-259,245,245,-257,-256,245,245,245,245,245,-258,-296,-295,-294,-293,-292,-305,135,135,135,135,-234,-233,135,-231,135,135,-217,135,-230,135,-84,-218,135,135,135,-337,-337,-198,135,-281,-282,135,-290,-291,135,-275,-337,-284,-229,-232,135,-221,135,-83,-219,-68,135,-28,-337,135,-11,135,135,-220,135,135,135,-284,135,135,-306,135,-337,-299,135,-225,-224,-222,-84,-300,135,135,135,-226,-223,135,-228,-227,]),'RPAREN':([2,3,5,6,7,10,11,12,13,18,20,22,23,26,27,30,33,34,35,36,39,42,43,44,46,48,49,50,54,56,58,60,62,68,71,73,76,77,80,81,82,86,96,98,100,101,103,104,105,106,107,109,111,118,125,127,129,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,157,158,159,160,161,162,163,164,166,167,168,169,176,177,178,183,187,191,198,199,200,203,207,208,209,210,211,212,213,215,216,221,222,224,225,230,232,234,235,236,237,238,240,241,261,263,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,283,294,312,314,316,317,328,340,341,342,343,344,345,346,347,348,355,356,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,408,409,411,414,415,416,417,418,422,433,439,442,443,448,449,452,453,454,457,458,460,461,462,463,464,465,468,476,478,480,482,483,486,487,489,490,492,495,501,503,507,511,512,516,517,518,519,524,525,526,527,529,530,531,532,544,545,547,551,553,556,559,560,563,565,566,567,570,571,572,573,],[-113,-128,-124,-110,-106,-104,-107,-125,-105,-99,-109,-120,-115,-102,-126,-108,-238,-111,-337,-122,-129,-29,-121,-116,-112,-52,-123,-117,-119,-114,-130,-118,-103,-96,-98,-54,-131,-37,-95,-101,-97,-53,-147,-146,-167,-166,-28,-180,-182,-27,200,-100,-126,-337,216,-55,-337,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,240,-255,241,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-337,-252,312,-149,-336,-183,-181,-30,332,340,-17,341,-186,-337,-18,-184,-191,-38,355,356,-274,-239,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,407,-279,-143,411,413,-235,-278,-203,-140,-204,-1,-337,-141,-145,-2,-206,-14,-127,-153,-151,-148,-168,-36,-35,-337,-190,-204,-56,-188,-45,-189,-44,-43,476,477,478,479,480,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-310,483,-305,-205,-23,-24,489,490,-337,-13,-218,-152,-150,-170,-169,510,-31,-34,-204,-57,-337,-192,-185,-187,-39,-42,-240,-237,-281,-282,-290,-291,-236,-275,-213,-207,-211,532,535,537,539,-33,-32,544,545,-41,-40,-254,-311,547,-307,-209,-208,-210,-212,-51,-50,-306,-299,-337,568,-46,-49,-308,-300,-337,574,-48,-47,-309,577,]),'STRUCT':([0,1,3,7,10,11,13,14,16,17,19,20,21,25,26,27,29,30,38,39,40,42,45,47,48,52,53,55,58,59,61,62,63,64,65,66,67,75,85,86,87,90,91,93,94,95,97,99,105,118,119,120,121,122,123,124,129,172,174,180,181,182,184,185,186,188,189,190,191,198,200,214,223,229,231,233,239,240,241,267,278,284,285,286,289,291,298,300,301,302,303,305,308,312,313,315,318,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,446,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[24,-337,-128,-106,-104,-107,-105,-64,-60,-67,-66,-109,24,-65,-102,-337,-131,-108,-63,-129,24,-29,-62,-70,-52,-337,-337,-337,-130,24,-71,-103,-337,-9,-131,-91,-10,24,24,-53,-337,-88,24,24,-93,24,-335,24,-182,24,-87,-90,-94,-92,-61,-72,24,24,24,-73,24,-89,24,24,24,-159,-160,-156,-336,-183,-30,24,-74,24,24,24,24,-174,-175,24,24,-76,-79,-82,-75,-77,24,-81,-215,-214,-80,-216,-78,-127,24,24,-157,-69,-36,-35,24,24,24,-234,-233,24,-231,-217,-230,-81,-84,-218,-158,-31,-34,24,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LONG':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[23,-337,-113,-128,23,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,23,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,23,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,23,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,23,-131,-95,-101,-97,23,-53,-126,23,-88,23,23,-93,23,-147,-335,-146,23,-167,-166,-182,-100,-126,23,-87,-90,-94,-92,-61,-72,23,-144,-142,23,23,23,-73,23,-89,23,23,23,-149,-159,-160,-156,-336,23,-183,-30,23,23,-74,23,23,23,23,-174,-175,23,-143,-140,23,-141,-145,-76,-79,-82,-75,-77,23,-81,-215,-214,-80,-216,-78,-127,23,-153,23,-151,-148,-157,-168,-69,-36,-35,23,23,23,-234,-233,23,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,23,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PLUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,224,227,229,230,231,232,233,234,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,273,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,137,-335,-28,-182,-27,137,-337,-87,-72,-337,137,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-274,-314,137,-327,137,-283,-287,-325,-304,-322,-302,-255,-315,-289,249,-328,-316,-288,-329,-320,-276,-323,137,-284,137,137,-312,137,-336,-183,137,137,-28,-337,137,-28,-337,-274,-337,137,-326,137,-280,137,-277,-334,-332,-331,-333,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,-298,-297,137,137,-279,-278,-337,-76,-79,-82,-75,137,-77,137,137,-81,-215,-214,-80,-216,137,-78,-312,137,137,-69,-284,137,137,-284,137,137,-244,-247,-245,-241,-242,-246,-248,137,-250,-251,-243,-249,-12,137,137,-11,249,249,249,-260,249,249,249,-259,249,249,-257,-256,249,249,249,249,249,-258,-296,-295,-294,-293,-292,-305,137,137,137,137,-234,-233,137,-231,137,137,-217,137,-230,137,-84,-218,137,137,137,-337,-337,-198,137,-281,-282,137,-290,-291,137,-275,-337,-284,-229,-232,137,-221,137,-83,-219,-68,137,-28,-337,137,-11,137,137,-220,137,137,137,-284,137,137,-306,137,-337,-299,137,-225,-224,-222,-84,-300,137,137,137,-226,-223,137,-228,-227,]),'ELLIPSIS':([350,],[462,]),'U32STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,139,-335,-28,-182,-27,139,-337,-87,-72,-337,139,-286,-285,-330,139,-327,139,-283,-287,235,-328,-288,-329,139,-284,139,139,139,-336,-183,139,139,-28,-337,139,-28,-337,-337,139,139,139,-334,-332,-331,-333,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,-337,-76,-79,-82,-75,139,-77,139,139,-81,-215,-214,-80,-216,139,-78,139,139,-69,-284,139,139,-284,139,139,-244,-247,-245,-241,-242,-246,-248,139,-250,-251,-243,-249,-12,139,139,-11,139,139,139,139,-234,-233,139,-231,139,139,-217,139,-230,139,-84,-218,139,139,139,-337,-337,-198,139,139,139,-337,-284,-229,-232,139,-221,139,-83,-219,-68,139,-28,-337,139,-11,139,139,-220,139,139,139,-284,139,139,139,-337,139,-225,-224,-222,-84,139,139,139,-226,-223,139,-228,-227,]),'GT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,250,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,250,-262,-260,-264,250,-263,-259,-266,250,-257,-256,-265,250,250,250,250,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'GOTO':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,287,-336,-76,-79,-82,-75,-77,287,-81,-215,-214,-80,-216,287,-78,-69,-234,-233,-231,287,-217,-230,287,-84,-218,287,-229,-232,-221,287,-83,-219,-68,287,-220,287,287,-225,-224,-222,-84,287,287,-226,-223,287,-228,-227,]),'ENUM':([0,1,3,7,10,11,13,14,16,17,19,20,21,25,26,27,29,30,38,39,40,42,45,47,48,52,53,55,58,59,61,62,63,64,65,66,67,75,85,86,87,90,91,93,94,95,97,99,105,118,119,120,121,122,123,124,129,172,174,180,181,182,184,185,186,188,189,190,191,198,200,214,223,229,231,233,239,240,241,267,278,284,285,286,289,291,298,300,301,302,303,305,308,312,313,315,318,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,446,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[32,-337,-128,-106,-104,-107,-105,-64,-60,-67,-66,-109,32,-65,-102,-337,-131,-108,-63,-129,32,-29,-62,-70,-52,-337,-337,-337,-130,32,-71,-103,-337,-9,-131,-91,-10,32,32,-53,-337,-88,32,32,-93,32,-335,32,-182,32,-87,-90,-94,-92,-61,-72,32,32,32,-73,32,-89,32,32,32,-159,-160,-156,-336,-183,-30,32,-74,32,32,32,32,-174,-175,32,32,-76,-79,-82,-75,-77,32,-81,-215,-214,-80,-216,-78,-127,32,32,-157,-69,-36,-35,32,32,32,-234,-233,32,-231,-217,-230,-81,-84,-218,-158,-31,-34,32,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PERIOD':([97,132,133,134,136,138,139,140,141,143,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,227,230,235,236,237,238,261,263,310,371,376,402,403,404,405,407,411,470,472,474,482,483,488,520,526,527,547,550,551,563,565,572,],[-335,-317,-321,-318,-303,-324,-330,-313,-319,-301,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,264,-323,-312,-336,372,-326,-334,-332,-331,-333,-298,-297,-312,-199,372,-296,-295,-294,-293,-292,-305,-202,372,-200,-290,-291,372,-201,548,-307,-306,372,-299,-308,-300,-309,]),'GE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,254,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,254,-262,-260,-264,254,-263,-259,-266,254,-257,-256,-265,254,254,254,254,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'INT_CONST_DEC':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,140,-335,-28,-182,-27,140,-337,-87,-72,-337,140,-286,-285,140,140,-283,-287,-288,140,-284,140,140,140,-336,-183,140,140,-28,-337,140,-28,-337,-337,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,-337,-76,-79,-82,-75,140,-77,140,140,-81,-215,-214,-80,-216,140,-78,140,140,-69,-284,140,140,-284,140,140,-244,-247,-245,-241,-242,-246,-248,140,-250,-251,-243,-249,-12,140,140,-11,140,140,140,140,-234,-233,140,-231,140,140,-217,140,-230,140,-84,-218,140,140,140,-337,-337,-198,140,140,140,-337,-284,-229,-232,140,-221,140,-83,-219,-68,140,-28,-337,140,-11,140,140,-220,140,140,140,-284,140,140,140,-337,140,-225,-224,-222,-84,140,140,140,-226,-223,140,-228,-227,]),'ARROW':([132,133,134,136,138,139,140,141,143,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,230,235,236,237,238,261,263,310,402,403,404,405,407,411,482,483,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,262,-323,-312,-336,-326,-334,-332,-331,-333,-298,-297,-312,-296,-295,-294,-293,-292,-305,-290,-291,-306,-299,-300,]),'_STATIC_ASSERT':([0,14,16,17,19,25,38,45,47,59,61,97,119,123,124,180,181,191,223,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[41,-64,-60,-67,-66,-65,-63,-62,-70,41,-71,-335,-87,-61,-72,-73,41,-336,-74,-76,-79,-82,-75,-77,41,-81,-215,-214,-80,-216,41,-78,-69,-234,-233,-231,41,-217,-230,41,-84,-218,41,-229,-232,-221,41,-83,-219,-68,41,-220,41,41,-225,-224,-222,-84,41,41,-226,-223,41,-228,-227,]),'CHAR':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[46,-337,-113,-128,46,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,46,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,46,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,46,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,46,-131,-95,-101,-97,46,-53,-126,46,-88,46,46,-93,46,-147,-335,-146,46,-167,-166,-182,-100,-126,46,-87,-90,-94,-92,-61,-72,46,-144,-142,46,46,46,-73,46,-89,46,46,46,-149,-159,-160,-156,-336,46,-183,-30,46,46,-74,46,46,46,46,-174,-175,46,-143,-140,46,-141,-145,-76,-79,-82,-75,-77,46,-81,-215,-214,-80,-216,-78,-127,46,-153,46,-151,-148,-157,-168,-69,-36,-35,46,46,46,-234,-233,46,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,46,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'HEX_FLOAT_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,141,-335,-28,-182,-27,141,-337,-87,-72,-337,141,-286,-285,141,141,-283,-287,-288,141,-284,141,141,141,-336,-183,141,141,-28,-337,141,-28,-337,-337,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,-337,-76,-79,-82,-75,141,-77,141,141,-81,-215,-214,-80,-216,141,-78,141,141,-69,-284,141,141,-284,141,141,-244,-247,-245,-241,-242,-246,-248,141,-250,-251,-243,-249,-12,141,141,-11,141,141,141,141,-234,-233,141,-231,141,141,-217,141,-230,141,-84,-218,141,141,141,-337,-337,-198,141,141,141,-337,-284,-229,-232,141,-221,141,-83,-219,-68,141,-28,-337,141,-11,141,141,-220,141,141,141,-284,141,141,141,-337,141,-225,-224,-222,-84,141,141,141,-226,-223,141,-228,-227,]),'DOUBLE':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[50,-337,-113,-128,50,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,50,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,50,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,50,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,50,-131,-95,-101,-97,50,-53,-126,50,-88,50,50,-93,50,-147,-335,-146,50,-167,-166,-182,-100,-126,50,-87,-90,-94,-92,-61,-72,50,-144,-142,50,50,50,-73,50,-89,50,50,50,-149,-159,-160,-156,-336,50,-183,-30,50,50,-74,50,50,50,50,-174,-175,50,-143,-140,50,-141,-145,-76,-79,-82,-75,-77,50,-81,-215,-214,-80,-216,-78,-127,50,-153,50,-151,-148,-157,-168,-69,-36,-35,50,50,50,-234,-233,50,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,50,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'MINUSEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,358,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'INT_CONST_OCT':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,145,-335,-28,-182,-27,145,-337,-87,-72,-337,145,-286,-285,145,145,-283,-287,-288,145,-284,145,145,145,-336,-183,145,145,-28,-337,145,-28,-337,-337,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,-337,-76,-79,-82,-75,145,-77,145,145,-81,-215,-214,-80,-216,145,-78,145,145,-69,-284,145,145,-284,145,145,-244,-247,-245,-241,-242,-246,-248,145,-250,-251,-243,-249,-12,145,145,-11,145,145,145,145,-234,-233,145,-231,145,145,-217,145,-230,145,-84,-218,145,145,145,-337,-337,-198,145,145,145,-337,-284,-229,-232,145,-221,145,-83,-219,-68,145,-28,-337,145,-11,145,145,-220,145,145,145,-284,145,145,145,-337,145,-225,-224,-222,-84,145,145,145,-226,-223,145,-228,-227,]),'TIMESEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,367,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'OR':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,259,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,259,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,259,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'SHORT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[2,-337,-113,-128,2,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,2,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,2,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,2,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,2,-131,-95,-101,-97,2,-53,-126,2,-88,2,2,-93,2,-147,-335,-146,2,-167,-166,-182,-100,-126,2,-87,-90,-94,-92,-61,-72,2,-144,-142,2,2,2,-73,2,-89,2,2,2,-149,-159,-160,-156,-336,2,-183,-30,2,2,-74,2,2,2,2,-174,-175,2,-143,-140,2,-141,-145,-76,-79,-82,-75,-77,2,-81,-215,-214,-80,-216,-78,-127,2,-153,2,-151,-148,-157,-168,-69,-36,-35,2,2,2,-234,-233,2,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,2,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'RETURN':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,290,-336,-76,-79,-82,-75,-77,290,-81,-215,-214,-80,-216,290,-78,-69,-234,-233,-231,290,-217,-230,290,-84,-218,290,-229,-232,-221,290,-83,-219,-68,290,-220,290,290,-225,-224,-222,-84,290,290,-226,-223,290,-228,-227,]),'RSHIFTEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,368,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'_ALIGNAS':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,109,111,118,119,123,124,129,142,147,174,177,180,181,182,184,185,186,187,188,189,190,191,192,200,211,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[8,8,-113,-128,8,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,8,-120,-115,-65,-102,8,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,8,8,-119,8,-114,-130,8,-118,-71,-103,8,-131,-96,-98,8,-131,-95,-101,-97,8,-53,8,8,-88,8,8,-147,-335,-146,8,-167,-166,-100,-126,8,-87,-61,-72,8,-144,-142,8,8,-73,8,-89,8,8,8,-149,-159,-160,-156,-336,8,-30,8,-74,8,8,8,8,-174,-175,8,-143,-140,8,-141,-145,-76,-79,-82,-75,-77,8,-81,-215,-214,-80,-216,-78,-127,8,-153,8,-151,-148,-157,-168,-69,-36,-35,8,8,8,-234,-233,8,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,8,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'RESTRICT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,103,105,109,111,117,118,119,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[39,39,-113,-128,39,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,39,-120,-115,-65,-102,39,-131,-108,-238,-111,39,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,39,39,-119,39,-114,-130,39,-118,-71,-103,39,-131,-96,-98,39,-131,-95,-101,-97,39,-53,39,39,-88,39,39,-147,-335,-146,39,-167,-166,39,-182,-100,-126,39,39,-87,-61,-72,39,39,-144,-142,39,39,39,-73,39,-89,39,39,39,-149,-159,-160,-156,-336,39,-183,-30,39,39,39,39,39,-74,39,39,39,39,-174,-175,39,-143,-140,39,-141,-145,39,-76,-79,-82,-75,-77,39,-81,-215,-214,-80,-216,-78,-127,39,-153,39,-151,-148,-157,-168,-69,-36,-35,39,39,39,-234,-233,39,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,39,39,-229,-232,-221,-83,-219,-68,-33,-32,39,39,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'STATIC':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,105,109,111,117,118,119,123,124,128,129,180,181,182,187,191,198,200,205,211,219,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,536,554,555,557,558,575,576,578,579,],[10,10,-113,-128,10,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,10,-120,-115,-65,-102,10,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,10,10,-119,10,-114,-130,10,-118,-71,-103,10,-131,-96,-98,10,-131,-95,-101,-97,-53,10,10,-88,10,-147,-335,-146,-167,-166,-182,-100,-126,206,10,-87,-61,-72,220,10,-73,10,-89,-149,-336,-183,-30,338,10,353,-74,-174,-175,10,-76,-79,-82,-75,-77,10,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,10,10,10,-234,-233,10,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,515,10,-229,-232,-221,-83,-219,-68,-33,-32,542,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'SIZEOF':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,146,-335,-28,-182,-27,146,-337,-87,-72,-337,146,-286,-285,146,146,-283,-287,-288,146,-284,146,146,146,-336,-183,146,146,-28,-337,146,-28,-337,-337,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,-337,-76,-79,-82,-75,146,-77,146,146,-81,-215,-214,-80,-216,146,-78,146,146,-69,-284,146,146,-284,146,146,-244,-247,-245,-241,-242,-246,-248,146,-250,-251,-243,-249,-12,146,146,-11,146,146,146,146,-234,-233,146,-231,146,146,-217,146,-230,146,-84,-218,146,146,146,-337,-337,-198,146,146,146,-337,-284,-229,-232,146,-221,146,-83,-219,-68,146,-28,-337,146,-11,146,146,-220,146,146,146,-284,146,146,146,-337,146,-225,-224,-222,-84,146,146,146,-226,-223,146,-228,-227,]),'UNSIGNED':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[22,-337,-113,-128,22,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,22,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,22,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,22,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,22,-131,-95,-101,-97,22,-53,-126,22,-88,22,22,-93,22,-147,-335,-146,22,-167,-166,-182,-100,-126,22,-87,-90,-94,-92,-61,-72,22,-144,-142,22,22,22,-73,22,-89,22,22,22,-149,-159,-160,-156,-336,22,-183,-30,22,22,-74,22,22,22,22,-174,-175,22,-143,-140,22,-141,-145,-76,-79,-82,-75,-77,22,-81,-215,-214,-80,-216,-78,-127,22,-153,22,-151,-148,-157,-168,-69,-36,-35,22,22,22,-234,-233,22,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,22,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'UNION':([0,1,3,7,10,11,13,14,16,17,19,20,21,25,26,27,29,30,38,39,40,42,45,47,48,52,53,55,58,59,61,62,63,64,65,66,67,75,85,86,87,90,91,93,94,95,97,99,105,118,119,120,121,122,123,124,129,172,174,180,181,182,184,185,186,188,189,190,191,198,200,214,223,229,231,233,239,240,241,267,278,284,285,286,289,291,298,300,301,302,303,305,308,312,313,315,318,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,446,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[28,-337,-128,-106,-104,-107,-105,-64,-60,-67,-66,-109,28,-65,-102,-337,-131,-108,-63,-129,28,-29,-62,-70,-52,-337,-337,-337,-130,28,-71,-103,-337,-9,-131,-91,-10,28,28,-53,-337,-88,28,28,-93,28,-335,28,-182,28,-87,-90,-94,-92,-61,-72,28,28,28,-73,28,-89,28,28,28,-159,-160,-156,-336,-183,-30,28,-74,28,28,28,28,-174,-175,28,28,-76,-79,-82,-75,-77,28,-81,-215,-214,-80,-216,-78,-127,28,28,-157,-69,-36,-35,28,28,28,-234,-233,28,-231,-217,-230,-81,-84,-218,-158,-31,-34,28,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'COLON':([2,3,5,6,12,22,23,33,34,36,39,42,43,44,46,48,49,50,54,56,58,60,73,74,76,77,86,96,98,100,101,111,127,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,179,187,191,192,200,216,224,225,230,232,234,235,236,237,238,240,241,261,263,268,269,272,273,275,279,280,295,310,312,314,316,317,324,328,340,341,355,356,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,407,411,431,442,443,445,448,449,453,454,464,465,468,476,478,480,482,483,486,487,511,512,518,519,524,547,551,565,],[-113,-128,-124,-110,-125,-120,-115,-238,-111,-122,-129,-29,-121,-116,-112,-52,-123,-117,-119,-114,-130,-118,-54,-179,-131,-37,-53,-147,-146,-167,-166,-126,-55,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-178,-149,-336,319,-30,-38,-274,-239,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,-279,-143,-235,-278,-140,-141,-145,429,440,-127,-153,-151,-148,447,-168,-36,-35,-44,-43,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,481,-270,-258,-296,-295,-294,-293,-292,-305,502,-152,-150,319,-170,-169,-31,-34,-39,-42,-240,-237,-281,-282,-290,-291,-236,-275,-33,-32,-41,-40,-254,-306,-299,-300,]),'$end':([0,9,14,16,17,19,25,38,45,47,57,59,61,119,123,124,180,191,223,332,439,510,],[-337,0,-64,-60,-67,-66,-65,-63,-62,-70,-59,-58,-71,-87,-61,-72,-73,-336,-74,-69,-218,-68,]),'WSTRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,148,-335,-28,-182,-27,148,-337,-87,-72,-337,148,-286,-285,-330,148,-327,148,-283,-287,237,-328,-288,-329,148,-284,148,148,148,-336,-183,148,148,-28,-337,148,-28,-337,-337,148,148,148,-334,-332,-331,-333,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,-337,-76,-79,-82,-75,148,-77,148,148,-81,-215,-214,-80,-216,148,-78,148,148,-69,-284,148,148,-284,148,148,-244,-247,-245,-241,-242,-246,-248,148,-250,-251,-243,-249,-12,148,148,-11,148,148,148,148,-234,-233,148,-231,148,148,-217,148,-230,148,-84,-218,148,148,148,-337,-337,-198,148,148,148,-337,-284,-229,-232,148,-221,148,-83,-219,-68,148,-28,-337,148,-11,148,148,-220,148,148,148,-284,148,148,148,-337,148,-225,-224,-222,-84,148,148,148,-226,-223,148,-228,-227,]),'DIVIDE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,252,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,252,252,252,252,252,252,252,252,252,252,-257,-256,252,252,252,252,252,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'FOR':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,292,-336,-76,-79,-82,-75,-77,292,-81,-215,-214,-80,-216,292,-78,-69,-234,-233,-231,292,-217,-230,292,-84,-218,292,-229,-232,-221,292,-83,-219,-68,292,-220,292,292,-225,-224,-222,-84,292,292,-226,-223,292,-228,-227,]),'PLUSPLUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,145,146,148,149,150,151,152,153,154,156,160,161,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,227,229,230,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,482,483,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,149,-335,-28,-182,-27,149,-337,-87,-72,-337,149,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-314,149,-327,149,-283,-287,-325,-304,-322,-302,-315,-289,-328,-316,-288,-329,-320,263,-323,149,-284,149,149,-312,149,-336,-183,149,149,-28,-337,149,-28,-337,-337,149,-326,149,149,-334,-332,-331,-333,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,-298,-297,149,149,-337,-76,-79,-82,-75,149,-77,149,149,-81,-215,-214,-80,-216,149,-78,-312,149,149,-69,-284,149,149,-284,149,149,-244,-247,-245,-241,-242,-246,-248,149,-250,-251,-243,-249,-12,149,149,-11,-296,-295,-294,-293,-292,-305,149,149,149,149,-234,-233,149,-231,149,149,-217,149,-230,149,-84,-218,149,149,149,-337,-337,-198,149,149,-290,-291,149,-337,-284,-229,-232,149,-221,149,-83,-219,-68,149,-28,-337,149,-11,149,149,-220,149,149,149,-284,149,149,-306,149,-337,-299,149,-225,-224,-222,-84,-300,149,149,149,-226,-223,149,-228,-227,]),'EQUALS':([42,48,73,74,75,77,78,86,110,127,132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,179,191,197,200,216,224,230,232,234,235,236,237,238,261,263,268,273,310,340,341,355,356,371,376,402,403,404,405,407,411,453,454,464,465,470,474,478,480,482,483,487,511,512,518,519,520,547,551,565,],[-29,-52,-54,-179,-178,-37,131,-53,201,-55,-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-178,-336,329,-30,-38,360,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-36,-35,-44,-43,-199,475,-296,-295,-294,-293,-292,-305,-31,-34,-39,-42,-202,-200,-281,-282,-290,-291,-275,-33,-32,-41,-40,-201,-306,-299,-300,]),'ELSE':([61,124,191,284,285,286,289,291,300,303,308,332,424,425,428,435,437,438,439,496,497,500,505,506,510,536,554,555,557,558,575,576,578,579,],[-71,-72,-336,-76,-79,-82,-75,-77,-81,-80,-78,-69,-234,-233,-231,-230,-81,-84,-218,-229,-232,-221,-83,-219,-68,-220,-225,-224,-222,569,-226,-223,-228,-227,]),'ANDEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,365,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'EQ':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,256,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,256,-262,-260,-264,-268,-263,-259,-266,256,-257,-256,-265,256,-267,256,256,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'AND':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,224,227,229,230,231,232,233,234,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,273,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,150,-335,-28,-182,-27,150,-337,-87,-72,-337,150,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-274,-314,150,-327,150,-283,-287,-325,-304,-322,-302,-255,-315,-289,257,-328,-316,-288,-329,-320,-276,-323,150,-284,150,150,-312,150,-336,-183,150,150,-28,-337,150,-28,-337,-274,-337,150,-326,150,-280,150,-277,-334,-332,-331,-333,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,-298,-297,150,150,-279,-278,-337,-76,-79,-82,-75,150,-77,150,150,-81,-215,-214,-80,-216,150,-78,-312,150,150,-69,-284,150,150,-284,150,150,-244,-247,-245,-241,-242,-246,-248,150,-250,-251,-243,-249,-12,150,150,-11,-261,257,-262,-260,-264,-268,-263,-259,-266,257,-257,-256,-265,257,-267,-269,257,-258,-296,-295,-294,-293,-292,-305,150,150,150,150,-234,-233,150,-231,150,150,-217,150,-230,150,-84,-218,150,150,150,-337,-337,-198,150,-281,-282,150,-290,-291,150,-275,-337,-284,-229,-232,150,-221,150,-83,-219,-68,150,-28,-337,150,-11,150,150,-220,150,150,150,-284,150,150,-306,150,-337,-299,150,-225,-224,-222,-84,-300,150,150,150,-226,-223,150,-228,-227,]),'TYPEID':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,72,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,103,104,105,106,109,111,118,119,120,121,122,123,124,126,129,142,147,172,174,180,181,182,184,185,186,187,188,189,190,191,192,198,199,200,202,211,214,223,229,231,233,239,240,241,262,264,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,344,350,422,424,425,427,428,432,435,437,438,439,442,443,445,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[33,-337,-113,-128,77,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,33,-120,-115,-154,-65,-102,-126,-155,-131,-108,96,100,-238,-111,-337,-122,-63,-129,33,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,33,-118,-71,-103,-337,-9,-131,-91,-10,-96,77,-98,77,33,-131,-95,-101,-97,33,-53,-126,77,-88,33,33,-93,33,-147,-335,-146,33,-167,-166,-28,-180,-182,-27,-100,-126,33,-87,-90,-94,-92,-61,-72,77,33,-144,-142,33,33,-73,33,-89,33,33,33,-149,-159,-160,-156,-336,77,-183,-181,-30,77,347,33,-74,33,33,33,33,-174,-175,402,404,33,-143,-140,33,-141,-145,-76,-79,-82,-75,-77,33,-81,-215,-214,-80,-216,-78,-127,33,-153,33,-151,-148,-157,-168,-69,-36,-35,33,347,33,33,-234,-233,33,-231,-217,-230,-81,-84,-218,-152,-150,77,-158,-170,-169,-31,-34,33,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LBRACE':([21,24,28,31,32,42,48,61,75,86,88,90,92,93,96,97,98,100,101,119,124,130,131,181,182,191,200,201,227,229,284,285,286,289,291,298,300,301,302,303,305,307,308,332,340,341,369,375,377,413,424,425,428,429,432,435,437,438,439,440,453,454,472,475,477,478,479,488,496,497,500,502,505,506,510,511,512,521,522,535,536,537,539,550,554,555,557,558,569,574,575,576,577,578,579,],[-337,-154,-155,97,97,-29,-52,-71,-337,-53,-7,-88,97,-8,97,-335,97,97,97,-87,-72,97,97,97,-89,-336,-30,97,-337,97,-76,-79,-82,-75,-77,97,-81,-215,-214,-80,-216,97,-78,-69,-36,-35,-12,97,-11,97,-234,-233,-231,97,-217,-230,97,-84,-218,97,-31,-34,-337,-198,97,97,97,-337,-229,-232,-221,97,-83,-219,-68,-33,-32,97,-11,97,-220,97,97,-337,-225,-224,-222,-84,97,97,-226,-223,97,-228,-227,]),'PPHASH':([0,14,16,17,19,25,38,45,47,59,61,119,123,124,180,191,223,332,439,510,],[47,-64,-60,-67,-66,-65,-63,-62,-70,47,-71,-87,-61,-72,-73,-336,-74,-69,-218,-68,]),'INT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[56,-337,-113,-128,56,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,56,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,56,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,56,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,56,-131,-95,-101,-97,56,-53,-126,56,-88,56,56,-93,56,-147,-335,-146,56,-167,-166,-182,-100,-126,56,-87,-90,-94,-92,-61,-72,56,-144,-142,56,56,56,-73,56,-89,56,56,56,-149,-159,-160,-156,-336,56,-183,-30,56,56,-74,56,56,56,56,-174,-175,56,-143,-140,56,-141,-145,-76,-79,-82,-75,-77,56,-81,-215,-214,-80,-216,-78,-127,56,-153,56,-151,-148,-157,-168,-69,-36,-35,56,56,56,-234,-233,56,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,56,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'SIGNED':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[54,-337,-113,-128,54,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,54,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,54,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,54,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,54,-131,-95,-101,-97,54,-53,-126,54,-88,54,54,-93,54,-147,-335,-146,54,-167,-166,-182,-100,-126,54,-87,-90,-94,-92,-61,-72,54,-144,-142,54,54,54,-73,54,-89,54,54,54,-149,-159,-160,-156,-336,54,-183,-30,54,54,-74,54,54,54,54,-174,-175,54,-143,-140,54,-141,-145,-76,-79,-82,-75,-77,54,-81,-215,-214,-80,-216,-78,-127,54,-153,54,-151,-148,-157,-168,-69,-36,-35,54,54,54,-234,-233,54,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,54,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'CONTINUE':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,293,-336,-76,-79,-82,-75,-77,293,-81,-215,-214,-80,-216,293,-78,-69,-234,-233,-231,293,-217,-230,293,-84,-218,293,-229,-232,-221,293,-83,-219,-68,293,-220,293,293,-225,-224,-222,-84,293,293,-226,-223,293,-228,-227,]),'NOT':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,151,-335,-28,-182,-27,151,-337,-87,-72,-337,151,-286,-285,151,151,-283,-287,-288,151,-284,151,151,151,-336,-183,151,151,-28,-337,151,-28,-337,-337,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,-337,-76,-79,-82,-75,151,-77,151,151,-81,-215,-214,-80,-216,151,-78,151,151,-69,-284,151,151,-284,151,151,-244,-247,-245,-241,-242,-246,-248,151,-250,-251,-243,-249,-12,151,151,-11,151,151,151,151,-234,-233,151,-231,151,151,-217,151,-230,151,-84,-218,151,151,151,-337,-337,-198,151,151,151,-337,-284,-229,-232,151,-221,151,-83,-219,-68,151,-28,-337,151,-11,151,151,-220,151,151,151,-284,151,151,151,-337,151,-225,-224,-222,-84,151,151,151,-226,-223,151,-228,-227,]),'OREQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,366,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'MOD':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,260,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,260,260,260,260,260,260,260,260,260,260,-257,-256,260,260,260,260,260,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'RSHIFT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,242,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,242,-262,-260,242,242,242,-259,242,242,-257,-256,242,242,242,242,242,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'DEFAULT':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,295,-336,-76,-79,-82,-75,-77,295,-81,-215,-214,-80,-216,295,-78,-69,-234,-233,-231,295,-217,-230,295,-84,-218,295,-229,-232,-221,295,-83,-219,-68,295,-220,295,295,-225,-224,-222,-84,295,295,-226,-223,295,-228,-227,]),'_NORETURN':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[20,20,-113,-128,20,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,20,-120,-115,-65,-102,20,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,20,20,-119,20,-114,-130,20,-118,-71,-103,20,-131,-96,-98,20,-131,-95,-101,-97,-53,20,20,-88,20,-147,-335,-146,-167,-166,-100,-126,20,-87,-61,-72,20,-73,20,-89,-149,-336,-30,20,-74,-174,-175,20,-76,-79,-82,-75,-77,20,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,20,20,20,-234,-233,20,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,20,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'__INT128':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[43,-337,-113,-128,43,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,43,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,43,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,43,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,43,-131,-95,-101,-97,43,-53,-126,43,-88,43,43,-93,43,-147,-335,-146,43,-167,-166,-182,-100,-126,43,-87,-90,-94,-92,-61,-72,43,-144,-142,43,43,43,-73,43,-89,43,43,43,-149,-159,-160,-156,-336,43,-183,-30,43,43,-74,43,43,43,43,-174,-175,43,-143,-140,43,-141,-145,-76,-79,-82,-75,-77,43,-81,-215,-214,-80,-216,-78,-127,43,-153,43,-151,-148,-157,-168,-69,-36,-35,43,43,43,-234,-233,43,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,43,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'WHILE':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,436,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,296,-336,-76,-79,-82,-75,-77,296,-81,-215,-214,-80,-216,296,-78,-69,-234,-233,-231,296,-217,-230,504,296,-84,-218,296,-229,-232,-221,296,-83,-219,-68,296,-220,296,296,-225,-224,-222,-84,296,296,-226,-223,296,-228,-227,]),'U8CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,154,-335,-28,-182,-27,154,-337,-87,-72,-337,154,-286,-285,154,154,-283,-287,-288,154,-284,154,154,154,-336,-183,154,154,-28,-337,154,-28,-337,-337,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,-337,-76,-79,-82,-75,154,-77,154,154,-81,-215,-214,-80,-216,154,-78,154,154,-69,-284,154,154,-284,154,154,-244,-247,-245,-241,-242,-246,-248,154,-250,-251,-243,-249,-12,154,154,-11,154,154,154,154,-234,-233,154,-231,154,154,-217,154,-230,154,-84,-218,154,154,154,-337,-337,-198,154,154,154,-337,-284,-229,-232,154,-221,154,-83,-219,-68,154,-28,-337,154,-11,154,154,-220,154,154,154,-284,154,154,154,-337,154,-225,-224,-222,-84,154,154,154,-226,-223,154,-228,-227,]),'_ALIGNOF':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,155,-335,-28,-182,-27,155,-337,-87,-72,-337,155,-286,-285,155,155,-283,-287,-288,155,-284,155,155,155,-336,-183,155,155,-28,-337,155,-28,-337,-337,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,-337,-76,-79,-82,-75,155,-77,155,155,-81,-215,-214,-80,-216,155,-78,155,155,-69,-284,155,155,-284,155,155,-244,-247,-245,-241,-242,-246,-248,155,-250,-251,-243,-249,-12,155,155,-11,155,155,155,155,-234,-233,155,-231,155,155,-217,155,-230,155,-84,-218,155,155,155,-337,-337,-198,155,155,155,-337,-284,-229,-232,155,-221,155,-83,-219,-68,155,-28,-337,155,-11,155,155,-220,155,155,155,-284,155,155,155,-337,155,-225,-224,-222,-84,155,155,155,-226,-223,155,-228,-227,]),'EXTERN':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[13,13,-113,-128,13,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,13,-120,-115,-65,-102,13,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,13,13,-119,13,-114,-130,13,-118,-71,-103,13,-131,-96,-98,13,-131,-95,-101,-97,-53,13,13,-88,13,-147,-335,-146,-167,-166,-100,-126,13,-87,-61,-72,13,-73,13,-89,-149,-336,-30,13,-74,-174,-175,13,-76,-79,-82,-75,-77,13,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,13,13,13,-234,-233,13,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,13,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'CASE':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,297,-336,-76,-79,-82,-75,-77,297,-81,-215,-214,-80,-216,297,-78,-69,-234,-233,-231,297,-217,-230,297,-84,-218,297,-229,-232,-221,297,-83,-219,-68,297,-220,297,297,-225,-224,-222,-84,297,297,-226,-223,297,-228,-227,]),'LAND':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,255,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,255,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'REGISTER':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[62,62,-113,-128,62,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,62,-120,-115,-65,-102,62,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,62,62,-119,62,-114,-130,62,-118,-71,-103,62,-131,-96,-98,62,-131,-95,-101,-97,-53,62,62,-88,62,-147,-335,-146,-167,-166,-100,-126,62,-87,-61,-72,62,-73,62,-89,-149,-336,-30,62,-74,-174,-175,62,-76,-79,-82,-75,-77,62,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,62,62,62,-234,-233,62,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,62,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'MODEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,359,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'NE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,247,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,247,-262,-260,-264,-268,-263,-259,-266,247,-257,-256,-265,247,-267,247,247,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'SWITCH':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,299,-336,-76,-79,-82,-75,-77,299,-81,-215,-214,-80,-216,299,-78,-69,-234,-233,-231,299,-217,-230,299,-84,-218,299,-229,-232,-221,299,-83,-219,-68,299,-220,299,299,-225,-224,-222,-84,299,299,-226,-223,299,-228,-227,]),'INT_CONST_HEX':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,160,-335,-28,-182,-27,160,-337,-87,-72,-337,160,-286,-285,160,160,-283,-287,-288,160,-284,160,160,160,-336,-183,160,160,-28,-337,160,-28,-337,-337,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,-337,-76,-79,-82,-75,160,-77,160,160,-81,-215,-214,-80,-216,160,-78,160,160,-69,-284,160,160,-284,160,160,-244,-247,-245,-241,-242,-246,-248,160,-250,-251,-243,-249,-12,160,160,-11,160,160,160,160,-234,-233,160,-231,160,160,-217,160,-230,160,-84,-218,160,160,160,-337,-337,-198,160,160,160,-337,-284,-229,-232,160,-221,160,-83,-219,-68,160,-28,-337,160,-11,160,160,-220,160,160,160,-284,160,160,160,-337,160,-225,-224,-222,-84,160,160,160,-226,-223,160,-228,-227,]),'_COMPLEX':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[60,-337,-113,-128,60,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,60,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,60,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,60,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,60,-131,-95,-101,-97,60,-53,-126,60,-88,60,60,-93,60,-147,-335,-146,60,-167,-166,-182,-100,-126,60,-87,-90,-94,-92,-61,-72,60,-144,-142,60,60,60,-73,60,-89,60,60,60,-149,-159,-160,-156,-336,60,-183,-30,60,60,-74,60,60,60,60,-174,-175,60,-143,-140,60,-141,-145,-76,-79,-82,-75,-77,60,-81,-215,-214,-80,-216,-78,-127,60,-153,60,-151,-148,-157,-168,-69,-36,-35,60,60,60,-234,-233,60,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,60,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PPPRAGMASTR':([61,],[124,]),'PLUSEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,362,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'U32CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,138,-335,-28,-182,-27,138,-337,-87,-72,-337,138,-286,-285,138,138,-283,-287,-288,138,-284,138,138,138,-336,-183,138,138,-28,-337,138,-28,-337,-337,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,-337,-76,-79,-82,-75,138,-77,138,138,-81,-215,-214,-80,-216,138,-78,138,138,-69,-284,138,138,-284,138,138,-244,-247,-245,-241,-242,-246,-248,138,-250,-251,-243,-249,-12,138,138,-11,138,138,138,138,-234,-233,138,-231,138,138,-217,138,-230,138,-84,-218,138,138,138,-337,-337,-198,138,138,138,-337,-284,-229,-232,138,-221,138,-83,-219,-68,138,-28,-337,138,-11,138,138,-220,138,138,138,-284,138,138,138,-337,138,-225,-224,-222,-84,138,138,138,-226,-223,138,-228,-227,]),'CONDOP':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,258,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'U8STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,163,-335,-28,-182,-27,163,-337,-87,-72,-337,163,-286,-285,-330,163,-327,163,-283,-287,236,-328,-288,-329,163,-284,163,163,163,-336,-183,163,163,-28,-337,163,-28,-337,-337,163,163,163,-334,-332,-331,-333,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,-337,-76,-79,-82,-75,163,-77,163,163,-81,-215,-214,-80,-216,163,-78,163,163,-69,-284,163,163,-284,163,163,-244,-247,-245,-241,-242,-246,-248,163,-250,-251,-243,-249,-12,163,163,-11,163,163,163,163,-234,-233,163,-231,163,163,-217,163,-230,163,-84,-218,163,163,163,-337,-337,-198,163,163,163,-337,-284,-229,-232,163,-221,163,-83,-219,-68,163,-28,-337,163,-11,163,163,-220,163,163,163,-284,163,163,163,-337,163,-225,-224,-222,-84,163,163,163,-226,-223,163,-228,-227,]),'BREAK':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,304,-336,-76,-79,-82,-75,-77,304,-81,-215,-214,-80,-216,304,-78,-69,-234,-233,-231,304,-217,-230,304,-84,-218,304,-229,-232,-221,304,-83,-219,-68,304,-220,304,304,-225,-224,-222,-84,304,304,-226,-223,304,-228,-227,]),'VOLATILE':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,103,105,109,111,117,118,119,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[58,58,-113,-128,58,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,58,-120,-115,-65,-102,58,-131,-108,-238,-111,58,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,58,58,-119,58,-114,-130,58,-118,-71,-103,58,-131,-96,-98,58,-131,-95,-101,-97,58,-53,58,58,-88,58,58,-147,-335,-146,58,-167,-166,58,-182,-100,-126,58,58,-87,-61,-72,58,58,-144,-142,58,58,58,-73,58,-89,58,58,58,-149,-159,-160,-156,-336,58,-183,-30,58,58,58,58,58,-74,58,58,58,58,-174,-175,58,-143,-140,58,-141,-145,58,-76,-79,-82,-75,-77,58,-81,-215,-214,-80,-216,-78,-127,58,-153,58,-151,-148,-157,-168,-69,-36,-35,58,58,58,-234,-233,58,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,58,58,-229,-232,-221,-83,-219,-68,-33,-32,58,58,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PPPRAGMA':([0,14,16,17,19,25,38,45,47,59,61,97,99,119,123,124,180,181,184,185,186,188,189,190,191,223,284,285,286,289,291,298,300,301,302,303,305,307,308,313,315,318,332,424,425,428,429,432,435,437,438,439,440,446,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[61,-64,-60,-67,-66,-65,-63,-62,-70,61,-71,-335,61,-87,-61,-72,-73,61,61,61,61,-159,-160,-156,-336,-74,-76,-79,-82,-75,-77,61,-81,-215,-214,-80,-216,61,-78,61,61,-157,-69,-234,-233,-231,61,-217,-230,61,-84,-218,61,-158,-229,-232,-221,61,-83,-219,-68,61,-220,61,61,-225,-224,-222,-84,61,61,-226,-223,61,-228,-227,]),'INLINE':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[30,30,-113,-128,30,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,30,-120,-115,-65,-102,30,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,30,30,-119,30,-114,-130,30,-118,-71,-103,30,-131,-96,-98,30,-131,-95,-101,-97,-53,30,30,-88,30,-147,-335,-146,-167,-166,-100,-126,30,-87,-61,-72,30,-73,30,-89,-149,-336,-30,30,-74,-174,-175,30,-76,-79,-82,-75,-77,30,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,30,30,30,-234,-233,30,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,30,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'INT_CONST_BIN':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,164,-335,-28,-182,-27,164,-337,-87,-72,-337,164,-286,-285,164,164,-283,-287,-288,164,-284,164,164,164,-336,-183,164,164,-28,-337,164,-28,-337,-337,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,-337,-76,-79,-82,-75,164,-77,164,164,-81,-215,-214,-80,-216,164,-78,164,164,-69,-284,164,164,-284,164,164,-244,-247,-245,-241,-242,-246,-248,164,-250,-251,-243,-249,-12,164,164,-11,164,164,164,164,-234,-233,164,-231,164,164,-217,164,-230,164,-84,-218,164,164,164,-337,-337,-198,164,164,164,-337,-284,-229,-232,164,-221,164,-83,-219,-68,164,-28,-337,164,-11,164,164,-220,164,164,164,-284,164,164,164,-337,164,-225,-224,-222,-84,164,164,164,-226,-223,164,-228,-227,]),'DO':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,307,-336,-76,-79,-82,-75,-77,307,-81,-215,-214,-80,-216,307,-78,-69,-234,-233,-231,307,-217,-230,307,-84,-218,307,-229,-232,-221,307,-83,-219,-68,307,-220,307,307,-225,-224,-222,-84,307,307,-226,-223,307,-228,-227,]),'LNOT':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,165,-335,-28,-182,-27,165,-337,-87,-72,-337,165,-286,-285,165,165,-283,-287,-288,165,-284,165,165,165,-336,-183,165,165,-28,-337,165,-28,-337,-337,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,-337,-76,-79,-82,-75,165,-77,165,165,-81,-215,-214,-80,-216,165,-78,165,165,-69,-284,165,165,-284,165,165,-244,-247,-245,-241,-242,-246,-248,165,-250,-251,-243,-249,-12,165,165,-11,165,165,165,165,-234,-233,165,-231,165,165,-217,165,-230,165,-84,-218,165,165,165,-337,-337,-198,165,165,165,-337,-284,-229,-232,165,-221,165,-83,-219,-68,165,-28,-337,165,-11,165,165,-220,165,165,165,-284,165,165,165,-337,165,-225,-224,-222,-84,165,165,165,-226,-223,165,-228,-227,]),'CONST':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,103,105,109,111,117,118,119,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[3,3,-113,-128,3,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,3,-120,-115,-65,-102,3,-131,-108,-238,-111,3,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,3,3,-119,3,-114,-130,3,-118,-71,-103,3,-131,-96,-98,3,-131,-95,-101,-97,3,-53,3,3,-88,3,3,-147,-335,-146,3,-167,-166,3,-182,-100,-126,3,3,-87,-61,-72,3,3,-144,-142,3,3,3,-73,3,-89,3,3,3,-149,-159,-160,-156,-336,3,-183,-30,3,3,3,3,3,-74,3,3,3,3,-174,-175,3,-143,-140,3,-141,-145,3,-76,-79,-82,-75,-77,3,-81,-215,-214,-80,-216,-78,-127,3,-153,3,-151,-148,-157,-168,-69,-36,-35,3,3,3,-234,-233,3,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,3,3,-229,-232,-221,-83,-219,-68,-33,-32,3,3,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LSHIFT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,244,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,244,-262,-260,244,244,244,-259,244,244,-257,-256,244,244,244,244,244,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'LOR':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,243,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,167,-335,-28,-182,-27,167,-337,-87,-72,-337,167,-286,-285,167,167,-283,-287,-288,167,-284,167,167,167,-336,-183,167,167,-28,-337,167,-28,-337,-337,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,-337,-76,-79,-82,-75,167,-77,167,167,-81,-215,-214,-80,-216,167,-78,167,167,-69,-284,167,167,-284,167,167,-244,-247,-245,-241,-242,-246,-248,167,-250,-251,-243,-249,-12,167,167,-11,167,167,167,167,-234,-233,167,-231,167,167,-217,167,-230,167,-84,-218,167,167,167,-337,-337,-198,167,167,167,-337,-284,-229,-232,167,-221,167,-83,-219,-68,167,-28,-337,167,-11,167,167,-220,167,167,167,-284,167,167,167,-337,167,-225,-224,-222,-84,167,167,167,-226,-223,167,-228,-227,]),'U16STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,166,-335,-28,-182,-27,166,-337,-87,-72,-337,166,-286,-285,-330,166,-327,166,-283,-287,238,-328,-288,-329,166,-284,166,166,166,-336,-183,166,166,-28,-337,166,-28,-337,-337,166,166,166,-334,-332,-331,-333,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,-337,-76,-79,-82,-75,166,-77,166,166,-81,-215,-214,-80,-216,166,-78,166,166,-69,-284,166,166,-284,166,166,-244,-247,-245,-241,-242,-246,-248,166,-250,-251,-243,-249,-12,166,166,-11,166,166,166,166,-234,-233,166,-231,166,166,-217,166,-230,166,-84,-218,166,166,166,-337,-337,-198,166,166,166,-337,-284,-229,-232,166,-221,166,-83,-219,-68,166,-28,-337,166,-11,166,166,-220,166,166,166,-284,166,166,166,-337,166,-225,-224,-222,-84,166,166,166,-226,-223,166,-228,-227,]),'RBRACE':([61,97,99,119,124,132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,181,184,185,186,188,189,190,191,195,196,197,224,225,227,228,230,232,234,235,236,237,238,261,263,268,273,284,285,286,289,291,298,300,301,302,303,305,306,308,309,313,315,318,325,326,327,332,370,374,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,424,425,428,432,435,437,438,439,446,450,451,468,469,472,473,476,478,480,482,483,487,496,497,500,505,506,510,523,524,528,536,546,547,550,551,554,555,557,558,565,575,576,578,579,],[-71,-335,191,-87,-72,-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-337,191,191,191,-159,-160,-156,-336,-171,191,-176,-274,-239,-337,-193,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-76,-79,-82,-75,-77,-6,-81,-215,-214,-80,-216,-5,-78,191,191,191,-157,191,191,-172,-69,191,-22,-21,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-234,-233,-231,-217,-230,-81,-84,-218,-158,-173,-177,-240,-194,191,-196,-237,-281,-282,-290,-291,-275,-229,-232,-221,-83,-219,-68,-195,-254,191,-220,-197,-306,191,-299,-225,-224,-222,-84,-300,-226,-223,-228,-227,]),'_BOOL':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[34,-337,-113,-128,34,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,34,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,34,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,34,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,34,-131,-95,-101,-97,34,-53,-126,34,-88,34,34,-93,34,-147,-335,-146,34,-167,-166,-182,-100,-126,34,-87,-90,-94,-92,-61,-72,34,-144,-142,34,34,34,-73,34,-89,34,34,34,-149,-159,-160,-156,-336,34,-183,-30,34,34,-74,34,34,34,34,-174,-175,34,-143,-140,34,-141,-145,-76,-79,-82,-75,-77,34,-81,-215,-214,-80,-216,-78,-127,34,-153,34,-151,-148,-157,-168,-69,-36,-35,34,34,34,-234,-233,34,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,34,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,246,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,246,-262,-260,-264,246,-263,-259,-266,246,-257,-256,-265,246,246,246,246,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'SEMI':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,70,71,73,74,75,76,77,78,79,80,81,82,83,84,86,87,89,91,94,96,97,98,99,100,101,108,109,110,111,113,114,115,119,120,121,122,123,124,127,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,179,180,181,184,185,186,187,188,189,190,191,192,200,216,217,223,224,225,226,228,230,232,234,235,236,237,238,240,241,261,263,268,269,272,273,275,279,280,284,285,286,288,289,290,291,293,294,298,300,301,302,303,304,305,306,307,308,310,312,313,314,315,316,317,318,320,321,322,323,324,328,330,331,332,340,341,355,356,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,423,424,425,426,427,428,429,432,433,435,437,438,439,440,442,443,444,446,448,449,453,454,464,465,468,469,476,478,480,482,483,486,487,496,497,498,499,500,502,505,506,508,509,510,511,512,518,519,523,524,533,534,535,536,537,539,547,551,552,554,555,557,558,565,568,569,574,575,576,577,578,579,],[19,-337,-113,-128,-337,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,-337,-29,-121,-116,-62,-112,-70,-52,-123,-117,119,-337,-337,-119,-337,-114,-130,19,-118,-71,-103,-337,-9,-131,-91,-10,-96,-20,-98,-54,-179,-178,-131,-37,-134,-85,-95,-101,-97,-19,-132,-53,-126,-337,-337,-93,-147,-335,-146,188,-167,-166,-136,-100,-138,-126,-16,-86,-15,-87,-90,-94,-92,-61,-72,-55,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-178,-73,-337,188,188,188,-149,-159,-160,-156,-336,-337,-30,-38,-133,-74,-274,-239,-135,-193,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,-279,-143,-235,-278,-140,-141,-145,-76,-79,-82,424,-75,425,-77,428,-14,-337,-81,-215,-214,-80,435,-216,-13,-337,-78,-312,-127,188,-153,188,-151,-148,-157,-26,-25,446,-161,-163,-168,-139,-137,-69,-36,-35,-44,-43,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,496,-234,-233,497,-337,-231,-337,-217,-13,-230,-81,-84,-218,-337,-152,-150,-165,-158,-170,-169,-31,-34,-39,-42,-240,-194,-237,-281,-282,-290,-291,-236,-275,-229,-232,533,-337,-221,-337,-83,-219,-162,-164,-68,-33,-32,-41,-40,-195,-254,-337,553,-337,-220,-337,-337,-306,-299,566,-225,-224,-222,-84,-300,575,-337,-337,-226,-223,-337,-228,-227,]),'_THREAD_LOCAL':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[11,11,-113,-128,11,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,11,-120,-115,-65,-102,11,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,11,11,-119,11,-114,-130,11,-118,-71,-103,11,-131,-96,-98,11,-131,-95,-101,-97,-53,11,11,-88,11,-147,-335,-146,-167,-166,-100,-126,11,-87,-61,-72,11,-73,11,-89,-149,-336,-30,11,-74,-174,-175,11,-76,-79,-82,-75,-77,11,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,11,11,11,-234,-233,11,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,11,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,248,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,248,-262,-260,-264,248,-263,-259,-266,248,-257,-256,-265,248,248,248,248,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'COMMA':([2,3,5,6,7,10,11,12,13,18,20,22,23,26,27,30,33,34,35,36,39,42,43,44,46,48,49,50,54,56,58,60,62,68,70,71,73,74,75,76,77,78,80,81,82,84,86,96,98,100,101,103,104,105,106,108,109,110,111,113,127,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,177,178,179,187,191,195,196,197,198,199,200,203,210,211,212,213,215,216,217,224,225,226,228,230,232,234,235,236,237,238,240,241,261,263,268,269,270,272,273,274,275,276,277,279,280,281,283,294,310,312,314,316,317,320,323,324,325,326,327,328,330,331,340,341,343,344,345,346,347,348,355,356,374,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,414,426,442,443,444,448,449,450,451,453,454,458,461,463,464,465,468,469,473,476,478,480,482,483,486,487,489,490,492,501,503,507,508,509,511,512,518,519,523,524,525,528,529,530,531,532,544,545,546,547,551,556,559,560,564,565,570,571,],[-113,-128,-124,-110,-106,-104,-107,-125,-105,-99,-109,-120,-115,-102,-126,-108,-238,-111,-337,-122,-129,-29,-121,-116,-112,-52,-123,-117,-119,-114,-130,-118,-103,-96,126,-98,-54,-179,-178,-131,-37,-134,-95,-101,-97,-132,-53,-147,-146,-167,-166,-28,-180,-182,-27,-136,-100,-138,-126,202,-55,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-337,-252,-178,-149,-336,-171,327,-176,-183,-181,-30,333,-186,-337,349,350,-191,-38,-133,-274,-239,-135,-193,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,-279,-143,412,-235,-278,-203,-140,-204,-1,-141,-145,-2,-206,412,-312,-127,-153,-151,-148,445,-161,-163,327,327,-172,-168,-139,-137,-36,-35,-190,-204,-56,-188,-45,-189,-44,-43,472,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,412,-270,-258,-296,-295,-294,-293,412,-292,-310,484,485,-305,-205,412,-152,-150,-165,-170,-169,-173,-177,-31,-34,-57,-192,-187,-39,-42,-240,-194,-196,-237,-281,-282,-290,-291,-236,-275,-213,-207,-211,412,412,412,-162,-164,-33,-32,-41,-40,-195,-254,-311,550,-209,-208,-210,-212,-51,-50,-197,-306,-299,412,-46,-49,412,-300,-48,-47,]),'U16CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,169,-335,-28,-182,-27,169,-337,-87,-72,-337,169,-286,-285,169,169,-283,-287,-288,169,-284,169,169,169,-336,-183,169,169,-28,-337,169,-28,-337,-337,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,-337,-76,-79,-82,-75,169,-77,169,169,-81,-215,-214,-80,-216,169,-78,169,169,-69,-284,169,169,-284,169,169,-244,-247,-245,-241,-242,-246,-248,169,-250,-251,-243,-249,-12,169,169,-11,169,169,169,169,-234,-233,169,-231,169,169,-217,169,-230,169,-84,-218,169,169,169,-337,-337,-198,169,169,169,-337,-284,-229,-232,169,-221,169,-83,-219,-68,169,-28,-337,169,-11,169,169,-220,169,169,169,-284,169,169,169,-337,169,-225,-224,-222,-84,169,169,169,-226,-223,169,-228,-227,]),'OFFSETOF':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,170,-335,-28,-182,-27,170,-337,-87,-72,-337,170,-286,-285,170,170,-283,-287,-288,170,-284,170,170,170,-336,-183,170,170,-28,-337,170,-28,-337,-337,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,-337,-76,-79,-82,-75,170,-77,170,170,-81,-215,-214,-80,-216,170,-78,170,170,-69,-284,170,170,-284,170,170,-244,-247,-245,-241,-242,-246,-248,170,-250,-251,-243,-249,-12,170,170,-11,170,170,170,170,-234,-233,170,-231,170,170,-217,170,-230,170,-84,-218,170,170,170,-337,-337,-198,170,170,170,-337,-284,-229,-232,170,-221,170,-83,-219,-68,170,-28,-337,170,-11,170,170,-220,170,170,170,-284,170,170,170,-337,170,-225,-224,-222,-84,170,170,170,-226,-223,170,-228,-227,]),'_ATOMIC':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,103,105,109,111,117,118,119,120,121,122,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,214,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[29,65,-113,-128,76,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,65,-120,-115,-65,-102,65,-131,-108,-238,-111,76,-122,-63,-129,112,-29,-121,-116,-62,-112,-70,-52,-123,-117,65,65,-119,65,-114,-130,29,-118,-71,-103,65,-9,-131,-91,-10,-96,-98,65,-131,-95,-101,-97,29,-53,65,76,-88,112,65,-93,29,-147,-335,-146,29,-167,-166,76,-182,-100,-126,76,29,-87,-90,-94,-92,-61,-72,76,29,-144,-142,65,29,76,-73,65,-89,29,29,29,-149,-159,-160,-156,-336,76,-183,-30,76,76,76,112,76,76,-74,29,29,29,29,-174,-175,29,-143,-140,29,-141,-145,76,-76,-79,-82,-75,-77,65,-81,-215,-214,-80,-216,-78,-127,29,-153,29,-151,-148,-157,-168,-69,-36,-35,29,29,29,-234,-233,65,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,76,29,-229,-232,-221,-83,-219,-68,-33,-32,76,76,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'TYPEDEF':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[7,7,-113,-128,7,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,7,-120,-115,-65,-102,7,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,7,7,-119,7,-114,-130,7,-118,-71,-103,7,-131,-96,-98,7,-131,-95,-101,-97,-53,7,7,-88,7,-147,-335,-146,-167,-166,-100,-126,7,-87,-61,-72,7,-73,7,-89,-149,-336,-30,7,-74,-174,-175,7,-76,-79,-82,-75,-77,7,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,7,7,7,-234,-233,7,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,7,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'XOR':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,251,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,251,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,251,-267,-269,251,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'AUTO':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[26,26,-113,-128,26,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,26,-120,-115,-65,-102,26,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,26,26,-119,26,-114,-130,26,-118,-71,-103,26,-131,-96,-98,26,-131,-95,-101,-97,-53,26,26,-88,26,-147,-335,-146,-167,-166,-100,-126,26,-87,-61,-72,26,-73,26,-89,-149,-336,-30,26,-74,-174,-175,26,-76,-79,-82,-75,-77,26,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,26,26,26,-234,-233,26,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,26,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'DIVEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,357,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'TIMES':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,22,23,25,26,27,29,30,33,34,35,36,37,38,39,40,43,44,45,46,47,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,76,80,81,82,85,87,89,91,94,96,97,98,100,101,103,104,105,106,109,111,116,117,119,120,121,122,123,124,126,128,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,177,180,181,187,191,192,198,201,202,204,205,206,211,218,219,220,223,224,227,229,230,231,232,233,234,235,236,237,238,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,269,273,275,278,279,280,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,312,314,316,317,319,328,329,332,336,338,339,342,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,442,443,445,447,448,449,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[35,-337,-113,-128,35,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,-120,-115,-65,-102,-126,-131,-108,-238,-111,-337,-122,35,-63,-129,35,-121,-116,-62,-112,-70,-123,-117,-337,-337,-119,-337,-114,-130,35,-118,-71,-103,-337,-9,-131,-91,-10,-96,35,-98,-131,-95,-101,-97,173,-126,35,35,-93,-147,-335,-146,-167,-166,-28,35,-182,-27,-100,-126,173,-337,-87,-90,-94,-92,-61,-72,35,-337,173,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-144,-301,-274,-314,173,-142,-327,173,-283,-287,-325,-304,-322,-302,-255,-315,-289,253,-328,-316,-288,-329,-320,-276,-323,173,-284,173,173,-312,35,-73,173,-149,-336,35,-183,173,35,336,-28,-337,35,352,-28,-337,-74,-274,-337,173,-326,173,-280,173,-277,-334,-332,-331,-333,-174,-175,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,-298,-297,173,173,-279,-143,-278,-140,35,-141,-145,420,-76,-79,-82,-75,173,-77,173,173,-81,-215,-214,-80,-216,173,-78,-312,-127,-153,-151,-148,173,-168,173,-69,-284,173,173,35,-284,173,173,-244,-247,-245,-241,-242,-246,-248,173,-250,-251,-243,-249,-12,173,173,-11,253,253,253,253,253,253,253,253,253,253,-257,-256,253,253,253,253,253,-258,-296,-295,-294,-293,-292,-305,173,173,173,494,-234,-233,173,-231,173,173,-217,173,-230,173,-84,-218,173,173,-152,-150,35,173,-170,-169,-337,-337,-198,173,-281,-282,173,-290,-291,173,-275,-337,-284,-229,-232,173,-221,173,-83,-219,-68,541,-28,-337,173,-11,173,173,-220,173,173,173,-284,173,173,-306,173,-337,-299,173,-225,-224,-222,-84,-300,173,173,173,-226,-223,173,-228,-227,]),'LPAREN':([0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,19,20,22,23,25,26,27,29,30,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,72,73,76,77,80,81,82,85,86,87,89,91,94,96,97,98,100,101,103,104,105,106,109,111,112,116,117,119,120,121,122,123,124,126,127,128,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,152,153,154,155,156,160,161,163,164,165,166,167,168,169,170,171,173,174,175,176,177,180,181,187,191,192,198,199,200,201,202,204,205,206,211,216,218,219,220,223,227,229,230,231,233,235,236,237,238,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,269,275,276,278,279,280,282,283,284,285,286,289,290,291,292,296,297,298,299,300,301,302,303,305,307,308,310,311,312,314,316,317,319,328,329,332,336,338,339,340,341,342,344,345,347,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,402,403,404,405,407,411,412,413,414,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,442,443,445,447,448,449,453,454,457,458,459,464,465,472,475,477,481,482,483,484,488,489,490,492,494,496,497,499,500,502,504,505,506,510,511,512,513,514,515,518,519,521,522,529,530,531,532,533,535,536,537,538,539,541,542,543,544,545,547,549,550,551,553,554,555,557,558,559,560,565,566,569,570,571,574,575,576,577,578,579,],[37,-337,-113,-128,69,-124,-110,-106,85,-104,-107,-125,-105,-64,37,-60,-67,-99,-66,-109,-120,-115,-65,-102,-126,95,-108,-238,-111,-337,-122,37,-63,-129,37,116,-29,-121,-116,-62,-112,-70,118,-123,-117,-337,-337,-119,-337,-114,-130,37,-118,-71,-103,-337,-9,95,-91,-10,-96,69,-98,69,129,-131,-37,-95,-101,-97,174,118,-126,69,37,-93,-147,-335,-146,-167,-166,-28,-180,-182,-27,-100,-126,95,174,-337,-87,-90,-94,-92,-61,-72,69,129,-337,229,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-144,-301,-314,231,-142,-327,233,-283,-287,-325,-304,-322,239,-302,-315,-289,-328,-316,-288,-329,-320,266,-323,267,174,-284,229,233,-312,278,-73,229,-149,-336,69,-183,-181,-30,229,69,229,-28,-337,342,-38,229,-28,-337,-74,-337,229,-326,229,229,-334,-332,-331,-333,-174,-175,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,229,174,174,-298,-297,229,229,-143,-140,278,278,-141,-145,-337,422,-76,-79,-82,-75,229,-77,427,430,174,229,434,-81,-215,-214,-80,-216,229,-78,-312,441,-127,-153,-151,-148,174,-168,174,-69,-284,229,229,-36,-35,342,342,460,-45,-284,229,229,-44,-43,-244,-247,-245,-241,-242,-246,-248,229,-250,-251,-243,-249,-12,174,229,-11,-296,-295,-294,-293,-292,-305,229,174,422,229,229,-234,-233,229,-231,229,229,-217,229,-230,229,-84,-218,229,229,-152,-150,69,174,-170,-169,-31,-34,342,460,-337,-39,-42,-337,-198,174,174,-290,-291,229,-337,-213,-207,-211,-284,-229,-232,229,-221,229,538,-83,-219,-68,-33,-32,229,-28,-337,-41,-40,229,-11,-209,-208,-210,-212,229,229,-220,229,229,229,-284,229,229,-51,-50,-306,229,-337,-299,229,-225,-224,-222,-84,-46,-49,-300,229,229,-48,-47,229,-226,-223,229,-228,-227,]),'MINUSMINUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,145,146,148,149,150,151,152,153,154,156,160,161,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,227,229,230,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,482,483,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,175,-335,-28,-182,-27,175,-337,-87,-72,-337,175,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-314,175,-327,175,-283,-287,-325,-304,-322,-302,-315,-289,-328,-316,-288,-329,-320,261,-323,175,-284,175,175,-312,175,-336,-183,175,175,-28,-337,175,-28,-337,-337,175,-326,175,175,-334,-332,-331,-333,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,-298,-297,175,175,-337,-76,-79,-82,-75,175,-77,175,175,-81,-215,-214,-80,-216,175,-78,-312,175,175,-69,-284,175,175,-284,175,175,-244,-247,-245,-241,-242,-246,-248,175,-250,-251,-243,-249,-12,175,175,-11,-296,-295,-294,-293,-292,-305,175,175,175,175,-234,-233,175,-231,175,175,-217,175,-230,175,-84,-218,175,175,175,-337,-337,-198,175,175,-290,-291,175,-337,-284,-229,-232,175,-221,175,-83,-219,-68,175,-28,-337,175,-11,175,175,-220,175,175,175,-284,175,175,-306,175,-337,-299,175,-225,-224,-222,-84,-300,175,175,175,-226,-223,175,-228,-227,]),'ID':([0,1,2,3,4,5,6,7,10,11,12,13,14,15,16,17,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,43,44,45,46,47,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,72,76,80,81,82,85,87,89,91,94,96,97,98,100,101,102,103,104,105,106,109,111,116,117,118,119,120,121,122,123,124,126,128,129,131,135,137,142,146,147,149,150,151,165,171,173,174,175,180,181,187,191,192,193,194,198,199,201,202,204,205,206,211,218,219,220,223,227,229,231,233,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,262,264,265,266,269,275,279,280,282,284,285,286,287,289,290,291,297,298,300,301,302,303,305,307,308,312,314,316,317,319,327,328,329,332,336,338,339,342,344,349,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,372,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,442,443,445,447,448,449,457,459,460,472,475,477,481,484,485,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,548,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[42,-337,-113,-128,42,-124,-110,-106,-104,-107,-125,-105,-64,42,-60,-67,-99,-66,-109,-120,-115,-154,-65,-102,-126,-155,-131,-108,98,101,-238,-111,-337,-122,42,-63,-129,42,-121,-116,-62,-112,-70,-123,-117,-337,-337,-119,-337,-114,-130,42,-118,-71,-103,-337,-9,-131,-91,-10,-96,42,-98,42,-131,-95,-101,-97,176,-126,42,42,-93,-147,-335,-146,-167,-166,197,-28,-180,-182,-27,-100,-126,176,-337,176,-87,-90,-94,-92,-61,-72,42,-337,176,176,-286,-285,-144,176,-142,176,-283,-287,-288,176,-284,176,176,-73,310,-149,-336,42,197,197,-183,-181,176,42,176,-28,-337,42,176,-28,-337,-74,-337,176,176,176,-174,-175,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,403,405,176,176,-143,-140,-141,-145,-337,-76,-79,-82,423,-75,176,-77,176,310,-81,-215,-214,-80,-216,310,-78,-127,-153,-151,-148,176,197,-168,176,-69,-284,176,176,42,42,176,-284,176,176,-244,-247,-245,-241,-242,-246,-248,176,-250,-251,-243,-249,-12,176,176,176,-11,176,176,176,176,-234,-233,176,-231,310,176,-217,176,-230,310,-84,-218,310,176,-152,-150,42,176,-170,-169,42,-337,176,-337,-198,176,176,176,176,-337,-284,-229,-232,176,-221,310,-83,-219,-68,176,-28,-337,176,-11,176,310,-220,310,176,310,-284,176,176,176,176,-337,176,-225,-224,-222,-84,176,310,310,-226,-223,310,-228,-227,]),'IF':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,311,-336,-76,-79,-82,-75,-77,311,-81,-215,-214,-80,-216,311,-78,-69,-234,-233,-231,311,-217,-230,311,-84,-218,311,-229,-232,-221,311,-83,-219,-68,311,-220,311,311,-225,-224,-222,-84,311,311,-226,-223,311,-228,-227,]),'STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,136,137,146,149,150,151,152,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,230,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,333,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,452,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,152,-335,-28,-182,-27,152,-337,-87,-72,-337,152,-286,230,-285,152,152,-283,-287,-325,-288,152,-284,152,152,152,-336,-183,152,152,-28,-337,152,-28,-337,-337,152,-326,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,-337,-76,-79,-82,-75,152,-77,152,152,-81,-215,-214,-80,-216,152,-78,152,152,-69,152,-284,152,152,-284,152,152,-244,-247,-245,-241,-242,-246,-248,152,-250,-251,-243,-249,-12,152,152,-11,152,152,152,152,-234,-233,152,-231,152,152,-217,152,-230,152,-84,-218,152,152,152,230,-337,-337,-198,152,152,152,-337,-284,-229,-232,152,-221,152,-83,-219,-68,152,-28,-337,152,-11,152,152,-220,152,152,152,-284,152,152,152,-337,152,-225,-224,-222,-84,152,152,152,-226,-223,152,-228,-227,]),'FLOAT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[44,-337,-113,-128,44,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,44,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,44,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,44,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,44,-131,-95,-101,-97,44,-53,-126,44,-88,44,44,-93,44,-147,-335,-146,44,-167,-166,-182,-100,-126,44,-87,-90,-94,-92,-61,-72,44,-144,-142,44,44,44,-73,44,-89,44,44,44,-149,-159,-160,-156,-336,44,-183,-30,44,44,-74,44,44,44,44,-174,-175,44,-143,-140,44,-141,-145,-76,-79,-82,-75,-77,44,-81,-215,-214,-80,-216,-78,-127,44,-153,44,-151,-148,-157,-168,-69,-36,-35,44,44,44,-234,-233,44,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,44,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'XOREQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,361,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'LSHIFTEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,363,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'RBRACKET':([3,39,58,76,103,105,106,117,128,132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,191,198,204,205,218,219,224,225,230,232,234,235,236,237,238,261,263,268,272,273,282,334,335,336,337,351,352,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,406,407,411,419,420,421,455,456,459,466,467,468,471,476,478,480,482,483,486,487,491,493,494,513,514,524,540,541,547,551,561,562,564,565,],[-128,-129,-130,-131,-28,-182,-27,-337,-337,-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-336,-183,-337,-28,-337,-28,-274,-239,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-235,-278,-337,453,-4,454,-3,464,465,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,482,-292,-305,-337,492,-337,511,512,-337,518,519,-240,520,-237,-281,-282,-290,-291,-236,-275,529,530,531,-337,-28,-254,559,560,-306,-299,570,571,572,-300,]),} + +_lr_action = {} +for _k, _v in _lr_action_items.items(): + for _x,_y in zip(_v[0],_v[1]): + if not _x in _lr_action: _lr_action[_x] = {} + _lr_action[_x][_k] = _y +del _lr_action_items + +_lr_goto_items = {'expression_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[284,284,284,284,284,284,284,284,284,284,284,284,284,]),'struct_or_union_specifier':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,]),'init_declarator_list':([4,89,],[70,70,]),'init_declarator_list_opt':([4,89,],[79,79,]),'iteration_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[285,285,285,285,285,285,285,285,285,285,285,285,285,]),'static_assert':([0,59,181,298,307,429,437,440,502,535,537,539,569,574,577,],[17,17,286,286,286,286,286,286,286,286,286,286,286,286,286,]),'unified_string_literal':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,333,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,452,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,]),'assignment_expression_opt':([204,218,419,421,513,],[334,351,491,493,540,]),'brace_open':([31,32,92,96,98,100,101,130,131,181,201,229,298,307,375,413,429,437,440,477,478,479,502,521,535,537,539,569,574,577,],[99,102,181,184,185,193,194,181,227,181,227,181,181,181,227,488,181,181,181,488,488,488,181,227,181,181,181,181,181,181,]),'enumerator':([102,193,194,327,],[195,195,195,450,]),'typeid_noparen_declarator':([211,],[348,]),'type_qualifier_list_opt':([35,117,128,206,220,282,459,515,],[104,204,218,339,354,419,513,543,]),'declaration_specifiers_no_type_opt':([1,27,52,53,55,63,87,],[66,94,120,121,122,94,94,]),'expression_opt':([181,298,307,427,429,437,440,499,502,533,535,537,539,553,566,569,574,577,],[288,288,288,498,288,288,288,534,288,552,288,288,288,567,573,288,288,288,]),'designation':([227,472,488,550,],[369,369,369,369,]),'parameter_list':([118,129,278,342,422,460,],[213,213,213,213,213,213,]),'alignment_specifier':([0,1,4,21,27,52,53,55,59,63,75,85,87,89,93,95,99,118,129,174,177,181,184,185,186,192,211,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[53,53,81,53,53,53,53,53,53,53,53,142,53,81,53,142,142,53,53,142,280,53,142,142,142,280,81,142,142,142,142,142,53,53,142,142,53,53,53,53,53,]),'labeled_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[289,289,289,289,289,289,289,289,289,289,289,289,289,]),'abstract_declarator':([177,211,278,342,],[281,281,418,418,]),'translation_unit':([0,],[59,]),'init_declarator':([4,89,126,202,],[84,84,217,331,]),'direct_abstract_declarator':([177,211,276,278,342,344,457,],[283,283,414,283,283,414,414,]),'designator_list':([227,472,488,550,],[376,376,376,376,]),'identifier':([85,116,118,129,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,349,353,354,364,372,373,375,412,413,419,421,427,429,430,434,437,440,441,447,460,477,481,484,485,499,502,513,521,533,535,537,538,539,542,543,548,549,553,566,569,574,577,],[143,143,215,215,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,461,143,143,143,470,143,143,143,143,143,143,143,143,143,143,143,143,143,143,215,143,143,143,527,143,143,143,143,143,143,143,143,143,143,143,563,143,143,143,143,143,143,]),'offsetof_member_designator':([485,],[526,]),'unary_expression':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[144,144,224,232,234,144,224,273,224,224,224,224,224,224,224,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,224,144,144,224,224,224,144,224,224,144,144,224,224,224,224,224,144,224,224,144,224,224,224,224,224,224,224,224,224,144,144,144,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,]),'abstract_declarator_opt':([177,211,],[274,343,]),'initializer':([131,201,375,521,],[226,330,473,546,]),'direct_id_declarator':([0,4,15,37,40,59,69,72,89,91,126,192,202,211,342,344,445,457,],[48,48,86,48,48,48,48,86,48,48,48,48,48,48,48,86,48,86,]),'struct_declaration_list':([99,184,185,],[186,313,315,]),'pp_directive':([0,59,],[14,14,]),'declaration_list':([21,75,],[93,93,]),'id_init_declarator':([40,91,],[108,108,]),'type_specifier':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[18,18,109,18,18,147,109,18,147,147,18,18,269,147,18,147,147,147,109,147,147,147,147,147,18,18,147,147,18,18,18,18,18,]),'compound_statement':([92,130,181,229,298,307,429,437,440,502,535,537,539,569,574,577,],[180,223,291,378,291,291,291,291,291,291,291,291,291,291,291,291,]),'pointer':([0,4,37,40,59,69,89,91,104,126,177,192,202,211,278,342,445,],[15,72,15,15,15,72,72,15,199,72,276,72,72,344,276,457,72,]),'typeid_declarator':([4,69,89,126,192,202,445,],[74,125,74,74,74,74,74,]),'id_init_declarator_list':([40,91,],[113,113,]),'declarator':([4,89,126,192,202,445,],[78,78,78,324,78,324,]),'argument_expression_list':([266,],[409,]),'struct_declarator_list_opt':([192,],[322,]),'block_item_list':([181,],[298,]),'parameter_type_list_opt':([278,342,422,],[417,417,495,]),'struct_declarator':([192,445,],[323,508,]),'type_qualifier':([0,1,4,21,27,35,52,53,55,59,63,75,85,87,89,93,95,99,103,117,118,128,129,172,174,177,181,184,185,186,192,205,206,211,219,220,229,231,233,239,267,278,282,298,313,315,342,350,422,427,459,460,514,515,],[52,52,80,52,52,105,52,52,52,52,52,52,105,52,80,52,105,105,198,105,52,105,52,198,105,279,52,105,105,105,279,198,105,80,198,105,105,105,105,105,105,52,105,52,105,105,52,52,52,52,105,52,198,105,]),'assignment_operator':([224,],[364,]),'expression':([174,181,229,231,233,258,265,290,298,307,427,429,430,434,437,440,441,499,502,533,535,537,538,539,549,553,566,569,574,577,],[270,294,270,270,270,399,406,426,294,294,294,294,501,503,294,294,507,294,294,294,294,294,556,294,564,294,294,294,294,294,]),'storage_class_specifier':([0,1,4,21,27,52,53,55,59,63,75,87,89,93,118,129,181,211,278,298,342,350,422,427,460,],[1,1,68,1,1,1,1,1,1,1,1,1,68,1,1,1,1,68,1,1,1,1,1,1,1,]),'unified_wstring_literal':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,]),'translation_unit_or_empty':([0,],[9,]),'initializer_list_opt':([227,],[370,]),'brace_close':([99,184,185,186,196,309,313,315,325,326,370,472,528,550,],[187,314,316,317,328,439,442,443,448,449,469,523,551,565,]),'direct_typeid_declarator':([4,69,72,89,126,192,202,445,],[73,73,127,73,73,73,73,73,]),'external_declaration':([0,59,],[16,123,]),'pragmacomp_or_statement':([307,429,440,502,535,537,539,569,574,577,],[436,500,506,536,554,555,557,576,578,579,]),'type_name':([85,95,174,229,231,233,239,267,],[157,183,271,379,380,381,382,410,]),'typedef_name':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,]),'pppragma_directive':([0,59,99,181,184,185,186,298,307,313,315,429,437,440,502,535,537,539,569,574,577,],[25,25,189,300,189,189,189,300,437,189,189,437,300,437,437,437,437,437,437,437,437,]),'statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[301,301,438,438,505,438,438,438,438,558,438,438,438,]),'cast_expression':([85,116,131,171,174,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[158,158,158,268,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,487,158,158,158,158,158,158,158,158,158,158,487,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,]),'atomic_specifier':([0,1,21,27,40,52,53,55,59,63,75,85,87,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[27,63,87,63,111,63,63,63,27,63,87,111,63,111,87,111,111,27,27,111,111,87,111,111,111,111,111,111,111,111,111,27,87,111,111,27,27,27,87,27,]),'struct_declarator_list':([192,],[320,]),'empty':([0,1,4,21,27,35,40,52,53,55,63,75,87,89,91,117,118,128,129,177,181,192,204,206,211,218,220,227,278,282,298,307,342,419,421,422,427,429,437,440,459,460,472,488,499,502,513,515,533,535,537,539,550,553,566,569,574,577,],[57,64,83,88,64,106,115,64,64,64,64,88,64,83,115,106,208,106,208,277,306,321,337,106,277,337,106,377,415,106,433,433,415,337,337,415,433,433,433,433,106,208,522,522,433,433,337,106,433,433,433,433,522,433,433,433,433,433,]),'parameter_declaration':([118,129,278,342,350,422,460,],[210,210,210,210,463,210,210,]),'primary_expression':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,]),'declaration':([0,21,59,75,93,181,298,427,],[38,90,38,90,182,302,302,499,]),'declaration_specifiers_no_type':([0,1,21,27,52,53,55,59,63,75,87,93,118,129,181,278,298,342,350,422,427,460,],[40,67,91,67,67,67,67,40,67,91,67,91,214,214,91,214,91,214,214,214,91,214,]),'jump_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[303,303,303,303,303,303,303,303,303,303,303,303,303,]),'enumerator_list':([102,193,194,],[196,325,326,]),'block_item':([181,298,],[305,432,]),'constant_expression':([85,116,297,319,329,373,447,],[159,203,431,444,451,471,509,]),'identifier_list_opt':([118,129,460,],[207,221,516,]),'constant':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,]),'type_specifier_no_typeid':([0,4,21,40,59,75,85,89,91,93,95,99,118,129,172,174,177,181,184,185,186,192,211,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[12,71,12,12,12,12,12,71,12,12,12,12,12,12,12,12,275,12,12,12,12,275,71,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,]),'struct_declaration':([99,184,185,186,313,315,],[190,190,190,318,318,318,]),'direct_typeid_noparen_declarator':([211,344,],[345,458,]),'id_declarator':([0,4,37,40,59,69,89,91,126,192,202,211,342,445,],[21,75,107,110,21,107,179,110,179,179,179,346,107,179,]),'selection_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[308,308,308,308,308,308,308,308,308,308,308,308,308,]),'postfix_expression':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,]),'initializer_list':([227,488,],[374,528,]),'unary_operator':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,]),'struct_or_union':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,]),'block_item_list_opt':([181,],[309,]),'assignment_expression':([131,174,181,201,204,218,229,231,233,258,265,266,290,298,307,338,339,353,354,364,375,412,419,421,427,429,430,434,437,440,441,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[228,272,272,228,335,335,272,272,272,272,272,408,272,272,272,455,456,466,467,468,228,486,335,335,272,272,272,272,272,272,272,525,272,272,335,228,272,272,272,272,272,561,562,272,272,272,272,272,272,]),'designation_opt':([227,472,488,550,],[375,521,375,521,]),'parameter_type_list':([118,129,278,342,422,460,],[209,222,416,416,416,517,]),'type_qualifier_list':([35,85,95,99,117,128,174,184,185,186,206,220,229,231,233,239,267,282,313,315,459,515,],[103,172,172,172,205,219,172,172,172,172,103,103,172,172,172,172,172,103,172,172,514,103,]),'designator':([227,376,472,488,550,],[371,474,371,371,371,]),'id_init_declarator_list_opt':([40,91,],[114,114,]),'declaration_specifiers':([0,21,59,75,93,118,129,181,278,298,342,350,422,427,460,],[4,89,4,89,89,211,211,89,211,89,211,211,211,89,211,]),'identifier_list':([118,129,460,],[212,212,212,]),'declaration_list_opt':([21,75,],[92,130,]),'function_definition':([0,59,],[45,45,]),'binary_expression':([85,116,131,174,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,419,421,427,429,430,434,437,440,441,447,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[162,162,162,162,162,162,162,162,162,162,162,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,162,400,401,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,]),'enum_specifier':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,]),'decl_body':([0,21,59,75,93,181,298,427,],[51,51,51,51,51,51,51,51,]),'function_specifier':([0,1,4,21,27,52,53,55,59,63,75,87,89,93,118,129,181,211,278,298,342,350,422,427,460,],[55,55,82,55,55,55,55,55,55,55,55,55,82,55,55,55,55,82,55,55,55,55,55,55,55,]),'specifier_qualifier_list':([85,95,99,174,184,185,186,229,231,233,239,267,313,315,],[177,177,192,177,192,192,192,177,177,177,177,177,192,192,]),'conditional_expression':([85,116,131,174,181,201,204,218,229,231,233,258,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,419,421,427,429,430,434,437,440,441,447,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[178,178,225,225,225,225,225,225,225,225,225,225,225,225,225,178,225,225,178,178,225,225,225,225,225,178,225,225,225,225,225,225,225,225,225,225,225,178,524,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,]),} + +_lr_goto = {} +for _k, _v in _lr_goto_items.items(): + for _x, _y in zip(_v[0], _v[1]): + if not _x in _lr_goto: _lr_goto[_x] = {} + _lr_goto[_x][_k] = _y +del _lr_goto_items +_lr_productions = [ + ("S' -> translation_unit_or_empty","S'",1,None,None,None), + ('abstract_declarator_opt -> empty','abstract_declarator_opt',1,'p_abstract_declarator_opt','plyparser.py',43), + ('abstract_declarator_opt -> abstract_declarator','abstract_declarator_opt',1,'p_abstract_declarator_opt','plyparser.py',44), + ('assignment_expression_opt -> empty','assignment_expression_opt',1,'p_assignment_expression_opt','plyparser.py',43), + ('assignment_expression_opt -> assignment_expression','assignment_expression_opt',1,'p_assignment_expression_opt','plyparser.py',44), + ('block_item_list_opt -> empty','block_item_list_opt',1,'p_block_item_list_opt','plyparser.py',43), + ('block_item_list_opt -> block_item_list','block_item_list_opt',1,'p_block_item_list_opt','plyparser.py',44), + ('declaration_list_opt -> empty','declaration_list_opt',1,'p_declaration_list_opt','plyparser.py',43), + ('declaration_list_opt -> declaration_list','declaration_list_opt',1,'p_declaration_list_opt','plyparser.py',44), + ('declaration_specifiers_no_type_opt -> empty','declaration_specifiers_no_type_opt',1,'p_declaration_specifiers_no_type_opt','plyparser.py',43), + ('declaration_specifiers_no_type_opt -> declaration_specifiers_no_type','declaration_specifiers_no_type_opt',1,'p_declaration_specifiers_no_type_opt','plyparser.py',44), + ('designation_opt -> empty','designation_opt',1,'p_designation_opt','plyparser.py',43), + ('designation_opt -> designation','designation_opt',1,'p_designation_opt','plyparser.py',44), + ('expression_opt -> empty','expression_opt',1,'p_expression_opt','plyparser.py',43), + ('expression_opt -> expression','expression_opt',1,'p_expression_opt','plyparser.py',44), + ('id_init_declarator_list_opt -> empty','id_init_declarator_list_opt',1,'p_id_init_declarator_list_opt','plyparser.py',43), + ('id_init_declarator_list_opt -> id_init_declarator_list','id_init_declarator_list_opt',1,'p_id_init_declarator_list_opt','plyparser.py',44), + ('identifier_list_opt -> empty','identifier_list_opt',1,'p_identifier_list_opt','plyparser.py',43), + ('identifier_list_opt -> identifier_list','identifier_list_opt',1,'p_identifier_list_opt','plyparser.py',44), + ('init_declarator_list_opt -> empty','init_declarator_list_opt',1,'p_init_declarator_list_opt','plyparser.py',43), + ('init_declarator_list_opt -> init_declarator_list','init_declarator_list_opt',1,'p_init_declarator_list_opt','plyparser.py',44), + ('initializer_list_opt -> empty','initializer_list_opt',1,'p_initializer_list_opt','plyparser.py',43), + ('initializer_list_opt -> initializer_list','initializer_list_opt',1,'p_initializer_list_opt','plyparser.py',44), + ('parameter_type_list_opt -> empty','parameter_type_list_opt',1,'p_parameter_type_list_opt','plyparser.py',43), + ('parameter_type_list_opt -> parameter_type_list','parameter_type_list_opt',1,'p_parameter_type_list_opt','plyparser.py',44), + ('struct_declarator_list_opt -> empty','struct_declarator_list_opt',1,'p_struct_declarator_list_opt','plyparser.py',43), + ('struct_declarator_list_opt -> struct_declarator_list','struct_declarator_list_opt',1,'p_struct_declarator_list_opt','plyparser.py',44), + ('type_qualifier_list_opt -> empty','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','plyparser.py',43), + ('type_qualifier_list_opt -> type_qualifier_list','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','plyparser.py',44), + ('direct_id_declarator -> ID','direct_id_declarator',1,'p_direct_id_declarator_1','plyparser.py',126), + ('direct_id_declarator -> LPAREN id_declarator RPAREN','direct_id_declarator',3,'p_direct_id_declarator_2','plyparser.py',126), + ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_id_declarator',5,'p_direct_id_declarator_3','plyparser.py',126), + ('direct_id_declarator -> direct_id_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_id_declarator',6,'p_direct_id_declarator_4','plyparser.py',126), + ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_id_declarator',6,'p_direct_id_declarator_4','plyparser.py',127), + ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_id_declarator',5,'p_direct_id_declarator_5','plyparser.py',126), + ('direct_id_declarator -> direct_id_declarator LPAREN parameter_type_list RPAREN','direct_id_declarator',4,'p_direct_id_declarator_6','plyparser.py',126), + ('direct_id_declarator -> direct_id_declarator LPAREN identifier_list_opt RPAREN','direct_id_declarator',4,'p_direct_id_declarator_6','plyparser.py',127), + ('direct_typeid_declarator -> TYPEID','direct_typeid_declarator',1,'p_direct_typeid_declarator_1','plyparser.py',126), + ('direct_typeid_declarator -> LPAREN typeid_declarator RPAREN','direct_typeid_declarator',3,'p_direct_typeid_declarator_2','plyparser.py',126), + ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_typeid_declarator',5,'p_direct_typeid_declarator_3','plyparser.py',126), + ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_typeid_declarator',6,'p_direct_typeid_declarator_4','plyparser.py',126), + ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_typeid_declarator',6,'p_direct_typeid_declarator_4','plyparser.py',127), + ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_typeid_declarator',5,'p_direct_typeid_declarator_5','plyparser.py',126), + ('direct_typeid_declarator -> direct_typeid_declarator LPAREN parameter_type_list RPAREN','direct_typeid_declarator',4,'p_direct_typeid_declarator_6','plyparser.py',126), + ('direct_typeid_declarator -> direct_typeid_declarator LPAREN identifier_list_opt RPAREN','direct_typeid_declarator',4,'p_direct_typeid_declarator_6','plyparser.py',127), + ('direct_typeid_noparen_declarator -> TYPEID','direct_typeid_noparen_declarator',1,'p_direct_typeid_noparen_declarator_1','plyparser.py',126), + ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_typeid_noparen_declarator',5,'p_direct_typeid_noparen_declarator_3','plyparser.py',126), + ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_typeid_noparen_declarator',6,'p_direct_typeid_noparen_declarator_4','plyparser.py',126), + ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_typeid_noparen_declarator',6,'p_direct_typeid_noparen_declarator_4','plyparser.py',127), + ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_typeid_noparen_declarator',5,'p_direct_typeid_noparen_declarator_5','plyparser.py',126), + ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LPAREN parameter_type_list RPAREN','direct_typeid_noparen_declarator',4,'p_direct_typeid_noparen_declarator_6','plyparser.py',126), + ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LPAREN identifier_list_opt RPAREN','direct_typeid_noparen_declarator',4,'p_direct_typeid_noparen_declarator_6','plyparser.py',127), + ('id_declarator -> direct_id_declarator','id_declarator',1,'p_id_declarator_1','plyparser.py',126), + ('id_declarator -> pointer direct_id_declarator','id_declarator',2,'p_id_declarator_2','plyparser.py',126), + ('typeid_declarator -> direct_typeid_declarator','typeid_declarator',1,'p_typeid_declarator_1','plyparser.py',126), + ('typeid_declarator -> pointer direct_typeid_declarator','typeid_declarator',2,'p_typeid_declarator_2','plyparser.py',126), + ('typeid_noparen_declarator -> direct_typeid_noparen_declarator','typeid_noparen_declarator',1,'p_typeid_noparen_declarator_1','plyparser.py',126), + ('typeid_noparen_declarator -> pointer direct_typeid_noparen_declarator','typeid_noparen_declarator',2,'p_typeid_noparen_declarator_2','plyparser.py',126), + ('translation_unit_or_empty -> translation_unit','translation_unit_or_empty',1,'p_translation_unit_or_empty','c_parser.py',509), + ('translation_unit_or_empty -> empty','translation_unit_or_empty',1,'p_translation_unit_or_empty','c_parser.py',510), + ('translation_unit -> external_declaration','translation_unit',1,'p_translation_unit_1','c_parser.py',518), + ('translation_unit -> translation_unit external_declaration','translation_unit',2,'p_translation_unit_2','c_parser.py',524), + ('external_declaration -> function_definition','external_declaration',1,'p_external_declaration_1','c_parser.py',534), + ('external_declaration -> declaration','external_declaration',1,'p_external_declaration_2','c_parser.py',539), + ('external_declaration -> pp_directive','external_declaration',1,'p_external_declaration_3','c_parser.py',544), + ('external_declaration -> pppragma_directive','external_declaration',1,'p_external_declaration_3','c_parser.py',545), + ('external_declaration -> SEMI','external_declaration',1,'p_external_declaration_4','c_parser.py',550), + ('external_declaration -> static_assert','external_declaration',1,'p_external_declaration_5','c_parser.py',555), + ('static_assert -> _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN','static_assert',6,'p_static_assert_declaration','c_parser.py',560), + ('static_assert -> _STATIC_ASSERT LPAREN constant_expression RPAREN','static_assert',4,'p_static_assert_declaration','c_parser.py',561), + ('pp_directive -> PPHASH','pp_directive',1,'p_pp_directive','c_parser.py',569), + ('pppragma_directive -> PPPRAGMA','pppragma_directive',1,'p_pppragma_directive','c_parser.py',575), + ('pppragma_directive -> PPPRAGMA PPPRAGMASTR','pppragma_directive',2,'p_pppragma_directive','c_parser.py',576), + ('function_definition -> id_declarator declaration_list_opt compound_statement','function_definition',3,'p_function_definition_1','c_parser.py',586), + ('function_definition -> declaration_specifiers id_declarator declaration_list_opt compound_statement','function_definition',4,'p_function_definition_2','c_parser.py',604), + ('statement -> labeled_statement','statement',1,'p_statement','c_parser.py',619), + ('statement -> expression_statement','statement',1,'p_statement','c_parser.py',620), + ('statement -> compound_statement','statement',1,'p_statement','c_parser.py',621), + ('statement -> selection_statement','statement',1,'p_statement','c_parser.py',622), + ('statement -> iteration_statement','statement',1,'p_statement','c_parser.py',623), + ('statement -> jump_statement','statement',1,'p_statement','c_parser.py',624), + ('statement -> pppragma_directive','statement',1,'p_statement','c_parser.py',625), + ('statement -> static_assert','statement',1,'p_statement','c_parser.py',626), + ('pragmacomp_or_statement -> pppragma_directive statement','pragmacomp_or_statement',2,'p_pragmacomp_or_statement','c_parser.py',674), + ('pragmacomp_or_statement -> statement','pragmacomp_or_statement',1,'p_pragmacomp_or_statement','c_parser.py',675), + ('decl_body -> declaration_specifiers init_declarator_list_opt','decl_body',2,'p_decl_body','c_parser.py',694), + ('decl_body -> declaration_specifiers_no_type id_init_declarator_list_opt','decl_body',2,'p_decl_body','c_parser.py',695), + ('declaration -> decl_body SEMI','declaration',2,'p_declaration','c_parser.py',755), + ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','c_parser.py',764), + ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','c_parser.py',765), + ('declaration_specifiers_no_type -> type_qualifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_1','c_parser.py',775), + ('declaration_specifiers_no_type -> storage_class_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_2','c_parser.py',780), + ('declaration_specifiers_no_type -> function_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_3','c_parser.py',785), + ('declaration_specifiers_no_type -> atomic_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_4','c_parser.py',792), + ('declaration_specifiers_no_type -> alignment_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_5','c_parser.py',797), + ('declaration_specifiers -> declaration_specifiers type_qualifier','declaration_specifiers',2,'p_declaration_specifiers_1','c_parser.py',802), + ('declaration_specifiers -> declaration_specifiers storage_class_specifier','declaration_specifiers',2,'p_declaration_specifiers_2','c_parser.py',807), + ('declaration_specifiers -> declaration_specifiers function_specifier','declaration_specifiers',2,'p_declaration_specifiers_3','c_parser.py',812), + ('declaration_specifiers -> declaration_specifiers type_specifier_no_typeid','declaration_specifiers',2,'p_declaration_specifiers_4','c_parser.py',817), + ('declaration_specifiers -> type_specifier','declaration_specifiers',1,'p_declaration_specifiers_5','c_parser.py',822), + ('declaration_specifiers -> declaration_specifiers_no_type type_specifier','declaration_specifiers',2,'p_declaration_specifiers_6','c_parser.py',827), + ('declaration_specifiers -> declaration_specifiers alignment_specifier','declaration_specifiers',2,'p_declaration_specifiers_7','c_parser.py',832), + ('storage_class_specifier -> AUTO','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',837), + ('storage_class_specifier -> REGISTER','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',838), + ('storage_class_specifier -> STATIC','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',839), + ('storage_class_specifier -> EXTERN','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',840), + ('storage_class_specifier -> TYPEDEF','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',841), + ('storage_class_specifier -> _THREAD_LOCAL','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',842), + ('function_specifier -> INLINE','function_specifier',1,'p_function_specifier','c_parser.py',847), + ('function_specifier -> _NORETURN','function_specifier',1,'p_function_specifier','c_parser.py',848), + ('type_specifier_no_typeid -> VOID','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',853), + ('type_specifier_no_typeid -> _BOOL','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',854), + ('type_specifier_no_typeid -> CHAR','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',855), + ('type_specifier_no_typeid -> SHORT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',856), + ('type_specifier_no_typeid -> INT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',857), + ('type_specifier_no_typeid -> LONG','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',858), + ('type_specifier_no_typeid -> FLOAT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',859), + ('type_specifier_no_typeid -> DOUBLE','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',860), + ('type_specifier_no_typeid -> _COMPLEX','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',861), + ('type_specifier_no_typeid -> SIGNED','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',862), + ('type_specifier_no_typeid -> UNSIGNED','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',863), + ('type_specifier_no_typeid -> __INT128','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',864), + ('type_specifier -> typedef_name','type_specifier',1,'p_type_specifier','c_parser.py',869), + ('type_specifier -> enum_specifier','type_specifier',1,'p_type_specifier','c_parser.py',870), + ('type_specifier -> struct_or_union_specifier','type_specifier',1,'p_type_specifier','c_parser.py',871), + ('type_specifier -> type_specifier_no_typeid','type_specifier',1,'p_type_specifier','c_parser.py',872), + ('type_specifier -> atomic_specifier','type_specifier',1,'p_type_specifier','c_parser.py',873), + ('atomic_specifier -> _ATOMIC LPAREN type_name RPAREN','atomic_specifier',4,'p_atomic_specifier','c_parser.py',879), + ('type_qualifier -> CONST','type_qualifier',1,'p_type_qualifier','c_parser.py',886), + ('type_qualifier -> RESTRICT','type_qualifier',1,'p_type_qualifier','c_parser.py',887), + ('type_qualifier -> VOLATILE','type_qualifier',1,'p_type_qualifier','c_parser.py',888), + ('type_qualifier -> _ATOMIC','type_qualifier',1,'p_type_qualifier','c_parser.py',889), + ('init_declarator_list -> init_declarator','init_declarator_list',1,'p_init_declarator_list','c_parser.py',894), + ('init_declarator_list -> init_declarator_list COMMA init_declarator','init_declarator_list',3,'p_init_declarator_list','c_parser.py',895), + ('init_declarator -> declarator','init_declarator',1,'p_init_declarator','c_parser.py',903), + ('init_declarator -> declarator EQUALS initializer','init_declarator',3,'p_init_declarator','c_parser.py',904), + ('id_init_declarator_list -> id_init_declarator','id_init_declarator_list',1,'p_id_init_declarator_list','c_parser.py',909), + ('id_init_declarator_list -> id_init_declarator_list COMMA init_declarator','id_init_declarator_list',3,'p_id_init_declarator_list','c_parser.py',910), + ('id_init_declarator -> id_declarator','id_init_declarator',1,'p_id_init_declarator','c_parser.py',915), + ('id_init_declarator -> id_declarator EQUALS initializer','id_init_declarator',3,'p_id_init_declarator','c_parser.py',916), + ('specifier_qualifier_list -> specifier_qualifier_list type_specifier_no_typeid','specifier_qualifier_list',2,'p_specifier_qualifier_list_1','c_parser.py',923), + ('specifier_qualifier_list -> specifier_qualifier_list type_qualifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_2','c_parser.py',928), + ('specifier_qualifier_list -> type_specifier','specifier_qualifier_list',1,'p_specifier_qualifier_list_3','c_parser.py',933), + ('specifier_qualifier_list -> type_qualifier_list type_specifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_4','c_parser.py',938), + ('specifier_qualifier_list -> alignment_specifier','specifier_qualifier_list',1,'p_specifier_qualifier_list_5','c_parser.py',943), + ('specifier_qualifier_list -> specifier_qualifier_list alignment_specifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_6','c_parser.py',948), + ('struct_or_union_specifier -> struct_or_union ID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','c_parser.py',956), + ('struct_or_union_specifier -> struct_or_union TYPEID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','c_parser.py',957), + ('struct_or_union_specifier -> struct_or_union brace_open struct_declaration_list brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_2','c_parser.py',967), + ('struct_or_union_specifier -> struct_or_union brace_open brace_close','struct_or_union_specifier',3,'p_struct_or_union_specifier_2','c_parser.py',968), + ('struct_or_union_specifier -> struct_or_union ID brace_open struct_declaration_list brace_close','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','c_parser.py',985), + ('struct_or_union_specifier -> struct_or_union ID brace_open brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_3','c_parser.py',986), + ('struct_or_union_specifier -> struct_or_union TYPEID brace_open struct_declaration_list brace_close','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','c_parser.py',987), + ('struct_or_union_specifier -> struct_or_union TYPEID brace_open brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_3','c_parser.py',988), + ('struct_or_union -> STRUCT','struct_or_union',1,'p_struct_or_union','c_parser.py',1004), + ('struct_or_union -> UNION','struct_or_union',1,'p_struct_or_union','c_parser.py',1005), + ('struct_declaration_list -> struct_declaration','struct_declaration_list',1,'p_struct_declaration_list','c_parser.py',1012), + ('struct_declaration_list -> struct_declaration_list struct_declaration','struct_declaration_list',2,'p_struct_declaration_list','c_parser.py',1013), + ('struct_declaration -> specifier_qualifier_list struct_declarator_list_opt SEMI','struct_declaration',3,'p_struct_declaration_1','c_parser.py',1021), + ('struct_declaration -> SEMI','struct_declaration',1,'p_struct_declaration_2','c_parser.py',1059), + ('struct_declaration -> pppragma_directive','struct_declaration',1,'p_struct_declaration_3','c_parser.py',1064), + ('struct_declarator_list -> struct_declarator','struct_declarator_list',1,'p_struct_declarator_list','c_parser.py',1069), + ('struct_declarator_list -> struct_declarator_list COMMA struct_declarator','struct_declarator_list',3,'p_struct_declarator_list','c_parser.py',1070), + ('struct_declarator -> declarator','struct_declarator',1,'p_struct_declarator_1','c_parser.py',1078), + ('struct_declarator -> declarator COLON constant_expression','struct_declarator',3,'p_struct_declarator_2','c_parser.py',1083), + ('struct_declarator -> COLON constant_expression','struct_declarator',2,'p_struct_declarator_2','c_parser.py',1084), + ('enum_specifier -> ENUM ID','enum_specifier',2,'p_enum_specifier_1','c_parser.py',1092), + ('enum_specifier -> ENUM TYPEID','enum_specifier',2,'p_enum_specifier_1','c_parser.py',1093), + ('enum_specifier -> ENUM brace_open enumerator_list brace_close','enum_specifier',4,'p_enum_specifier_2','c_parser.py',1098), + ('enum_specifier -> ENUM ID brace_open enumerator_list brace_close','enum_specifier',5,'p_enum_specifier_3','c_parser.py',1103), + ('enum_specifier -> ENUM TYPEID brace_open enumerator_list brace_close','enum_specifier',5,'p_enum_specifier_3','c_parser.py',1104), + ('enumerator_list -> enumerator','enumerator_list',1,'p_enumerator_list','c_parser.py',1109), + ('enumerator_list -> enumerator_list COMMA','enumerator_list',2,'p_enumerator_list','c_parser.py',1110), + ('enumerator_list -> enumerator_list COMMA enumerator','enumerator_list',3,'p_enumerator_list','c_parser.py',1111), + ('alignment_specifier -> _ALIGNAS LPAREN type_name RPAREN','alignment_specifier',4,'p_alignment_specifier','c_parser.py',1122), + ('alignment_specifier -> _ALIGNAS LPAREN constant_expression RPAREN','alignment_specifier',4,'p_alignment_specifier','c_parser.py',1123), + ('enumerator -> ID','enumerator',1,'p_enumerator','c_parser.py',1128), + ('enumerator -> ID EQUALS constant_expression','enumerator',3,'p_enumerator','c_parser.py',1129), + ('declarator -> id_declarator','declarator',1,'p_declarator','c_parser.py',1144), + ('declarator -> typeid_declarator','declarator',1,'p_declarator','c_parser.py',1145), + ('pointer -> TIMES type_qualifier_list_opt','pointer',2,'p_pointer','c_parser.py',1257), + ('pointer -> TIMES type_qualifier_list_opt pointer','pointer',3,'p_pointer','c_parser.py',1258), + ('type_qualifier_list -> type_qualifier','type_qualifier_list',1,'p_type_qualifier_list','c_parser.py',1287), + ('type_qualifier_list -> type_qualifier_list type_qualifier','type_qualifier_list',2,'p_type_qualifier_list','c_parser.py',1288), + ('parameter_type_list -> parameter_list','parameter_type_list',1,'p_parameter_type_list','c_parser.py',1293), + ('parameter_type_list -> parameter_list COMMA ELLIPSIS','parameter_type_list',3,'p_parameter_type_list','c_parser.py',1294), + ('parameter_list -> parameter_declaration','parameter_list',1,'p_parameter_list','c_parser.py',1302), + ('parameter_list -> parameter_list COMMA parameter_declaration','parameter_list',3,'p_parameter_list','c_parser.py',1303), + ('parameter_declaration -> declaration_specifiers id_declarator','parameter_declaration',2,'p_parameter_declaration_1','c_parser.py',1322), + ('parameter_declaration -> declaration_specifiers typeid_noparen_declarator','parameter_declaration',2,'p_parameter_declaration_1','c_parser.py',1323), + ('parameter_declaration -> declaration_specifiers abstract_declarator_opt','parameter_declaration',2,'p_parameter_declaration_2','c_parser.py',1334), + ('identifier_list -> identifier','identifier_list',1,'p_identifier_list','c_parser.py',1366), + ('identifier_list -> identifier_list COMMA identifier','identifier_list',3,'p_identifier_list','c_parser.py',1367), + ('initializer -> assignment_expression','initializer',1,'p_initializer_1','c_parser.py',1376), + ('initializer -> brace_open initializer_list_opt brace_close','initializer',3,'p_initializer_2','c_parser.py',1381), + ('initializer -> brace_open initializer_list COMMA brace_close','initializer',4,'p_initializer_2','c_parser.py',1382), + ('initializer_list -> designation_opt initializer','initializer_list',2,'p_initializer_list','c_parser.py',1390), + ('initializer_list -> initializer_list COMMA designation_opt initializer','initializer_list',4,'p_initializer_list','c_parser.py',1391), + ('designation -> designator_list EQUALS','designation',2,'p_designation','c_parser.py',1402), + ('designator_list -> designator','designator_list',1,'p_designator_list','c_parser.py',1410), + ('designator_list -> designator_list designator','designator_list',2,'p_designator_list','c_parser.py',1411), + ('designator -> LBRACKET constant_expression RBRACKET','designator',3,'p_designator','c_parser.py',1416), + ('designator -> PERIOD identifier','designator',2,'p_designator','c_parser.py',1417), + ('type_name -> specifier_qualifier_list abstract_declarator_opt','type_name',2,'p_type_name','c_parser.py',1422), + ('abstract_declarator -> pointer','abstract_declarator',1,'p_abstract_declarator_1','c_parser.py',1434), + ('abstract_declarator -> pointer direct_abstract_declarator','abstract_declarator',2,'p_abstract_declarator_2','c_parser.py',1442), + ('abstract_declarator -> direct_abstract_declarator','abstract_declarator',1,'p_abstract_declarator_3','c_parser.py',1447), + ('direct_abstract_declarator -> LPAREN abstract_declarator RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_1','c_parser.py',1457), + ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_2','c_parser.py',1461), + ('direct_abstract_declarator -> LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_3','c_parser.py',1472), + ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET TIMES RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_4','c_parser.py',1482), + ('direct_abstract_declarator -> LBRACKET TIMES RBRACKET','direct_abstract_declarator',3,'p_direct_abstract_declarator_5','c_parser.py',1493), + ('direct_abstract_declarator -> direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',4,'p_direct_abstract_declarator_6','c_parser.py',1502), + ('direct_abstract_declarator -> LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_7','c_parser.py',1512), + ('block_item -> declaration','block_item',1,'p_block_item','c_parser.py',1523), + ('block_item -> statement','block_item',1,'p_block_item','c_parser.py',1524), + ('block_item_list -> block_item','block_item_list',1,'p_block_item_list','c_parser.py',1531), + ('block_item_list -> block_item_list block_item','block_item_list',2,'p_block_item_list','c_parser.py',1532), + ('compound_statement -> brace_open block_item_list_opt brace_close','compound_statement',3,'p_compound_statement_1','c_parser.py',1538), + ('labeled_statement -> ID COLON pragmacomp_or_statement','labeled_statement',3,'p_labeled_statement_1','c_parser.py',1544), + ('labeled_statement -> CASE constant_expression COLON pragmacomp_or_statement','labeled_statement',4,'p_labeled_statement_2','c_parser.py',1548), + ('labeled_statement -> DEFAULT COLON pragmacomp_or_statement','labeled_statement',3,'p_labeled_statement_3','c_parser.py',1552), + ('selection_statement -> IF LPAREN expression RPAREN pragmacomp_or_statement','selection_statement',5,'p_selection_statement_1','c_parser.py',1556), + ('selection_statement -> IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement','selection_statement',7,'p_selection_statement_2','c_parser.py',1560), + ('selection_statement -> SWITCH LPAREN expression RPAREN pragmacomp_or_statement','selection_statement',5,'p_selection_statement_3','c_parser.py',1564), + ('iteration_statement -> WHILE LPAREN expression RPAREN pragmacomp_or_statement','iteration_statement',5,'p_iteration_statement_1','c_parser.py',1569), + ('iteration_statement -> DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI','iteration_statement',7,'p_iteration_statement_2','c_parser.py',1573), + ('iteration_statement -> FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement','iteration_statement',9,'p_iteration_statement_3','c_parser.py',1577), + ('iteration_statement -> FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement','iteration_statement',8,'p_iteration_statement_4','c_parser.py',1581), + ('jump_statement -> GOTO ID SEMI','jump_statement',3,'p_jump_statement_1','c_parser.py',1586), + ('jump_statement -> BREAK SEMI','jump_statement',2,'p_jump_statement_2','c_parser.py',1590), + ('jump_statement -> CONTINUE SEMI','jump_statement',2,'p_jump_statement_3','c_parser.py',1594), + ('jump_statement -> RETURN expression SEMI','jump_statement',3,'p_jump_statement_4','c_parser.py',1598), + ('jump_statement -> RETURN SEMI','jump_statement',2,'p_jump_statement_4','c_parser.py',1599), + ('expression_statement -> expression_opt SEMI','expression_statement',2,'p_expression_statement','c_parser.py',1604), + ('expression -> assignment_expression','expression',1,'p_expression','c_parser.py',1611), + ('expression -> expression COMMA assignment_expression','expression',3,'p_expression','c_parser.py',1612), + ('assignment_expression -> LPAREN compound_statement RPAREN','assignment_expression',3,'p_parenthesized_compound_expression','c_parser.py',1624), + ('typedef_name -> TYPEID','typedef_name',1,'p_typedef_name','c_parser.py',1628), + ('assignment_expression -> conditional_expression','assignment_expression',1,'p_assignment_expression','c_parser.py',1632), + ('assignment_expression -> unary_expression assignment_operator assignment_expression','assignment_expression',3,'p_assignment_expression','c_parser.py',1633), + ('assignment_operator -> EQUALS','assignment_operator',1,'p_assignment_operator','c_parser.py',1646), + ('assignment_operator -> XOREQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1647), + ('assignment_operator -> TIMESEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1648), + ('assignment_operator -> DIVEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1649), + ('assignment_operator -> MODEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1650), + ('assignment_operator -> PLUSEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1651), + ('assignment_operator -> MINUSEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1652), + ('assignment_operator -> LSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1653), + ('assignment_operator -> RSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1654), + ('assignment_operator -> ANDEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1655), + ('assignment_operator -> OREQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1656), + ('constant_expression -> conditional_expression','constant_expression',1,'p_constant_expression','c_parser.py',1661), + ('conditional_expression -> binary_expression','conditional_expression',1,'p_conditional_expression','c_parser.py',1665), + ('conditional_expression -> binary_expression CONDOP expression COLON conditional_expression','conditional_expression',5,'p_conditional_expression','c_parser.py',1666), + ('binary_expression -> cast_expression','binary_expression',1,'p_binary_expression','c_parser.py',1674), + ('binary_expression -> binary_expression TIMES binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1675), + ('binary_expression -> binary_expression DIVIDE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1676), + ('binary_expression -> binary_expression MOD binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1677), + ('binary_expression -> binary_expression PLUS binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1678), + ('binary_expression -> binary_expression MINUS binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1679), + ('binary_expression -> binary_expression RSHIFT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1680), + ('binary_expression -> binary_expression LSHIFT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1681), + ('binary_expression -> binary_expression LT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1682), + ('binary_expression -> binary_expression LE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1683), + ('binary_expression -> binary_expression GE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1684), + ('binary_expression -> binary_expression GT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1685), + ('binary_expression -> binary_expression EQ binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1686), + ('binary_expression -> binary_expression NE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1687), + ('binary_expression -> binary_expression AND binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1688), + ('binary_expression -> binary_expression OR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1689), + ('binary_expression -> binary_expression XOR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1690), + ('binary_expression -> binary_expression LAND binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1691), + ('binary_expression -> binary_expression LOR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1692), + ('cast_expression -> unary_expression','cast_expression',1,'p_cast_expression_1','c_parser.py',1700), + ('cast_expression -> LPAREN type_name RPAREN cast_expression','cast_expression',4,'p_cast_expression_2','c_parser.py',1704), + ('unary_expression -> postfix_expression','unary_expression',1,'p_unary_expression_1','c_parser.py',1708), + ('unary_expression -> PLUSPLUS unary_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1712), + ('unary_expression -> MINUSMINUS unary_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1713), + ('unary_expression -> unary_operator cast_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1714), + ('unary_expression -> SIZEOF unary_expression','unary_expression',2,'p_unary_expression_3','c_parser.py',1719), + ('unary_expression -> SIZEOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','c_parser.py',1720), + ('unary_expression -> _ALIGNOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','c_parser.py',1721), + ('unary_operator -> AND','unary_operator',1,'p_unary_operator','c_parser.py',1729), + ('unary_operator -> TIMES','unary_operator',1,'p_unary_operator','c_parser.py',1730), + ('unary_operator -> PLUS','unary_operator',1,'p_unary_operator','c_parser.py',1731), + ('unary_operator -> MINUS','unary_operator',1,'p_unary_operator','c_parser.py',1732), + ('unary_operator -> NOT','unary_operator',1,'p_unary_operator','c_parser.py',1733), + ('unary_operator -> LNOT','unary_operator',1,'p_unary_operator','c_parser.py',1734), + ('postfix_expression -> primary_expression','postfix_expression',1,'p_postfix_expression_1','c_parser.py',1739), + ('postfix_expression -> postfix_expression LBRACKET expression RBRACKET','postfix_expression',4,'p_postfix_expression_2','c_parser.py',1743), + ('postfix_expression -> postfix_expression LPAREN argument_expression_list RPAREN','postfix_expression',4,'p_postfix_expression_3','c_parser.py',1747), + ('postfix_expression -> postfix_expression LPAREN RPAREN','postfix_expression',3,'p_postfix_expression_3','c_parser.py',1748), + ('postfix_expression -> postfix_expression PERIOD ID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1753), + ('postfix_expression -> postfix_expression PERIOD TYPEID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1754), + ('postfix_expression -> postfix_expression ARROW ID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1755), + ('postfix_expression -> postfix_expression ARROW TYPEID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1756), + ('postfix_expression -> postfix_expression PLUSPLUS','postfix_expression',2,'p_postfix_expression_5','c_parser.py',1762), + ('postfix_expression -> postfix_expression MINUSMINUS','postfix_expression',2,'p_postfix_expression_5','c_parser.py',1763), + ('postfix_expression -> LPAREN type_name RPAREN brace_open initializer_list brace_close','postfix_expression',6,'p_postfix_expression_6','c_parser.py',1768), + ('postfix_expression -> LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close','postfix_expression',7,'p_postfix_expression_6','c_parser.py',1769), + ('primary_expression -> identifier','primary_expression',1,'p_primary_expression_1','c_parser.py',1774), + ('primary_expression -> constant','primary_expression',1,'p_primary_expression_2','c_parser.py',1778), + ('primary_expression -> unified_string_literal','primary_expression',1,'p_primary_expression_3','c_parser.py',1782), + ('primary_expression -> unified_wstring_literal','primary_expression',1,'p_primary_expression_3','c_parser.py',1783), + ('primary_expression -> LPAREN expression RPAREN','primary_expression',3,'p_primary_expression_4','c_parser.py',1788), + ('primary_expression -> OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN','primary_expression',6,'p_primary_expression_5','c_parser.py',1792), + ('offsetof_member_designator -> identifier','offsetof_member_designator',1,'p_offsetof_member_designator','c_parser.py',1800), + ('offsetof_member_designator -> offsetof_member_designator PERIOD identifier','offsetof_member_designator',3,'p_offsetof_member_designator','c_parser.py',1801), + ('offsetof_member_designator -> offsetof_member_designator LBRACKET expression RBRACKET','offsetof_member_designator',4,'p_offsetof_member_designator','c_parser.py',1802), + ('argument_expression_list -> assignment_expression','argument_expression_list',1,'p_argument_expression_list','c_parser.py',1814), + ('argument_expression_list -> argument_expression_list COMMA assignment_expression','argument_expression_list',3,'p_argument_expression_list','c_parser.py',1815), + ('identifier -> ID','identifier',1,'p_identifier','c_parser.py',1824), + ('constant -> INT_CONST_DEC','constant',1,'p_constant_1','c_parser.py',1828), + ('constant -> INT_CONST_OCT','constant',1,'p_constant_1','c_parser.py',1829), + ('constant -> INT_CONST_HEX','constant',1,'p_constant_1','c_parser.py',1830), + ('constant -> INT_CONST_BIN','constant',1,'p_constant_1','c_parser.py',1831), + ('constant -> INT_CONST_CHAR','constant',1,'p_constant_1','c_parser.py',1832), + ('constant -> FLOAT_CONST','constant',1,'p_constant_2','c_parser.py',1851), + ('constant -> HEX_FLOAT_CONST','constant',1,'p_constant_2','c_parser.py',1852), + ('constant -> CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1868), + ('constant -> WCHAR_CONST','constant',1,'p_constant_3','c_parser.py',1869), + ('constant -> U8CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1870), + ('constant -> U16CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1871), + ('constant -> U32CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1872), + ('unified_string_literal -> STRING_LITERAL','unified_string_literal',1,'p_unified_string_literal','c_parser.py',1883), + ('unified_string_literal -> unified_string_literal STRING_LITERAL','unified_string_literal',2,'p_unified_string_literal','c_parser.py',1884), + ('unified_wstring_literal -> WSTRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1894), + ('unified_wstring_literal -> U8STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1895), + ('unified_wstring_literal -> U16STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1896), + ('unified_wstring_literal -> U32STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1897), + ('unified_wstring_literal -> unified_wstring_literal WSTRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1898), + ('unified_wstring_literal -> unified_wstring_literal U8STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1899), + ('unified_wstring_literal -> unified_wstring_literal U16STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1900), + ('unified_wstring_literal -> unified_wstring_literal U32STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1901), + ('brace_open -> LBRACE','brace_open',1,'p_brace_open','c_parser.py',1911), + ('brace_close -> RBRACE','brace_close',1,'p_brace_close','c_parser.py',1917), + ('empty -> ','empty',0,'p_empty','c_parser.py',1923), +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD index 0a3068478..27be1aa46 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD +++ b/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD @@ -1,5 +1,5 @@ -../../Scripts/pyserial-miniterm.exe,sha256=AkD7vCt4y6zfo8amSiWU_PKRuD6CFmdxslX51wbeSII,108450 -../../Scripts/pyserial-ports.exe,sha256=DiIEEmXbmAbeh-Qj3I3uO7ZQ3Y_AiKRVdnNiKW2BQZw,108452 +../../Scripts/pyserial-miniterm.exe,sha256=Hm7AFKdXFsRt4Ksru9UPZjaunm6n2W7yJzTwZq864ZM,108450 +../../Scripts/pyserial-ports.exe,sha256=qXJH7bWCjQBzU2d4BO2gHx8yomLDzuerCJv87HByi4s,108452 pyserial-3.5.dist-info/DESCRIPTION.rst,sha256=rXXIUFeAsfXq2YS7DGkztGmXez-G7gAwbwdBL8t9KME,320 pyserial-3.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 pyserial-3.5.dist-info/METADATA,sha256=QqirfpTvC3uqfpTNrGXWuSVMYIR29jASDJkAB79HKUM,1650 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE new file mode 100644 index 000000000..946608575 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE @@ -0,0 +1,2 @@ +Released to the public domain. Original implementation can be found at +http://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA new file mode 100644 index 000000000..400b93f52 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA @@ -0,0 +1,355 @@ +Metadata-Version: 2.1 +Name: reedsolo +Version: 1.5.4 +Summary: Pure-Python Reed Solomon encoder/decoder +Home-page: https://github.com/tomerfiliba/reedsolomon +Author: Tomer Filiba +Author-email: tomerfiliba@gmail.com +Maintainer: Stephen Karl Larroque +Maintainer-email: lrq3000@gmail.com +License: Public Domain +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: POSIX :: Linux +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Cython +Classifier: Topic :: Communications +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: System :: Archiving :: Backup +Classifier: Topic :: System :: Recovery Tools +Description-Content-Type: text/x-rst +License-File: LICENSE + +Reed Solomon +============ + +|PyPI-Status| |PyPI-Versions| |PyPI-Downloads| + +|Build-Status| |Coverage| + +A pure-python `universal errors-and-erasures Reed-Solomon Codec `_ +, based on the wonderful tutorial at `wikiversity `_, +written by "Bobmath" and "LRQ3000". + +------------------------------------ + +.. contents:: Table of contents + :backlinks: top + :local: + + +Installation +------------ + +.. code:: sh + + pip install --upgrade reedsolo + +.. note:: + + When installing from source using ``python setup.py install``, the setup.py will try to build the Cython optimized module ``creedsolo.pyx`` if Cython is installed. You can override this behavior by typing: ``python setup.py install --nocython``. + + A pre-transpiled ``creedsolo.c`` is also available, and can be compiled without Cython by typing: ``python setup.py install --native-compile``. + + The package on ``pip`` includes a pre-compiled ``creedsolo.pyd`` module for Windows 10 x64. + +Usage +----- + +Basic usage with high-level RSCodec class +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + # Initialization + >>> from reedsolo import RSCodec + >>> rsc = RSCodec(10) # 10 ecc symbols + + # Encoding + >>> rsc.encode([1,2,3,4]) + b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M' + >>> rsc.encode(bytearray([1,2,3,4])) + bytearray(b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M') + >>> rsc.encode(b'hello world') + b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa' + # Note that chunking is supported transparently to encode any string length. + + # Decoding (repairing) + >>> rsc.decode(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')[0] + b'hello world' + >>> rsc.decode(b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 3 errors + b'hello world' + >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 5 errors + b'hello world' + >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdXX\xf3\xa8\xaa')[0] # 6 errors - fail + Traceback (most recent call last): + ... + reedsolo.ReedSolomonError: Too many (or few) errors found by Chien Search for the errata locator polynomial! + +**Important upgrade notice for pre-1.0 users:** Note that ``RSCodec.decode()`` returns 3 variables: + + 1. the decoded (corrected) message + 2. the decoded message and error correction code (which is itself also corrected) + 3. and the list of positions of the errata (errors and erasures) + +Here is an example: + +.. code:: python + + >>> tampered_msg = b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa' + >>> decoded_msg, decoded_msgecc, errata_pos = rsc.decode(tampered_msg) + >>> print(decoded_msg) # decoded/corrected message + bytearray(b'hello world') + >>> print(decoded_msgecc) # decoded/corrected message and ecc symbols + bytearray(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa') + >>> print(errata_pos) # errata_pos is returned as a bytearray, hardly intelligible + bytearray(b'\x10\t\x02') + >>> print(list(errata_pos)) # convert to a list to get the errata positions as integer indices + [16, 9, 2] + +Since we failed to decode with 6 errors with a codec set to 10 error correction code (ecc) symbols, let's try to use a bigger codec, with 12 ecc symbols. + +.. code:: python + + >>> rsc = RSCodec(12) # using 2 more ecc symbols (to correct max 6 errors or 12 erasures) + >>> rsc.encode(b'hello world') + b'hello world?Ay\xb2\xbc\xdc\x01q\xb9\xe3\xe2=' + >>> rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')[0] # 6 errors - ok, but any more would fail + b'hello world' + >>> rsc.decode(b'helXXXXXXXXXXy\xb2XX\x01q\xb9\xe3\xe2=', erase_pos=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16])[0] # 12 erasures - OK + b'hello world' + +This shows that we can decode twice as many erasures (where we provide the location of errors ourselves) than errors (with unknown locations). This is the cost of error correction compared to erasure correction. + +To get the maximum number of errors OR erasures that can be independently corrected (ie, not simultaneously): + +.. code:: python + + >>> maxerrors, maxerasures = rsc.maxerrata(verbose=True) + This codec can correct up to 6 errors and 12 erasures independently + >>> print(maxerrors, maxerasures) + 6 12 + +To get the maximum number of errors AND erasures that can be simultaneously corrected, you need to specify the number of errors or erasures you expect: + +.. code:: python + + >>> maxerrors, maxerasures = rsc.maxerrata(erasures=6, verbose=True) # we know the number of erasures, will calculate how many errors we can afford + This codec can correct up to 3 errors and 6 erasures simultaneously + >>> print(maxerrors, maxerasures) + 3 6 + >>> maxerrors, maxerasures = rsc.maxerrata(errors=5, verbose=True) # we know the number of errors, will calculate how many erasures we can afford + This codec can correct up to 5 errors and 2 erasures simultaneously + >>> print(maxerrors, maxerasures) + 5 2 + +Note that if a chunk has more errors and erasures than the Singleton Bound as calculated by the ``maxerrata()`` method, the codec will try to raise a ``ReedSolomonError`` exception, +but may very well not detect any error either (this is a theoretical limitation of error correction codes). In other words, error correction codes are unreliable to detect if a chunk of a message +is corrupted beyond the Singleton Bound. If you want more reliability in errata detection, use a checksum or hash such as SHA or MD5 on your message, these are much more reliable and have no bounds +on the number of errata (the only potential issue is with collision but the probability is very very low). + +To check if a message is tampered given its error correction symbols, without decoding, use the ``check()`` method: + +.. code:: python + + # Checking + >> rsc.check(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') # Tampered message will return False + [False] + >> rmes, rmesecc, errata_pos = rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') + >> rsc.check(rmesecc) # Corrected or untampered message will return True + [True] + >> print('Number of detected errors and erasures: %i, their positions: %s' % (len(errata_pos), list(errata_pos))) + Number of detected errors and erasures: 6, their positions: [16, 15, 12, 11, 10, 9] + +By default, most Reed-Solomon codecs are limited to characters that can be encoded in 256 bits and with a length of maximum 256 characters. But this codec is universal, you can reduce or increase the length and maximum character value by increasing the Galois Field: + +.. code:: python + + # To use longer chunks or bigger values than 255 (may be very slow) + >> rsc = RSCodec(12, nsize=4095) # always use a power of 2 minus 1 + >> rsc = RSCodec(12, c_exp=12) # alternative way to set nsize=4095 + >> mes = 'a' * (4095-12) + >> mesecc = rsc.encode(mes) + >> mesecc[2] = 1 + >> mesecc[-1] = 1 + >> rmes, rmesecc, errata_pos = rsc.decode(mesecc) + >> rsc.check(mesecc) + [False] + >> rsc.check(rmesecc) + [True] + +Note that the ``RSCodec`` class supports transparent chunking, so you don't need to increase the Galois Field to support longer messages, but characters will still be limited to 256 bits (or +whatever field you set with ``c_exp``). + +Low-level usage via direct access to math functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want full control, you can skip the API and directly use the library as-is. Here's how: + +First you need to init the precomputed tables: + +.. code:: python + + >> import reedsolo as rs + >> rs.init_tables(0x11d) + +Pro tip: if you get the error: ValueError: byte must be in range(0, 256), please check that your prime polynomial is correct for your field. +Pro tip2: by default, you can only encode messages of max length and max symbol value = 256. If you want to encode bigger messages, +please use the following (where c_exp is the exponent of your Galois Field, eg, 12 = max length 2^12 = 4096): + +.. code:: python + + >> prim = rs.find_prime_polys(c_exp=12, fast_primes=True, single=True) + >> rs.init_tables(c_exp=12, prim=prim) + +Let's define our RS message and ecc size: + +.. code:: python + + >> n = 255 # length of total message+ecc + >> nsym = 12 # length of ecc + >> mes = "a" * (n-nsym) # generate a sample message + +To optimize, you can precompute the generator polynomial: + +.. code:: python + + >> gen = rs.rs_generator_poly_all(n) + +Then to encode: + +.. code:: python + + >> mesecc = rs.rs_encode_msg(mes, nsym, gen=gen[nsym]) + +Let's tamper our message: + +.. code:: python + + >> mesecc[1] = 0 + +To decode: + +.. code:: python + + >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym, erase_pos=erase_pos) + +Note that both the message and the ecc are corrected (if possible of course). +Pro tip: if you know a few erasures positions, you can specify them in a list ``erase_pos`` to double the repair power. But you can also just specify an empty list. + +You can check how many errors and/or erasures were corrected, which can be useful to design adaptive bitrate algorithms: + +.. code:: python + + >> print('A total of %i errata were corrected over all chunks of this message.' % len(errata_pos)) + +If the decoding fails, it will normally automatically check and raise a ReedSolomonError exception that you can handle. +However if you want to manually check if the repaired message is correct, you can do so: + +.. code:: python + + >> rs.rs_check(rmes + recc, nsym) + +Note: if you want to use multiple reedsolomon with different parameters, you need to backup the globals and restore them before calling reedsolo functions: + +.. code:: python + + >> rs.init_tables() + >> global gf_log, gf_exp, field_charac + >> bak_gf_log, bak_gf_exp, bak_field_charac = gf_log, gf_exp, field_charac + + +Then at anytime, you can do: + +.. code:: python + + >> global gf_log, gf_exp, field_charac + >> gf_log, gf_exp, field_charac = bak_gf_log, bak_gf_exp, bak_field_charac + >> mesecc = rs.rs_encode_msg(mes, nsym) + >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym) + +The globals backup is not necessary if you use RSCodec, it will be automatically managed. + +Read the sourcecode's comments for more info about how it works, and for the various parameters you can setup if +you need to interface with other RS codecs. + +Extended description +-------------------- +The code of wikiversity is here consolidated into a nice API with exceptions handling. +The algorithm can correct up to 2*e+v <= nsym, where e is the number of errors, +v the number of erasures and nsym = n-k = the number of ECC (error correction code) symbols. +This means that you can either correct exactly floor(nsym/2) errors, or nsym erasures +(errors where you know the position), and a combination of both errors and erasures. +This is called the Singleton Bound, and is the maximum/optimal theoretical number +of erasures and errors any error correction algorithm can correct (although there +are experimental approaches to go a bit further, named list decoding, not implemented +here, but feel free to do pull request!). +The code should work on pretty much any reasonable version of python (2.4-3.7), +but I'm only testing on 2.7 and 3.7. Python 3.8 should work except for Cython which is +currently incompatible with this version. + +The codec has quite reasonable performances if you either use PyPy on the pure-python +implementation (reedsolo.py) or either if you compile the Cython extension creedsolo.pyx +(which is about 2x faster than PyPy). You can expect encoding rates of several MB/s. + +This library is also thoroughly unit tested so that nearly any encoding/decoding case should be covered. + +The codec is universal, meaning that it can decode any message encoded by another RS encoder +as long as you provide the correct parameters. +Note however that if you use higher fields (ie, bigger c_exp), the algorithms will be slower, first because +we cannot then use the optimized bytearray() structure but only array.array('i', ...), and also because +Reed-Solomon's complexity is quadratic (both in encoding and decoding), so this means that the longer +your messages, the longer it will take to encode/decode (quadratically!). + +The algorithm itself can handle messages of a length up to (2^c_exp)-1 symbols per message (or chunk), including the ECC symbols, +and each symbol can have a value of up to (2^c_exp)-1 (indeed, both the message length and the maximum +value for one character is constrained by the same mathematical reason). By default, we use the field GF(2^8), +which means that you are limited to values between 0 and 255 (perfect to represent a single hexadecimal +symbol on computers, so you can encode any binary stream) and limited to messages+ecc of maximum +length 255. However, you can "chunk" longer messages to fit them into the message length limit. +The ``RSCodec`` class will automatically apply chunking, by splitting longer messages into chunks and +encode/decode them separately; it shouldn't make a difference from an API perspective (ie, from your POV). + + +To use the Cython implementation, you need to ``pip install cython`` and a C++ compiler (Microsoft Visual C++ 14.0 for Windows and Python 3.7). Then you can simply cd to the root of the folder where creedsolo.pyx is, and type ``python setup.py build_ext --inplace``. Alternatively, you can generate just the C++ code by typing `cython -3 creedsolo.pyx`. When building a distributable egg or installing the module from source, the Cython module will be automatically transpiled and compiled if both Cython and a C compiler are installed. This behavior can be modified using the ``--nocython`` and ``--native-compile`` arguments for ``setup.py``. + +Authors +------- + +This module was conceived and developed by Tomer Filiba. + +It was further extended and is currently maintained by Stephen Karl Larroque. + +For a list of all contributors, see `this page `_. + +License +------- + +This software is released to the Public Domain. + +If the Public Domain is not adequate for your purpose, you can instead consider this module under the MIT License as you prefer. + + +.. |PyPI-Status| image:: https://img.shields.io/pypi/v/reedsolo.svg + :target: https://pypi.org/project/reedsolo +.. |PyPI-Versions| image:: https://img.shields.io/pypi/pyversions/reedsolo.svg?logo=python&logoColor=white + :target: https://pypi.org/project/reedsolo +.. |PyPI-Downloads| image:: https://img.shields.io/pypi/dm/reedsolo.svg?label=pypi%20downloads&logo=python&logoColor=white + :target: https://pypi.org/project/reedsolo +.. |Build-Status| image:: https://travis-ci.org/tomerfiliba/reedsolomon.svg?branch=master + :target: https://travis-ci.org/tomerfiliba/reedsolomon +.. |Coverage| image:: https://coveralls.io/repos/tomerfiliba/reedsolomon/badge.svg?branch=master&service=github + :target: https://coveralls.io/github/tomerfiliba/reedsolomon?branch=master diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD new file mode 100644 index 000000000..d05831fc4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/reedsolo.cpython-310.pyc,, +reedsolo-1.5.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +reedsolo-1.5.4.dist-info/LICENSE,sha256=C6vXtXPvw9z1uzwotYMVByb3ddPdjVTWzQYoNHNbPXM,139 +reedsolo-1.5.4.dist-info/METADATA,sha256=jfDZlhjMM7sfawn78EpGyhp4eKpwWY_ZQzdMs-WGgiw,16817 +reedsolo-1.5.4.dist-info/RECORD,, +reedsolo-1.5.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +reedsolo-1.5.4.dist-info/top_level.txt,sha256=QRYQ78lo_mIFvTV1fxOFpLZDd9xSvrR81HlgWAx7ZqU,9 +reedsolo.py,sha256=xxSRANmvY0jwOlkEiwlMX5EpJ3kl5msQBRPQ5mtYPJM,70730 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL new file mode 100644 index 000000000..57e3d840d --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.38.4) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt new file mode 100644 index 000000000..200965002 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt @@ -0,0 +1 @@ +reedsolo diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py new file mode 100644 index 000000000..d98acfc1f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py @@ -0,0 +1,974 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (c) 2012-2015 Tomer Filiba +# Copyright (c) 2015 rotorgit +# Copyright (c) 2015-2020 Stephen Larroque + +''' +Reed Solomon +============ + +A pure-python `universal errors-and-erasures Reed-Solomon Codec `_ +, based on the wonderful tutorial at +`wikiversity `_, +written by "Bobmath" and "LRQ3000". + +The code of wikiversity is here consolidated into a nice API with exceptions handling. +The algorithm can correct up to 2*e+v <= nsym, where e is the number of errors, +v the number of erasures and nsym = n-k = the number of ECC (error correction code) symbols. +This means that you can either correct exactly floor(nsym/2) errors, or nsym erasures +(errors where you know the position), and a combination of both errors and erasures. +The code should work on pretty much any reasonable version of python (2.4-3.5), +but I'm only testing on 2.7 - 3.4. + +.. note:: + The codec is universal, meaning that it can decode any message encoded by another RS encoder + as long as you provide the correct parameters. + Note however that if you use higher fields (ie, bigger c_exp), the algorithms will be slower, first because + we cannot then use the optimized bytearray() structure but only array.array('i', ...), and also because + Reed-Solomon's complexity is quadratic (both in encoding and decoding), so this means that the longer + your messages, the longer it will take to encode/decode (quadratically!). + + The algorithm itself can handle messages up to (2^c_exp)-1 symbols, including the ECC symbols, + and each symbol can have a value of up to (2^c_exp)-1 (indeed, both the message length and the maximum + value for one character is constrained by the same mathematical reason). By default, we use the field GF(2^8), + which means that you are limited to values between 0 and 255 (perfect to represent a single hexadecimal + symbol on computers, so you can encode any binary stream) and limited to messages+ecc of maximum + length 255. However, you can "chunk" longer messages to fit them into the message length limit. + The ``RSCodec`` class will automatically apply chunking, by splitting longer messages into chunks and + encode/decode them separately; it shouldn't make a difference from an API perspective (ie, from your POV). + +:: + + # Initialization + >>> from reedsolo import RSCodec + >>> rsc = RSCodec(10) # 10 ecc symbols + + # Encoding + >>> rsc.encode([1,2,3,4]) + b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M' + >>> rsc.encode(bytearray([1,2,3,4])) + bytearray(b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M') + >>> rsc.encode(b'hello world') + b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa' + # Note that chunking is supported transparently to encode any string length. + + # Decoding (repairing) + >>> rsc.decode(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')[0] + b'hello world' + >>> rsc.decode(b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 3 errors + b'hello world' + >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 5 errors + b'hello world' + >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdXX\xf3\xa8\xaa')[0] # 6 errors - fail + Traceback (most recent call last): + ... + ReedSolomonError: Could not locate error + + >>> rsc = RSCodec(12) # using 2 more ecc symbols (to correct max 6 errors or 12 erasures) + >>> rsc.encode(b'hello world') + b'hello world?Ay\xb2\xbc\xdc\x01q\xb9\xe3\xe2=' + >>> rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')[0] # 6 errors - ok + b'hello world' + >>> rsc.decode(b'helXXXXXXXXXXy\xb2XX\x01q\xb9\xe3\xe2=', erase_pos=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16])[0] # 12 erasures - OK + b'hello world' + + # Checking + >> rsc.check(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') + [False] + >> rmes, rmesecc = rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') + >> rsc.check(rmesecc) + [True] + + # To use longer chunks or bigger values than 255 (may be very slow) + >> rsc = RSCodec(12, nsize=4095) # always use a power of 2 minus 1 + >> rsc = RSCodec(12, c_exp=12) # alternative way to set nsize=4095 + >> mes = 'a' * (4095-12) + >> mesecc = rsc.encode(mes) + >> mesecc[2] = 1 + >> mesecc[-1] = 1 + >> rmes, rmesecc = rsc.decode(mesecc) + >> rsc.check(mesecc) + [False] + >> rsc.check(rmesecc) + [True] + + If you want full control, you can skip the API and directly use the library as-is. Here's how: + + First you need to init the precomputed tables: + >> import reedsolo as rs + >> rs.init_tables(0x11d) + Pro tip: if you get the error: ValueError: byte must be in range(0, 256), please check that your prime polynomial is correct for your field. + Pro tip2: by default, you can only encode messages of max length and max symbol value = 256. If you want to encode bigger messages, + please use the following (where c_exp is the exponent of your Galois Field, eg, 12 = max length 2^12 = 4096): + >> prim = rs.find_prime_polys(c_exp=12, fast_primes=True, single=True) + >> rs.init_tables(c_exp=12, prim=prim) + + Let's define our RS message and ecc size: + >> n = 255 # length of total message+ecc + >> nsym = 12 # length of ecc + >> mes = "a" * (n-nsym) # generate a sample message + + To optimize, you can precompute the generator polynomial: + >> gen = rs.rs_generator_poly_all(n) + + Then to encode: + >> mesecc = rs.rs_encode_msg(mes, nsym, gen=gen[nsym]) + + Let's tamper our message: + >> mesecc[1] = 0 + + To decode: + >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym, erase_pos=erase_pos) + Note that both the message and the ecc are corrected (if possible of course). + Pro tip: if you know a few erasures positions, you can specify them in a list `erase_pos` to double the repair power. But you can also just specify an empty list. + + If the decoding fails, it will normally automatically check and raise a ReedSolomonError exception that you can handle. + However if you want to manually check if the repaired message is correct, you can do so: + >> rs.rs_check(rmes + recc, nsym) + + Note: if you want to use multiple reedsolomon with different parameters, you need to backup the globals and restore them before calling reedsolo functions: + >> rs.init_tables() + >> global gf_log, gf_exp, field_charac + >> bak_gf_log, bak_gf_exp, bak_field_charac = gf_log, gf_exp, field_charac + Then at anytime, you can do: + >> global gf_log, gf_exp, field_charac + >> gf_log, gf_exp, field_charac = bak_gf_log, bak_gf_exp, bak_field_charac + >> mesecc = rs.rs_encode_msg(mes, nsym) + >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym) + The globals backup is not necessary if you use RSCodec, it will be automatically managed. + + Read the sourcecode's comments for more info about how it works, and for the various parameters you can setup if + you need to interface with other RS codecs. + +''' + +# TODO IMPORTANT: try to keep the same convention for the ordering of polynomials inside lists throughout the code and functions (because for now there are a lot of list reversing in order to make it work, you never know the order of a polynomial, ie, if the first coefficient is the major degree or the constant term...). + +import itertools +import math + + +################### INIT and stuff ################### + +try: # pragma: no cover + bytearray + _bytearray = bytearray +except NameError: # pragma: no cover + from array import array + def _bytearray(obj = 0, encoding = "latin-1"): # pragma: no cover + '''Simple bytearray replacement''' + # always use Latin-1 and not UTF8 because Latin-1 maps the first 256 characters to their bytevalue equivalents. UTF8 may mangle your data (particularly at vale 128) + if isinstance(obj, str): + obj = [ord(ch) for ch in obj.encode(encoding)] + elif isinstance(obj, int): + obj = [0] * obj + return array("B", obj) + +try: # pragma: no cover + xrange +except NameError: # pragma: no cover + # compatibility with Python 3+ + xrange = range + +class ReedSolomonError(Exception): + pass + +gf_exp = _bytearray([1] * 512) # For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple multiplication of two numbers can be resolved without calling % 255. For more infos on how to generate this extended exponentiation table, see paper: "Fast software implementation of finite field operations", Cheng Huang and Lihao Xu, Washington University in St. Louis, Tech. Rep (2003). +gf_log = _bytearray(256) +field_charac = int(2**8 - 1) + +################### GALOIS FIELD ELEMENTS MATHS ################### + +def rwh_primes1(n): + # http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188 + ''' Returns a list of primes < n ''' + sieve = [True] * int(n/2) + for i in xrange(3,int(n**0.5)+1,2): + if sieve[int(i/2)]: + sieve[int((i*i)/2)::i] = [False] * int((n-i*i-1)/(2*i)+1) + return [2] + [2*i+1 for i in xrange(1,int(n/2)) if sieve[i]] + +def find_prime_polys(generator=2, c_exp=8, fast_primes=False, single=False): + '''Compute the list of prime polynomials for the given generator and galois field characteristic exponent.''' + # fast_primes will output less results but will be significantly faster. + # single will output the first prime polynomial found, so if all you want is to just find one prime polynomial to generate the LUT for Reed-Solomon to work, then just use that. + + # A prime polynomial (necessarily irreducible) is necessary to reduce the multiplications in the Galois Field, so as to avoid overflows. + # Why do we need a "prime polynomial"? Can't we just reduce modulo 255 (for GF(2^8) for example)? Because we need the values to be unique. + # For example: if the generator (alpha) = 2 and c_exp = 8 (GF(2^8) == GF(256)), then the generated Galois Field (0, 1, α, α^1, α^2, ..., α^(p-1)) will be galois field it becomes 0, 1, 2, 4, 8, 16, etc. However, upon reaching 128, the next value will be doubled (ie, next power of 2), which will give 256. Then we must reduce, because we have overflowed above the maximum value of 255. But, if we modulo 255, this will generate 256 == 1. Then 2, 4, 8, 16, etc. giving us a repeating pattern of numbers. This is very bad, as it's then not anymore a bijection (ie, a non-zero value doesn't have a unique index). That's why we can't just modulo 255, but we need another number above 255, which is called the prime polynomial. + # Why so much hassle? Because we are using precomputed look-up tables for multiplication: instead of multiplying a*b, we precompute alpha^a, alpha^b and alpha^(a+b), so that we can just use our lookup table at alpha^(a+b) and get our result. But just like in our original field we had 0,1,2,...,p-1 distinct unique values, in our "LUT" field using alpha we must have unique distinct values (we don't care that they are different from the original field as long as they are unique and distinct). That's why we need to avoid duplicated values, and to avoid duplicated values we need to use a prime irreducible polynomial. + + # Here is implemented a bruteforce approach to find all these prime polynomials, by generating every possible prime polynomials (ie, every integers between field_charac+1 and field_charac*2), and then we build the whole Galois Field, and we reject the candidate prime polynomial if it duplicates even one value or if it generates a value above field_charac (ie, cause an overflow). + # Note that this algorithm is slow if the field is too big (above 12), because it's an exhaustive search algorithm. There are probabilistic approaches, and almost surely prime approaches, but there is no determistic polynomial time algorithm to find irreducible monic polynomials. More info can be found at: http://people.mpi-inf.mpg.de/~csaha/lectures/lec9.pdf + # Another faster algorithm may be found at Adleman, Leonard M., and Hendrik W. Lenstra. "Finding irreducible polynomials over finite fields." Proceedings of the eighteenth annual ACM symposium on Theory of computing. ACM, 1986. + + # Prepare the finite field characteristic (2^p - 1), this also represent the maximum possible value in this field + root_charac = 2 # we're in GF(2) + field_charac = int(root_charac**c_exp - 1) + field_charac_next = int(root_charac**(c_exp+1) - 1) + + prim_candidates = [] + if fast_primes: + prim_candidates = rwh_primes1(field_charac_next) # generate maybe prime polynomials and check later if they really are irreducible + prim_candidates = [x for x in prim_candidates if x > field_charac] # filter out too small primes + else: + prim_candidates = xrange(field_charac+2, field_charac_next, root_charac) # try each possible prime polynomial, but skip even numbers (because divisible by 2 so necessarily not irreducible) + + # Start of the main loop + correct_primes = [] + for prim in prim_candidates: # try potential candidates primitive irreducible polys + seen = _bytearray(field_charac+1) # memory variable to indicate if a value was already generated in the field (value at index x is set to 1) or not (set to 0 by default) + conflict = False # flag to know if there was at least one conflict + + # Second loop, build the whole Galois Field + x = 1 + for i in xrange(field_charac): + # Compute the next value in the field (ie, the next power of alpha/generator) + x = gf_mult_noLUT(x, generator, prim, field_charac+1) + + # Rejection criterion: if the value overflowed (above field_charac) or is a duplicate of a previously generated power of alpha, then we reject this polynomial (not prime) + if x > field_charac or seen[x] == 1: + conflict = True + break + # Else we flag this value as seen (to maybe detect future duplicates), and we continue onto the next power of alpha + else: + seen[x] = 1 + + # End of the second loop: if there's no conflict (no overflow nor duplicated value), this is a prime polynomial! + if not conflict: + correct_primes.append(prim) + if single: return prim + + # Return the list of all prime polynomials + return correct_primes # you can use the following to print the hexadecimal representation of each prime polynomial: print [hex(i) for i in correct_primes] + +def init_tables(prim=0x11d, generator=2, c_exp=8): + '''Precompute the logarithm and anti-log tables for faster computation later, using the provided primitive polynomial. + These tables are used for multiplication/division since addition/substraction are simple XOR operations inside GF of characteristic 2. + The basic idea is quite simple: since b**(log_b(x), log_b(y)) == x * y given any number b (the base or generator of the logarithm), then we can use any number b to precompute logarithm and anti-log (exponentiation) tables to use for multiplying two numbers x and y. + That's why when we use a different base/generator number, the log and anti-log tables are drastically different, but the resulting computations are the same given any such tables. + For more infos, see https://en.wikipedia.org/wiki/Finite_field_arithmetic#Implementation_tricks + ''' + # generator is the generator number (the "increment" that will be used to walk through the field by multiplication, this must be a prime number). This is basically the base of the logarithm/anti-log tables. Also often noted "alpha" in academic books. + # prim is the primitive/prime (binary) polynomial and must be irreducible (ie, it can't represented as the product of two smaller polynomials). It's a polynomial in the binary sense: each bit is a coefficient, but in fact it's an integer between field_charac+1 and field_charac*2, and not a list of gf values. The prime polynomial will be used to reduce the overflows back into the range of the Galois Field without duplicating values (all values should be unique). See the function find_prime_polys() and: http://research.swtch.com/field and http://www.pclviewer.com/rs2/galois.html + # note that the choice of generator or prime polynomial doesn't matter very much: any two finite fields of size p^n have identical structure, even if they give the individual elements different names (ie, the coefficients of the codeword will be different, but the final result will be the same: you can always correct as many errors/erasures with any choice for those parameters). That's why it makes sense to refer to all the finite fields, and all decoders based on Reed-Solomon, of size p^n as one concept: GF(p^n). It can however impact sensibly the speed (because some parameters will generate sparser tables). + # c_exp is the exponent for the field's characteristic GF(2^c_exp) + + # Redefine _bytearray() in case we need to support integers or messages of length > 256 + global _bytearray + if c_exp <= 8: + _bytearray = bytearray + else: + from array import array + def _bytearray(obj = 0, encoding = "latin-1"): + '''Fake bytearray replacement, supporting int values above 255''' + # always use Latin-1 and not UTF8 because Latin-1 maps the first 256 characters to their bytevalue equivalents. UTF8 may mangle your data (particularly at vale 128) + if isinstance(obj, str): # obj is a string, convert to list of ints + obj = obj.encode(encoding) + if isinstance(obj, str): # Py2 str: convert to list of ascii ints + obj = [ord(chr) for chr in obj] + elif isinstance(obj, bytes): # Py3 bytes: characters are bytes, need to convert to int for array.array('i', obj) + obj = [int(chr) for chr in obj] + else: + raise(ValueError, "Type of object not recognized!") + elif isinstance(obj, int): # compatibility with list preallocation bytearray(int) + obj = [0] * obj + # Else obj is a list of int, it's ok + return array("i", obj) + + # Init global tables + global gf_exp, gf_log, field_charac + field_charac = int(2**c_exp - 1) + gf_exp = _bytearray(field_charac * 2) # anti-log (exponential) table. The first two elements will always be [GF256int(1), generator] + gf_log = _bytearray(field_charac+1) # log table, log[0] is impossible and thus unused + + # For each possible value in the galois field 2^8, we will pre-compute the logarithm and anti-logarithm (exponential) of this value + # To do that, we generate the Galois Field F(2^p) by building a list starting with the element 0 followed by the (p-1) successive powers of the generator α : 1, α, α^1, α^2, ..., α^(p-1). + x = 1 + for i in xrange(field_charac): # we could skip index 255 which is equal to index 0 because of modulo: g^255==g^0 but either way, this does not change the later outputs (ie, the ecc symbols will be the same either way) + gf_exp[i] = x # compute anti-log for this value and store it in a table + gf_log[x] = i # compute log at the same time + x = gf_mult_noLUT(x, generator, prim, field_charac+1) + + # If you use only generator==2 or a power of 2, you can use the following which is faster than gf_mult_noLUT(): + #x <<= 1 # multiply by 2 (change 1 by another number y to multiply by a power of 2^y) + #if x & 0x100: # similar to x >= 256, but a lot faster (because 0x100 == 256) + #x ^= prim # substract the primary polynomial to the current value (instead of 255, so that we get a unique set made of coprime numbers), this is the core of the tables generation + + # Optimization: double the size of the anti-log table so that we don't need to mod 255 to stay inside the bounds (because we will mainly use this table for the multiplication of two GF numbers, no more). + for i in xrange(field_charac, field_charac * 2): + gf_exp[i] = gf_exp[i - field_charac] + + return [gf_log, gf_exp, field_charac] + +def gf_add(x, y): + return x ^ y + +def gf_sub(x, y): + return x ^ y # in binary galois field, substraction is just the same as addition (since we mod 2) + +def gf_neg(x): + return x + +def gf_inverse(x): + return gf_exp[field_charac - gf_log[x]] # gf_inverse(x) == gf_div(1, x) + +def gf_mul(x, y): + if x == 0 or y == 0: + return 0 + return gf_exp[(gf_log[x] + gf_log[y]) % field_charac] + +def gf_div(x, y): + if y == 0: + raise ZeroDivisionError() + if x == 0: + return 0 + return gf_exp[(gf_log[x] + field_charac - gf_log[y]) % field_charac] + +def gf_pow(x, power): + return gf_exp[(gf_log[x] * power) % field_charac] + +def gf_mult_noLUT_slow(x, y, prim=0): + '''Multiplication in Galois Fields without using a precomputed look-up table (and thus it's slower) by using the standard carry-less multiplication + modular reduction using an irreducible prime polynomial.''' + + ### Define bitwise carry-less operations as inner functions ### + def cl_mult(x,y): + '''Bitwise carry-less multiplication on integers''' + z = 0 + i = 0 + while (y>>i) > 0: + if y & (1<> bits: bits += 1 + return bits + + def cl_div(dividend, divisor=None): + '''Bitwise carry-less long division on integers and returns the remainder''' + # Compute the position of the most significant bit for each integers + dl1 = bit_length(dividend) + dl2 = bit_length(divisor) + # If the dividend is smaller than the divisor, just exit + if dl1 < dl2: + return dividend + # Else, align the most significant 1 of the divisor to the most significant 1 of the dividend (by shifting the divisor) + for i in xrange(dl1-dl2,-1,-1): + # Check that the dividend is divisible (useless for the first iteration but important for the next ones) + if dividend & (1 << i+dl2-1): + # If divisible, then shift the divisor to align the most significant bits and XOR (carry-less substraction) + dividend ^= divisor << i + return dividend + + ### Main GF multiplication routine ### + + # Multiply the gf numbers + result = cl_mult(x,y) + # Then do a modular reduction (ie, remainder from the division) with an irreducible primitive polynomial so that it stays inside GF bounds + if prim > 0: + result = cl_div(result, prim) + + return result + +def gf_mult_noLUT(x, y, prim=0, field_charac_full=256, carryless=True): + '''Galois Field integer multiplication using Russian Peasant Multiplication algorithm (faster than the standard multiplication + modular reduction). + If prim is 0 and carryless=False, then the function produces the result for a standard integers multiplication (no carry-less arithmetics nor modular reduction).''' + r = 0 + while y: # while y is above 0 + if y & 1: r = r ^ x if carryless else r + x # y is odd, then add the corresponding x to r (the sum of all x's corresponding to odd y's will give the final product). Note that since we're in GF(2), the addition is in fact an XOR (very important because in GF(2) the multiplication and additions are carry-less, thus it changes the result!). + y = y >> 1 # equivalent to y // 2 + x = x << 1 # equivalent to x*2 + if prim > 0 and x & field_charac_full: x = x ^ prim # GF modulo: if x >= 256 then apply modular reduction using the primitive polynomial (we just substract, but since the primitive number can be above 256 then we directly XOR). + + return r + + +################### GALOIS FIELD POLYNOMIALS MATHS ################### + +def gf_poly_scale(p, x): + return _bytearray([gf_mul(p[i], x) for i in xrange(len(p))]) + +def gf_poly_add(p, q): + r = _bytearray( max(len(p), len(q)) ) + r[len(r)-len(p):len(r)] = p + #for i in xrange(len(p)): + #r[i + len(r) - len(p)] = p[i] + for i in xrange(len(q)): + r[i + len(r) - len(q)] ^= q[i] + return r + +def gf_poly_mul(p, q): + '''Multiply two polynomials, inside Galois Field (but the procedure is generic). Optimized function by precomputation of log.''' + # Pre-allocate the result array + r = _bytearray(len(p) + len(q) - 1) + # Precompute the logarithm of p + lp = [gf_log[p[i]] for i in xrange(len(p))] + # Compute the polynomial multiplication (just like the outer product of two vectors, we multiply each coefficients of p with all coefficients of q) + for j in xrange(len(q)): + qj = q[j] # optimization: load the coefficient once + if qj != 0: # log(0) is undefined, we need to check that + lq = gf_log[qj] # Optimization: precache the logarithm of the current coefficient of q + for i in xrange(len(p)): + if p[i] != 0: # log(0) is undefined, need to check that... + r[i + j] ^= gf_exp[lp[i] + lq] # equivalent to: r[i + j] = gf_add(r[i+j], gf_mul(p[i], q[j])) + return r + +def gf_poly_mul_simple(p, q): # simple equivalent way of multiplying two polynomials without precomputation, but thus it's slower + '''Multiply two polynomials, inside Galois Field''' + # Pre-allocate the result array + r = _bytearray(len(p) + len(q) - 1) + # Compute the polynomial multiplication (just like the outer product of two vectors, we multiply each coefficients of p with all coefficients of q) + for j in xrange(len(q)): + for i in xrange(len(p)): + r[i + j] ^= gf_mul(p[i], q[j]) # equivalent to: r[i + j] = gf_add(r[i+j], gf_mul(p[i], q[j])) -- you can see it's your usual polynomial multiplication + return r + +def gf_poly_neg(poly): + '''Returns the polynomial with all coefficients negated. In GF(2^p), negation does not change the coefficient, so we return the polynomial as-is.''' + return poly + +def gf_poly_div(dividend, divisor): + '''Fast polynomial division by using Extended Synthetic Division and optimized for GF(2^p) computations (doesn't work with standard polynomials outside of this galois field).''' + # CAUTION: this function expects polynomials to follow the opposite convention at decoding: the terms must go from the biggest to lowest degree (while most other functions here expect a list from lowest to biggest degree). eg: 1 + 2x + 5x^2 = [5, 2, 1], NOT [1, 2, 5] + + msg_out = _bytearray(dividend) # Copy the dividend list and pad with 0 where the ecc bytes will be computed + #normalizer = divisor[0] # precomputing for performance + for i in xrange(len(dividend) - (len(divisor)-1)): + #msg_out[i] /= normalizer # for general polynomial division (when polynomials are non-monic), the usual way of using synthetic division is to divide the divisor g(x) with its leading coefficient (call it a). In this implementation, this means:we need to compute: coef = msg_out[i] / gen[0]. For more infos, see http://en.wikipedia.org/wiki/Synthetic_division + coef = msg_out[i] # precaching + if coef != 0: # log(0) is undefined, so we need to avoid that case explicitly (and it's also a good optimization). In fact if you remove it, it should still work because gf_mul() will take care of the condition. But it's still a good practice to put the condition here. + for j in xrange(1, len(divisor)): # in synthetic division, we always skip the first coefficient of the divisior, because it's only used to normalize the dividend coefficient + if divisor[j] != 0: # log(0) is undefined + msg_out[i + j] ^= gf_mul(divisor[j], coef) # equivalent to the more mathematically correct (but xoring directly is faster): msg_out[i + j] += -divisor[j] * coef + + # The resulting msg_out contains both the quotient and the remainder, the remainder being the size of the divisor (the remainder has necessarily the same degree as the divisor -- not length but degree == length-1 -- since it's what we couldn't divide from the dividend), so we compute the index where this separation is, and return the quotient and remainder. + separator = -(len(divisor)-1) + return msg_out[:separator], msg_out[separator:] # return quotient, remainder. + +def gf_poly_square(poly): # pragma: no cover + '''Linear time implementation of polynomial squaring. For details, see paper: "A fast software implementation for arithmetic operations in GF (2n)". De Win, E., Bosselaers, A., Vandenberghe, S., De Gersem, P., & Vandewalle, J. (1996, January). In Advances in Cryptology - Asiacrypt'96 (pp. 65-76). Springer Berlin Heidelberg.''' + length = len(poly) + out = _bytearray(2*length - 1) + for i in xrange(length-1): + p = poly[i] + k = 2*i + if p != 0: + #out[k] = gf_exp[(2*gf_log[p]) % field_charac] # not necessary to modulo (2^r)-1 since gf_exp is duplicated up to 510. + out[k] = gf_exp[2*gf_log[p]] + #else: # not necessary since the output is already initialized to an array of 0 + #out[k] = 0 + out[2*length-2] = gf_exp[2*gf_log[poly[length-1]]] + if out[0] == 0: out[0] = 2*poly[1] - 1 + return out + +def gf_poly_eval(poly, x): + '''Evaluates a polynomial in GF(2^p) given the value for x. This is based on Horner's scheme for maximum efficiency.''' + y = poly[0] + for i in xrange(1, len(poly)): + y = gf_mul(y, x) ^ poly[i] + return y + + +################### REED-SOLOMON ENCODING ################### + +def rs_generator_poly(nsym, fcr=0, generator=2): + '''Generate an irreducible generator polynomial (necessary to encode a message into Reed-Solomon)''' + g = _bytearray([1]) + for i in xrange(nsym): + g = gf_poly_mul(g, [1, gf_pow(generator, i+fcr)]) + return g + +def rs_generator_poly_all(max_nsym, fcr=0, generator=2): + '''Generate all irreducible generator polynomials up to max_nsym (usually you can use n, the length of the message+ecc). Very useful to reduce processing time if you want to encode using variable schemes and nsym rates.''' + g_all = {} + g_all[0] = g_all[1] = _bytearray([1]) + for nsym in xrange(max_nsym): + g_all[nsym] = rs_generator_poly(nsym, fcr, generator) + return g_all + +def rs_simple_encode_msg(msg_in, nsym, fcr=0, generator=2, gen=None): + '''Simple Reed-Solomon encoding (mainly an example for you to understand how it works, because it's slower than the inlined function below)''' + global field_charac + if (len(msg_in) + nsym) > field_charac: raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in)+nsym, field_charac)) + if gen is None: gen = rs_generator_poly(nsym, fcr, generator) + + # Pad the message, then divide it by the irreducible generator polynomial + _, remainder = gf_poly_div(msg_in + _bytearray(len(gen)-1), gen) + # The remainder is our RS code! Just append it to our original message to get our full codeword (this represents a polynomial of max 256 terms) + msg_out = msg_in + remainder + # Return the codeword + return msg_out + +def rs_encode_msg(msg_in, nsym, fcr=0, generator=2, gen=None): + '''Reed-Solomon main encoding function, using polynomial division (Extended Synthetic Division, the fastest algorithm available to my knowledge), better explained at http://research.swtch.com/field''' + global field_charac + if (len(msg_in) + nsym) > field_charac: raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in)+nsym, field_charac)) + if gen is None: gen = rs_generator_poly(nsym, fcr, generator) + + msg_in = _bytearray(msg_in) + msg_out = _bytearray(msg_in) + _bytearray(len(gen)-1) # init msg_out with the values inside msg_in and pad with len(gen)-1 bytes (which is the number of ecc symbols). + + # Precompute the logarithm of every items in the generator + lgen = _bytearray([gf_log[gen[j]] for j in xrange(len(gen))]) + + # Extended synthetic division main loop + # Fastest implementation with PyPy (but the Cython version in creedsolo.pyx is about 2x faster) + for i in xrange(len(msg_in)): + coef = msg_out[i] # Note that it's msg_out here, not msg_in. Thus, we reuse the updated value at each iteration (this is how Synthetic Division works: instead of storing in a temporary register the intermediate values, we directly commit them to the output). + # coef = gf_mul(msg_out[i], gf_inverse(gen[0])) # for general polynomial division (when polynomials are non-monic), the usual way of using synthetic division is to divide the divisor g(x) with its leading coefficient (call it a). In this implementation, this means:we need to compute: coef = msg_out[i] / gen[0] + if coef != 0: # log(0) is undefined, so we need to manually check for this case. There's no need to check the divisor here because we know it can't be 0 since we generated it. + lcoef = gf_log[coef] # precaching + + for j in xrange(1, len(gen)): # in synthetic division, we always skip the first coefficient of the divisior, because it's only used to normalize the dividend coefficient (which is here useless since the divisor, the generator polynomial, is always monic) + #if gen[j] != 0: # log(0) is undefined so we need to check that, but it slow things down in fact and it's useless in our case (reed-solomon encoding) since we know that all coefficients in the generator are not 0 + msg_out[i + j] ^= gf_exp[lcoef + lgen[j]] # optimization, equivalent to gf_mul(gen[j], msg_out[i]) and we just substract it to msg_out[i+j] (but since we are in GF256, it's equivalent to an addition and to an XOR). In other words, this is simply a "multiply-accumulate operation" + + # Recopy the original message bytes (overwrites the part where the quotient was computed) + msg_out[:len(msg_in)] = msg_in # equivalent to c = mprime - b, where mprime is msg_in padded with [0]*nsym + return msg_out + + +################### REED-SOLOMON DECODING ################### + +def rs_calc_syndromes(msg, nsym, fcr=0, generator=2): + '''Given the received codeword msg and the number of error correcting symbols (nsym), computes the syndromes polynomial. + Mathematically, it's essentially equivalent to a Fourrier Transform (Chien search being the inverse). + ''' + # Note the "[0] +" : we add a 0 coefficient for the lowest degree (the constant). This effectively shifts the syndrome, and will shift every computations depending on the syndromes (such as the errors locator polynomial, errors evaluator polynomial, etc. but not the errors positions). + # This is not necessary as anyway syndromes are defined such as there are only non-zero coefficients (the only 0 is the shift of the constant here) and subsequent computations will/must account for the shift by skipping the first iteration (eg, the often seen range(1, n-k+1)), but you can also avoid prepending the 0 coeff and adapt every subsequent computations to start from 0 instead of 1. + return [0] + [gf_poly_eval(msg, gf_pow(generator, i+fcr)) for i in xrange(nsym)] + +def rs_correct_errata(msg_in, synd, err_pos, fcr=0, generator=2): # err_pos is a list of the positions of the errors/erasures/errata + '''Forney algorithm, computes the values (error magnitude) to correct the input message.''' + global field_charac + msg = _bytearray(msg_in) + # calculate errata locator polynomial to correct both errors and erasures (by combining the errors positions given by the error locator polynomial found by BM with the erasures positions given by caller) + coef_pos = [len(msg) - 1 - p for p in err_pos] # need to convert the positions to coefficients degrees for the errata locator algo to work (eg: instead of [0, 1, 2] it will become [len(msg)-1, len(msg)-2, len(msg) -3]) + err_loc = rs_find_errata_locator(coef_pos, generator) + # calculate errata evaluator polynomial (often called Omega or Gamma in academic papers) + err_eval = rs_find_error_evaluator(synd[::-1], err_loc, len(err_loc)-1)[::-1] + + # Second part of Chien search to get the error location polynomial X from the error positions in err_pos (the roots of the error locator polynomial, ie, where it evaluates to 0) + X = [] # will store the position of the errors + for i in xrange(len(coef_pos)): + l = field_charac - coef_pos[i] + X.append( gf_pow(generator, -l) ) + + # Forney algorithm: compute the magnitudes + E = _bytearray(len(msg)) # will store the values that need to be corrected (substracted) to the message containing errors. This is sometimes called the error magnitude polynomial. + Xlength = len(X) + for i, Xi in enumerate(X): + + Xi_inv = gf_inverse(Xi) + + # Compute the formal derivative of the error locator polynomial (see Blahut, Algebraic codes for data transmission, pp 196-197). + # the formal derivative of the errata locator is used as the denominator of the Forney Algorithm, which simply says that the ith error value is given by error_evaluator(gf_inverse(Xi)) / error_locator_derivative(gf_inverse(Xi)). See Blahut, Algebraic codes for data transmission, pp 196-197. + err_loc_prime_tmp = [] + for j in xrange(Xlength): + if j != i: + err_loc_prime_tmp.append( gf_sub(1, gf_mul(Xi_inv, X[j])) ) + # compute the product, which is the denominator of the Forney algorithm (errata locator derivative) + err_loc_prime = 1 + for coef in err_loc_prime_tmp: + err_loc_prime = gf_mul(err_loc_prime, coef) + # equivalent to: err_loc_prime = functools.reduce(gf_mul, err_loc_prime_tmp, 1) + + # Test if we could find the errata locator, else we raise an Exception (because else since we divide y by err_loc_prime to compute the magnitude, we will get a ZeroDivisionError exception otherwise) + if err_loc_prime == 0: + raise ReedSolomonError("Decoding failed: Forney algorithm could not properly detect where the errors are located (errata locator prime is 0).") + + # Compute y (evaluation of the errata evaluator polynomial) + # This is a more faithful translation of the theoretical equation contrary to the old forney method. Here it is exactly copy/pasted from the included presentation decoding_rs.pdf: Yl = omega(Xl.inverse()) / prod(1 - Xj*Xl.inverse()) for j in len(X) (in the paper it's for j in s, but it's useless when len(X) < s because we compute neutral terms 1 for nothing, and wrong when correcting more than s erasures or erasures+errors since it prevents computing all required terms). + # Thus here this method works with erasures too because firstly we fixed the equation to be like the theoretical one (don't know why it was modified in _old_forney(), if it's an optimization, it doesn't enhance anything), and secondly because we removed the product bound on s, which prevented computing errors and erasures above the s=(n-k)//2 bound. + y = gf_poly_eval(err_eval[::-1], Xi_inv) # numerator of the Forney algorithm (errata evaluator evaluated) + y = gf_mul(gf_pow(Xi, 1-fcr), y) # adjust to fcr parameter + + # Compute the magnitude + magnitude = gf_div(y, err_loc_prime) # magnitude value of the error, calculated by the Forney algorithm (an equation in fact): dividing the errata evaluator with the errata locator derivative gives us the errata magnitude (ie, value to repair) the ith symbol + E[err_pos[i]] = magnitude # store the magnitude for this error into the magnitude polynomial + + # Apply the correction of values to get our message corrected! (note that the ecc bytes also gets corrected!) + # (this isn't the Forney algorithm, we just apply the result of decoding here) + msg = gf_poly_add(msg, E) # equivalent to Ci = Ri - Ei where Ci is the correct message, Ri the received (senseword) message, and Ei the errata magnitudes (minus is replaced by XOR since it's equivalent in GF(2^p)). So in fact here we substract from the received message the errors magnitude, which logically corrects the value to what it should be. + return msg + +def rs_find_error_locator(synd, nsym, erase_loc=None, erase_count=0): + '''Find error/errata locator and evaluator polynomials with Berlekamp-Massey algorithm''' + # The idea is that BM will iteratively estimate the error locator polynomial. + # To do this, it will compute a Discrepancy term called Delta, which will tell us if the error locator polynomial needs an update or not + # (hence why it's called discrepancy: it tells us when we are getting off board from the correct value). + + # Init the polynomials + if erase_loc: # if the erasure locator polynomial is supplied, we init with its value, so that we include erasures in the final locator polynomial + err_loc = _bytearray(erase_loc) + old_loc = _bytearray(erase_loc) + else: + err_loc = _bytearray([1]) # This is the main variable we want to fill, also called Sigma in other notations or more formally the errors/errata locator polynomial. + old_loc = _bytearray([1]) # BM is an iterative algorithm, and we need the errata locator polynomial of the previous iteration in order to update other necessary variables. + #L = 0 # update flag variable, not needed here because we use an alternative equivalent way of checking if update is needed (but using the flag could potentially be faster depending on if using length(list) is taking linear time in your language, here in Python it's constant so it's as fast. + + # Fix the syndrome shifting: when computing the syndrome, some implementations may prepend a 0 coefficient for the lowest degree term (the constant). This is a case of syndrome shifting, thus the syndrome will be bigger than the number of ecc symbols (I don't know what purpose serves this shifting). If that's the case, then we need to account for the syndrome shifting when we use the syndrome such as inside BM, by skipping those prepended coefficients. + # Another way to detect the shifting is to detect the 0 coefficients: by definition, a syndrome does not contain any 0 coefficient (except if there are no errors/erasures, in this case they are all 0). This however doesn't work with the modified Forney syndrome, which set to 0 the coefficients corresponding to erasures, leaving only the coefficients corresponding to errors. + synd_shift = 0 + if len(synd) > nsym: synd_shift = len(synd) - nsym + + for i in xrange(nsym-erase_count): # generally: nsym-erase_count == len(synd), except when you input a partial erase_loc and using the full syndrome instead of the Forney syndrome, in which case nsym-erase_count is more correct (len(synd) will fail badly with IndexError). + if erase_loc: # if an erasures locator polynomial was provided to init the errors locator polynomial, then we must skip the FIRST erase_count iterations (not the last iterations, this is very important!) + K = erase_count+i+synd_shift + else: # if erasures locator is not provided, then either there's no erasures to account or we use the Forney syndromes, so we don't need to use erase_count nor erase_loc (the erasures have been trimmed out of the Forney syndromes). + K = i+synd_shift + + # Compute the discrepancy Delta + # Here is the close-to-the-books operation to compute the discrepancy Delta: it's a simple polynomial multiplication of error locator with the syndromes, and then we get the Kth element. + #delta = gf_poly_mul(err_loc[::-1], synd)[K] # theoretically it should be gf_poly_add(synd[::-1], [1])[::-1] instead of just synd, but it seems it's not absolutely necessary to correctly decode. + # But this can be optimized: since we only need the Kth element, we don't need to compute the polynomial multiplication for any other element but the Kth. Thus to optimize, we compute the polymul only at the item we need, skipping the rest (avoiding a nested loop, thus we are linear time instead of quadratic). + # This optimization is actually described in several figures of the book "Algebraic codes for data transmission", Blahut, Richard E., 2003, Cambridge university press. + delta = synd[K] + for j in xrange(1, len(err_loc)): + delta ^= gf_mul(err_loc[-(j+1)], synd[K - j]) # delta is also called discrepancy. Here we do a partial polynomial multiplication (ie, we compute the polynomial multiplication only for the term of degree K). Should be equivalent to brownanrs.polynomial.mul_at(). + #print "delta", K, delta, list(gf_poly_mul(err_loc[::-1], synd)) # debugline + + # Shift polynomials to compute the next degree + old_loc = old_loc + _bytearray([0]) + + # Iteratively estimate the errata locator and evaluator polynomials + if delta != 0: # Update only if there's a discrepancy + if len(old_loc) > len(err_loc): # Rule B (rule A is implicitly defined because rule A just says that we skip any modification for this iteration) + #if 2*L <= K+erase_count: # equivalent to len(old_loc) > len(err_loc), as long as L is correctly computed + # Computing errata locator polynomial Sigma + new_loc = gf_poly_scale(old_loc, delta) + old_loc = gf_poly_scale(err_loc, gf_inverse(delta)) # effectively we are doing err_loc * 1/delta = err_loc // delta + err_loc = new_loc + # Update the update flag + #L = K - L # the update flag L is tricky: in Blahut's schema, it's mandatory to use `L = K - L - erase_count` (and indeed in a previous draft of this function, if you forgot to do `- erase_count` it would lead to correcting only 2*(errors+erasures) <= (n-k) instead of 2*errors+erasures <= (n-k)), but in this latest draft, this will lead to a wrong decoding in some cases where it should correctly decode! Thus you should try with and without `- erase_count` to update L on your own implementation and see which one works OK without producing wrong decoding failures. + + # Update with the discrepancy + err_loc = gf_poly_add(err_loc, gf_poly_scale(old_loc, delta)) + + # Check if the result is correct, that there's not too many errors to correct + err_loc = list(itertools.dropwhile(lambda x: x == 0, err_loc)) # drop leading 0s, else errs will not be of the correct size + errs = len(err_loc) - 1 + if (errs-erase_count) * 2 + erase_count > nsym: + raise ReedSolomonError("Too many errors to correct") + + return err_loc + +def rs_find_errata_locator(e_pos, generator=2): + '''Compute the erasures/errors/errata locator polynomial from the erasures/errors/errata positions (the positions must be relative to the x coefficient, eg: "hello worldxxxxxxxxx" is tampered to "h_ll_ worldxxxxxxxxx" with xxxxxxxxx being the ecc of length n-k=9, here the string positions are [1, 4], but the coefficients are reversed since the ecc characters are placed as the first coefficients of the polynomial, thus the coefficients of the erased characters are n-1 - [1, 4] = [18, 15] = erasures_loc to be specified as an argument.''' + # See: http://ocw.usu.edu/Electrical_and_Computer_Engineering/Error_Control_Coding/lecture7.pdf and Blahut, Richard E. "Transform techniques for error control codes." IBM Journal of Research and development 23.3 (1979): 299-315. http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.600&rep=rep1&type=pdf and also a MatLab implementation here: http://www.mathworks.com/matlabcentral/fileexchange/23567-reed-solomon-errors-and-erasures-decoder/content//RS_E_E_DEC.m + e_loc = [1] # just to init because we will multiply, so it must be 1 so that the multiplication starts correctly without nulling any term + # erasures_loc is very simple to compute: erasures_loc = prod(1 - x*alpha**i) for i in erasures_pos and where alpha is the alpha chosen to evaluate polynomials (here in this library it's gf(3)). To generate c*x where c is a constant, we simply generate a Polynomial([c, 0]) where 0 is the constant and c is positionned to be the coefficient for x^1. + for i in e_pos: + e_loc = gf_poly_mul( e_loc, gf_poly_add(_bytearray([1]), [gf_pow(generator, i), 0]) ) + return e_loc + +def rs_find_error_evaluator(synd, err_loc, nsym): + '''Compute the error (or erasures if you supply sigma=erasures locator polynomial, or errata) evaluator polynomial Omega from the syndrome and the error/erasures/errata locator Sigma. Omega is already computed at the same time as Sigma inside the Berlekamp-Massey implemented above, but in case you modify Sigma, you can recompute Omega afterwards using this method, or just ensure that Omega computed by BM is correct given Sigma.''' + # Omega(x) = [ Synd(x) * Error_loc(x) ] mod x^(n-k+1) + _, remainder = gf_poly_div( gf_poly_mul(synd, err_loc), ([1] + [0]*(nsym+1)) ) # first multiply syndromes * errata_locator, then do a polynomial division to truncate the polynomial to the required length + + # Faster way that is equivalent + #remainder = gf_poly_mul(synd, err_loc) # first multiply the syndromes with the errata locator polynomial + #remainder = remainder[len(remainder)-(nsym+1):] # then divide by a polynomial of the length we want, which is equivalent to slicing the list (which represents the polynomial) + + return remainder + +def rs_find_errors(err_loc, nmess, generator=2): + '''Find the roots (ie, where evaluation = zero) of error polynomial by bruteforce trial, this is a sort of Chien's search (but less efficient, Chien's search is a way to evaluate the polynomial such that each evaluation only takes constant time).''' + # nmess = length of whole codeword (message + ecc symbols) + errs = len(err_loc) - 1 + err_pos = [] + for i in xrange(nmess): # normally we should try all 2^8 possible values, but here we optimize to just check the interesting symbols + if gf_poly_eval(err_loc, gf_pow(generator, i)) == 0: # It's a 0? Bingo, it's a root of the error locator polynomial, in other terms this is the location of an error + err_pos.append(nmess - 1 - i) + # Sanity check: the number of errors/errata positions found should be exactly the same as the length of the errata locator polynomial + if len(err_pos) != errs: + # TODO: to decode messages+ecc with length n > 255, we may try to use a bruteforce approach: the correct positions ARE in the final array j, but the problem is because we are above the Galois Field's range, there is a wraparound so that for example if j should be [0, 1, 2, 3], we will also get [255, 256, 257, 258] (because 258 % 255 == 3, same for the other values), so we can't discriminate. The issue is that fixing any errs_nb errors among those will always give a correct output message (in the sense that the syndrome will be all 0), so we may not even be able to check if that's correct or not, so I'm not sure the bruteforce approach may even be possible. + raise ReedSolomonError("Too many (or few) errors found by Chien Search for the errata locator polynomial!") + return err_pos + +def rs_forney_syndromes(synd, pos, nmess, generator=2): + # Compute Forney syndromes, which computes a modified syndromes to compute only errors (erasures are trimmed out). Do not confuse this with Forney algorithm, which allows to correct the message based on the location of errors. + erase_pos_reversed = [nmess-1-p for p in pos] # prepare the coefficient degree positions (instead of the erasures positions) + + # Optimized method, all operations are inlined + fsynd = list(synd[1:]) # make a copy and trim the first coefficient which is always 0 by definition + for i in xrange(len(pos)): + x = gf_pow(generator, erase_pos_reversed[i]) + for j in xrange(len(fsynd) - 1): + fsynd[j] = gf_mul(fsynd[j], x) ^ fsynd[j + 1] + #fsynd.pop() # useless? it doesn't change the results of computations to leave it there + + # Theoretical way of computing the modified Forney syndromes: fsynd = (erase_loc * synd) % x^(n-k) -- although the trimming by using x^(n-k) is maybe not necessary as many books do not even mention it (and it works without trimming) + # See Shao, H. M., Truong, T. K., Deutsch, L. J., & Reed, I. S. (1986, April). A single chip VLSI Reed-Solomon decoder. In Acoustics, Speech, and Signal Processing, IEEE International Conference on ICASSP'86. (Vol. 11, pp. 2151-2154). IEEE.ISO 690 + #erase_loc = rs_find_errata_locator(erase_pos_reversed, generator=generator) # computing the erasures locator polynomial + #fsynd = gf_poly_mul(erase_loc[::-1], synd[1:]) # then multiply with the syndrome to get the untrimmed forney syndrome + #fsynd = fsynd[len(pos):] # then trim the first erase_pos coefficients which are useless. Seems to be not necessary, but this reduces the computation time later in BM (thus it's an optimization). + + return fsynd + +def rs_correct_msg(msg_in, nsym, fcr=0, generator=2, erase_pos=None, only_erasures=False): + '''Reed-Solomon main decoding function''' + global field_charac + if len(msg_in) > field_charac: + # Note that it is in fact possible to encode/decode messages that are longer than field_charac, but because this will be above the field, this will generate more error positions during Chien Search than it should, because this will generate duplicate values, which should normally be prevented thank's to the prime polynomial reduction (eg, because it can't discriminate between error at position 1 or 256, both being exactly equal under galois field 2^8). So it's really not advised to do it, but it's possible (but then you're not guaranted to be able to correct any error/erasure on symbols with a position above the length of field_charac -- if you really need a bigger message without chunking, then you should better enlarge c_exp so that you get a bigger field). + raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in), field_charac)) + + msg_out = _bytearray(msg_in) # copy of message + # erasures: set them to null bytes for easier decoding (but this is not necessary, they will be corrected anyway, but debugging will be easier with null bytes because the error locator polynomial values will only depend on the errors locations, not their values) + if erase_pos is None: + erase_pos = [] + else: + for e_pos in erase_pos: + msg_out[e_pos] = 0 + # check if there are too many erasures to correct (beyond the Singleton bound) + if len(erase_pos) > nsym: raise ReedSolomonError("Too many erasures to correct") + # prepare the syndrome polynomial using only errors (ie: errors = characters that were either replaced by null byte or changed to another character, but we don't know their positions) + synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) + # check if there's any error/erasure in the input codeword. If not (all syndromes coefficients are 0), then just return the codeword as-is. + if max(synd) == 0: + return msg_out[:-nsym], msg_out[-nsym:], [] # no errors + + # Find errors locations + if only_erasures: + err_pos = [] + else: + # compute the Forney syndromes, which hide the erasures from the original syndrome (so that BM will just have to deal with errors, not erasures) + fsynd = rs_forney_syndromes(synd, erase_pos, len(msg_out), generator) + # compute the error locator polynomial using Berlekamp-Massey + err_loc = rs_find_error_locator(fsynd, nsym, erase_count=len(erase_pos)) + # locate the message errors using Chien search (or bruteforce search) + err_pos = rs_find_errors(err_loc[::-1], len(msg_out), generator) + if err_pos is None: + raise ReedSolomonError("Could not locate error") + + # Find errors values and apply them to correct the message + # compute errata evaluator and errata magnitude polynomials, then correct errors and erasures + msg_out = rs_correct_errata(msg_out, synd, erase_pos + err_pos, fcr, generator) # note that we here use the original syndrome, not the forney syndrome (because we will correct both errors and erasures, so we need the full syndrome) + # check if the final message is fully repaired + synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) + if max(synd) > 0: + raise ReedSolomonError("Could not correct message") + # return the successfully decoded message + return msg_out[:-nsym], msg_out[-nsym:], erase_pos + err_pos # also return the corrected ecc block so that the user can check(), and the position of errors to allow for adaptive bitrate algorithm to check how the number of errors vary + +def rs_correct_msg_nofsynd(msg_in, nsym, fcr=0, generator=2, erase_pos=None, only_erasures=False): + '''Reed-Solomon main decoding function, without using the modified Forney syndromes''' + global field_charac + if len(msg_in) > field_charac: + raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in), field_charac)) + + msg_out = _bytearray(msg_in) # copy of message + # erasures: set them to null bytes for easier decoding (but this is not necessary, they will be corrected anyway, but debugging will be easier with null bytes because the error locator polynomial values will only depend on the errors locations, not their values) + if erase_pos is None: + erase_pos = [] + else: + for e_pos in erase_pos: + msg_out[e_pos] = 0 + # check if there are too many erasures + if len(erase_pos) > nsym: raise ReedSolomonError("Too many erasures to correct") + # prepare the syndrome polynomial using only errors (ie: errors = characters that were either replaced by null byte or changed to another character, but we don't know their positions) + synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) + # check if there's any error/erasure in the input codeword. If not (all syndromes coefficients are 0), then just return the codeword as-is. + if max(synd) == 0: + return msg_out[:-nsym], msg_out[-nsym:], [] # no errors + + # prepare erasures locator and evaluator polynomials + erase_loc = None + #erase_eval = None + erase_count = 0 + if erase_pos: + erase_count = len(erase_pos) + erase_pos_reversed = [len(msg_out)-1-eras for eras in erase_pos] + erase_loc = rs_find_errata_locator(erase_pos_reversed, generator=generator) + #erase_eval = rs_find_error_evaluator(synd[::-1], erase_loc, len(erase_loc)-1) + + # prepare errors/errata locator polynomial + if only_erasures: + err_loc = erase_loc[::-1] + #err_eval = erase_eval[::-1] + else: + err_loc = rs_find_error_locator(synd, nsym, erase_loc=erase_loc, erase_count=erase_count) + err_loc = err_loc[::-1] + #err_eval = rs_find_error_evaluator(synd[::-1], err_loc[::-1], len(err_loc)-1)[::-1] # find error/errata evaluator polynomial (not really necessary since we already compute it at the same time as the error locator poly in BM) + + # locate the message errors + err_pos = rs_find_errors(err_loc, len(msg_out), generator) # find the roots of the errata locator polynomial (ie: the positions of the errors/errata) + if err_pos is None: + raise ReedSolomonError("Could not locate error") + + # compute errata evaluator and errata magnitude polynomials, then correct errors and erasures + msg_out = rs_correct_errata(msg_out, synd, err_pos, fcr=fcr, generator=generator) + # check if the final message is fully repaired + synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) + if max(synd) > 0: + raise ReedSolomonError("Could not correct message") + # return the successfully decoded message + return msg_out[:-nsym], msg_out[-nsym:], erase_pos + err_pos # also return the corrected ecc block so that the user can check(), and the position of errors to allow for adaptive bitrate algorithm to check how the number of errors vary + +def rs_check(msg, nsym, fcr=0, generator=2): + '''Returns true if the message + ecc has no error of false otherwise (may not always catch a wrong decoding or a wrong message, particularly if there are too many errors -- above the Singleton bound --, but it usually does)''' + return ( max(rs_calc_syndromes(msg, nsym, fcr, generator)) == 0 ) + + +#=================================================================================================== +# API +#=================================================================================================== +class RSCodec(object): + ''' + A Reed Solomon encoder/decoder. After initializing the object, use ``encode`` to encode a + (byte)string to include the RS correction code, and pass such an encoded (byte)string to + ``decode`` to extract the original message (if the number of errors allows for correct decoding). + The ``nsym`` argument is the length of the correction code, and it determines the number of + error bytes (if I understand this correctly, half of ``nsym`` is correctable) + ''' + ''' + Modifications by rotorgit 2/3/2015: + Added support for US FAA ADSB UAT RS FEC, by allowing user to specify + different primitive polynomial and non-zero first consecutive root (fcr). + For UAT/ADSB use, set fcr=120 and prim=0x187 when instantiating + the class; leaving them out will default for previous values (0 and + 0x11d) + ''' + + def __init__(self, nsym=10, nsize=255, fcr=0, prim=0x11d, generator=2, c_exp=8, single_gen=True): + '''Initialize the Reed-Solomon codec. Note that different parameters change the internal values (the ecc symbols, look-up table values, etc) but not the output result (whether your message can be repaired or not, there is no influence of the parameters). + nsym : number of ecc symbols (you can repair nsym/2 errors and nsym erasures. + nsize : maximum length of each chunk. If higher than 255, will use a higher Galois Field, but the algorithm's complexity and computational cost will raise quadratically... + single_gen : if you want to use the same RSCodec for different nsym parameters (but nsize the same), then set single_gen = False. + ''' + + # Auto-setup if galois field or message length is different than default (exponent 8) + if nsize > 255 and c_exp <= 8: # nsize (chunksize) is larger than the galois field, we resize the galois field + # Get the next closest power of two + c_exp = int(math.log(2 ** (math.floor(math.log(nsize) / math.log(2)) + 1), 2)) + if c_exp != 8 and prim == 0x11d: # prim was not correctly defined, find one + prim = find_prime_polys(generator=generator, c_exp=c_exp, fast_primes=True, single=True) + if nsize == 255: # resize chunk size if not set + nsize = int(2**c_exp - 1) + + # Memorize variables + self.nsym = nsym # number of ecc symbols (ie, the repairing rate will be r=(nsym/2)/nsize, so for example if you have nsym=5 and nsize=10, you have a rate r=0.25, so you can correct up to 0.25% errors (or exactly 2 symbols out of 10), and 0.5% erasures (5 symbols out of 10). + self.nsize = nsize # maximum length of one chunk (ie, message + ecc symbols after encoding, for the message alone it's nsize-nsym) + self.fcr = fcr # first consecutive root, can be any value between 0 and (2**c_exp)-1 + self.prim = prim # prime irreducible polynomial, use find_prime_polys() to find a prime poly + self.generator = generator # generator integer, must be prime + self.c_exp = c_exp # exponent of the field's characteristic. This both defines the maximum value per symbol and the maximum length of one chunk. By default it's GF(2^8), do not change if you're not sure what it means. + + # Initialize the look-up tables for easy and quick multiplication/division + self.gf_log, self.gf_exp, self.field_charac = init_tables(prim, generator, c_exp) + # Precompute the generator polynomials + if single_gen: + self.gen = {} + self.gen[nsym] = rs_generator_poly(nsym, fcr=fcr, generator=generator) + else: + self.gen = rs_generator_poly_all(nsize, fcr=fcr, generator=generator) + + def chunk(self, data, chunksize): + '''Split a long message into chunks''' + for i in xrange(0, len(data), chunksize): + # Split the long message in a chunk + chunk = data[i:i+chunksize] + yield chunk + + def encode(self, data, nsym=None): + '''Encode a message (ie, add the ecc symbols) using Reed-Solomon, whatever the length of the message because we use chunking''' + # Restore precomputed tables (allow to use multiple RSCodec in one script) + global gf_log, gf_exp, field_charac + gf_log, gf_exp, field_charac = self.gf_log, self.gf_exp, self.field_charac + + if not nsym: + nsym = self.nsym + + if isinstance(data, str): + data = _bytearray(data) + enc = _bytearray() + for chunk in self.chunk(data, self.nsize - self.nsym): + enc.extend(rs_encode_msg(chunk, self.nsym, fcr=self.fcr, generator=self.generator, gen=self.gen[nsym])) + return enc + + def decode(self, data, nsym=None, erase_pos=None, only_erasures=False): + '''Repair a message, whatever its size is, by using chunking. May return a wrong result if number of errors > nsym. + Note that it returns a couple of vars: the repaired messages, and the repaired messages+ecc (useful for checking). + Usage: rmes, rmesecc = RSCodec.decode(data). + ''' + # erase_pos is a list of positions where you know (or greatly suspect at least) there is an erasure (ie, wrong character but you know it's at this position). Just input the list of all positions you know there are errors, and this method will automatically split the erasures positions to attach to the corresponding data chunk. + + # Restore precomputed tables (allow to use multiple RSCodec in one script) + global gf_log, gf_exp, field_charac + gf_log, gf_exp, field_charac = self.gf_log, self.gf_exp, self.field_charac + + if not nsym: + nsym = self.nsym + + if isinstance(data, str): + data = _bytearray(data) + dec = _bytearray() + dec_full = _bytearray() + errata_pos_all = _bytearray() + for chunk in self.chunk(data, self.nsize): + # Extract the erasures for this chunk + e_pos = [] + if erase_pos: + # First extract the erasures for this chunk (all erasures below the maximum chunk length) + e_pos = [x for x in erase_pos if x <= self.nsize] + # Then remove the extract erasures from the big list and also decrement all subsequent positions values by nsize (the current chunk's size) so as to prepare the correct alignment for the next iteration + erase_pos = [x - (self.nsize+1) for x in erase_pos if x > self.nsize] + # Decode/repair this chunk! + rmes, recc, errata_pos = rs_correct_msg(chunk, nsym, fcr=self.fcr, generator=self.generator, erase_pos=e_pos, only_erasures=only_erasures) + dec.extend(rmes) + dec_full.extend(rmes+recc) + errata_pos_all.extend(errata_pos) + return dec, dec_full, errata_pos_all + + def check(self, data, nsym=None): + '''Check if a message+ecc stream is not corrupted (or fully repaired). Note: may return a wrong result if number of errors > nsym.''' + if not nsym: + nsym = self.nsym + if isinstance(data, str): + data = _bytearray(data) + check = [] + for chunk in self.chunk(data, self.nsize): + check.append(rs_check(chunk, nsym, fcr=self.fcr, generator=self.generator)) + return check + + def maxerrata(self, errors=None, erasures=None, verbose=False): + '''Return the Singleton Bound for the current codec, which is the max number of errata (errors and erasures) that the codec can decode/correct. + Beyond the Singleton Bound (too many errors/erasures), the algorithm will try to raise an exception, but it may also not detect any problem with the message and return 0 errors. + Hence why you should use checksums if your goal is to detect errors (as opposed to correcting them), as checksums have no bounds on the number of errors, the only limitation being the probability of collisions. + By default, return a tuple wth the maximum number of errors (2nd output) OR erasures (2nd output) that can be corrected. + If errors or erasures (not both) is specified as argument, computes the remaining **simultaneous** correction capacity (eg, if errors specified, compute the number of erasures that can be simultaneously corrected). + Set verbose to True to get print a report.''' + nsym = self.nsym + # Compute the maximum number of errors OR erasures + maxerrors = int(nsym/2) # always floor the number, we can't correct half a symbol, it's all or nothing + maxerasures = nsym + # Compute the maximum of simultaneous errors AND erasures + if erasures is not None and erasures >= 0: + # We know the erasures count, we want to know how many errors we can correct simultaneously + if erasures > maxerasures: + raise ReedSolomonError("Specified number of errors or erasures exceeding the Singleton Bound!") + maxerrors = int((nsym-erasures)/2) + if verbose: + print('This codec can correct up to %i errors and %i erasures simultaneously' % (maxerrors, erasures)) + # Return a tuple with the maximum number of simultaneously corrected errors and erasures + return maxerrors, erasures + if errors is not None and errors >= 0: + # We know the errors count, we want to know how many erasures we can correct simultaneously + if errors > maxerrors: + raise ReedSolomonError("Specified number of errors or erasures exceeding the Singleton Bound!") + maxerasures = int(nsym-(errors*2)) + if verbose: + print('This codec can correct up to %i errors and %i erasures simultaneously' % (errors, maxerasures)) + # Return a tuple with the maximum number of simultaneously corrected errors and erasures + return errors, maxerasures + # Return a tuple with the maximum number of errors and erasures (independently corrected) + if verbose: + print('This codec can correct up to %i errors and %i erasures independently' % (maxerrors, maxerasures)) + return maxerrors, maxerasures diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE new file mode 100644 index 000000000..de6633112 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE @@ -0,0 +1,18 @@ +Copyright (c) 2010-2020 Benjamin Peterson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA new file mode 100644 index 000000000..6d7525c2e --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA @@ -0,0 +1,49 @@ +Metadata-Version: 2.1 +Name: six +Version: 1.16.0 +Summary: Python 2 and 3 compatibility utilities +Home-page: https://github.com/benjaminp/six +Author: Benjamin Peterson +Author-email: benjamin@python.org +License: MIT +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Software Development :: Libraries +Classifier: Topic :: Utilities +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* + +.. image:: https://img.shields.io/pypi/v/six.svg + :target: https://pypi.org/project/six/ + :alt: six on PyPI + +.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master + :target: https://travis-ci.org/benjaminp/six + :alt: six on TravisCI + +.. image:: https://readthedocs.org/projects/six/badge/?version=latest + :target: https://six.readthedocs.io/ + :alt: six's documentation on Read the Docs + +.. image:: https://img.shields.io/badge/license-MIT-green.svg + :target: https://github.com/benjaminp/six/blob/master/LICENSE + :alt: MIT License badge + +Six is a Python 2 and 3 compatibility library. It provides utility functions +for smoothing over the differences between the Python versions with the goal of +writing Python code that is compatible on both Python versions. See the +documentation for more information on what is provided. + +Six supports Python 2.7 and 3.3+. It is contained in only one Python +file, so it can be easily copied into your project. (The copyright and license +notice must be retained.) + +Online documentation is at https://six.readthedocs.io/. + +Bugs can be reported to https://github.com/benjaminp/six. The code can also +be found there. + + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD new file mode 100644 index 000000000..ed0b59873 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD @@ -0,0 +1,8 @@ +__pycache__/six.cpython-310.pyc,, +six-1.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +six-1.16.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066 +six-1.16.0.dist-info/METADATA,sha256=VQcGIFCAEmfZcl77E5riPCN4v2TIsc_qtacnjxKHJoI,1795 +six-1.16.0.dist-info/RECORD,, +six-1.16.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +six-1.16.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 +six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL new file mode 100644 index 000000000..01b8fc7d4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt new file mode 100644 index 000000000..ffe2fce49 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt @@ -0,0 +1 @@ +six diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six.py b/dependencies/windows_amd64/python/Lib/site-packages/six.py new file mode 100644 index 000000000..4e15675d8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/six.py @@ -0,0 +1,998 @@ +# Copyright (c) 2010-2020 Benjamin Peterson +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +"""Utilities for writing code that runs on Python 2 and 3""" + +from __future__ import absolute_import + +import functools +import itertools +import operator +import sys +import types + +__author__ = "Benjamin Peterson " +__version__ = "1.16.0" + + +# Useful for very coarse version differentiation. +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY34 = sys.version_info[0:2] >= (3, 4) + +if PY3: + string_types = str, + integer_types = int, + class_types = type, + text_type = str + binary_type = bytes + + MAXSIZE = sys.maxsize +else: + string_types = basestring, + integer_types = (int, long) + class_types = (type, types.ClassType) + text_type = unicode + binary_type = str + + if sys.platform.startswith("java"): + # Jython always uses 32 bits. + MAXSIZE = int((1 << 31) - 1) + else: + # It's possible to have sizeof(long) != sizeof(Py_ssize_t). + class X(object): + + def __len__(self): + return 1 << 31 + try: + len(X()) + except OverflowError: + # 32-bit + MAXSIZE = int((1 << 31) - 1) + else: + # 64-bit + MAXSIZE = int((1 << 63) - 1) + del X + +if PY34: + from importlib.util import spec_from_loader +else: + spec_from_loader = None + + +def _add_doc(func, doc): + """Add documentation to a function.""" + func.__doc__ = doc + + +def _import_module(name): + """Import module, returning the module after the last dot.""" + __import__(name) + return sys.modules[name] + + +class _LazyDescr(object): + + def __init__(self, name): + self.name = name + + def __get__(self, obj, tp): + result = self._resolve() + setattr(obj, self.name, result) # Invokes __set__. + try: + # This is a bit ugly, but it avoids running this again by + # removing this descriptor. + delattr(obj.__class__, self.name) + except AttributeError: + pass + return result + + +class MovedModule(_LazyDescr): + + def __init__(self, name, old, new=None): + super(MovedModule, self).__init__(name) + if PY3: + if new is None: + new = name + self.mod = new + else: + self.mod = old + + def _resolve(self): + return _import_module(self.mod) + + def __getattr__(self, attr): + _module = self._resolve() + value = getattr(_module, attr) + setattr(self, attr, value) + return value + + +class _LazyModule(types.ModuleType): + + def __init__(self, name): + super(_LazyModule, self).__init__(name) + self.__doc__ = self.__class__.__doc__ + + def __dir__(self): + attrs = ["__doc__", "__name__"] + attrs += [attr.name for attr in self._moved_attributes] + return attrs + + # Subclasses should override this + _moved_attributes = [] + + +class MovedAttribute(_LazyDescr): + + def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): + super(MovedAttribute, self).__init__(name) + if PY3: + if new_mod is None: + new_mod = name + self.mod = new_mod + if new_attr is None: + if old_attr is None: + new_attr = name + else: + new_attr = old_attr + self.attr = new_attr + else: + self.mod = old_mod + if old_attr is None: + old_attr = name + self.attr = old_attr + + def _resolve(self): + module = _import_module(self.mod) + return getattr(module, self.attr) + + +class _SixMetaPathImporter(object): + + """ + A meta path importer to import six.moves and its submodules. + + This class implements a PEP302 finder and loader. It should be compatible + with Python 2.5 and all existing versions of Python3 + """ + + def __init__(self, six_module_name): + self.name = six_module_name + self.known_modules = {} + + def _add_module(self, mod, *fullnames): + for fullname in fullnames: + self.known_modules[self.name + "." + fullname] = mod + + def _get_module(self, fullname): + return self.known_modules[self.name + "." + fullname] + + def find_module(self, fullname, path=None): + if fullname in self.known_modules: + return self + return None + + def find_spec(self, fullname, path, target=None): + if fullname in self.known_modules: + return spec_from_loader(fullname, self) + return None + + def __get_module(self, fullname): + try: + return self.known_modules[fullname] + except KeyError: + raise ImportError("This loader does not know module " + fullname) + + def load_module(self, fullname): + try: + # in case of a reload + return sys.modules[fullname] + except KeyError: + pass + mod = self.__get_module(fullname) + if isinstance(mod, MovedModule): + mod = mod._resolve() + else: + mod.__loader__ = self + sys.modules[fullname] = mod + return mod + + def is_package(self, fullname): + """ + Return true, if the named module is a package. + + We need this method to get correct spec objects with + Python 3.4 (see PEP451) + """ + return hasattr(self.__get_module(fullname), "__path__") + + def get_code(self, fullname): + """Return None + + Required, if is_package is implemented""" + self.__get_module(fullname) # eventually raises ImportError + return None + get_source = get_code # same as get_code + + def create_module(self, spec): + return self.load_module(spec.name) + + def exec_module(self, module): + pass + +_importer = _SixMetaPathImporter(__name__) + + +class _MovedItems(_LazyModule): + + """Lazy loading of moved objects""" + __path__ = [] # mark as package + + +_moved_attributes = [ + MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), + MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), + MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), + MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), + MovedAttribute("intern", "__builtin__", "sys"), + MovedAttribute("map", "itertools", "builtins", "imap", "map"), + MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), + MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), + MovedAttribute("getoutput", "commands", "subprocess"), + MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), + MovedAttribute("reduce", "__builtin__", "functools"), + MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), + MovedAttribute("StringIO", "StringIO", "io"), + MovedAttribute("UserDict", "UserDict", "collections"), + MovedAttribute("UserList", "UserList", "collections"), + MovedAttribute("UserString", "UserString", "collections"), + MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), + MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), + MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), + MovedModule("builtins", "__builtin__"), + MovedModule("configparser", "ConfigParser"), + MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), + MovedModule("copyreg", "copy_reg"), + MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), + MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), + MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), + MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), + MovedModule("http_cookies", "Cookie", "http.cookies"), + MovedModule("html_entities", "htmlentitydefs", "html.entities"), + MovedModule("html_parser", "HTMLParser", "html.parser"), + MovedModule("http_client", "httplib", "http.client"), + MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), + MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), + MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), + MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), + MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), + MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), + MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), + MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), + MovedModule("cPickle", "cPickle", "pickle"), + MovedModule("queue", "Queue"), + MovedModule("reprlib", "repr"), + MovedModule("socketserver", "SocketServer"), + MovedModule("_thread", "thread", "_thread"), + MovedModule("tkinter", "Tkinter"), + MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), + MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), + MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), + MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), + MovedModule("tkinter_tix", "Tix", "tkinter.tix"), + MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), + MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), + MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), + MovedModule("tkinter_colorchooser", "tkColorChooser", + "tkinter.colorchooser"), + MovedModule("tkinter_commondialog", "tkCommonDialog", + "tkinter.commondialog"), + MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), + MovedModule("tkinter_font", "tkFont", "tkinter.font"), + MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), + MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", + "tkinter.simpledialog"), + MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), + MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), + MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), + MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), + MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), + MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), +] +# Add windows specific modules. +if sys.platform == "win32": + _moved_attributes += [ + MovedModule("winreg", "_winreg"), + ] + +for attr in _moved_attributes: + setattr(_MovedItems, attr.name, attr) + if isinstance(attr, MovedModule): + _importer._add_module(attr, "moves." + attr.name) +del attr + +_MovedItems._moved_attributes = _moved_attributes + +moves = _MovedItems(__name__ + ".moves") +_importer._add_module(moves, "moves") + + +class Module_six_moves_urllib_parse(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_parse""" + + +_urllib_parse_moved_attributes = [ + MovedAttribute("ParseResult", "urlparse", "urllib.parse"), + MovedAttribute("SplitResult", "urlparse", "urllib.parse"), + MovedAttribute("parse_qs", "urlparse", "urllib.parse"), + MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), + MovedAttribute("urldefrag", "urlparse", "urllib.parse"), + MovedAttribute("urljoin", "urlparse", "urllib.parse"), + MovedAttribute("urlparse", "urlparse", "urllib.parse"), + MovedAttribute("urlsplit", "urlparse", "urllib.parse"), + MovedAttribute("urlunparse", "urlparse", "urllib.parse"), + MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), + MovedAttribute("quote", "urllib", "urllib.parse"), + MovedAttribute("quote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote", "urllib", "urllib.parse"), + MovedAttribute("unquote_plus", "urllib", "urllib.parse"), + MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), + MovedAttribute("urlencode", "urllib", "urllib.parse"), + MovedAttribute("splitquery", "urllib", "urllib.parse"), + MovedAttribute("splittag", "urllib", "urllib.parse"), + MovedAttribute("splituser", "urllib", "urllib.parse"), + MovedAttribute("splitvalue", "urllib", "urllib.parse"), + MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), + MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), + MovedAttribute("uses_params", "urlparse", "urllib.parse"), + MovedAttribute("uses_query", "urlparse", "urllib.parse"), + MovedAttribute("uses_relative", "urlparse", "urllib.parse"), +] +for attr in _urllib_parse_moved_attributes: + setattr(Module_six_moves_urllib_parse, attr.name, attr) +del attr + +Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes + +_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), + "moves.urllib_parse", "moves.urllib.parse") + + +class Module_six_moves_urllib_error(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_error""" + + +_urllib_error_moved_attributes = [ + MovedAttribute("URLError", "urllib2", "urllib.error"), + MovedAttribute("HTTPError", "urllib2", "urllib.error"), + MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), +] +for attr in _urllib_error_moved_attributes: + setattr(Module_six_moves_urllib_error, attr.name, attr) +del attr + +Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes + +_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), + "moves.urllib_error", "moves.urllib.error") + + +class Module_six_moves_urllib_request(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_request""" + + +_urllib_request_moved_attributes = [ + MovedAttribute("urlopen", "urllib2", "urllib.request"), + MovedAttribute("install_opener", "urllib2", "urllib.request"), + MovedAttribute("build_opener", "urllib2", "urllib.request"), + MovedAttribute("pathname2url", "urllib", "urllib.request"), + MovedAttribute("url2pathname", "urllib", "urllib.request"), + MovedAttribute("getproxies", "urllib", "urllib.request"), + MovedAttribute("Request", "urllib2", "urllib.request"), + MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), + MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), + MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), + MovedAttribute("BaseHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), + MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), + MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), + MovedAttribute("FileHandler", "urllib2", "urllib.request"), + MovedAttribute("FTPHandler", "urllib2", "urllib.request"), + MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), + MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), + MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), + MovedAttribute("urlretrieve", "urllib", "urllib.request"), + MovedAttribute("urlcleanup", "urllib", "urllib.request"), + MovedAttribute("URLopener", "urllib", "urllib.request"), + MovedAttribute("FancyURLopener", "urllib", "urllib.request"), + MovedAttribute("proxy_bypass", "urllib", "urllib.request"), + MovedAttribute("parse_http_list", "urllib2", "urllib.request"), + MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), +] +for attr in _urllib_request_moved_attributes: + setattr(Module_six_moves_urllib_request, attr.name, attr) +del attr + +Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes + +_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), + "moves.urllib_request", "moves.urllib.request") + + +class Module_six_moves_urllib_response(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_response""" + + +_urllib_response_moved_attributes = [ + MovedAttribute("addbase", "urllib", "urllib.response"), + MovedAttribute("addclosehook", "urllib", "urllib.response"), + MovedAttribute("addinfo", "urllib", "urllib.response"), + MovedAttribute("addinfourl", "urllib", "urllib.response"), +] +for attr in _urllib_response_moved_attributes: + setattr(Module_six_moves_urllib_response, attr.name, attr) +del attr + +Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes + +_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), + "moves.urllib_response", "moves.urllib.response") + + +class Module_six_moves_urllib_robotparser(_LazyModule): + + """Lazy loading of moved objects in six.moves.urllib_robotparser""" + + +_urllib_robotparser_moved_attributes = [ + MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), +] +for attr in _urllib_robotparser_moved_attributes: + setattr(Module_six_moves_urllib_robotparser, attr.name, attr) +del attr + +Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes + +_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), + "moves.urllib_robotparser", "moves.urllib.robotparser") + + +class Module_six_moves_urllib(types.ModuleType): + + """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" + __path__ = [] # mark as package + parse = _importer._get_module("moves.urllib_parse") + error = _importer._get_module("moves.urllib_error") + request = _importer._get_module("moves.urllib_request") + response = _importer._get_module("moves.urllib_response") + robotparser = _importer._get_module("moves.urllib_robotparser") + + def __dir__(self): + return ['parse', 'error', 'request', 'response', 'robotparser'] + +_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), + "moves.urllib") + + +def add_move(move): + """Add an item to six.moves.""" + setattr(_MovedItems, move.name, move) + + +def remove_move(name): + """Remove item from six.moves.""" + try: + delattr(_MovedItems, name) + except AttributeError: + try: + del moves.__dict__[name] + except KeyError: + raise AttributeError("no such move, %r" % (name,)) + + +if PY3: + _meth_func = "__func__" + _meth_self = "__self__" + + _func_closure = "__closure__" + _func_code = "__code__" + _func_defaults = "__defaults__" + _func_globals = "__globals__" +else: + _meth_func = "im_func" + _meth_self = "im_self" + + _func_closure = "func_closure" + _func_code = "func_code" + _func_defaults = "func_defaults" + _func_globals = "func_globals" + + +try: + advance_iterator = next +except NameError: + def advance_iterator(it): + return it.next() +next = advance_iterator + + +try: + callable = callable +except NameError: + def callable(obj): + return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) + + +if PY3: + def get_unbound_function(unbound): + return unbound + + create_bound_method = types.MethodType + + def create_unbound_method(func, cls): + return func + + Iterator = object +else: + def get_unbound_function(unbound): + return unbound.im_func + + def create_bound_method(func, obj): + return types.MethodType(func, obj, obj.__class__) + + def create_unbound_method(func, cls): + return types.MethodType(func, None, cls) + + class Iterator(object): + + def next(self): + return type(self).__next__(self) + + callable = callable +_add_doc(get_unbound_function, + """Get the function out of a possibly unbound function""") + + +get_method_function = operator.attrgetter(_meth_func) +get_method_self = operator.attrgetter(_meth_self) +get_function_closure = operator.attrgetter(_func_closure) +get_function_code = operator.attrgetter(_func_code) +get_function_defaults = operator.attrgetter(_func_defaults) +get_function_globals = operator.attrgetter(_func_globals) + + +if PY3: + def iterkeys(d, **kw): + return iter(d.keys(**kw)) + + def itervalues(d, **kw): + return iter(d.values(**kw)) + + def iteritems(d, **kw): + return iter(d.items(**kw)) + + def iterlists(d, **kw): + return iter(d.lists(**kw)) + + viewkeys = operator.methodcaller("keys") + + viewvalues = operator.methodcaller("values") + + viewitems = operator.methodcaller("items") +else: + def iterkeys(d, **kw): + return d.iterkeys(**kw) + + def itervalues(d, **kw): + return d.itervalues(**kw) + + def iteritems(d, **kw): + return d.iteritems(**kw) + + def iterlists(d, **kw): + return d.iterlists(**kw) + + viewkeys = operator.methodcaller("viewkeys") + + viewvalues = operator.methodcaller("viewvalues") + + viewitems = operator.methodcaller("viewitems") + +_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") +_add_doc(itervalues, "Return an iterator over the values of a dictionary.") +_add_doc(iteritems, + "Return an iterator over the (key, value) pairs of a dictionary.") +_add_doc(iterlists, + "Return an iterator over the (key, [values]) pairs of a dictionary.") + + +if PY3: + def b(s): + return s.encode("latin-1") + + def u(s): + return s + unichr = chr + import struct + int2byte = struct.Struct(">B").pack + del struct + byte2int = operator.itemgetter(0) + indexbytes = operator.getitem + iterbytes = iter + import io + StringIO = io.StringIO + BytesIO = io.BytesIO + del io + _assertCountEqual = "assertCountEqual" + if sys.version_info[1] <= 1: + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" + else: + _assertRaisesRegex = "assertRaisesRegex" + _assertRegex = "assertRegex" + _assertNotRegex = "assertNotRegex" +else: + def b(s): + return s + # Workaround for standalone backslash + + def u(s): + return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") + unichr = unichr + int2byte = chr + + def byte2int(bs): + return ord(bs[0]) + + def indexbytes(buf, i): + return ord(buf[i]) + iterbytes = functools.partial(itertools.imap, ord) + import StringIO + StringIO = BytesIO = StringIO.StringIO + _assertCountEqual = "assertItemsEqual" + _assertRaisesRegex = "assertRaisesRegexp" + _assertRegex = "assertRegexpMatches" + _assertNotRegex = "assertNotRegexpMatches" +_add_doc(b, """Byte literal""") +_add_doc(u, """Text literal""") + + +def assertCountEqual(self, *args, **kwargs): + return getattr(self, _assertCountEqual)(*args, **kwargs) + + +def assertRaisesRegex(self, *args, **kwargs): + return getattr(self, _assertRaisesRegex)(*args, **kwargs) + + +def assertRegex(self, *args, **kwargs): + return getattr(self, _assertRegex)(*args, **kwargs) + + +def assertNotRegex(self, *args, **kwargs): + return getattr(self, _assertNotRegex)(*args, **kwargs) + + +if PY3: + exec_ = getattr(moves.builtins, "exec") + + def reraise(tp, value, tb=None): + try: + if value is None: + value = tp() + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None + tb = None + +else: + def exec_(_code_, _globs_=None, _locs_=None): + """Execute code in a namespace.""" + if _globs_ is None: + frame = sys._getframe(1) + _globs_ = frame.f_globals + if _locs_ is None: + _locs_ = frame.f_locals + del frame + elif _locs_ is None: + _locs_ = _globs_ + exec("""exec _code_ in _globs_, _locs_""") + + exec_("""def reraise(tp, value, tb=None): + try: + raise tp, value, tb + finally: + tb = None +""") + + +if sys.version_info[:2] > (3,): + exec_("""def raise_from(value, from_value): + try: + raise value from from_value + finally: + value = None +""") +else: + def raise_from(value, from_value): + raise value + + +print_ = getattr(moves.builtins, "print", None) +if print_ is None: + def print_(*args, **kwargs): + """The new-style print function for Python 2.4 and 2.5.""" + fp = kwargs.pop("file", sys.stdout) + if fp is None: + return + + def write(data): + if not isinstance(data, basestring): + data = str(data) + # If the file has an encoding, encode unicode with it. + if (isinstance(fp, file) and + isinstance(data, unicode) and + fp.encoding is not None): + errors = getattr(fp, "errors", None) + if errors is None: + errors = "strict" + data = data.encode(fp.encoding, errors) + fp.write(data) + want_unicode = False + sep = kwargs.pop("sep", None) + if sep is not None: + if isinstance(sep, unicode): + want_unicode = True + elif not isinstance(sep, str): + raise TypeError("sep must be None or a string") + end = kwargs.pop("end", None) + if end is not None: + if isinstance(end, unicode): + want_unicode = True + elif not isinstance(end, str): + raise TypeError("end must be None or a string") + if kwargs: + raise TypeError("invalid keyword arguments to print()") + if not want_unicode: + for arg in args: + if isinstance(arg, unicode): + want_unicode = True + break + if want_unicode: + newline = unicode("\n") + space = unicode(" ") + else: + newline = "\n" + space = " " + if sep is None: + sep = space + if end is None: + end = newline + for i, arg in enumerate(args): + if i: + write(sep) + write(arg) + write(end) +if sys.version_info[:2] < (3, 3): + _print = print_ + + def print_(*args, **kwargs): + fp = kwargs.get("file", sys.stdout) + flush = kwargs.pop("flush", False) + _print(*args, **kwargs) + if flush and fp is not None: + fp.flush() + +_add_doc(reraise, """Reraise an exception.""") + +if sys.version_info[0:2] < (3, 4): + # This does exactly the same what the :func:`py3:functools.update_wrapper` + # function does on Python versions after 3.2. It sets the ``__wrapped__`` + # attribute on ``wrapper`` object and it doesn't raise an error if any of + # the attributes mentioned in ``assigned`` and ``updated`` are missing on + # ``wrapped`` object. + def _update_wrapper(wrapper, wrapped, + assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + continue + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + wrapper.__wrapped__ = wrapped + return wrapper + _update_wrapper.__doc__ = functools.update_wrapper.__doc__ + + def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, + updated=functools.WRAPPER_UPDATES): + return functools.partial(_update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + wraps.__doc__ = functools.wraps.__doc__ + +else: + wraps = functools.wraps + + +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + if sys.version_info[:2] >= (3, 7): + # This version introduced PEP 560 that requires a bit + # of extra care (we mimic what is done by __build_class__). + resolved_bases = types.resolve_bases(bases) + if resolved_bases is not bases: + d['__orig_bases__'] = bases + else: + resolved_bases = bases + return meta(name, resolved_bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + + +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get('__slots__') + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop('__dict__', None) + orig_vars.pop('__weakref__', None) + if hasattr(cls, '__qualname__'): + orig_vars['__qualname__'] = cls.__qualname__ + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +def ensure_binary(s, encoding='utf-8', errors='strict'): + """Coerce **s** to six.binary_type. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> encoded to `bytes` + - `bytes` -> `bytes` + """ + if isinstance(s, binary_type): + return s + if isinstance(s, text_type): + return s.encode(encoding, errors) + raise TypeError("not expecting type '%s'" % type(s)) + + +def ensure_str(s, encoding='utf-8', errors='strict'): + """Coerce *s* to `str`. + + For Python 2: + - `unicode` -> encoded to `str` + - `str` -> `str` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + # Optimization: Fast return for the common case. + if type(s) is str: + return s + if PY2 and isinstance(s, text_type): + return s.encode(encoding, errors) + elif PY3 and isinstance(s, binary_type): + return s.decode(encoding, errors) + elif not isinstance(s, (text_type, binary_type)): + raise TypeError("not expecting type '%s'" % type(s)) + return s + + +def ensure_text(s, encoding='utf-8', errors='strict'): + """Coerce *s* to six.text_type. + + For Python 2: + - `unicode` -> `unicode` + - `str` -> `unicode` + + For Python 3: + - `str` -> `str` + - `bytes` -> decoded to `str` + """ + if isinstance(s, binary_type): + return s.decode(encoding, errors) + elif isinstance(s, text_type): + return s + else: + raise TypeError("not expecting type '%s'" % type(s)) + + +def python_2_unicode_compatible(klass): + """ + A class decorator that defines __unicode__ and __str__ methods under Python 2. + Under Python 3 it does nothing. + + To support Python 2 and 3 with a single code base, define a __str__ method + returning text and apply this decorator to the class. + """ + if PY2: + if '__str__' not in klass.__dict__: + raise ValueError("@python_2_unicode_compatible cannot be applied " + "to %s because it doesn't define __str__()." % + klass.__name__) + klass.__unicode__ = klass.__str__ + klass.__str__ = lambda self: self.__unicode__().encode('utf-8') + return klass + + +# Complete the moves implementation. +# This code is at the end of this module to speed up module loading. +# Turn this module into a package. +__path__ = [] # required for PEP 302 and PEP 451 +__package__ = __name__ # see PEP 366 @ReservedAssignment +if globals().get("__spec__") is not None: + __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable +# Remove other six meta path importers, since they cause problems. This can +# happen if six is removed from sys.modules and then reloaded. (Setuptools does +# this for some reason.) +if sys.meta_path: + for i, importer in enumerate(sys.meta_path): + # Here's some real nastiness: Another "instance" of the six module might + # be floating around. Therefore, we can't use isinstance() to check for + # the six meta path importer, since the other six instance will have + # inserted an importer with different class. + if (type(importer).__name__ == "_SixMetaPathImporter" and + importer.name == __name__): + del sys.meta_path[i] + break + del i, importer +# Finally, add the importer to the meta path import hook. +sys.meta_path.append(_importer) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD index 4948997ad..5c2f70a15 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD @@ -1,4 +1,4 @@ -../../Scripts/wheel.exe,sha256=EbtNdNgC3qU5rxKTCXwGwTxbmhvxEh20BojOpGir_Eo,108438 +../../Scripts/wheel.exe,sha256=JPGdDoSzEi6F9BWdUC6piDtUCwaDdIvNcEWSoDjYIu4,108438 wheel-0.38.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 wheel-0.38.4.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107 wheel-0.38.4.dist-info/METADATA,sha256=3j4KgVZCY7eZyOrwDKYoTuAcfr_gXAbxx1yGhR9DssA,2110 diff --git a/dependencies/windows_amd64/python/Scripts/espefuse.exe b/dependencies/windows_amd64/python/Scripts/espefuse.exe new file mode 100644 index 0000000000000000000000000000000000000000..877add0e0c75b4583b455d3ba2d9743d48d19503 GIT binary patch literal 108448 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 literal 0 HcmV?d00001 diff --git a/dependencies/windows_amd64/python/Scripts/espsecure.exe b/dependencies/windows_amd64/python/Scripts/espsecure.exe new file mode 100644 index 0000000000000000000000000000000000000000..228b40f5b01aaeb12b0b442a3f3163567123080a GIT binary patch literal 108449 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 GuK7PI`$y*h literal 0 HcmV?d00001 diff --git a/dependencies/windows_amd64/python/Scripts/pip.exe b/dependencies/windows_amd64/python/Scripts/pip.exe index ac217c204b54b16d0388e179ccf735826ed930c4..81a80f8950760a147efe63f6c98f635cb2394ba0 100644 GIT binary patch delta 31 jcmZ2{o^A1YwuUW?*~?hGXJ~~@&tJy)5yDDY&S(z+)j2wuUW?*~?gbW@v>?&tJy)0m4dN&S(z+)e{VZ delta 31 jcmZ22wuUW?*~?ghCQl5Vp1+Lo1B8{loY5Wt-pdV9 diff --git a/dependencies/windows_amd64/python/Scripts/pyserial-ports.exe b/dependencies/windows_amd64/python/Scripts/pyserial-ports.exe index 8e3795c7b7f672a42112bf224b4a7f50d605a93a..a80b1fccbbba3a0603bcb5a935c28f7288677639 100644 GIT binary patch delta 31 jcmZ2-o^8o_wuUW?*~?gbW@v>?&tJy)3BpQU&S(z+)u#-L delta 31 jcmZ2-o^8o_wuUW?*~?ghCQl5Vp1+Lo6NHtzoY5Wt-(L+` diff --git a/dependencies/windows_amd64/python/Scripts/wheel.exe b/dependencies/windows_amd64/python/Scripts/wheel.exe index ae9c34101c7135c1f807c11f08b3cd4b5dc5d15e..3ab4bc63c381ad146c868d228cfa479bf09fbf74 100644 GIT binary patch delta 31 jcmbPso^9HBwuUW?*~?fwW@v>?&tJy)1i}hm&S(z+(JKr~ delta 31 jcmbPso^9HBwuUW?*~?gbCr=EWp1+Lo34|5CoY5Wt+Q$tS From a931bf76df476d7e550017d6cb0ee4ed53369bbe Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Wed, 1 Feb 2023 10:46:24 +0200 Subject: [PATCH 14/54] Unified categories naming convention across binary & platformio based firmware sources --- devices/axis-2400.json | 4 +-- devices/axis-backpack.json | 2 +- devices/betafpv-2400.json | 20 ++++++------ devices/betafpv-900.json | 10 +++--- devices/diy-2400.json | 48 ++++++++++++++--------------- devices/diy-900.json | 16 +++++----- devices/diy-bluetooth-joystick.json | 2 +- devices/emax-2400.json | 4 +-- devices/emax-900.json | 4 +-- devices/foxeer-2400.json | 2 +- devices/frsky-900.json | 16 +++++----- devices/ghost-2400.json | 6 ++-- devices/happymodel-2400.json | 12 ++++---- devices/happymodel-900.json | 8 ++--- devices/hglrc-2400.json | 4 +-- devices/hglrc-900.json | 2 +- devices/iflight-2400.json | 4 +-- devices/iflight-900.json | 4 +-- devices/jumper-2400.json | 8 ++--- devices/jumper-900.json | 2 +- devices/matek-2400.json | 10 +++--- devices/namimnorc-2400.json | 10 +++--- devices/namimnorc-900.json | 8 ++--- devices/neutronrc-900.json | 2 +- devices/quadcopters-2400.json | 6 ++-- devices/radiomaster-2400.json | 14 ++++----- devices/siyi-2400.json | 6 ++-- devices/vantac-2400.json | 4 +-- 28 files changed, 119 insertions(+), 119 deletions(-) diff --git a/devices/axis-2400.json b/devices/axis-2400.json index e26d3a738..e213bb225 100644 --- a/devices/axis-2400.json +++ b/devices/axis-2400.json @@ -1,6 +1,6 @@ [ { - "category": "AXIS 2.4 GHz", + "category": "Axisflying", "name": "AXIS THOR 2400TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "AXIS 2.4 GHz", + "category": "Axisflying", "name": "AXIS THOR 2400RX", "targets": [ { diff --git a/devices/axis-backpack.json b/devices/axis-backpack.json index 6a64ee426..ab98d75d6 100644 --- a/devices/axis-backpack.json +++ b/devices/axis-backpack.json @@ -1,7 +1,7 @@ [ { "category": "TX", - "name": "AXIS THOR TX Backpack", + "name": "Axisflying THOR TX Backpack", "targets": [ { "flashingMethod": "UART", diff --git a/devices/betafpv-2400.json b/devices/betafpv-2400.json index 9c06a3609..597cfdd95 100644 --- a/devices/betafpv-2400.json +++ b/devices/betafpv-2400.json @@ -1,6 +1,6 @@ [ { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV 2400 TX Nano", "targets": [ { @@ -27,7 +27,7 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "HiYOUNGER 2.4 GHz", + "category": "HiYOUNGER", "name": "HiYOUNGER TX2G4 2400 TX", "wikiUrl": "", "abbreviatedName": "HiYOUNGER TX2G4", @@ -36,7 +36,7 @@ ] }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV 2400 TX Micro", "targets": [ { @@ -64,7 +64,7 @@ "aliases": [] }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV 2400 TX Micro 1000mW", "targets": [ { @@ -91,7 +91,7 @@ "aliases": [] }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV LiteRadio 3 Pro", "targets": [ { @@ -121,7 +121,7 @@ "aliases": [] }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV Nano 2400 RX", "targets": [ { @@ -164,21 +164,21 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "HiYOUNGER 2.4 GHz", + "category": "HiYOUNGER", "name": "HiYOUNGER RX24T 2400 RX", "wikiUrl": "", "abbreviatedName": "HiYOUNGER RX24T", "verifiedHardware": false }, { - "category": "JHEMCU 2.4 GHz", + "category": "JHEMCU", "name": "JHEMCU RX24T 2400 RX", "wikiUrl": "", "abbreviatedName": "JHEMCU RX24T", "verifiedHardware": false }, { - "category": "EMAX 2.4 GHz", + "category": "EMAX", "name": "EMAX 2.4GHz PA RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/diy2400/", "abbreviatedName": "EMAX 2400RX" @@ -186,7 +186,7 @@ ] }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV SuperD 2.4GHz RX", "targets": [ { diff --git a/devices/betafpv-900.json b/devices/betafpv-900.json index 2ff17cf18..f8e77c72a 100644 --- a/devices/betafpv-900.json +++ b/devices/betafpv-900.json @@ -1,6 +1,6 @@ [ { - "category": "BETAFPV 900 MHz", + "category": "BETAFPV", "name": "BETAFPV 900 TX", "targets": [ { @@ -29,7 +29,7 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "HiYOUNGER 900 MHz", + "category": "HiYOUNGER", "name": "HiYOUNGER 900 TX", "wikiUrl": "", "abbreviatedName": "HiYOUNGER 900 TX", @@ -38,7 +38,7 @@ ] }, { - "category": "BETAFPV 900 MHz", + "category": "BETAFPV", "name": "BETAFPV 900 TX Micro", "targets": [ { @@ -68,7 +68,7 @@ "aliases": [] }, { - "category": "BETAFPV 900 MHz", + "category": "BETAFPV", "name": "BETAFPV 900 RX", "targets": [ { @@ -101,7 +101,7 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "HiYOUNGER 900 MHz", + "category": "HiYOUNGER", "name": "HiYOUNGER 900 RX", "wikiUrl": "", "abbreviatedName": "HiYOUNGER 900 RX", diff --git a/devices/diy-2400.json b/devices/diy-2400.json index 77f21766e..95aff9e82 100644 --- a/devices/diy-2400.json +++ b/devices/diy-2400.json @@ -1,6 +1,6 @@ [ { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "Experimental 2.4GHz TX", "targets": [ { @@ -26,7 +26,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 RX PWM VARIO", "targets": [ { @@ -52,7 +52,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 TX ESP32 SX1280 Mini", "targets": [ { @@ -79,7 +79,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 TX ESP32 SX1280 E28", "targets": [ { @@ -106,7 +106,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 DUPLETX", "targets": [ { @@ -136,7 +136,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 TX ESP32 SX1280 LORA1280F27", "targets": [ { @@ -163,7 +163,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 RX ESP8285 SX1280", "targets": [ { @@ -194,79 +194,79 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "Flywoo 2.4 GHz", + "category": "Flywoo", "name": "Flywoo EL24E 2400 RX", "wikiUrl": "", "abbreviatedName": "Flywoo EL24E", "verifiedHardware": false }, { - "category": "Flywoo 2.4 GHz", + "category": "Flywoo", "name": "Flywoo EL24P 2400 RX", "wikiUrl": "", "abbreviatedName": "Flywoo EL24P", "verifiedHardware": false }, { - "category": "HiYOUNGER 2.4 GHz", + "category": "HiYOUNGER", "name": "HiYOUNGER SP24S 2400 RX", "wikiUrl": "", "abbreviatedName": "HiYOUNGER SP24S", "verifiedHardware": false }, { - "category": "HiYOUNGER 2.4 GHz", + "category": "HiYOUNGER", "name": "HiYOUNGER EP24S 2400 RX", "wikiUrl": "", "abbreviatedName": "HiYOUNGER EP24S", "verifiedHardware": false }, { - "category": "JHEMCU 2.4 GHz", + "category": "JHEMCU", "name": "JHEMCU SP24S 2400 RX", "wikiUrl": "", "abbreviatedName": "JHEMCU SP24S", "verifiedHardware": false }, { - "category": "JHEMCU 2.4 GHz", + "category": "JHEMCU", "name": "JHEMCU EP24S 2400 RX", "wikiUrl": "", "abbreviatedName": "JHEMCU EP24S", "verifiedHardware": false }, { - "category": "Jumper 2.4 GHz", + "category": "Jumper", "name": "Jumper AION Mini 2400 RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/jumper-aion/", "abbreviatedName": "AION RX24 MINI" }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV Lite 2400 RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/betafpv2400/", "abbreviatedName": "BFPV Lite 2G4RX" }, { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV AIO 2400 RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/betafpv2400/", "abbreviatedName": "BFPV AIO 2G4RX" }, { - "category": "GEPRC 2.4 GHz", + "category": "GEPRC", "name": "GEPRC Nano 2400 RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/geprc2400/", "abbreviatedName": "GEPRC Nano 2G4RX" }, { - "category": "EMAX 2.4 GHz", + "category": "EMAX", "name": "EMAX 2.4GHz RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/diy2400/", "abbreviatedName": "EMAX 2400RX" }, { - "category": "Foxeer 2.4 GHz", + "category": "Foxeer", "name": "Foxeer Lite 2.4 GHz", "wikiUrl": "", "abbreviatedName": "Foxeer Lite 2.4" @@ -274,7 +274,7 @@ ] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 RX PWMP", "targets": [ { @@ -299,13 +299,13 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "BETAFPV 2.4 GHz", + "category": "BETAFPV", "name": "BETAFPV PWM 2400 RX", "wikiUrl": "", "abbreviatedName": "BFPV PWM 2G4RX" }, { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster ER5A/C 2.4GHz PWM RX", "wikiUrl": "", "abbreviatedName": "RM ER5A/C 2G4RX" @@ -313,7 +313,7 @@ ] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 RX PWMP EX", "targets": [ { @@ -339,7 +339,7 @@ "aliases": [] }, { - "category": "DIY 2.4 GHz", + "category": "DIY devices", "name": "DIY 2400 RX STM32 CCG Nano v0 5", "targets": [ { diff --git a/devices/diy-900.json b/devices/diy-900.json index 0605d7702..12ccf134f 100644 --- a/devices/diy-900.json +++ b/devices/diy-900.json @@ -1,6 +1,6 @@ [ { - "category": "DIY 900 MHz", + "category": "DIY devices", "name": "DIY 900 TX TTGO V1 SX127x", "targets": [ { @@ -29,7 +29,7 @@ "aliases": [] }, { - "category": "DIY 900 MHz", + "category": "DIY devices", "name": "DIY 900 TX TTGO V2 SX127x", "targets": [ { @@ -58,7 +58,7 @@ "aliases": [] }, { - "category": "DIY 900 MHz", + "category": "DIY devices", "name": "DIY 900 TX ESP32 SX127x E19", "targets": [ { @@ -88,7 +88,7 @@ "aliases": [] }, { - "category": "DIY 900 MHz", + "category": "DIY devices", "name": "DIY 900 TX ESP32 SX127x RFM95", "targets": [ { @@ -117,7 +117,7 @@ "aliases": [] }, { - "category": "DIY 900 MHz", + "category": "DIY devices", "name": "DIY 900 RX ESP8285 SX127x", "targets": [ { @@ -150,13 +150,13 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "GEPRC 900 MHz", + "category": "GEPRC", "name": "GEPRC Nano 900 RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/geprc900/", "abbreviatedName": "GEPRC Nano 900RX" }, { - "category": "EMAX 900 MHz", + "category": "EMAX", "name": "EMAX 900MHz RX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/receivers/diy900/", "abbreviatedName": "EMAX 900RX" @@ -164,7 +164,7 @@ ] }, { - "category": "DIY 900 MHz", + "category": "DIY devices", "name": "DIY 900 RX PWMP", "targets": [ { diff --git a/devices/diy-bluetooth-joystick.json b/devices/diy-bluetooth-joystick.json index 58103f962..dfe589723 100644 --- a/devices/diy-bluetooth-joystick.json +++ b/devices/diy-bluetooth-joystick.json @@ -1,6 +1,6 @@ [ { - "category": "DIY Bluetooth Joystick", + "category": "DIY devices", "name": "DIY Bluetooth Joystick", "targets": [ { diff --git a/devices/emax-2400.json b/devices/emax-2400.json index 43a418f47..a2a4ed4de 100644 --- a/devices/emax-2400.json +++ b/devices/emax-2400.json @@ -1,6 +1,6 @@ [ { - "category": "EMAX 2.4 GHz", + "category": "EMAX", "name": "EMAX OLED 2.4GHz TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "EMAX 2.4 GHz", + "category": "EMAX", "name": "EMAX Nano 2.4GHz TX", "targets": [ { diff --git a/devices/emax-900.json b/devices/emax-900.json index 0eb125744..89c092ef9 100644 --- a/devices/emax-900.json +++ b/devices/emax-900.json @@ -1,6 +1,6 @@ [ { - "category": "EMAX 900 MHz", + "category": "EMAX", "name": "EMAX OLED 900MHz TX", "targets": [ { @@ -29,7 +29,7 @@ "aliases": [] }, { - "category": "EMAX 900 MHz", + "category": "EMAX", "name": "EMAX Nano 900MHz TX", "targets": [ { diff --git a/devices/foxeer-2400.json b/devices/foxeer-2400.json index 940bfb5f4..df61a5d8b 100644 --- a/devices/foxeer-2400.json +++ b/devices/foxeer-2400.json @@ -1,6 +1,6 @@ [ { - "category": "Foxeer 2.4 GHz", + "category": "Foxeer", "name": "Foxeer 2.4GHz RX", "targets": [ { diff --git a/devices/frsky-900.json b/devices/frsky-900.json index 794177f71..297811335 100644 --- a/devices/frsky-900.json +++ b/devices/frsky-900.json @@ -1,6 +1,6 @@ [ { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky TX R9M", "targets": [ { @@ -34,7 +34,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky TX R9M LITE", "targets": [ { @@ -62,7 +62,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky TX R9M LITE PRO", "targets": [ { @@ -87,7 +87,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky RX R9MM R9MINI", "targets": [ { @@ -114,7 +114,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky RX R9SLIMPLUS", "targets": [ { @@ -140,7 +140,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky RX R9SLIM", "targets": [ { @@ -166,7 +166,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky RX R9SLIMPLUS OTA", "targets": [ { @@ -192,7 +192,7 @@ "aliases": [] }, { - "category": "Frsky R9", + "category": "Frsky", "name": "Frsky RX R9MX", "targets": [ { diff --git a/devices/ghost-2400.json b/devices/ghost-2400.json index 9c4fc4b2f..8fedc8931 100644 --- a/devices/ghost-2400.json +++ b/devices/ghost-2400.json @@ -1,6 +1,6 @@ [ { - "category": "ImmersionRC Ghost", + "category": "ImmersionRC", "name": "GHOST 2400 TX", "targets": [ { @@ -23,7 +23,7 @@ "aliases": [] }, { - "category": "ImmersionRC Ghost", + "category": "ImmersionRC", "name": "GHOST 2400 TX LITE", "targets": [ { @@ -46,7 +46,7 @@ "aliases": [] }, { - "category": "ImmersionRC Ghost", + "category": "ImmersionRC", "name": "GHOST ATTO 2400 RX", "targets": [ { diff --git a/devices/happymodel-2400.json b/devices/happymodel-2400.json index 201754ce7..941c8874a 100644 --- a/devices/happymodel-2400.json +++ b/devices/happymodel-2400.json @@ -1,6 +1,6 @@ [ { - "category": "Happymodel 2.4 GHz", + "category": "Happymodel", "name": "HappyModel ES24TX 2400 TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "Happymodel 2.4 GHz", + "category": "Happymodel", "name": "HM ES24TX Pro Series 2400 TX", "targets": [ { @@ -54,7 +54,7 @@ "aliases": [] }, { - "category": "Happymodel 2.4 GHz", + "category": "Happymodel", "name": "HappyModel EP 2400 RX", "targets": [ { @@ -85,7 +85,7 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster RP1/2 2400 RX", "wikiUrl": "", "abbreviatedName": "RM RP 2400RX" @@ -93,7 +93,7 @@ ] }, { - "category": "Happymodel 2.4 GHz", + "category": "Happymodel", "name": "HappyModel EP1 Dual 2400 RX", "targets": [ { @@ -125,7 +125,7 @@ "aliases": [] }, { - "category": "Happymodel 2.4 GHz", + "category": "Happymodel", "name": "HappyModel PP 2400 RX", "targets": [ { diff --git a/devices/happymodel-900.json b/devices/happymodel-900.json index 48076b182..42f946d2e 100644 --- a/devices/happymodel-900.json +++ b/devices/happymodel-900.json @@ -1,6 +1,6 @@ [ { - "category": "Happymodel 900 MHz", + "category": "Happymodel", "name": "HappyModel TX ES900TX", "targets": [ { @@ -30,7 +30,7 @@ "aliases": [] }, { - "category": "Happymodel 900 MHz", + "category": "Happymodel", "name": "HappyModel RX ES900RX", "targets": [ { @@ -64,7 +64,7 @@ "aliases": [] }, { - "category": "Happymodel 900 MHz", + "category": "Happymodel", "name": "HappyModel TX ES915TX", "targets": [ { @@ -101,7 +101,7 @@ "aliases": [] }, { - "category": "Happymodel 900 MHz", + "category": "Happymodel", "name": "HappyModel RX ES915RX", "targets": [ { diff --git a/devices/hglrc-2400.json b/devices/hglrc-2400.json index 47dc4fbd1..3810afad6 100644 --- a/devices/hglrc-2400.json +++ b/devices/hglrc-2400.json @@ -1,6 +1,6 @@ [ { - "category": "HGLRC 2.4 GHz", + "category": "HGLRC", "name": "HGLRC Hermes 2400 TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "HGLRC 2.4 GHz", + "category": "HGLRC", "name": "HGLRC Hermes 2400 RX", "targets": [ { diff --git a/devices/hglrc-900.json b/devices/hglrc-900.json index 90f636c4d..5489dc031 100644 --- a/devices/hglrc-900.json +++ b/devices/hglrc-900.json @@ -1,6 +1,6 @@ [ { - "category": "HGLRC 900 MHz", + "category": "HGLRC", "name": "HGLRC Hermes 900 RX", "targets": [ { diff --git a/devices/iflight-2400.json b/devices/iflight-2400.json index 2f0baed74..1fba030c2 100644 --- a/devices/iflight-2400.json +++ b/devices/iflight-2400.json @@ -1,6 +1,6 @@ [ { - "category": "iFlight 2.4 GHz", + "category": "iFlight", "name": "iFlight 2400TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "iFlight 2.4 GHz", + "category": "iFlight", "name": "iFlight 2400RX", "targets": [ { diff --git a/devices/iflight-900.json b/devices/iflight-900.json index b47f32ae8..cd3cf94fb 100644 --- a/devices/iflight-900.json +++ b/devices/iflight-900.json @@ -1,6 +1,6 @@ [ { - "category": "iFlight 900 MHz", + "category": "iFlight", "name": "iFlight 900TX", "targets": [ { @@ -29,7 +29,7 @@ "aliases": [] }, { - "category": "iFlight 900 MHz", + "category": "iFlight", "name": "iFlight 900RX", "targets": [ { diff --git a/devices/jumper-2400.json b/devices/jumper-2400.json index 236811e29..ed0aa75bf 100644 --- a/devices/jumper-2400.json +++ b/devices/jumper-2400.json @@ -1,6 +1,6 @@ [ { - "category": "Jumper 2.4 GHz", + "category": "Jumper", "name": "Jumper AION Nano 2400 TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "Jumper 2.4 GHz", + "category": "Jumper", "name": "Jumper AION T-Pro 2400 TX", "targets": [ { @@ -57,7 +57,7 @@ "aliases": [] }, { - "category": "Jumper 2.4 GHz", + "category": "Jumper", "name": "Jumper AION T-Lite 2400 TX", "targets": [ { @@ -87,7 +87,7 @@ "aliases": [] }, { - "category": "Jumper 2.4 GHz", + "category": "Jumper", "name": "Jumper AION Nano 2400 RX", "targets": [ { diff --git a/devices/jumper-900.json b/devices/jumper-900.json index 83ddd2d61..34ba4fe5e 100644 --- a/devices/jumper-900.json +++ b/devices/jumper-900.json @@ -1,6 +1,6 @@ [ { - "category": "Jumper R900", + "category": "Jumper", "name": "Jumper RX R900MINI", "targets": [ { diff --git a/devices/matek-2400.json b/devices/matek-2400.json index a59a05673..d2f47a68a 100644 --- a/devices/matek-2400.json +++ b/devices/matek-2400.json @@ -1,6 +1,6 @@ [ { - "category": "Matek 2.4 GHz", + "category": "Matek", "name": "Matek 2400 RX", "targets": [ { @@ -32,7 +32,7 @@ "aliases": [] }, { - "category": "Matek 2.4 GHz", + "category": "Matek", "name": "Matek 2400 RX PWM", "targets": [ { @@ -58,7 +58,7 @@ "aliases": [] }, { - "category": "Matek 2.4 GHz", + "category": "Matek", "name": "Matek 2400 RX R24D", "targets": [ { @@ -89,13 +89,13 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster RP3 Diversity 2400 RX", "wikiUrl": "", "abbreviatedName": "RM RP3 2400RX" }, { - "category": "Skystars 2.4 GHz", + "category": "Skystars", "name": "Skystars SS24D 2400 RX", "wikiUrl": "", "abbreviatedName": "Skystars SS24D", diff --git a/devices/namimnorc-2400.json b/devices/namimnorc-2400.json index c52f7ce93..b76627d91 100644 --- a/devices/namimnorc-2400.json +++ b/devices/namimnorc-2400.json @@ -1,6 +1,6 @@ [ { - "category": "NamimnoRC 2.4 GHz", + "category": "NamimnoRC", "name": "NamimnoRC FLASH 2400 TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "NamimnoRC 2.4 GHz", + "category": "NamimnoRC", "name": "NamimnoRC FLASH 2400 OLED TX", "targets": [ { @@ -54,7 +54,7 @@ "aliases": [] }, { - "category": "NamimnoRC 2.4 GHz", + "category": "NamimnoRC", "name": "NamimnoRC FLASH 2400 RX", "targets": [ { @@ -78,7 +78,7 @@ "aliases": [] }, { - "category": "NamimnoRC 2.4 GHz", + "category": "NamimnoRC", "name": "NamimnoRC FLASH 2400 ESP RX", "targets": [ { @@ -110,7 +110,7 @@ "aliases": [] }, { - "category": "NamimnoRC 2.4 GHz", + "category": "NamimnoRC", "name": "NamimnoRC FLASH 2400 ESP Diversity RX", "targets": [ { diff --git a/devices/namimnorc-900.json b/devices/namimnorc-900.json index c49e4e887..de830075b 100644 --- a/devices/namimnorc-900.json +++ b/devices/namimnorc-900.json @@ -1,6 +1,6 @@ [ { - "category": "NamimnoRC VOYAGER 900 MHz", + "category": "NamimnoRC", "name": "NamimnoRC VOYAGER 900 TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "NamimnoRC VOYAGER 900 MHz", + "category": "NamimnoRC", "name": "NamimnoRC VOYAGER 900 OLED TX", "targets": [ { @@ -54,7 +54,7 @@ "aliases": [] }, { - "category": "NamimnoRC VOYAGER 900 MHz", + "category": "NamimnoRC", "name": "NamimnoRC VOYAGER 900 RX", "targets": [ { @@ -80,7 +80,7 @@ "aliases": [] }, { - "category": "NamimnoRC VOYAGER 900 MHz", + "category": "NamimnoRC", "name": "NamimnoRC VOYAGER 900 ESP RX", "targets": [ { diff --git a/devices/neutronrc-900.json b/devices/neutronrc-900.json index 313cfe7a1..9ff6113f9 100644 --- a/devices/neutronrc-900.json +++ b/devices/neutronrc-900.json @@ -1,6 +1,6 @@ [ { - "category": "NeutronRC 900 MHz", + "category": "NeutronRC", "name": "NeutronRC 900 RX", "targets": [ { diff --git a/devices/quadcopters-2400.json b/devices/quadcopters-2400.json index 3a33d3ec4..1bccc6734 100644 --- a/devices/quadcopters-2400.json +++ b/devices/quadcopters-2400.json @@ -1,6 +1,6 @@ [ { - "category": "QuadKopters 2.4 Ghz", + "category": "QuadKopters", "name": "QuadKopters JR 2400 TX", "targets": [ { @@ -26,7 +26,7 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "QuadKopters 2.4 Ghz", + "category": "QuadKopters", "name": "QuadKopters Slim 2400 TX", "wikiUrl": "", "abbreviatedName": "QuadKopters 2G4" @@ -34,7 +34,7 @@ ] }, { - "category": "QuadKopters 2.4 Ghz", + "category": "QuadKopters", "name": "QuadKopters NANO RX", "targets": [ { diff --git a/devices/radiomaster-2400.json b/devices/radiomaster-2400.json index 5e2db59cc..39203834e 100644 --- a/devices/radiomaster-2400.json +++ b/devices/radiomaster-2400.json @@ -1,6 +1,6 @@ [ { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster TX16S 2400 TX", "targets": [ { @@ -30,7 +30,7 @@ "aliases": [] }, { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster Zorro 2400 TX", "targets": [ { @@ -59,7 +59,7 @@ "deviceType": "ExpressLRS", "aliases": [ { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster TX12 2400 TX", "wikiUrl": "https://www.expresslrs.org/{version}/quick-start/transmitters/rm-internal/", "abbreviatedName": "RM TX12" @@ -67,7 +67,7 @@ ] }, { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster Ranger 2400 TX", "targets": [ { @@ -93,7 +93,7 @@ "deviceType": "ExpressLRS" }, { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster Ranger Micro 2400 TX", "targets": [ { @@ -120,7 +120,7 @@ "aliases": [] }, { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster Ranger Nano 2400 TX", "targets": [ { @@ -147,7 +147,7 @@ "aliases": [] }, { - "category": "RadioMaster 2.4 GHz", + "category": "RadioMaster", "name": "RadioMaster Boxer 2400 TX", "targets": [ { diff --git a/devices/siyi-2400.json b/devices/siyi-2400.json index 4ce84d80d..e3755603b 100644 --- a/devices/siyi-2400.json +++ b/devices/siyi-2400.json @@ -1,6 +1,6 @@ [ { - "category": "SIYI 2.4 GHz", + "category": "SIYI", "name": "FM30 TX", "targets": [ { @@ -24,7 +24,7 @@ "aliases": [] }, { - "category": "SIYI 2.4 GHz", + "category": "SIYI", "name": "FM30 RX MINI", "targets": [ { @@ -48,7 +48,7 @@ "aliases": [] }, { - "category": "SIYI 2.4 GHz", + "category": "SIYI", "name": "FM30 RX MINI AS TX", "targets": [ { diff --git a/devices/vantac-2400.json b/devices/vantac-2400.json index db9776ef1..c46803276 100644 --- a/devices/vantac-2400.json +++ b/devices/vantac-2400.json @@ -1,6 +1,6 @@ [ { - "category": "Vantac 2.4 GHz", + "category": "Vantac", "name": "Vantac Lite 2400 TX", "targets": [ { @@ -27,7 +27,7 @@ "aliases": [] }, { - "category": "Vantac 2.4 GHz", + "category": "Vantac", "name": "Vantac 2400 RX", "targets": [ { From b7d15138c091f29b2bad870e5c6a6563f1f3fa3d Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Wed, 1 Feb 2023 11:05:16 +0200 Subject: [PATCH 15/54] Clear all repositories on clearFirmwareFiles() mutation. Fixes #431. Specify --dir to binary_configurator.py --- .../src/library/FirmwareDownloader/index.ts | 18 ++++++++++++++++++ .../BinaryConfigurator/index.ts | 4 +++- .../DeviceDescriptionsLoader/index.ts | 10 ++++++++++ .../services/BinaryFlashingStrategy/index.ts | 12 +++++++++++- .../PlatformioFlashingStrategy/index.ts | 5 +++++ .../src/services/TargetsLoader/GitTargets.ts | 10 ++++++++++ src/api/src/services/TargetsLoader/index.ts | 2 ++ src/ui/config/index.ts | 18 ++++++++++++++---- 8 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/api/src/library/FirmwareDownloader/index.ts b/src/api/src/library/FirmwareDownloader/index.ts index 7c81b5ab8..6554db69c 100644 --- a/src/api/src/library/FirmwareDownloader/index.ts +++ b/src/api/src/library/FirmwareDownloader/index.ts @@ -165,6 +165,12 @@ export class GitFirmwareDownloader implements IFirmwareDownloader { const directory = this.getRepoDirectory(repository); await this.syncRepo(repository, srcFolder); const git = this.getSimpleGit(directory); + this.logger.log('check out tag', { + repository, + directory, + srcFolder, + tagName, + }); await git.checkout(tagName); return { path: this.getSourceDirectory(directory, srcFolder), @@ -179,6 +185,12 @@ export class GitFirmwareDownloader implements IFirmwareDownloader { const directory = this.getRepoDirectory(repository); await this.syncRepo(repository, srcFolder); const git = this.getSimpleGit(directory); + this.logger.log('check out branch', { + repository, + directory, + srcFolder, + branch, + }); await git.checkout(`origin/${branch}`); return { path: this.getSourceDirectory(directory, srcFolder), @@ -193,6 +205,12 @@ export class GitFirmwareDownloader implements IFirmwareDownloader { const directory = this.getRepoDirectory(repository); await this.syncRepo(repository, srcFolder); const git = this.getSimpleGit(directory); + this.logger.log('check out commit', { + repository, + directory, + srcFolder, + commit, + }); await git.checkout(commit); return { path: this.getSourceDirectory(directory, srcFolder), diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index 9990074cc..ba4b804bb 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -149,16 +149,18 @@ export default class BinaryConfigurator { async run( firmwareSourcePath: string, + hardwareDefinitionsPath: string, firmwareBinaryPath: string, flags: string[][], onUpdate: OnOutputFunc = NoOpFunc ) { this.logger.log('flags', { flags: maskSensitiveFlags(flags), + hardwareDefinitionsPath, firmwareBinaryPath, }); const binaryConfiguratorArgs = [ - ...this.stringifyFlags(flags), + ...this.stringifyFlags([...flags, ['--dir', hardwareDefinitionsPath]]), firmwareBinaryPath, ]; return this.python.runPythonScript( diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts index 5a7df4117..ae81afa8a 100644 --- a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts @@ -18,6 +18,7 @@ import UserDefine from '../../../models/UserDefine'; import TargetUserDefinesFactory from '../../../factories/TargetUserDefinesFactory'; import UserDefineKey from '../../../library/FirmwareBuilder/Enum/UserDefineKey'; import PullRequest from '../../../models/PullRequest'; +import { removeDirectoryContents } from '../../FlashingStrategyLocator/artefacts'; export interface GitRepository { url: string; @@ -317,4 +318,13 @@ export default class DeviceDescriptionsLoader { } return userDefines; } + + async clearCache() { + await this.mutex.tryLockWithTimeout(60000); + try { + return await removeDirectoryContents(this.targetStoragePath); + } finally { + this.mutex.unlock(); + } + } } diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index c3475f576..64defff96 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -125,7 +125,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { params.source === FirmwareSource.GitTag && semver.compare(params.gitTag, '3.0.0') >= 0) || (params.source === FirmwareSource.GitBranch && - params.gitBranch === 'master') + params.gitBranch === 'master') || + (params.source === FirmwareSource.GitBranch && + params.gitBranch === 'flasher') ) { return true; } @@ -392,12 +394,15 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildFirmwareStep.BUILDING_FIRMWARE ); + const hardwareDefinitionsPath = firmwareSourcePath; + // TODO: pip3 install pyserial esptool // python-serial const flags: string[][] = this.binaryConfigurator.buildBinaryConfigFlags(params); const binaryConfiguratorResult = await this.binaryConfigurator.run( firmwareSourcePath, + hardwareDefinitionsPath, firmwareBinaryPath, flags, (output) => { @@ -456,6 +461,11 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } async clearFirmwareFiles(): Promise { + await this.deviceDescriptionsLoader.clearCache(); + + this.logger.log('BinaryConfigurator - clearFirmwareFiles', { + firmwaresPath: this.firmwaresPath, + }); return removeDirectoryContents(this.firmwaresPath); } } diff --git a/src/api/src/services/PlatformioFlashingStrategy/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts index b9cfd8aae..b019fbece 100644 --- a/src/api/src/services/PlatformioFlashingStrategy/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -431,6 +431,11 @@ export default class PlatformioFlashingStrategyService } async clearFirmwareFiles(): Promise { + await this.targetsLoader.clearCache(); + + this.logger.log('PlatformioConfigurator - clearFirmwareFiles', { + firmwaresPath: this.firmwaresPath, + }); return removeDirectoryContents(this.firmwaresPath); } } diff --git a/src/api/src/services/TargetsLoader/GitTargets.ts b/src/api/src/services/TargetsLoader/GitTargets.ts index 02d8554f4..6e704b185 100644 --- a/src/api/src/services/TargetsLoader/GitTargets.ts +++ b/src/api/src/services/TargetsLoader/GitTargets.ts @@ -12,6 +12,7 @@ import { GitFirmwareDownloader, } from '../../library/FirmwareDownloader'; import Mutex from '../../library/Mutex'; +import { removeDirectoryContents } from '../FlashingStrategyLocator/artefacts'; @Service() export default class GitTargetsService extends TargetsLoader { @@ -119,4 +120,13 @@ export default class GitTargetsService extends TargetsLoader { this.mutex.unlock(); } } + + async clearCache() { + await this.mutex.tryLockWithTimeout(60000); + try { + return await removeDirectoryContents(this.targetStoragePath); + } finally { + this.mutex.unlock(); + } + } } diff --git a/src/api/src/services/TargetsLoader/index.ts b/src/api/src/services/TargetsLoader/index.ts index d3e1e439c..0fbcf85fa 100644 --- a/src/api/src/services/TargetsLoader/index.ts +++ b/src/api/src/services/TargetsLoader/index.ts @@ -13,4 +13,6 @@ export default abstract class TargetsLoader { args: TargetArgs, gitRepository: GitRepository ): Promise; + + abstract clearCache(): Promise; } diff --git a/src/ui/config/index.ts b/src/ui/config/index.ts index 50c5c4593..b0e36c72c 100644 --- a/src/ui/config/index.ts +++ b/src/ui/config/index.ts @@ -16,12 +16,22 @@ export const Config: IConfig = { facebookGroupUrl: 'https://www.facebook.com/groups/636441730280366', discordUrl: 'https://discord.gg/dS6ReFY', openCollectiveUrl: 'https://opencollective.com/expresslrs', + // expressLRSGit: { + // cloneUrl: 'https://github.com/ExpressLRS/ExpressLRS', + // url: 'https://github.com/ExpressLRS/ExpressLRS', + // owner: 'ExpressLRS', + // repositoryName: 'ExpressLRS', + // rawRepoUrl: 'https://raw.githubusercontent.com/ExpressLRS/ExpressLRS', + // srcFolder: 'src', + // tagExcludes: ['<2.5.0'], + // }, expressLRSGit: { - cloneUrl: 'https://github.com/ExpressLRS/ExpressLRS', - url: 'https://github.com/ExpressLRS/ExpressLRS', - owner: 'ExpressLRS', + // cloneUrl: 'https://github.com/ExpressLRS/ExpressLRS', + cloneUrl: 'https://github.com/pkendall64/ExpressLRS', + url: 'https://github.com/pkendall64/ExpressLRS', + owner: 'pkendall64', repositoryName: 'ExpressLRS', - rawRepoUrl: 'https://raw.githubusercontent.com/ExpressLRS/ExpressLRS', + rawRepoUrl: 'https://raw.githubusercontent.com/pkendall64/ExpressLRS', srcFolder: 'src', tagExcludes: ['<2.5.0'], }, From b056a189ecc3abc044c4680a9260f1f84a8df803 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 4 Feb 2023 18:51:41 +0200 Subject: [PATCH 16/54] Integrate cloudflare r2 artifactory --- src/api/index.ts | 6 ++ src/api/src/config/index.ts | 2 + .../graphql/resolvers/Firmware.resolver.ts | 15 +-- .../src/library/FirmwareDownloader/index.ts | 8 ++ .../CloudBinariesCache/index.ts | 45 +++++++- .../services/BinaryFlashingStrategy/index.ts | 101 +++++++++++++++--- .../FlashingStrategy.ts | 6 +- .../services/FlashingStrategyLocator/index.ts | 10 +- .../PlatformioFlashingStrategy/index.ts | 9 +- src/main.dev.ts | 8 ++ src/ui/config/index.ts | 18 +--- 11 files changed, 169 insertions(+), 59 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index c0e691686..6917716e4 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -42,6 +42,7 @@ import Python from './src/library/Python'; import BinaryFlashingStrategyService from './src/services/BinaryFlashingStrategy'; import DeviceDescriptionsLoader from './src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader'; import BinaryConfigurator from './src/services/BinaryFlashingStrategy/BinaryConfigurator'; +import CloudBinariesCache from './src/services/BinaryFlashingStrategy/CloudBinariesCache'; export default class ApiServer { app: Express | undefined; @@ -142,6 +143,10 @@ export default class ApiServer { config.PATH, path.join(config.userDataPath, 'firmwares', 'binary-targets') ); + const cloudBinariesCache = new CloudBinariesCache( + config.cloudCacheServer, + config.firmwareCloudCachePath + ); const binaryConfigurator = new BinaryConfigurator(python, logger); const binaryFlashingStrategyService = new BinaryFlashingStrategyService( config.PATH, @@ -151,6 +156,7 @@ export default class ApiServer { platformio, firmwareBuilder, deviceDescriptionsLoader, + cloudBinariesCache, logger ); diff --git a/src/api/src/config/index.ts b/src/api/src/config/index.ts index de9528803..463d3a781 100644 --- a/src/api/src/config/index.ts +++ b/src/api/src/config/index.ts @@ -9,6 +9,8 @@ export interface IConfig { multicastDnsSimulatorEnabled: boolean; devicesPath: string; firmwaresPath: string; + cloudCacheServer: string; + firmwareCloudCachePath: string; PATH: string; env: NodeJS.ProcessEnv; getPlatformioPath: string; diff --git a/src/api/src/graphql/resolvers/Firmware.resolver.ts b/src/api/src/graphql/resolvers/Firmware.resolver.ts index d2d0c3bce..be3f64ccc 100644 --- a/src/api/src/graphql/resolvers/Firmware.resolver.ts +++ b/src/api/src/graphql/resolvers/Firmware.resolver.ts @@ -43,8 +43,7 @@ export default class FirmwareResolver { ): Promise { const strategy = await this.flashingStrategyLocatorService.locate( args, - gitRepository.url, - gitRepository.srcFolder + gitRepository ); return strategy.availableFirmwareTargets(args, gitRepository); } @@ -56,8 +55,7 @@ export default class FirmwareResolver { ): Promise { const strategy = await this.flashingStrategyLocatorService.locate( args, - gitRepository.url, - gitRepository.srcFolder + gitRepository ); return strategy.targetDeviceOptions(args, gitRepository); } @@ -69,14 +67,9 @@ export default class FirmwareResolver { ): Promise { const strategy = await this.flashingStrategyLocatorService.locate( input.firmware, - gitRepository.url, - gitRepository.srcFolder - ); - return strategy.buildFlashFirmware( - input, - gitRepository.url, - gitRepository.srcFolder + gitRepository ); + return strategy.buildFlashFirmware(input, gitRepository); } @Mutation(() => BuildUserDefinesTxtResult) diff --git a/src/api/src/library/FirmwareDownloader/index.ts b/src/api/src/library/FirmwareDownloader/index.ts index 6554db69c..1395e6392 100644 --- a/src/api/src/library/FirmwareDownloader/index.ts +++ b/src/api/src/library/FirmwareDownloader/index.ts @@ -33,6 +33,8 @@ export interface IFirmwareDownloader { srcFolder: string, commit: string ): Promise; + + currentCommitHash(repository: string): Promise; } export const findGitExecutable = async (envPath: string): Promise => { @@ -157,6 +159,12 @@ export class GitFirmwareDownloader implements IFirmwareDownloader { } } + async currentCommitHash(repository: string): Promise { + const directory = this.getRepoDirectory(repository); + const git = this.getSimpleGit(directory); + return git.revparse('HEAD'); + } + async checkoutTag( repository: string, srcFolder: string, diff --git a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts index 237c85c52..e8eef1cea 100644 --- a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts @@ -1 +1,44 @@ -export default class CloudBinariesCache {} +import fetch from 'node-fetch'; +import { writeFile } from 'fs/promises'; +import extractZip from 'extract-zip'; +import path from 'path'; +import mkdirp from 'mkdirp'; +import fs from 'fs'; + +export default class CloudBinariesCache { + constructor(private baseURL: string, private firmwareCachePath: string) {} + + async download(repositoryName: string, commitHash: string): Promise { + const workingDir = path.join( + this.firmwareCachePath, + repositoryName, + commitHash + ); + await mkdirp(workingDir); + const firmwareCacheDir = path.join(workingDir, 'firmware'); + + // if we have firmware in our local cache already no need to download it the second time. + if (fs.existsSync(firmwareCacheDir)) { + return firmwareCacheDir; + } + + const cacheUrl = `${this.baseURL}/${repositoryName}/${commitHash}/firmware.zip`; + const response = await fetch(cacheUrl); + console.log(response); + if (response.status !== 200) { + throw new Error( + `cached build for ${repositoryName}/${commitHash} is not available` + ); + } + + const outputZipFile = path.join(workingDir, 'firmware.zip'); + const buffer = await response.buffer(); + await writeFile(outputZipFile, buffer); + + await extractZip(outputZipFile, { + dir: workingDir, + }); + + return firmwareCacheDir; + } +} diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 64defff96..ae4bdb15e 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -3,6 +3,7 @@ import { Service } from 'typedi'; import { PubSubEngine } from 'graphql-subscriptions'; import * as os from 'os'; import semver from 'semver'; +import path from 'path'; import UserDefine from '../../models/UserDefine'; import FirmwareSource from '../../models/enum/FirmwareSource'; import Mutex from '../../library/Mutex'; @@ -14,8 +15,8 @@ import { LoggerService } from '../../logger'; import UserDefineKey from '../../library/FirmwareBuilder/Enum/UserDefineKey'; import { BuildFlashFirmwareParams } from '../FlashingStrategyLocator/BuildFlashFirmwareParams'; import { - removeDirectoryContents, createBinaryCopyWithCanonicalName, + removeDirectoryContents, } from '../FlashingStrategyLocator/artefacts'; import { FlashingStrategy, @@ -42,6 +43,7 @@ import { maskBuildFlashFirmwareParams, maskSensitiveData, } from '../FlashingStrategyLocator/masks'; +import CloudBinariesCache from './CloudBinariesCache'; @Service() export default class BinaryFlashingStrategyService implements FlashingStrategy { @@ -57,6 +59,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { private platformio: Platformio, private builder: FirmwareBuilder, private deviceDescriptionsLoader: DeviceDescriptionsLoader, + private cloudBinariesCache: CloudBinariesCache, private logger: LoggerService ) { this.mutex = new Mutex(); @@ -113,21 +116,16 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return false; } - async isCompatible( - params: IsCompatibleArgs, - gitRepositoryUrl: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _gitRepositorySrcFolder: string - ) { + async isCompatible(params: IsCompatibleArgs, gitRepository: GitRepository) { if ( - (gitRepositoryUrl.toLowerCase() === + (gitRepository.url.toLowerCase() === 'https://github.com/expresslrs/expresslrs'.toLowerCase() && params.source === FirmwareSource.GitTag && semver.compare(params.gitTag, '3.0.0') >= 0) || (params.source === FirmwareSource.GitBranch && params.gitBranch === 'master') || - (params.source === FirmwareSource.GitBranch && - params.gitBranch === 'flasher') + (params.source === FirmwareSource.GitPullRequest && + params.gitPullRequest?.number === 2064) ) { return true; } @@ -211,8 +209,42 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return firmwarePath; } - async isCachedBinariesAvailable(): Promise { - return false; + async getCurrentSourceCommit(gitRepositoryUrl: string): Promise { + let gitPath = ''; + try { + gitPath = await findGitExecutable(this.PATH); + } catch (e) { + this.logger?.error('failed to find git', undefined, { + PATH: this.PATH, + err: e, + }); + throw e; + } + this.logger?.log('git path', { + gitPath, + }); + + const firmwareDownload = new GitFirmwareDownloader( + { + baseDirectory: this.firmwaresPath, + gitBinaryLocation: gitPath, + }, + this.logger + ); + + return firmwareDownload.currentCommitHash(gitRepositoryUrl); + } + + isRequestCompatibleWithCache(params: BuildFlashFirmwareParams): boolean { + if (params.userDefinesMode === UserDefinesMode.Manual) { + return false; + } + + if (params.firmware.source === FirmwareSource.Local) { + return false; + } + + return true; } async compileBinary( @@ -320,9 +352,10 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { async buildFlashFirmware( params: BuildFlashFirmwareParams, - gitRepositoryUrl: string, - gitRepositorySrcFolder: string + gitRepository: GitRepository ): Promise { + const gitRepositoryUrl = gitRepository.url; + const gitRepositorySrcFolder = gitRepository.srcFolder; this.logger?.log('received build firmware request', { params: maskBuildFlashFirmwareParams(params), gitRepositoryUrl, @@ -374,10 +407,41 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } ); - const isCachedBinariesAvailable = await this.isCachedBinariesAvailable(); let firmwareBinaryPath = ''; - if (isCachedBinariesAvailable) { - this.logger.log('cached binaries are available'); + if (this.isRequestCompatibleWithCache(params)) { + const currentCommitHash = await this.getCurrentSourceCommit( + gitRepositoryUrl + ); + this.logger.log('firmware build request is compatible with cache', { + currentCommitHash, + }); + try { + const cacheLocation = await this.cloudBinariesCache.download( + gitRepository.repositoryName, + currentCommitHash + ); + const cachedBinaryPath = await this.getCachedBuildPath( + config.firmware, + params.userDefines + ); + firmwareBinaryPath = path.join(cacheLocation, cachedBinaryPath); + } catch (e) { + this.logger.log( + 'failed to get cached build, reverting to building firmware locally', + { + e, + currentCommitHash, + } + ); + const target = `${config.firmware}_via_UART`; + firmwareBinaryPath = await this.compileBinary( + target, + firmwareSourcePath, + params.userDefinesMode, + params.userDefinesTxt, + params.userDefines + ); + } } else { const target = `${config.firmware}_via_UART`; firmwareBinaryPath = await this.compileBinary( @@ -388,6 +452,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { params.userDefines ); } + this.logger.log('firmware binaries path', { + firmwareBinaryPath, + }); await this.updateProgress( BuildProgressNotificationType.Info, diff --git a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts index 57e237337..3dde6b598 100644 --- a/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts +++ b/src/api/src/services/FlashingStrategyLocator/FlashingStrategy.ts @@ -22,14 +22,12 @@ export interface FlashingStrategy { isCompatible: ( params: IsCompatibleArgs, - gitRepositoryUrl: string, - gitRepositorySrcFolder: string + gitRepository: GitRepository ) => Promise; buildFlashFirmware: ( params: BuildFlashFirmwareParams, - gitRepositoryUrl: string, - gitRepositorySrcFolder: string + gitRepository: GitRepository ) => Promise; availableFirmwareTargets: ( diff --git a/src/api/src/services/FlashingStrategyLocator/index.ts b/src/api/src/services/FlashingStrategyLocator/index.ts index b549cc4d5..d3b536aaa 100644 --- a/src/api/src/services/FlashingStrategyLocator/index.ts +++ b/src/api/src/services/FlashingStrategyLocator/index.ts @@ -2,6 +2,7 @@ import { Service } from 'typedi'; import { FlashingStrategy, IsCompatibleArgs } from './FlashingStrategy'; import { LoggerService } from '../../logger'; +import GitRepository from '../../graphql/inputs/GitRepositoryInput'; @Service() export default class FlashingStrategyLocatorService { @@ -12,18 +13,13 @@ export default class FlashingStrategyLocatorService { async locate( params: IsCompatibleArgs, - gitRepositoryUrl: string, - gitRepositorySrcFolder: string + gitRepository: GitRepository ): Promise { for (let i = 0; i < this.flashingStrategies.length; i++) { const strategy = this.flashingStrategies[i]; if ( // eslint-disable-next-line no-await-in-loop - await strategy.isCompatible( - params, - gitRepositoryUrl, - gitRepositorySrcFolder - ) + await strategy.isCompatible(params, gitRepository) ) { this.logger.log('picked flashing strategy', { strategy: strategy.name, diff --git a/src/api/src/services/PlatformioFlashingStrategy/index.ts b/src/api/src/services/PlatformioFlashingStrategy/index.ts index b019fbece..ad2e1e5fe 100644 --- a/src/api/src/services/PlatformioFlashingStrategy/index.ts +++ b/src/api/src/services/PlatformioFlashingStrategy/index.ts @@ -114,18 +114,17 @@ export default class PlatformioFlashingStrategyService // eslint-disable-next-line @typescript-eslint/no-unused-vars _params: IsCompatibleArgs, // eslint-disable-next-line @typescript-eslint/no-unused-vars - _gitRepositoryUrl: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - _gitRepositorySrcFolder: string + _gitRepository: GitRepository ) { return true; } async buildFlashFirmware( params: BuildFlashFirmwareParams, - gitRepositoryUrl: string, - gitRepositorySrcFolder: string + gitRepository: GitRepository ): Promise { + const gitRepositoryUrl = gitRepository.url; + const gitRepositorySrcFolder = gitRepository.srcFolder; this.logger?.log('received build firmware request', { params: maskBuildFlashFirmwareParams(params), gitRepositoryUrl, diff --git a/src/main.dev.ts b/src/main.dev.ts index 38f301f99..dbc61ac8c 100644 --- a/src/main.dev.ts +++ b/src/main.dev.ts @@ -188,6 +188,12 @@ const createWindow = async () => { logger.log('starting server...'); const firmwaresPath = path.join(userDataDirectory, 'firmwares', 'github'); await mkdirp(firmwaresPath); + const firmwareCloudCachePath = path.join( + userDataDirectory, + 'firmwares', + 'cloud' + ); + await mkdirp(firmwareCloudCachePath); const targetsStoragePath = path.join( userDataDirectory, 'firmwares', @@ -274,6 +280,8 @@ const createWindow = async () => { multicastDnsSimulatorEnabled: process.env.MULTICAST_DNS_SIMULATOR_ENABLED === 'true', firmwaresPath, + cloudCacheServer: 'https://pub-3d47e112f78c4850a8f0d49a65030eac.r2.dev', + firmwareCloudCachePath, getPlatformioPath, platformioStateTempStoragePath, PATH, diff --git a/src/ui/config/index.ts b/src/ui/config/index.ts index b0e36c72c..50c5c4593 100644 --- a/src/ui/config/index.ts +++ b/src/ui/config/index.ts @@ -16,22 +16,12 @@ export const Config: IConfig = { facebookGroupUrl: 'https://www.facebook.com/groups/636441730280366', discordUrl: 'https://discord.gg/dS6ReFY', openCollectiveUrl: 'https://opencollective.com/expresslrs', - // expressLRSGit: { - // cloneUrl: 'https://github.com/ExpressLRS/ExpressLRS', - // url: 'https://github.com/ExpressLRS/ExpressLRS', - // owner: 'ExpressLRS', - // repositoryName: 'ExpressLRS', - // rawRepoUrl: 'https://raw.githubusercontent.com/ExpressLRS/ExpressLRS', - // srcFolder: 'src', - // tagExcludes: ['<2.5.0'], - // }, expressLRSGit: { - // cloneUrl: 'https://github.com/ExpressLRS/ExpressLRS', - cloneUrl: 'https://github.com/pkendall64/ExpressLRS', - url: 'https://github.com/pkendall64/ExpressLRS', - owner: 'pkendall64', + cloneUrl: 'https://github.com/ExpressLRS/ExpressLRS', + url: 'https://github.com/ExpressLRS/ExpressLRS', + owner: 'ExpressLRS', repositoryName: 'ExpressLRS', - rawRepoUrl: 'https://raw.githubusercontent.com/pkendall64/ExpressLRS', + rawRepoUrl: 'https://raw.githubusercontent.com/ExpressLRS/ExpressLRS', srcFolder: 'src', tagExcludes: ['<2.5.0'], }, From c50dade87452f5db60e56a3f003c8e8666330329 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 5 Feb 2023 21:43:22 +0200 Subject: [PATCH 17/54] Ignore r2 directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8a9871824..246baceda 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ npm-debug.log.* /src/api/src/library/Platformio/.piocore-* /src/api/src/services/BinaryFlashingStrategy/firmware __pycache__ +r2 From e1feb56bdec7ec2b0b9db0c4b2d08fa7d929395c Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 5 Feb 2023 21:51:42 +0200 Subject: [PATCH 18/54] Fix regulatory domain link. Fix #477 --- src/ui/components/UserDefineDescription/index.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ui/components/UserDefineDescription/index.tsx b/src/ui/components/UserDefineDescription/index.tsx index 3cb4e2bff..2ae25061e 100644 --- a/src/ui/components/UserDefineDescription/index.tsx +++ b/src/ui/components/UserDefineDescription/index.tsx @@ -86,13 +86,12 @@ const UserDefineDescription: FunctionComponent = maximum power to 100mW, and enables LBT (Listen Before Talk).

      - Check our Wiki page for latest definition. - +

); From 1a339dbc2292916cd96bb8e1495fae0778c23815 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 5 Feb 2023 22:20:25 +0200 Subject: [PATCH 19/54] Fix category name for Foxeer 900 MHz --- devices/betafpv-900.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devices/betafpv-900.json b/devices/betafpv-900.json index 0ba633151..9eea4f4a4 100644 --- a/devices/betafpv-900.json +++ b/devices/betafpv-900.json @@ -108,7 +108,7 @@ "verifiedHardware": false }, { - "category": "Foxeer 900 MHz", + "category": "Foxeer", "name": "Foxeer 900 RX", "wikiUrl": "", "abbreviatedName": "Foxeer 900 RX" From 25e57df134681ed863519d1d93c715c600ddb610 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 5 Feb 2023 22:39:44 +0200 Subject: [PATCH 20/54] Use offensive user data directory only when username contains characters that do not work with platformio native build tools --- src/main.dev.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main.dev.ts b/src/main.dev.ts index b82e5ffb3..9f74847ad 100644 --- a/src/main.dev.ts +++ b/src/main.dev.ts @@ -61,19 +61,18 @@ logger.log('path', { PATH: process.env.PATH, }); -function isASCII(str: string) { - return /^[\x20-\x7F]*$/.test(str); -} - const isWindows = process.platform.startsWith('win'); const isMacOS = process.platform.startsWith('darwin'); let userDataDirectory = app.getPath('userData'); if (isWindows) { const dirtyUserDataDirectory = app.isPackaged - ? path.join('c:', `.${packageJson.name}`) - : path.join('c:', `.${packageJson.name}-dev`); + ? path.join('c:', 'ProgramData', packageJson.name) + : path.join('c:', 'ProgramData', `${packageJson.name}-dev`); try { + const isASCII = (str: string) => { + return /^[\x20-\x7F]*$/.test(str); + }; if (!isASCII(userDataDirectory)) { mkdirp.sync(dirtyUserDataDirectory); userDataDirectory = dirtyUserDataDirectory; From 715ee919460bd6b0a372734e92ce109be4137f5b Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Tue, 7 Feb 2023 21:05:55 +0200 Subject: [PATCH 21/54] Fix firmware dir --- .../services/BinaryFlashingStrategy/CloudBinariesCache/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts index e8eef1cea..9312c546a 100644 --- a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts @@ -15,7 +15,7 @@ export default class CloudBinariesCache { commitHash ); await mkdirp(workingDir); - const firmwareCacheDir = path.join(workingDir, 'firmware'); + const firmwareCacheDir = path.join(workingDir, 'dist', 'firmware'); // if we have firmware in our local cache already no need to download it the second time. if (fs.existsSync(firmwareCacheDir)) { From 9657a9b885a123a4d14c184387681140c387c57a Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Fri, 3 Mar 2023 17:52:38 -0500 Subject: [PATCH 22/54] Remove _pth file from the bundled windows python. The binary configurator script needs to import a local module, which is not allowed when python is in isolated mode. --- dependencies/windows_amd64/portablepy.ps1 | 5 ++++- dependencies/windows_amd64/python/python310._pth | 7 ------- 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 dependencies/windows_amd64/python/python310._pth diff --git a/dependencies/windows_amd64/portablepy.ps1 b/dependencies/windows_amd64/portablepy.ps1 index c0e0cdd8d..dcbc37610 100644 --- a/dependencies/windows_amd64/portablepy.ps1 +++ b/dependencies/windows_amd64/portablepy.ps1 @@ -31,7 +31,7 @@ $package = "$($destination)embedpython.zip" Invoke-WebRequest -Uri $source -OutFile $package Expand-Archive -Path $package -DestinationPath $destination - +<# "Writing $($pythonVersion)._pth" "Lib/site-packages @@ -41,7 +41,10 @@ Expand-Archive -Path $package -DestinationPath $destination # Uncomment to run site.main() automatically import site " | Out-File -FilePath "$($destination)$($pythonVersion)._pth" -encoding ASCII +#> +"Removing *._pth files because they put python into isolated mode, which doesn't allow a script to import other scripts in the same directory" +get-childitem "$($destination)" | Where-Object {$_.Name -match "\._pth"} | Remove-Item "Creating DLLs dir" mkdir "$($destination)DLLs" diff --git a/dependencies/windows_amd64/python/python310._pth b/dependencies/windows_amd64/python/python310._pth deleted file mode 100644 index 263e3d9ae..000000000 --- a/dependencies/windows_amd64/python/python310._pth +++ /dev/null @@ -1,7 +0,0 @@ -Lib/site-packages -.\python310.zip -. - -# Uncomment to run site.main() automatically -import site - From 35b8db3d0cb3237fc1c50e621eec855a4f191fdd Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 4 Mar 2023 19:52:45 +0200 Subject: [PATCH 23/54] Update IsCompatible --- src/api/src/services/BinaryFlashingStrategy/index.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index ae4bdb15e..cba1331e9 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -121,11 +121,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { (gitRepository.url.toLowerCase() === 'https://github.com/expresslrs/expresslrs'.toLowerCase() && params.source === FirmwareSource.GitTag && - semver.compare(params.gitTag, '3.0.0') >= 0) || + semver.compare(params.gitTag, '3.2.0') >= 0) || (params.source === FirmwareSource.GitBranch && - params.gitBranch === 'master') || - (params.source === FirmwareSource.GitPullRequest && - params.gitPullRequest?.number === 2064) + params.gitBranch === 'master') ) { return true; } From 7e59feedaa2c94b0d2c48d213a5bcfc3f72ad208 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 4 Mar 2023 20:11:03 +0200 Subject: [PATCH 24/54] Make everything compatible with the cloudbuilds --- .../services/BinaryFlashingStrategy/index.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index cba1331e9..1289a7e9c 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -118,17 +118,20 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { async isCompatible(params: IsCompatibleArgs, gitRepository: GitRepository) { if ( - (gitRepository.url.toLowerCase() === - 'https://github.com/expresslrs/expresslrs'.toLowerCase() && - params.source === FirmwareSource.GitTag && - semver.compare(params.gitTag, '3.2.0') >= 0) || - (params.source === FirmwareSource.GitBranch && - params.gitBranch === 'master') + gitRepository.url.toLowerCase() !== + 'https://github.com/expresslrs/expresslrs'.toLowerCase() ) { - return true; + return false; } - return false; + if ( + params.source === FirmwareSource.GitTag && + semver.lte(params.gitTag, '3.2.0') + ) { + return false; + } + + return true; } async downloadSource( From 413e0845a9b801a27d8ade35232f04b490a1d004 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Tue, 7 Mar 2023 00:35:01 +0200 Subject: [PATCH 25/54] Use artifactory domain --- src/main.dev.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.dev.ts b/src/main.dev.ts index 9f74847ad..c2307e4ed 100644 --- a/src/main.dev.ts +++ b/src/main.dev.ts @@ -290,7 +290,7 @@ const createWindow = async () => { multicastDnsSimulatorEnabled: process.env.MULTICAST_DNS_SIMULATOR_ENABLED === 'true', firmwaresPath, - cloudCacheServer: 'https://pub-3d47e112f78c4850a8f0d49a65030eac.r2.dev', + cloudCacheServer: 'https://artifactory.expresslrs.org', firmwareCloudCachePath, getPlatformioPath, platformioStateTempStoragePath, From 616c16cd6f1fa3f71f746bffc353a417f8a76d5d Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 8 Apr 2023 20:33:55 +0300 Subject: [PATCH 26/54] Add a warning about the disable cloud binarie scache when using manual user defines --- src/ui/components/DeviceOptionsForm/index.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ui/components/DeviceOptionsForm/index.tsx b/src/ui/components/DeviceOptionsForm/index.tsx index fcb9423f1..9569857cf 100644 --- a/src/ui/components/DeviceOptionsForm/index.tsx +++ b/src/ui/components/DeviceOptionsForm/index.tsx @@ -282,6 +282,11 @@ const DeviceOptionsForm: FunctionComponent = ( + + )} From 391ce5840e42950310a71c9d7603adbb6cc17c72 Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Tue, 11 Apr 2023 16:23:55 -0400 Subject: [PATCH 27/54] Remove esptool from the bundled python for windows --- dependencies/windows_amd64/portablepy.ps1 | 2 + .../_cffi_backend.cp310-win_amd64.pyd | Bin 181248 -> 0 bytes .../bitstring-3.1.9.dist-info/LICENSE | 21 - .../bitstring-3.1.9.dist-info/METADATA | 118 - .../bitstring-3.1.9.dist-info/RECORD | 8 - .../bitstring-3.1.9.dist-info/top_level.txt | 1 - .../python/Lib/site-packages/bitstring.py | 4469 ------------- .../cffi-1.15.1.dist-info/LICENSE | 26 - .../cffi-1.15.1.dist-info/METADATA | 34 - .../cffi-1.15.1.dist-info/RECORD | 44 - .../site-packages/cffi-1.15.1.dist-info/WHEEL | 5 - .../cffi-1.15.1.dist-info/entry_points.txt | 2 - .../cffi-1.15.1.dist-info/top_level.txt | 2 - .../python/Lib/site-packages/cffi/__init__.py | 14 - .../Lib/site-packages/cffi/_cffi_errors.h | 149 - .../Lib/site-packages/cffi/_cffi_include.h | 385 -- .../Lib/site-packages/cffi/_embedding.h | 528 -- .../python/Lib/site-packages/cffi/api.py | 965 --- .../Lib/site-packages/cffi/backend_ctypes.py | 1121 ---- .../Lib/site-packages/cffi/cffi_opcode.py | 187 - .../Lib/site-packages/cffi/commontypes.py | 80 - .../python/Lib/site-packages/cffi/cparser.py | 1006 --- .../python/Lib/site-packages/cffi/error.py | 31 - .../Lib/site-packages/cffi/ffiplatform.py | 127 - .../python/Lib/site-packages/cffi/lock.py | 30 - .../python/Lib/site-packages/cffi/model.py | 617 -- .../Lib/site-packages/cffi/parse_c_type.h | 181 - .../Lib/site-packages/cffi/pkgconfig.py | 121 - .../Lib/site-packages/cffi/recompiler.py | 1581 ----- .../Lib/site-packages/cffi/setuptools_ext.py | 219 - .../Lib/site-packages/cffi/vengine_cpy.py | 1076 --- .../Lib/site-packages/cffi/vengine_gen.py | 675 -- .../python/Lib/site-packages/cffi/verifier.py | 307 - .../cryptography-39.0.0.dist-info/LICENSE | 6 - .../LICENSE.APACHE | 202 - .../cryptography-39.0.0.dist-info/LICENSE.BSD | 27 - .../cryptography-39.0.0.dist-info/LICENSE.PSF | 41 - .../cryptography-39.0.0.dist-info/METADATA | 134 - .../cryptography-39.0.0.dist-info/RECORD | 180 - .../cryptography-39.0.0.dist-info/WHEEL | 5 - .../top_level.txt | 1 - .../site-packages/cryptography/__about__.py | 15 - .../site-packages/cryptography/__init__.py | 25 - .../site-packages/cryptography/exceptions.py | 68 - .../Lib/site-packages/cryptography/fernet.py | 220 - .../cryptography/hazmat/__init__.py | 10 - .../site-packages/cryptography/hazmat/_oid.py | 293 - .../cryptography/hazmat/backends/__init__.py | 10 - .../hazmat/backends/openssl/__init__.py | 8 - .../hazmat/backends/openssl/aead.py | 306 - .../hazmat/backends/openssl/backend.py | 2514 ------- .../hazmat/backends/openssl/ciphers.py | 281 - .../hazmat/backends/openssl/cmac.py | 87 - .../hazmat/backends/openssl/decode_asn1.py | 31 - .../hazmat/backends/openssl/dh.py | 317 - .../hazmat/backends/openssl/dsa.py | 236 - .../hazmat/backends/openssl/ec.py | 315 - .../hazmat/backends/openssl/ed25519.py | 155 - .../hazmat/backends/openssl/ed448.py | 156 - .../hazmat/backends/openssl/hashes.py | 86 - .../hazmat/backends/openssl/hmac.py | 84 - .../hazmat/backends/openssl/poly1305.py | 67 - .../hazmat/backends/openssl/rsa.py | 588 -- .../hazmat/backends/openssl/utils.py | 52 - .../hazmat/backends/openssl/x25519.py | 132 - .../hazmat/backends/openssl/x448.py | 117 - .../cryptography/hazmat/bindings/__init__.py | 3 - .../cryptography/hazmat/bindings/_openssl.pyd | Bin 3964416 -> 0 bytes .../cryptography/hazmat/bindings/_openssl.pyi | 8 - .../cryptography/hazmat/bindings/_rust.pyd | Bin 1686528 -> 0 bytes .../hazmat/bindings/_rust/__init__.pyi | 34 - .../hazmat/bindings/_rust/asn1.pyi | 16 - .../hazmat/bindings/_rust/ocsp.pyi | 25 - .../hazmat/bindings/_rust/pkcs7.pyi | 15 - .../hazmat/bindings/_rust/x509.pyi | 42 - .../hazmat/bindings/openssl/__init__.py | 3 - .../hazmat/bindings/openssl/_conditional.py | 360 - .../hazmat/bindings/openssl/binding.py | 244 - .../hazmat/primitives/__init__.py | 3 - .../hazmat/primitives/_asymmetric.py | 17 - .../hazmat/primitives/_cipheralgorithm.py | 43 - .../hazmat/primitives/_serialization.py | 168 - .../hazmat/primitives/asymmetric/__init__.py | 3 - .../hazmat/primitives/asymmetric/dh.py | 251 - .../hazmat/primitives/asymmetric/dsa.py | 288 - .../hazmat/primitives/asymmetric/ec.py | 483 -- .../hazmat/primitives/asymmetric/ed25519.py | 91 - .../hazmat/primitives/asymmetric/ed448.py | 87 - .../hazmat/primitives/asymmetric/padding.py | 101 - .../hazmat/primitives/asymmetric/rsa.py | 432 -- .../hazmat/primitives/asymmetric/types.py | 68 - .../hazmat/primitives/asymmetric/utils.py | 23 - .../hazmat/primitives/asymmetric/x25519.py | 81 - .../hazmat/primitives/asymmetric/x448.py | 81 - .../hazmat/primitives/ciphers/__init__.py | 26 - .../hazmat/primitives/ciphers/aead.py | 374 -- .../hazmat/primitives/ciphers/algorithms.py | 227 - .../hazmat/primitives/ciphers/base.py | 269 - .../hazmat/primitives/ciphers/modes.py | 275 - .../cryptography/hazmat/primitives/cmac.py | 64 - .../hazmat/primitives/constant_time.py | 13 - .../cryptography/hazmat/primitives/hashes.py | 261 - .../cryptography/hazmat/primitives/hmac.py | 70 - .../hazmat/primitives/kdf/__init__.py | 22 - .../hazmat/primitives/kdf/concatkdf.py | 127 - .../hazmat/primitives/kdf/hkdf.py | 100 - .../hazmat/primitives/kdf/kbkdf.py | 297 - .../hazmat/primitives/kdf/pbkdf2.py | 65 - .../hazmat/primitives/kdf/scrypt.py | 73 - .../hazmat/primitives/kdf/x963kdf.py | 62 - .../cryptography/hazmat/primitives/keywrap.py | 176 - .../cryptography/hazmat/primitives/padding.py | 224 - .../hazmat/primitives/poly1305.py | 60 - .../primitives/serialization/__init__.py | 46 - .../hazmat/primitives/serialization/base.py | 72 - .../hazmat/primitives/serialization/pkcs12.py | 226 - .../hazmat/primitives/serialization/pkcs7.py | 219 - .../hazmat/primitives/serialization/ssh.py | 758 --- .../hazmat/primitives/twofactor/__init__.py | 7 - .../hazmat/primitives/twofactor/hotp.py | 91 - .../hazmat/primitives/twofactor/totp.py | 48 - .../Lib/site-packages/cryptography/py.typed | 0 .../Lib/site-packages/cryptography/utils.py | 132 - .../cryptography/x509/__init__.py | 250 - .../site-packages/cryptography/x509/base.py | 1131 ---- .../x509/certificate_transparency.py | 96 - .../cryptography/x509/extensions.py | 2113 ------ .../cryptography/x509/general_name.py | 284 - .../site-packages/cryptography/x509/name.py | 460 -- .../site-packages/cryptography/x509/ocsp.py | 621 -- .../site-packages/cryptography/x509/oid.py | 31 - .../ecdsa-0.18.0.dist-info/INSTALLER | 1 - .../ecdsa-0.18.0.dist-info/LICENSE | 24 - .../ecdsa-0.18.0.dist-info/METADATA | 675 -- .../ecdsa-0.18.0.dist-info/RECORD | 64 - .../ecdsa-0.18.0.dist-info/WHEEL | 6 - .../ecdsa-0.18.0.dist-info/top_level.txt | 1 - .../Lib/site-packages/ecdsa/__init__.py | 90 - .../python/Lib/site-packages/ecdsa/_compat.py | 153 - .../python/Lib/site-packages/ecdsa/_rwlock.py | 86 - .../python/Lib/site-packages/ecdsa/_sha3.py | 181 - .../Lib/site-packages/ecdsa/_version.py | 21 - .../python/Lib/site-packages/ecdsa/curves.py | 513 -- .../python/Lib/site-packages/ecdsa/der.py | 409 -- .../python/Lib/site-packages/ecdsa/ecdh.py | 336 - .../python/Lib/site-packages/ecdsa/ecdsa.py | 859 --- .../python/Lib/site-packages/ecdsa/eddsa.py | 252 - .../Lib/site-packages/ecdsa/ellipticcurve.py | 1584 ----- .../python/Lib/site-packages/ecdsa/errors.py | 4 - .../python/Lib/site-packages/ecdsa/keys.py | 1612 ----- .../Lib/site-packages/ecdsa/numbertheory.py | 825 --- .../python/Lib/site-packages/ecdsa/rfc6979.py | 113 - .../Lib/site-packages/ecdsa/test_curves.py | 361 - .../Lib/site-packages/ecdsa/test_der.py | 476 -- .../Lib/site-packages/ecdsa/test_ecdh.py | 441 -- .../Lib/site-packages/ecdsa/test_ecdsa.py | 661 -- .../Lib/site-packages/ecdsa/test_eddsa.py | 1079 --- .../site-packages/ecdsa/test_ellipticcurve.py | 199 - .../Lib/site-packages/ecdsa/test_jacobi.py | 657 -- .../Lib/site-packages/ecdsa/test_keys.py | 959 --- .../ecdsa/test_malformed_sigs.py | 370 -- .../site-packages/ecdsa/test_numbertheory.py | 433 -- .../Lib/site-packages/ecdsa/test_pyecdsa.py | 2267 ------- .../Lib/site-packages/ecdsa/test_rw_lock.py | 180 - .../Lib/site-packages/ecdsa/test_sha3.py | 111 - .../python/Lib/site-packages/ecdsa/util.py | 433 -- .../Lib/site-packages/espefuse/__init__.py | 284 - .../Lib/site-packages/espefuse/__main__.py | 10 - .../site-packages/espefuse/efuse/__init__.py | 0 .../espefuse/efuse/base_fields.py | 725 -- .../espefuse/efuse/base_operations.py | 675 -- .../efuse/emulate_efuse_controller_base.py | 229 - .../espefuse/efuse/esp32/__init__.py | 3 - .../efuse/esp32/emulate_efuse_controller.py | 143 - .../espefuse/efuse/esp32/fields.py | 500 -- .../espefuse/efuse/esp32/mem_definition.py | 168 - .../espefuse/efuse/esp32/operations.py | 350 - .../espefuse/efuse/esp32c2/__init__.py | 3 - .../efuse/esp32c2/emulate_efuse_controller.py | 142 - .../espefuse/efuse/esp32c2/fields.py | 417 -- .../espefuse/efuse/esp32c2/mem_definition.py | 158 - .../espefuse/efuse/esp32c2/operations.py | 349 - .../espefuse/efuse/esp32c3/__init__.py | 3 - .../efuse/esp32c3/emulate_efuse_controller.py | 94 - .../espefuse/efuse/esp32c3/fields.py | 466 -- .../espefuse/efuse/esp32c3/mem_definition.py | 249 - .../espefuse/efuse/esp32c3/operations.py | 415 -- .../espefuse/efuse/esp32c6/__init__.py | 3 - .../efuse/esp32c6/emulate_efuse_controller.py | 94 - .../espefuse/efuse/esp32c6/fields.py | 466 -- .../espefuse/efuse/esp32c6/mem_definition.py | 263 - .../espefuse/efuse/esp32c6/operations.py | 415 -- .../espefuse/efuse/esp32h2beta1/__init__.py | 3 - .../esp32h2beta1/emulate_efuse_controller.py | 94 - .../espefuse/efuse/esp32h2beta1/fields.py | 454 -- .../efuse/esp32h2beta1/mem_definition.py | 229 - .../espefuse/efuse/esp32h2beta1/operations.py | 414 -- .../espefuse/efuse/esp32s2/__init__.py | 3 - .../efuse/esp32s2/emulate_efuse_controller.py | 94 - .../espefuse/efuse/esp32s2/fields.py | 524 -- .../espefuse/efuse/esp32s2/mem_definition.py | 310 - .../espefuse/efuse/esp32s2/operations.py | 525 -- .../espefuse/efuse/esp32s3/__init__.py | 3 - .../efuse/esp32s3/emulate_efuse_controller.py | 94 - .../espefuse/efuse/esp32s3/fields.py | 478 -- .../espefuse/efuse/esp32s3/mem_definition.py | 256 - .../espefuse/efuse/esp32s3/operations.py | 523 -- .../espefuse/efuse/esp32s3beta2/__init__.py | 3 - .../esp32s3beta2/emulate_efuse_controller.py | 94 - .../espefuse/efuse/esp32s3beta2/fields.py | 478 -- .../efuse/esp32s3beta2/mem_definition.py | 250 - .../espefuse/efuse/esp32s3beta2/operations.py | 523 -- .../espefuse/efuse/mem_definition_base.py | 59 - .../Lib/site-packages/espefuse/efuse/util.py | 49 - .../Lib/site-packages/espsecure/__init__.py | 1558 ----- .../Lib/site-packages/espsecure/__main__.py | 10 - .../esptool-4.4.dist-info/INSTALLER | 1 - .../esptool-4.4.dist-info/LICENSE | 339 - .../esptool-4.4.dist-info/METADATA | 57 - .../esptool-4.4.dist-info/RECORD | 158 - .../esptool-4.4.dist-info/entry_points.txt | 4 - .../esptool-4.4.dist-info/top_level.txt | 3 - .../Lib/site-packages/esptool/__init__.py | 1045 --- .../Lib/site-packages/esptool/__main__.py | 11 - .../Lib/site-packages/esptool/bin_image.py | 1221 ---- .../python/Lib/site-packages/esptool/cmds.py | 1160 ---- .../Lib/site-packages/esptool/loader.py | 1573 ----- .../site-packages/esptool/targets/__init__.py | 29 - .../site-packages/esptool/targets/esp32.py | 360 - .../site-packages/esptool/targets/esp32c2.py | 164 - .../site-packages/esptool/targets/esp32c3.py | 177 - .../site-packages/esptool/targets/esp32c6.py | 175 - .../esptool/targets/esp32c6beta.py | 23 - .../esptool/targets/esp32h2beta1.py | 154 - .../esptool/targets/esp32h2beta2.py | 42 - .../site-packages/esptool/targets/esp32s2.py | 300 - .../site-packages/esptool/targets/esp32s3.py | 275 - .../esptool/targets/esp32s3beta2.py | 41 - .../site-packages/esptool/targets/esp8266.py | 190 - .../targets/stub_flasher/stub_flasher_32.json | 7 - .../stub_flasher/stub_flasher_32c2.json | 7 - .../stub_flasher/stub_flasher_32c3.json | 7 - .../stub_flasher/stub_flasher_32c6.json | 7 - .../stub_flasher/stub_flasher_32c6beta.json | 7 - .../stub_flasher/stub_flasher_32h2beta1.json | 7 - .../stub_flasher/stub_flasher_32h2beta2.json | 7 - .../stub_flasher/stub_flasher_32s2.json | 7 - .../stub_flasher/stub_flasher_32s3.json | 7 - .../stub_flasher/stub_flasher_32s3beta2.json | 7 - .../stub_flasher/stub_flasher_8266.json | 7 - .../python/Lib/site-packages/esptool/util.py | 157 - .../pip-22.3.1.dist-info/INSTALLER | 1 - .../INSTALLER | 0 .../LICENSE.txt | 0 .../METADATA | 2 +- .../RECORD | 294 +- .../REQUESTED | 0 .../WHEEL | 0 .../entry_points.txt | 2 +- .../top_level.txt | 0 .../python/Lib/site-packages/pip/__init__.py | 2 +- .../site-packages/pip/_internal/build_env.py | 17 +- .../pip/_internal/cli/cmdoptions.py | 16 +- .../pip/_internal/commands/debug.py | 2 +- .../pip/_internal/commands/index.py | 1 + .../pip/_internal/commands/inspect.py | 7 +- .../pip/_internal/commands/install.py | 25 +- .../pip/_internal/commands/show.py | 6 + .../pip/_internal/commands/uninstall.py | 9 +- .../site-packages/pip/_internal/exceptions.py | 89 +- .../pip/_internal/index/collector.py | 2 +- .../pip/_internal/index/package_finder.py | 52 +- .../pip/_internal/locations/__init__.py | 63 +- .../pip/_internal/locations/_distutils.py | 9 +- .../pip/_internal/locations/_sysconfig.py | 5 - .../pip/_internal/locations/base.py | 2 +- .../pip/_internal/models/direct_url.py | 20 +- .../_internal/models/installation_report.py | 2 +- .../pip/_internal/models/link.py | 77 +- .../pip/_internal/network/auth.py | 189 +- .../_internal/operations/build/metadata.py | 6 +- .../operations/build/metadata_editable.py | 6 +- .../pip/_internal/operations/build/wheel.py | 4 +- .../operations/build/wheel_editable.py | 4 +- .../pip/_internal/operations/check.py | 2 +- .../site-packages/pip/_internal/pyproject.py | 7 +- .../pip/_internal/req/req_install.py | 14 +- .../pip/_internal/self_outdated_check.py | 11 +- .../pip/_internal/utils/egg_link.py | 7 +- .../site-packages/pip/_internal/utils/misc.py | 26 +- .../pip/_internal/utils/subprocess.py | 4 +- .../pip/_internal/utils/virtualenv.py | 12 +- .../site-packages/pip/_internal/vcs/bazaar.py | 2 +- .../pip/_internal/vcs/subversion.py | 2 +- .../pip/_vendor/certifi/__init__.py | 2 +- .../pip/_vendor/certifi/cacert.pem | 181 - .../pip/_vendor/chardet/__init__.py | 36 +- .../pip/_vendor/chardet/big5prober.py | 6 +- .../pip/_vendor/chardet/chardistribution.py | 54 +- .../pip/_vendor/chardet/charsetgroupprober.py | 31 +- .../pip/_vendor/chardet/charsetprober.py | 35 +- .../pip/_vendor/chardet/cli/chardetect.py | 42 +- .../pip/_vendor/chardet/codingstatemachine.py | 16 +- .../_vendor/chardet/codingstatemachinedict.py | 19 + .../pip/_vendor/chardet/cp949prober.py | 6 +- .../pip/_vendor/chardet/enums.py | 9 +- .../pip/_vendor/chardet/escprober.py | 26 +- .../pip/_vendor/chardet/escsm.py | 9 +- .../pip/_vendor/chardet/eucjpprober.py | 19 +- .../pip/_vendor/chardet/euckrprober.py | 6 +- .../pip/_vendor/chardet/euctwprober.py | 6 +- .../pip/_vendor/chardet/gb2312prober.py | 6 +- .../pip/_vendor/chardet/hebrewprober.py | 56 +- .../pip/_vendor/chardet/johabprober.py | 6 +- .../pip/_vendor/chardet/jpcntx.py | 31 +- .../pip/_vendor/chardet/latin1prober.py | 18 +- .../pip/_vendor/chardet/macromanprober.py | 162 + .../pip/_vendor/chardet/mbcharsetprober.py | 32 +- .../pip/_vendor/chardet/mbcsgroupprober.py | 3 +- .../pip/_vendor/chardet/mbcssm.py | 23 +- .../pip/_vendor/chardet/metadata/languages.py | 37 +- .../pip/_vendor/chardet/resultdict.py | 16 + .../pip/_vendor/chardet/sbcharsetprober.py | 52 +- .../pip/_vendor/chardet/sbcsgroupprober.py | 2 +- .../pip/_vendor/chardet/sjisprober.py | 19 +- .../pip/_vendor/chardet/universaldetector.py | 68 +- .../pip/_vendor/chardet/utf1632prober.py | 32 +- .../pip/_vendor/chardet/utf8prober.py | 16 +- .../pip/_vendor/chardet/version.py | 4 +- .../pip/_vendor/colorama/__init__.py | 5 +- .../pip/_vendor/colorama/ansitowin32.py | 17 +- .../pip/_vendor/colorama/initialise.py | 51 +- .../pip/_vendor/colorama/tests/__init__.py | 1 + .../pip/_vendor/colorama/tests/ansi_test.py | 76 + .../colorama/tests/ansitowin32_test.py | 294 + .../_vendor/colorama/tests/initialise_test.py | 189 + .../pip/_vendor/colorama/tests/isatty_test.py | 57 + .../pip/_vendor/colorama/tests/utils.py | 49 + .../_vendor/colorama/tests/winterm_test.py | 131 + .../pip/_vendor/colorama/win32.py | 28 + .../pip/_vendor/colorama/winterm.py | 28 +- .../pip/_vendor/distro/distro.py | 101 +- .../pip/_vendor/pep517/__init__.py | 6 - .../site-packages/pip/_vendor/pep517/build.py | 126 - .../site-packages/pip/_vendor/pep517/check.py | 207 - .../pip/_vendor/pep517/colorlog.py | 113 - .../pip/_vendor/pep517/dirtools.py | 19 - .../pip/_vendor/pep517/envbuild.py | 170 - .../pip/_vendor/pep517/in_process/__init__.py | 26 - .../site-packages/pip/_vendor/pep517/meta.py | 93 - .../pip/_vendor/platformdirs/__init__.py | 12 +- .../pip/_vendor/platformdirs/unix.py | 4 +- .../pip/_vendor/platformdirs/version.py | 8 +- .../pip/_vendor/platformdirs/windows.py | 4 +- .../pip/_vendor/pyproject_hooks/__init__.py | 23 + .../{pep517 => pyproject_hooks}/_compat.py | 0 .../wrappers.py => pyproject_hooks/_impl.py} | 196 +- .../pyproject_hooks/_in_process/__init__.py | 18 + .../_in_process}/_in_process.py | 8 +- .../pip/_vendor/requests/__init__.py | 4 +- .../pip/_vendor/requests/__version__.py | 6 +- .../pip/_vendor/requests/models.py | 2 +- .../pip/_vendor/rich/__init__.py | 7 +- .../pip/_vendor/rich/__main__.py | 8 - .../pip/_vendor/rich/_null_file.py | 83 + .../site-packages/pip/_vendor/rich/ansi.py | 2 +- .../Lib/site-packages/pip/_vendor/rich/box.py | 2 +- .../site-packages/pip/_vendor/rich/color.py | 5 +- .../site-packages/pip/_vendor/rich/console.py | 78 +- .../pip/_vendor/rich/filesize.py | 2 +- .../site-packages/pip/_vendor/rich/json.py | 4 +- .../site-packages/pip/_vendor/rich/layout.py | 4 +- .../site-packages/pip/_vendor/rich/logging.py | 23 +- .../site-packages/pip/_vendor/rich/panel.py | 67 +- .../site-packages/pip/_vendor/rich/pretty.py | 59 +- .../pip/_vendor/rich/progress.py | 12 +- .../pip/_vendor/rich/progress_bar.py | 2 +- .../site-packages/pip/_vendor/rich/repr.py | 9 +- .../site-packages/pip/_vendor/rich/scope.py | 2 +- .../site-packages/pip/_vendor/rich/style.py | 6 +- .../site-packages/pip/_vendor/rich/syntax.py | 15 +- .../site-packages/pip/_vendor/rich/table.py | 6 + .../site-packages/pip/_vendor/rich/text.py | 27 +- .../pip/_vendor/rich/traceback.py | 12 +- .../pip/_vendor/urllib3/_version.py | 2 +- .../pip/_vendor/urllib3/connectionpool.py | 2 +- .../pip/_vendor/urllib3/contrib/appengine.py | 2 +- .../pip/_vendor/urllib3/contrib/ntlmpool.py | 4 +- .../pip/_vendor/urllib3/contrib/pyopenssl.py | 7 +- .../pip/_vendor/urllib3/response.py | 13 + .../pip/_vendor/urllib3/util/retry.py | 2 +- .../pip/_vendor/urllib3/util/url.py | 2 +- .../Lib/site-packages/pip/_vendor/vendor.txt | 18 +- .../site-packages/pkg_resources/__init__.py | 601 +- .../pkg_resources/_vendor/appdirs.py | 608 -- .../_vendor/importlib_resources/_common.py | 147 +- .../_vendor/importlib_resources/_compat.py | 10 + .../_vendor/importlib_resources/_legacy.py | 3 +- .../_vendor/importlib_resources/abc.py | 63 +- .../_vendor/importlib_resources/readers.py | 16 +- .../_vendor/importlib_resources/simple.py | 70 +- .../pkg_resources/_vendor/jaraco/context.py | 75 + .../_vendor/more_itertools/__init__.py | 4 +- .../_vendor/more_itertools/more.py | 224 +- .../_vendor/more_itertools/recipes.py | 175 +- .../_vendor/packaging/__about__.py | 26 - .../_vendor/packaging/__init__.py | 30 +- .../_vendor/packaging/_elffile.py | 108 + .../_vendor/packaging/_manylinux.py | 153 +- .../_vendor/packaging/_musllinux.py | 72 +- .../_vendor/packaging/_parser.py | 328 + .../_vendor/packaging/_tokenizer.py | 188 + .../_vendor/packaging/markers.py | 199 +- .../_vendor/packaging/requirements.py | 123 +- .../_vendor/packaging/specifiers.py | 915 ++- .../pkg_resources/_vendor/packaging/tags.py | 75 +- .../pkg_resources/_vendor/packaging/utils.py | 11 +- .../_vendor/packaging/version.py | 333 +- .../_vendor/platformdirs/__init__.py | 342 + .../_vendor/platformdirs/__main__.py | 46 + .../_vendor/platformdirs/android.py | 120 + .../pkg_resources/_vendor/platformdirs/api.py | 156 + .../_vendor/platformdirs/macos.py | 64 + .../_vendor/platformdirs/unix.py | 181 + .../_vendor/platformdirs/version.py | 4 + .../_vendor/platformdirs/windows.py | 184 + .../_vendor/pyparsing/__init__.py | 331 - .../_vendor/pyparsing/actions.py | 207 - .../pkg_resources/_vendor/pyparsing/common.py | 424 -- .../pkg_resources/_vendor/pyparsing/core.py | 5814 ----------------- .../_vendor/pyparsing/diagram/__init__.py | 642 -- .../_vendor/pyparsing/exceptions.py | 267 - .../_vendor/pyparsing/helpers.py | 1088 --- .../_vendor/pyparsing/results.py | 760 --- .../_vendor/pyparsing/testing.py | 331 - .../_vendor/pyparsing/unicode.py | 352 - .../pkg_resources/_vendor/pyparsing/util.py | 235 - .../_vendor/typing_extensions.py | 2209 +++++++ .../pkg_resources/extern/__init__.py | 8 +- .../pycparser-2.21.dist-info/INSTALLER | 1 - .../pycparser-2.21.dist-info/LICENSE | 27 - .../pycparser-2.21.dist-info/METADATA | 31 - .../pycparser-2.21.dist-info/RECORD | 41 - .../pycparser-2.21.dist-info/WHEEL | 6 - .../pycparser-2.21.dist-info/top_level.txt | 1 - .../Lib/site-packages/pycparser/__init__.py | 90 - .../Lib/site-packages/pycparser/_ast_gen.py | 336 - .../site-packages/pycparser/_build_tables.py | 37 - .../Lib/site-packages/pycparser/_c_ast.cfg | 195 - .../site-packages/pycparser/ast_transforms.py | 164 - .../Lib/site-packages/pycparser/c_ast.py | 1125 ---- .../site-packages/pycparser/c_generator.py | 502 -- .../Lib/site-packages/pycparser/c_lexer.py | 554 -- .../Lib/site-packages/pycparser/c_parser.py | 1936 ------ .../Lib/site-packages/pycparser/lextab.py | 10 - .../site-packages/pycparser/ply/__init__.py | 5 - .../Lib/site-packages/pycparser/ply/cpp.py | 905 --- .../site-packages/pycparser/ply/ctokens.py | 133 - .../Lib/site-packages/pycparser/ply/lex.py | 1099 ---- .../Lib/site-packages/pycparser/ply/yacc.py | 3494 ---------- .../Lib/site-packages/pycparser/ply/ygen.py | 74 - .../Lib/site-packages/pycparser/plyparser.py | 133 - .../Lib/site-packages/pycparser/yacctab.py | 366 -- .../pyserial-3.5.dist-info/RECORD | 4 +- .../reedsolo-1.5.4.dist-info/INSTALLER | 1 - .../reedsolo-1.5.4.dist-info/LICENSE | 2 - .../reedsolo-1.5.4.dist-info/METADATA | 355 - .../reedsolo-1.5.4.dist-info/RECORD | 8 - .../reedsolo-1.5.4.dist-info/WHEEL | 5 - .../reedsolo-1.5.4.dist-info/top_level.txt | 1 - .../python/Lib/site-packages/reedsolo.py | 974 --- .../setuptools-65.6.3.dist-info/INSTALLER | 1 - .../setuptools-65.6.3.dist-info/WHEEL | 5 - .../INSTALLER | 0 .../LICENSE | 0 .../METADATA | 7 +- .../RECORD | 304 +- .../REQUESTED | 0 .../WHEEL | 2 +- .../entry_points.txt | 0 .../top_level.txt | 0 .../Lib/site-packages/setuptools/__init__.py | 21 + .../setuptools/_distutils/_collections.py | 2 +- .../setuptools/_distutils/_msvccompiler.py | 6 +- .../setuptools/_distutils/bcppcompiler.py | 9 +- .../setuptools/_distutils/ccompiler.py | 44 +- .../setuptools/_distutils/cmd.py | 6 +- .../setuptools/_distutils/command/bdist.py | 1 - .../_distutils/command/bdist_dumb.py | 1 - .../_distutils/command/bdist_rpm.py | 3 +- .../setuptools/_distutils/command/build.py | 1 - .../_distutils/command/build_clib.py | 9 +- .../_distutils/command/build_ext.py | 5 +- .../setuptools/_distutils/command/build_py.py | 7 +- .../_distutils/command/build_scripts.py | 1 - .../setuptools/_distutils/command/clean.py | 1 - .../setuptools/_distutils/command/config.py | 1 - .../setuptools/_distutils/command/install.py | 3 +- .../_distutils/command/install_data.py | 1 - .../_distutils/command/install_headers.py | 1 - .../_distutils/command/install_lib.py | 1 - .../_distutils/command/install_scripts.py | 1 - .../setuptools/_distutils/command/register.py | 1 - .../setuptools/_distutils/command/sdist.py | 5 +- .../setuptools/_distutils/command/upload.py | 1 - .../setuptools/_distutils/core.py | 2 +- .../setuptools/_distutils/cygwinccompiler.py | 8 +- .../setuptools/_distutils/dir_util.py | 2 +- .../setuptools/_distutils/dist.py | 18 +- .../setuptools/_distutils/fancy_getopt.py | 2 +- .../setuptools/_distutils/file_util.py | 1 - .../setuptools/_distutils/msvc9compiler.py | 5 +- .../setuptools/_distutils/msvccompiler.py | 3 - .../setuptools/_distutils/sysconfig.py | 11 +- .../setuptools/_distutils/text_file.py | 3 +- .../setuptools/_distutils/unixccompiler.py | 1 - .../setuptools/_distutils/util.py | 2 +- .../setuptools/_distutils/version.py | 1 - .../setuptools/_normalization.py | 117 + .../Lib/site-packages/setuptools/_path.py | 10 +- .../Lib/site-packages/setuptools/_reqs.py | 24 +- .../_vendor/importlib_metadata/__init__.py | 295 +- .../_vendor/importlib_metadata/_adapters.py | 22 + .../_vendor/importlib_metadata/_compat.py | 1 + .../_vendor/importlib_metadata/_meta.py | 9 +- .../_vendor/importlib_metadata/_py39compat.py | 35 + .../_vendor/importlib_resources/_common.py | 147 +- .../_vendor/importlib_resources/_compat.py | 10 + .../_vendor/importlib_resources/_legacy.py | 3 +- .../_vendor/importlib_resources/abc.py | 63 +- .../_vendor/importlib_resources/readers.py | 16 +- .../_vendor/importlib_resources/simple.py | 70 +- .../setuptools/_vendor/jaraco/context.py | 75 + .../setuptools/_vendor/packaging/__about__.py | 26 - .../setuptools/_vendor/packaging/__init__.py | 30 +- .../setuptools/_vendor/packaging/_elffile.py | 108 + .../_vendor/packaging/_manylinux.py | 153 +- .../_vendor/packaging/_musllinux.py | 72 +- .../setuptools/_vendor/packaging/_parser.py | 328 + .../_vendor/packaging/_tokenizer.py | 188 + .../setuptools/_vendor/packaging/markers.py | 199 +- .../_vendor/packaging/requirements.py | 123 +- .../_vendor/packaging/specifiers.py | 915 ++- .../setuptools/_vendor/packaging/tags.py | 75 +- .../setuptools/_vendor/packaging/utils.py | 11 +- .../setuptools/_vendor/packaging/version.py | 333 +- .../setuptools/_vendor/pyparsing/__init__.py | 331 - .../setuptools/_vendor/pyparsing/actions.py | 207 - .../setuptools/_vendor/pyparsing/common.py | 424 -- .../setuptools/_vendor/pyparsing/core.py | 5814 ----------------- .../_vendor/pyparsing/diagram/__init__.py | 642 -- .../_vendor/pyparsing/exceptions.py | 267 - .../setuptools/_vendor/pyparsing/helpers.py | 1088 --- .../setuptools/_vendor/pyparsing/results.py | 760 --- .../setuptools/_vendor/pyparsing/testing.py | 331 - .../setuptools/_vendor/pyparsing/unicode.py | 352 - .../setuptools/_vendor/pyparsing/util.py | 235 - .../site-packages/setuptools/build_meta.py | 2 +- .../setuptools/command/bdist_egg.py | 15 +- .../setuptools/command/develop.py | 17 +- .../setuptools/command/dist_info.py | 46 +- .../setuptools/command/editable_wheel.py | 98 +- .../setuptools/command/egg_info.py | 58 +- .../setuptools/command/install_egg_info.py | 5 +- .../setuptools/command/install_scripts.py | 9 +- .../setuptools/config/_apply_pyprojecttoml.py | 21 +- .../fastjsonschema_validations.py | 129 +- .../config/_validate_pyproject/formats.py | 18 +- .../setuptools/config/pyprojecttoml.py | 13 +- .../setuptools/config/setupcfg.py | 124 +- .../Lib/site-packages/setuptools/discovery.py | 35 +- .../Lib/site-packages/setuptools/dist.py | 51 +- .../setuptools/extern/__init__.py | 11 +- .../Lib/site-packages/setuptools/installer.py | 62 +- .../Lib/site-packages/setuptools/monkey.py | 6 - .../Lib/site-packages/setuptools/msvc.py | 17 - .../site-packages/setuptools/package_index.py | 40 +- .../Lib/site-packages/setuptools/version.py | 4 +- .../Lib/site-packages/setuptools/wheel.py | 29 +- .../six-1.16.0.dist-info/INSTALLER | 1 - .../six-1.16.0.dist-info/LICENSE | 18 - .../six-1.16.0.dist-info/METADATA | 49 - .../site-packages/six-1.16.0.dist-info/RECORD | 8 - .../site-packages/six-1.16.0.dist-info/WHEEL | 6 - .../six-1.16.0.dist-info/top_level.txt | 1 - .../python/Lib/site-packages/six.py | 998 --- .../wheel-0.38.4.dist-info/INSTALLER | 1 - .../wheel-0.38.4.dist-info/RECORD | 43 - .../wheel-0.38.4.dist-info/REQUESTED | 0 .../wheel-0.38.4.dist-info/WHEEL | 5 - .../wheel-0.38.4.dist-info/entry_points.txt | 5 - .../wheel-0.38.4.dist-info/top_level.txt | 1 - .../INSTALLER | 0 .../LICENSE.txt | 0 .../METADATA | 23 +- .../wheel-0.40.0.dist-info/RECORD | 63 + .../REQUESTED | 0 .../WHEEL | 3 +- .../wheel-0.40.0.dist-info/entry_points.txt | 6 + .../Lib/site-packages/wheel/__init__.py | 2 +- .../Lib/site-packages/wheel/bdist_wheel.py | 36 +- .../Lib/site-packages/wheel/cli/__init__.py | 49 + .../Lib/site-packages/wheel/cli/pack.py | 76 +- .../Lib/site-packages/wheel/cli/tags.py | 151 + .../Lib/site-packages/wheel/cli/unpack.py | 9 +- .../Lib/site-packages/wheel/macosx_libfile.py | 2 +- .../Lib/site-packages/wheel/metadata.py | 84 +- .../wheel/vendored/packaging/_elffile.py | 108 + .../wheel/vendored/packaging/_manylinux.py | 167 +- .../wheel/vendored/packaging/_musllinux.py | 80 +- .../wheel/vendored/packaging/_parser.py | 328 + .../wheel/vendored/packaging/_structures.py | 61 + .../wheel/vendored/packaging/_tokenizer.py | 188 + .../wheel/vendored/packaging/markers.py | 245 + .../wheel/vendored/packaging/requirements.py | 95 + .../wheel/vendored/packaging/specifiers.py | 1006 +++ .../wheel/vendored/packaging/tags.py | 120 +- .../wheel/vendored/packaging/utils.py | 141 + .../wheel/vendored/packaging/version.py | 563 ++ .../site-packages/wheel/vendored/vendor.txt | 1 + .../Lib/site-packages/wheel/wheelfile.py | 12 +- .../windows_amd64/python/Scripts/espefuse.exe | Bin 108448 -> 0 bytes .../python/Scripts/espsecure.exe | Bin 108449 -> 0 bytes .../windows_amd64/python/Scripts/esptool.exe | Bin 108447 -> 0 bytes .../windows_amd64/python/Scripts/pip.exe | Bin 108451 -> 108451 bytes .../windows_amd64/python/Scripts/pip3.10.exe | Bin 108451 -> 108451 bytes .../windows_amd64/python/Scripts/pip3.exe | Bin 108451 -> 108451 bytes .../python/Scripts/pyserial-miniterm.exe | Bin 108450 -> 108450 bytes .../python/Scripts/pyserial-ports.exe | Bin 108452 -> 108452 bytes .../windows_amd64/python/Scripts/wheel.exe | Bin 108438 -> 108438 bytes 630 files changed, 14864 insertions(+), 111768 deletions(-) delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/_cffi_backend.cp310-win_amd64.pyd delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/bitstring.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyd delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyi delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust.pyd delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/py.typed delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c2.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c3.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/INSTALLER rename dependencies/windows_amd64/python/Lib/site-packages/{bitstring-3.1.9.dist-info => pip-23.0.1.dist-info}/INSTALLER (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => pip-23.0.1.dist-info}/LICENSE.txt (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => pip-23.0.1.dist-info}/METADATA (99%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => pip-23.0.1.dist-info}/RECORD (81%) rename dependencies/windows_amd64/python/Lib/site-packages/{esptool-4.4.dist-info => pip-23.0.1.dist-info}/REQUESTED (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{esptool-4.4.dist-info => pip-23.0.1.dist-info}/WHEEL (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => pip-23.0.1.dist-info}/entry_points.txt (69%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => pip-23.0.1.dist-info}/top_level.txt (100%) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/macromanprober.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/resultdict.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/utils.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/build.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/check.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/colorlog.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/dirtools.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/envbuild.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/meta.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py rename dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/{pep517 => pyproject_hooks}/_compat.py (100%) rename dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/{pep517/wrappers.py => pyproject_hooks/_impl.py} (63%) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py rename dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/{pep517/in_process => pyproject_hooks/_in_process}/_in_process.py (96%) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/_null_file.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/appdirs.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/android.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/WHEEL rename dependencies/windows_amd64/python/Lib/site-packages/{cffi-1.15.1.dist-info => setuptools-67.6.1.dist-info}/INSTALLER (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{setuptools-65.6.3.dist-info => setuptools-67.6.1.dist-info}/LICENSE (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{setuptools-65.6.3.dist-info => setuptools-67.6.1.dist-info}/METADATA (96%) rename dependencies/windows_amd64/python/Lib/site-packages/{setuptools-65.6.3.dist-info => setuptools-67.6.1.dist-info}/RECORD (60%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => setuptools-67.6.1.dist-info}/REQUESTED (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{bitstring-3.1.9.dist-info => setuptools-67.6.1.dist-info}/WHEEL (65%) rename dependencies/windows_amd64/python/Lib/site-packages/{setuptools-65.6.3.dist-info => setuptools-67.6.1.dist-info}/entry_points.txt (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{setuptools-65.6.3.dist-info => setuptools-67.6.1.dist-info}/top_level.txt (100%) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_normalization.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_parser.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/common.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/core.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/results.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/util.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/six.py delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/INSTALLER delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/REQUESTED delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/WHEEL delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/entry_points.txt delete mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/top_level.txt rename dependencies/windows_amd64/python/Lib/site-packages/{cryptography-39.0.0.dist-info => wheel-0.40.0.dist-info}/INSTALLER (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{wheel-0.38.4.dist-info => wheel-0.40.0.dist-info}/LICENSE.txt (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{wheel-0.38.4.dist-info => wheel-0.40.0.dist-info}/METADATA (87%) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/RECORD rename dependencies/windows_amd64/python/Lib/site-packages/{setuptools-65.6.3.dist-info => wheel-0.40.0.dist-info}/REQUESTED (100%) rename dependencies/windows_amd64/python/Lib/site-packages/{pip-22.3.1.dist-info => wheel-0.40.0.dist-info}/WHEEL (64%) create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/entry_points.txt create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/tags.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_elffile.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_parser.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_structures.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_tokenizer.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/markers.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/requirements.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/specifiers.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/utils.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/version.py create mode 100644 dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/vendor.txt delete mode 100644 dependencies/windows_amd64/python/Scripts/espefuse.exe delete mode 100644 dependencies/windows_amd64/python/Scripts/espsecure.exe delete mode 100644 dependencies/windows_amd64/python/Scripts/esptool.exe diff --git a/dependencies/windows_amd64/portablepy.ps1 b/dependencies/windows_amd64/portablepy.ps1 index dcbc37610..357df0293 100644 --- a/dependencies/windows_amd64/portablepy.ps1 +++ b/dependencies/windows_amd64/portablepy.ps1 @@ -61,8 +61,10 @@ Invoke-WebRequest -OutFile "$($destination)get-pip.py" "https://bootstrap.pypa.i "Installing pyserial" & "$($destination)python.exe" -m pip install pyserial +<# Do not install esptool, the expresslrs firmware already bundles it "Installing esptool" & "$($destination)python.exe" -m pip install esptool +#> "Cleaning up" Remove-Item -Path "$($package)" -Confirm:$false -Force diff --git a/dependencies/windows_amd64/python/Lib/site-packages/_cffi_backend.cp310-win_amd64.pyd b/dependencies/windows_amd64/python/Lib/site-packages/_cffi_backend.cp310-win_amd64.pyd deleted file mode 100644 index b4cfe5120de1803b278c8216854e3888868af2db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181248 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 z

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%0DXYJ0|)Oy-DY9q#s(P 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 Bt4ja? diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE deleted file mode 100644 index a5ea96cb2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License - -Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA deleted file mode 100644 index 9ec1098ad..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/METADATA +++ /dev/null @@ -1,118 +0,0 @@ -Metadata-Version: 2.1 -Name: bitstring -Version: 3.1.9 -Summary: Simple construction, analysis and modification of binary data. -Home-page: https://github.com/scott-griffiths/bitstring -Author: Scott Griffiths -Author-email: dr.scottgriffiths@gmail.com -License: The MIT License: http://www.opensource.org/licenses/mit-license.php -Download-URL: https://pypi.python.org/pypi/bitstring/ -Platform: all -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Operating System :: OS Independent -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.2 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Topic :: Software Development :: Libraries :: Python Modules - - - -.. image:: /doc/bitstring_logo_small.png - -**bitstring** is a pure Python module designed to help make -the creation and analysis of binary data as simple and natural as possible. - -Bitstrings can be constructed from integers (big and little endian), hex, -octal, binary, strings or files. They can be sliced, joined, reversed, -inserted into, overwritten, etc. with simple functions or slice notation. -They can also be read from, searched and replaced, and navigated in, -similar to a file or stream. - -bitstring is open source software, and has been released under the MIT -licence. - -This module works in both Python 2.7 and Python 3.6+. - -Installation ------------- - -Probably all you need to do is:: - - pip install bitstring - - -Documentation -------------- -The manual for the bitstring module is available here -. It contains a walk-through of all -the features and a complete reference section. - -It is also available as a PDF at . - - -Simple Examples ---------------- -Creation:: - - >>> a = BitArray(bin='00101') - >>> b = Bits(a_file_object) - >>> c = BitArray('0xff, 0b101, 0o65, uint:6=22') - >>> d = pack('intle:16, hex=a, 0b1', 100, a='0x34f') - >>> e = pack('<16h', *range(16)) - -Different interpretations, slicing and concatenation:: - - >>> a = BitArray('0x1af') - >>> a.hex, a.bin, a.uint - ('1af', '000110101111', 431) - >>> a[10:3:-1].bin - '1110101' - >>> '0b100' + 3*a - BitArray('0x835e35e35, 0b111') - -Reading data sequentially:: - - >>> b = BitStream('0x160120f') - >>> b.read(12).hex - '160' - >>> b.pos = 0 - >>> b.read('uint:12') - 352 - >>> b.readlist('uint:12, bin:3') - [288, '111'] - -Searching, inserting and deleting:: - - >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f' - >>> c.find('0x48') - (8,) - >>> c.replace('0b001', '0xabc') - >>> c.insert('0b0000', pos=3) - >>> del c[12:16] - -Unit Tests ----------- - -The 500+ unit tests should all pass for Python 2.7 and later. To run them, from the `test` -directory run:: - - python -m unittest discover - ----- - -The bitstring module has been released as open source under the MIT License. -Copyright (c) 2006 Scott Griffiths - -For more information see the project's homepage on GitHub: - - - - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD deleted file mode 100644 index e6f631a67..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -__pycache__/bitstring.cpython-310.pyc,, -bitstring-3.1.9.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -bitstring-3.1.9.dist-info/LICENSE,sha256=qenFhBBtT5aGyaaqA25Qe15wRGM7istDZbwHTY0ARxE,1127 -bitstring-3.1.9.dist-info/METADATA,sha256=o79a_RpHIO_5JhwVLILTLS48OLLS5_fOHkzO37cGM-M,3501 -bitstring-3.1.9.dist-info/RECORD,, -bitstring-3.1.9.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92 -bitstring-3.1.9.dist-info/top_level.txt,sha256=9Xh4qfKH0fMhwxzzkSBk3uzTRGiYPPM1FuYFAgCB-ks,10 -bitstring.py,sha256=3WSVGMf9K8qpzIU8_rvVEzL8L7uNoKapOikGxsOxd-0,179627 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt deleted file mode 100644 index 3ef76dc10..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -bitstring diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring.py b/dependencies/windows_amd64/python/Lib/site-packages/bitstring.py deleted file mode 100644 index 22bd0dd40..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/bitstring.py +++ /dev/null @@ -1,4469 +0,0 @@ -#!/usr/bin/env python -r""" -This package defines classes that simplify bit-wise creation, manipulation and -interpretation of data. - -Classes: - -Bits -- An immutable container for binary data. -BitArray -- A mutable container for binary data. -ConstBitStream -- An immutable container with streaming methods. -BitStream -- A mutable container with streaming methods. - - Bits (base class) - / \ - + mutating methods / \ + streaming methods - / \ - BitArray ConstBitStream - \ / - \ / - \ / - BitStream - -Functions: - -pack -- Create a BitStream from a format string. - -Exceptions: - -Error -- Module exception base class. -CreationError -- Error during creation. -InterpretError -- Inappropriate interpretation of binary data. -ByteAlignError -- Whole byte position or length needed. -ReadError -- Reading or peeking past the end of a bitstring. - -https://github.com/scott-griffiths/bitstring -""" - -__licence__ = """ -The MIT License - -Copyright (c) 2006 Scott Griffiths (dr.scottgriffiths@gmail.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -""" - -__version__ = "3.1.9" - -__author__ = "Scott Griffiths" - -import numbers -import copy -import sys -import re -import binascii -import mmap -import os -import struct -import operator -import array -import io -import collections - -try: - collectionsAbc = collections.abc -except AttributeError: # Python 2.7 - collectionsAbc = collections - -byteorder = sys.byteorder - -bytealigned = False -"""Determines whether a number of methods default to working only on byte boundaries.""" - - -# Maximum number of digits to use in __str__ and __repr__. -MAX_CHARS = 250 - -# Maximum size of caches used for speed optimisations. -CACHE_SIZE = 1000 - -# Set this to True for extra assertions for debugging. -_debug = False - - -class Error(Exception): - """Base class for errors in the bitstring module.""" - - def __init__(self, *params): - self.msg = params[0] if params else '' - self.params = params[1:] - - def __str__(self): - if self.params: - return self.msg.format(*self.params) - return self.msg - - -class ReadError(Error, IndexError): - """Reading or peeking past the end of a bitstring.""" - - def __init__(self, *params): - Error.__init__(self, *params) - - -class InterpretError(Error, ValueError): - """Inappropriate interpretation of binary data.""" - - def __init__(self, *params): - Error.__init__(self, *params) - - -class ByteAlignError(Error): - """Whole-byte position or length needed.""" - - def __init__(self, *params): - Error.__init__(self, *params) - - -class CreationError(Error, ValueError): - """Inappropriate argument during bitstring creation.""" - - def __init__(self, *params): - Error.__init__(self, *params) - - -class ConstByteStore(object): - """Stores raw bytes together with a bit offset and length. - - Used internally - not part of public interface. - """ - - __slots__ = ('offset', '_rawarray', 'bitlength') - - def __init__(self, data, bitlength=None, offset=None): - """data is either a bytearray or a MmapByteArray""" - self._rawarray = data - if offset is None: - offset = 0 - if bitlength is None: - bitlength = 8 * len(data) - offset - self.offset = offset - self.bitlength = bitlength - - def __iter__(self): - start_byte, start_bit = divmod(self.offset, 8) - end_byte, end_bit = divmod(self.offset + self.bitlength, 8) - - for byte_index in xrange(start_byte, end_byte): - byte = self._rawarray[byte_index] - for bit in range(start_bit, 8): - yield bool(byte & (128 >> bit)) - start_bit = 0 - - if end_bit: - byte = self._rawarray[end_byte] - for bit in range(start_bit, end_bit): - yield bool(byte & (128 >> bit)) - - def _getbit_lsb0(self, pos): - assert 0 <= pos < self.bitlength - pos = self.bitlength - pos - 1 - byte, bit = divmod(self.offset + pos, 8) - return bool(self._rawarray[byte] & (128 >> bit)) - - def _getbit_msb0(self, pos): - assert 0 <= pos < self.bitlength - byte, bit = divmod(self.offset + pos, 8) - return bool(self._rawarray[byte] & (128 >> bit)) - - def getbyte(self, pos): - """Direct access to byte data.""" - return self._rawarray[pos] - - def getbyteslice(self, start, end): - """Direct access to byte data.""" - c = self._rawarray[start:end] - return c - - @property - def bytelength(self): - if not self.bitlength: - return 0 - sb = self.offset // 8 - eb = (self.offset + self.bitlength - 1) // 8 - return eb - sb + 1 - - def __copy__(self): - return ByteStore(self._rawarray[:], self.bitlength, self.offset) - - def _appendstore(self, store): - """Join another store on to the end of this one.""" - if not store.bitlength: - return - # Set new array offset to the number of bits in the final byte of current array. - store = offsetcopy(store, (self.offset + self.bitlength) % 8) - if store.offset: - # first do the byte with the join. - joinval = (self._rawarray.pop() & (255 ^ (255 >> store.offset)) | - (store.getbyte(0) & (255 >> store.offset))) - self._rawarray.append(joinval) - self._rawarray.extend(store._rawarray[1:]) - else: - self._rawarray.extend(store._rawarray) - self.bitlength += store.bitlength - - def _prependstore(self, store): - """Join another store on to the start of this one.""" - if not store.bitlength: - return - # Set the offset of copy of store so that it's final byte - # ends in a position that matches the offset of self, - # then join self on to the end of it. - store = offsetcopy(store, (self.offset - store.bitlength) % 8) - assert (store.offset + store.bitlength) % 8 == self.offset % 8 - bit_offset = self.offset % 8 - if bit_offset: - # first do the byte with the join. - joinval = (store.getbyte(-1) & (255 ^ (255 >> bit_offset)) | - (self._rawarray[self.byteoffset] & (255 >> bit_offset))) - store._rawarray[-1] = joinval - store._rawarray.extend(self._rawarray[self.byteoffset + 1: self.byteoffset + self.bytelength]) - else: - store._rawarray.extend(self._rawarray[self.byteoffset: self.byteoffset + self.bytelength]) - self._rawarray = store._rawarray - self.offset = store.offset - self.bitlength += store.bitlength - - @property - def byteoffset(self): - return self.offset // 8 - - @property - def rawbytes(self): - return self._rawarray - - -class ByteStore(ConstByteStore): - """Adding mutating methods to ConstByteStore - - Used internally - not part of public interface. - """ - __slots__ = () - - def _setbit_lsb0(self, pos): - assert 0 <= pos < self.bitlength - pos = self.bitlength - pos - 1 - byte, bit = divmod(self.offset + pos, 8) - self._rawarray[byte] |= (128 >> bit) - - def _setbit_msb0(self, pos): - assert 0 <= pos < self.bitlength - byte, bit = divmod(self.offset + pos, 8) - self._rawarray[byte] |= (128 >> bit) - - def _unsetbit_lsb0(self, pos): - assert 0 <= pos < self.bitlength - pos = self.bitlength - pos - 1 - byte, bit = divmod(self.offset + pos, 8) - self._rawarray[byte] &= ~(128 >> bit) - - def _unsetbit_msb0(self, pos): - assert 0 <= pos < self.bitlength - byte, bit = divmod(self.offset + pos, 8) - self._rawarray[byte] &= ~(128 >> bit) - - def _invertbit_lsb0(self, pos): - assert 0 <= pos < self.bitlength - pos = self.bitlength - pos - 1 - byte, bit = divmod(self.offset + pos, 8) - self._rawarray[byte] ^= (128 >> bit) - - def _invertbit_msb0(self, pos): - assert 0 <= pos < self.bitlength - byte, bit = divmod(self.offset + pos, 8) - self._rawarray[byte] ^= (128 >> bit) - - def setbyte(self, pos, value): - self._rawarray[pos] = value - - def setbyteslice(self, start, end, value): - self._rawarray[start:end] = value - - -def offsetcopy(s, newoffset): - """Return a copy of a ByteStore with the newoffset. - - Not part of public interface. - """ - assert 0 <= newoffset < 8 - if not s.bitlength: - return copy.copy(s) - else: - if newoffset == s.offset % 8: - return type(s)(s.getbyteslice(s.byteoffset, s.byteoffset + s.bytelength), s.bitlength, newoffset) - newdata = [] - d = s._rawarray - assert newoffset != s.offset % 8 - if newoffset < s.offset % 8: - # We need to shift everything left - shiftleft = s.offset % 8 - newoffset - # First deal with everything except for the final byte - for x in range(s.byteoffset, s.byteoffset + s.bytelength - 1): - newdata.append(((d[x] << shiftleft) & 0xff) + (d[x + 1] >> (8 - shiftleft))) - bits_in_last_byte = (s.offset + s.bitlength) % 8 - if not bits_in_last_byte: - bits_in_last_byte = 8 - if bits_in_last_byte > shiftleft: - newdata.append((d[s.byteoffset + s.bytelength - 1] << shiftleft) & 0xff) - else: # newoffset > s._offset % 8 - shiftright = newoffset - s.offset % 8 - newdata.append(s.getbyte(0) >> shiftright) - for x in range(s.byteoffset + 1, s.byteoffset + s.bytelength): - newdata.append(((d[x - 1] << (8 - shiftright)) & 0xff) + (d[x] >> shiftright)) - bits_in_last_byte = (s.offset + s.bitlength) % 8 - if not bits_in_last_byte: - bits_in_last_byte = 8 - if bits_in_last_byte + shiftright > 8: - newdata.append((d[s.byteoffset + s.bytelength - 1] << (8 - shiftright)) & 0xff) - new_s = type(s)(bytearray(newdata), s.bitlength, newoffset) - assert new_s.offset == newoffset - return new_s - - -def equal(a, b): - """Return True if ByteStores a == b. - - Not part of public interface. - """ - # We want to return False for inequality as soon as possible, which - # means we get lots of special cases. - # First the easy one - compare lengths: - a_bitlength = a.bitlength - b_bitlength = b.bitlength - if a_bitlength != b_bitlength: - return False - if not a_bitlength: - assert b_bitlength == 0 - return True - # Make 'a' the one with the smaller offset - if (a.offset % 8) > (b.offset % 8): - a, b = b, a - # and create some aliases - a_bitoff = a.offset % 8 - b_bitoff = b.offset % 8 - a_byteoffset = a.byteoffset - b_byteoffset = b.byteoffset - a_bytelength = a.bytelength - b_bytelength = b.bytelength - da = a._rawarray - db = b._rawarray - - # If they are pointing to the same data, they must be equal - if da is db and a.offset == b.offset: - return True - - if a_bitoff == b_bitoff: - bits_spare_in_last_byte = 8 - (a_bitoff + a_bitlength) % 8 - if bits_spare_in_last_byte == 8: - bits_spare_in_last_byte = 0 - # Special case for a, b contained in a single byte - if a_bytelength == 1: - a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength) - b_val = ((db[b_byteoffset] << b_bitoff) & 0xff) >> (8 - b_bitlength) - return a_val == b_val - # Otherwise check first byte - if da[a_byteoffset] & (0xff >> a_bitoff) != db[b_byteoffset] & (0xff >> b_bitoff): - return False - # then everything up to the last - b_a_offset = b_byteoffset - a_byteoffset - for x in range(1 + a_byteoffset, a_byteoffset + a_bytelength - 1): - if da[x] != db[b_a_offset + x]: - return False - # and finally the last byte - return (da[a_byteoffset + a_bytelength - 1] >> bits_spare_in_last_byte == - db[b_byteoffset + b_bytelength - 1] >> bits_spare_in_last_byte) - - assert a_bitoff != b_bitoff - # This is how much we need to shift a to the right to compare with b: - shift = b_bitoff - a_bitoff - # Special case for b only one byte long - if b_bytelength == 1: - assert a_bytelength == 1 - a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength) - b_val = ((db[b_byteoffset] << b_bitoff) & 0xff) >> (8 - b_bitlength) - return a_val == b_val - # Special case for a only one byte long - if a_bytelength == 1: - assert b_bytelength == 2 - a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength) - b_val = ((db[b_byteoffset] << 8) + db[b_byteoffset + 1]) << b_bitoff - b_val &= 0xffff - b_val >>= 16 - b_bitlength - return a_val == b_val - - # Compare first byte of b with bits from first byte of a - if (da[a_byteoffset] & (0xff >> a_bitoff)) >> shift != db[b_byteoffset] & (0xff >> b_bitoff): - return False - # Now compare every full byte of b with bits from 2 bytes of a - for x in range(1, b_bytelength - 1): - # Construct byte from 2 bytes in a to compare to byte in b - b_val = db[b_byteoffset + x] - a_val = ((da[a_byteoffset + x - 1] << 8) + da[a_byteoffset + x]) >> shift - a_val &= 0xff - if a_val != b_val: - return False - - # Now check bits in final byte of b - final_b_bits = (b.offset + b_bitlength) % 8 - if not final_b_bits: - final_b_bits = 8 - b_val = db[b_byteoffset + b_bytelength - 1] >> (8 - final_b_bits) - final_a_bits = (a.offset + a_bitlength) % 8 - if not final_a_bits: - final_a_bits = 8 - if b.bytelength > a_bytelength: - assert b_bytelength == a_bytelength + 1 - a_val = da[a_byteoffset + a_bytelength - 1] >> (8 - final_a_bits) - a_val &= 0xff >> (8 - final_b_bits) - return a_val == b_val - assert a_bytelength == b_bytelength - a_val = da[a_byteoffset + a_bytelength - 2] << 8 - a_val += da[a_byteoffset + a_bytelength - 1] - a_val >>= (8 - final_a_bits) - a_val &= 0xff >> (8 - final_b_bits) - return a_val == b_val - - -class MmapByteArray(object): - """Looks like a bytearray, but from an mmap. - - Not part of public interface. - """ - - __slots__ = ('filemap', 'filelength', 'source', 'byteoffset', 'bytelength') - - def __init__(self, source, bytelength=None, byteoffset=None): - self.source = source - source.seek(0, os.SEEK_END) - self.filelength = source.tell() - if byteoffset is None: - byteoffset = 0 - if bytelength is None: - bytelength = self.filelength - byteoffset - self.byteoffset = byteoffset - self.bytelength = bytelength - self.filemap = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ) - - def __getitem__(self, key): - try: - start = key.start - stop = key.stop - except AttributeError: - try: - assert 0 <= key < self.bytelength - return ord(self.filemap[key + self.byteoffset]) - except TypeError: - # for Python 3 - return self.filemap[key + self.byteoffset] - else: - if start is None: - start = 0 - if stop is None: - stop = self.bytelength - assert key.step is None - assert 0 <= start < self.bytelength - assert 0 <= stop <= self.bytelength - s = slice(start + self.byteoffset, stop + self.byteoffset) - return bytearray(self.filemap.__getitem__(s)) - - def __len__(self): - return self.bytelength - - -# This creates a dictionary for every possible byte with the value being -# the key with its bits reversed. -BYTE_REVERSAL_DICT = dict() - -# For Python 2.7/ 3.x coexistence -# Yes this is very very hacky. -if sys.version_info[0] == 2: - for i in range(256): - BYTE_REVERSAL_DICT[i] = chr(int("{0:08b}".format(i)[::-1], 2)) -else: - for i in range(256): - BYTE_REVERSAL_DICT[i] = bytes([int("{0:08b}".format(i)[::-1], 2)]) - from io import IOBase as file - xrange = range - basestring = str - -# Python 2.x octals start with '0', in Python 3 it's '0o' -LEADING_OCT_CHARS = len(oct(1)) - 1 - - -def tidy_input_string(s): - """Return string made lowercase and with all whitespace removed.""" - s = ''.join(s.split()).lower() - return s - - -INIT_NAMES = ('uint', 'int', 'ue', 'se', 'sie', 'uie', 'hex', 'oct', 'bin', 'bits', - 'uintbe', 'intbe', 'uintle', 'intle', 'uintne', 'intne', - 'float', 'floatbe', 'floatle', 'floatne', 'bytes', 'bool', 'pad') - -TOKEN_RE = re.compile(r'(?P' + '|'.join(INIT_NAMES) + - r')(:(?P[^=]+))?(=(?P.*))?$', re.IGNORECASE) -DEFAULT_UINT = re.compile(r'(?P[^=]+)?(=(?P.*))?$', re.IGNORECASE) - -MULTIPLICATIVE_RE = re.compile(r'(?P.*)\*(?P.+)') - -# Hex, oct or binary literals -LITERAL_RE = re.compile(r'(?P0([xob]))(?P.+)', re.IGNORECASE) - -# An endianness indicator followed by one or more struct.pack codes -STRUCT_PACK_RE = re.compile(r'(?P[<>@])?(?P(?:\d*[bBhHlLqQfd])+)$') - -# A number followed by a single character struct.pack code -STRUCT_SPLIT_RE = re.compile(r'\d*[bBhHlLqQfd]') - -# These replicate the struct.pack codes -# Big-endian -REPLACEMENTS_BE = {'b': 'intbe:8', 'B': 'uintbe:8', - 'h': 'intbe:16', 'H': 'uintbe:16', - 'l': 'intbe:32', 'L': 'uintbe:32', - 'q': 'intbe:64', 'Q': 'uintbe:64', - 'f': 'floatbe:32', 'd': 'floatbe:64'} -# Little-endian -REPLACEMENTS_LE = {'b': 'intle:8', 'B': 'uintle:8', - 'h': 'intle:16', 'H': 'uintle:16', - 'l': 'intle:32', 'L': 'uintle:32', - 'q': 'intle:64', 'Q': 'uintle:64', - 'f': 'floatle:32', 'd': 'floatle:64'} - -# Size in bytes of all the pack codes. -PACK_CODE_SIZE = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4, 'L': 4, - 'q': 8, 'Q': 8, 'f': 4, 'd': 8} - -_tokenname_to_initialiser = {'hex': 'hex', '0x': 'hex', '0X': 'hex', 'oct': 'oct', - '0o': 'oct', '0O': 'oct', 'bin': 'bin', '0b': 'bin', - '0B': 'bin', 'bits': 'auto', 'bytes': 'bytes', 'pad': 'pad'} - - -def structparser(token): - """Parse struct-like format string token into sub-token list.""" - m = STRUCT_PACK_RE.match(token) - if not m: - return [token] - else: - endian = m.group('endian') - if endian is None: - return [token] - # Split the format string into a list of 'q', '4h' etc. - formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt')) - # Now deal with multiplicative factors, 4h -> hhhh etc. - fmt = ''.join([f[-1] * int(f[:-1]) if len(f) != 1 else - f for f in formatlist]) - if endian == '@': - # Native endianness - if byteorder == 'little': - endian = '<' - else: - assert byteorder == 'big' - endian = '>' - if endian == '<': - tokens = [REPLACEMENTS_LE[c] for c in fmt] - else: - assert endian == '>' - tokens = [REPLACEMENTS_BE[c] for c in fmt] - return tokens - - -def tokenparser(fmt, keys=None, token_cache={}): - """Divide the format string into tokens and parse them. - - Return stretchy token and list of [initialiser, length, value] - initialiser is one of: hex, oct, bin, uint, int, se, ue, 0x, 0o, 0b etc. - length is None if not known, as is value. - - If the token is in the keyword dictionary (keys) then it counts as a - special case and isn't messed with. - - tokens must be of the form: [factor*][initialiser][:][length][=value] - - """ - try: - return token_cache[(fmt, keys)] - except KeyError: - token_key = (fmt, keys) - # Very inefficient expanding of brackets. - fmt = expand_brackets(fmt) - # Split tokens by ',' and remove whitespace - # The meta_tokens can either be ordinary single tokens or multiple - # struct-format token strings. - meta_tokens = (''.join(f.split()) for f in fmt.split(',')) - return_values = [] - stretchy_token = False - for meta_token in meta_tokens: - # See if it has a multiplicative factor - m = MULTIPLICATIVE_RE.match(meta_token) - if not m: - factor = 1 - else: - factor = int(m.group('factor')) - meta_token = m.group('token') - # See if it's a struct-like format - tokens = structparser(meta_token) - ret_vals = [] - for token in tokens: - if keys and token in keys: - # Don't bother parsing it, it's a keyword argument - ret_vals.append([token, None, None]) - continue - value = length = None - if token == '': - continue - # Match literal tokens of the form 0x... 0o... and 0b... - m = LITERAL_RE.match(token) - if m: - name = m.group('name') - value = m.group('value') - ret_vals.append([name, length, value]) - continue - # Match everything else: - m1 = TOKEN_RE.match(token) - if not m1: - # and if you don't specify a 'name' then the default is 'uint': - m2 = DEFAULT_UINT.match(token) - if not m2: - raise ValueError("Don't understand token '{0}'.".format(token)) - if m1: - name = m1.group('name') - length = m1.group('len') - if m1.group('value'): - value = m1.group('value') - else: - assert m2 - name = 'uint' - length = m2.group('len') - if m2.group('value'): - value = m2.group('value') - if name == 'bool': - if length is not None and length != '1': - raise ValueError("You can only specify one bit sized bool tokens or leave unspecified.") - length = 1 - if length is None and name not in ('se', 'ue', 'sie', 'uie'): - stretchy_token = True - if length is not None: - # Try converting length to int, otherwise check it's a key. - try: - length = int(length) - if length < 0: - raise Error - # For the 'bytes' token convert length to bits. - if name == 'bytes': - length *= 8 - except Error: - raise ValueError("Can't read a token with a negative length.") - except ValueError: - if not keys or length not in keys: - raise ValueError("Don't understand length '{0}' of token.".format(length)) - ret_vals.append([name, length, value]) - # This multiplies by the multiplicative factor, but this means that - # we can't allow keyword values as multipliers (e.g. n*uint:8). - # The only way to do this would be to return the factor in some fashion - # (we can't use the key's value here as it would mean that we couldn't - # sensibly continue to cache the function's results. (TODO). - return_values.extend(ret_vals * factor) - return_values = [tuple(x) for x in return_values] - if len(token_cache) < CACHE_SIZE: - token_cache[token_key] = stretchy_token, return_values - return stretchy_token, return_values - - -# Looks for first number*( -BRACKET_RE = re.compile(r'(?P\d+)\*\(') - - -def expand_brackets(s): - """Remove whitespace and expand all brackets.""" - s = ''.join(s.split()) - while True: - start = s.find('(') - if start == -1: - break - count = 1 # Number of hanging open brackets - p = start + 1 - while p < len(s): - if s[p] == '(': - count += 1 - if s[p] == ')': - count -= 1 - if not count: - break - p += 1 - if count: - raise ValueError("Unbalanced parenthesis in '{0}'.".format(s)) - if start == 0 or s[start - 1] != '*': - s = s[0:start] + s[start + 1:p] + s[p + 1:] - else: - m = BRACKET_RE.search(s) - if m: - factor = int(m.group('factor')) - matchstart = m.start('factor') - s = s[0:matchstart] + (factor - 1) * (s[start + 1:p] + ',') + s[start + 1:p] + s[p + 1:] - else: - raise ValueError("Failed to parse '{0}'.".format(s)) - return s - - -# This converts a single octal digit to 3 bits. -OCT_TO_BITS = ['{0:03b}'.format(i) for i in xrange(8)] - -# A dictionary of number of 1 bits contained in binary representation of any byte -BIT_COUNT = dict(zip(xrange(256), [bin(i).count('1') for i in xrange(256)])) - - -class Bits(object): - """A container holding an immutable sequence of bits. - - For a mutable container use the BitArray class instead. - - Methods: - - all() -- Check if all specified bits are set to 1 or 0. - any() -- Check if any of specified bits are set to 1 or 0. - count() -- Count the number of bits set to 1 or 0. - cut() -- Create generator of constant sized chunks. - endswith() -- Return whether the bitstring ends with a sub-string. - find() -- Find a sub-bitstring in the current bitstring. - findall() -- Find all occurrences of a sub-bitstring in the current bitstring. - join() -- Join bitstrings together using current bitstring. - rfind() -- Seek backwards to find a sub-bitstring. - split() -- Create generator of chunks split by a delimiter. - startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobytes() -- Return bitstring as bytes, padding if needed. - tofile() -- Write bitstring to file, padding if needed. - unpack() -- Interpret bits using format string. - - Special methods: - - Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. - - Properties: - - bin -- The bitstring as a binary string. - bool -- For single bit bitstrings, interpret as True or False. - bytes -- The bitstring as a bytes object. - float -- Interpret as a floating point number. - floatbe -- Interpret as a big-endian floating point number. - floatle -- Interpret as a little-endian floating point number. - floatne -- Interpret as a native-endian floating point number. - hex -- The bitstring as a hexadecimal string. - int -- Interpret as a two's complement signed integer. - intbe -- Interpret as a big-endian signed integer. - intle -- Interpret as a little-endian signed integer. - intne -- Interpret as a native-endian signed integer. - len -- Length of the bitstring in bits. - oct -- The bitstring as an octal string. - se -- Interpret as a signed exponential-Golomb code. - ue -- Interpret as an unsigned exponential-Golomb code. - sie -- Interpret as a signed interleaved exponential-Golomb code. - uie -- Interpret as an unsigned interleaved exponential-Golomb code. - uint -- Interpret as a two's complement unsigned integer. - uintbe -- Interpret as a big-endian unsigned integer. - uintle -- Interpret as a little-endian unsigned integer. - uintne -- Interpret as a native-endian unsigned integer. - - """ - - __slots__ = ('_datastore') - - def __init__(self, auto=None, length=None, offset=None, **kwargs): - """Either specify an 'auto' initialiser: - auto -- a string of comma separated tokens, an integer, a file object, - a bytearray, a boolean iterable, an array or another bitstring. - - Or initialise via **kwargs with one (and only one) of: - bytes -- raw data as a string, for example read from a binary file. - bin -- binary string representation, e.g. '0b001010'. - hex -- hexadecimal string representation, e.g. '0x2ef' - oct -- octal string representation, e.g. '0o777'. - uint -- an unsigned integer. - int -- a signed integer. - float -- a floating point number. - uintbe -- an unsigned big-endian whole byte integer. - intbe -- a signed big-endian whole byte integer. - floatbe - a big-endian floating point number. - uintle -- an unsigned little-endian whole byte integer. - intle -- a signed little-endian whole byte integer. - floatle -- a little-endian floating point number. - uintne -- an unsigned native-endian whole byte integer. - intne -- a signed native-endian whole byte integer. - floatne -- a native-endian floating point number. - se -- a signed exponential-Golomb code. - ue -- an unsigned exponential-Golomb code. - sie -- a signed interleaved exponential-Golomb code. - uie -- an unsigned interleaved exponential-Golomb code. - bool -- a boolean (True or False). - filename -- a file which will be opened in binary read-only mode. - - Other keyword arguments: - length -- length of the bitstring in bits, if needed and appropriate. - It must be supplied for all integer and float initialisers. - offset -- bit offset to the data. These offset bits are - ignored and this is mainly intended for use when - initialising using 'bytes' or 'filename'. - - """ - pass - - def __new__(cls, auto=None, length=None, offset=None, _cache={}, **kwargs): - # For instances auto-initialised with a string we intern the - # instance for re-use. - try: - if isinstance(auto, basestring): - try: - return _cache[auto] - except KeyError: - x = object.__new__(Bits) - try: - _, tokens = tokenparser(auto) - except ValueError as e: - raise CreationError(*e.args) - if offset is not None: - raise CreationError("offset should not be specified when using string initialisation.") - if length is not None: - raise CreationError("length should not be specified when using string initialisation.") - x._datastore = ConstByteStore(bytearray(0), 0, 0) - for token in tokens: - x._datastore._appendstore(Bits._init_with_token(*token)._datastore) - assert x._assertsanity() - if len(_cache) < CACHE_SIZE: - _cache[auto] = x - return x - if type(auto) == Bits: - return auto - except TypeError: - pass - x = super(Bits, cls).__new__(cls) - x._datastore = ConstByteStore(b'') - x._initialise(auto, length, offset, **kwargs) - return x - - def _initialise(self, auto, length, offset, **kwargs): - if length is not None and length < 0: - raise CreationError("bitstring length cannot be negative.") - if offset is not None and offset < 0: - raise CreationError("offset must be >= 0.") - if auto is not None: - self._initialise_from_auto(auto, length, offset) - return - if not kwargs: - # No initialisers, so initialise with nothing or zero bits - if length is not None and length != 0: - data = bytearray((length + 7) // 8) - self._setbytes_unsafe(data, length, 0) - return - self._setbytes_unsafe(bytearray(0), 0, 0) - return - k, v = kwargs.popitem() - try: - init_without_length_or_offset[k](self, v) - if length is not None or offset is not None: - raise CreationError("Cannot use length or offset with this initialiser.") - except KeyError: - try: - init_with_length_only[k](self, v, length) - if offset is not None: - raise CreationError("Cannot use offset with this initialiser.") - except KeyError: - if offset is None: - offset = 0 - try: - init_with_length_and_offset[k](self, v, length, offset) - except KeyError: - raise CreationError("Unrecognised keyword '{0}' used to initialise.", k) - - def _initialise_from_auto(self, auto, length, offset): - if offset is None: - offset = 0 - self._setauto(auto, length, offset) - return - - def __iter__(self): - return iter(self._datastore) - - def __copy__(self): - """Return a new copy of the Bits for the copy module.""" - # Note that if you want a new copy (different ID), use _copy instead. - # The copy can return self as it's immutable. - return self - - def __lt__(self, other): - raise TypeError("unorderable type: {0}".format(type(self).__name__)) - - def __gt__(self, other): - raise TypeError("unorderable type: {0}".format(type(self).__name__)) - - def __le__(self, other): - raise TypeError("unorderable type: {0}".format(type(self).__name__)) - - def __ge__(self, other): - raise TypeError("unorderable type: {0}".format(type(self).__name__)) - - def __add__(self, bs): - """Concatenate bitstrings and return new bitstring. - - bs -- the bitstring to append. - - """ - bs = Bits(bs) - if bs.len <= self.len: - s = self._copy() - s._addright(bs) - else: - s = bs._copy() - s = self.__class__(s) - s._addleft(self) - return s - - def __radd__(self, bs): - """Append current bitstring to bs and return new bitstring. - - bs -- the string for the 'auto' initialiser that will be appended to. - - """ - bs = self._converttobitstring(bs) - return bs.__add__(self) - - def __getitem__(self, key): - """Return a new bitstring representing a slice of the current bitstring. - - Indices are in units of the step parameter (default 1 bit). - Stepping is used to specify the number of bits in each item. - - >>> print BitArray('0b00110')[1:4] - '0b011' - >>> print BitArray('0x00112233')[1:3:8] - '0x1122' - - """ - length = self.len - if isinstance(key, slice): - step = key.step if key.step is not None else 1 - if step != 1: - # convert to binary string and use string slicing - bs = self.__class__() - if _lsb0: - start = length - key.start - 1 if key.start is not None else None - stop = length - key.stop - 1 if key.stop is not None else None - bs._setbin_unsafe(self._getbin().__getitem__(slice(start, stop, -step))[::-1]) - else: - bs._setbin_unsafe(self._getbin().__getitem__(key)) - return bs - start, stop = 0, length - if key.start is not None: - start = key.start - if key.start < 0: - start += stop - if key.stop is not None: - stop = key.stop - if key.stop < 0: - stop += length - start = max(start, 0) - stop = min(stop, length) - if start < stop: - return self._slice(start, stop) - else: - return self.__class__() - else: - # single element - if key < 0: - key += length - if not 0 <= key < length: - raise IndexError("Slice index out of range.") - # Single bit, return True or False - return self._datastore.getbit(key) - - def __len__(self): - """Return the length of the bitstring in bits.""" - return self._getlength() - - def __str__(self): - """Return approximate string representation of bitstring for printing. - - Short strings will be given wholly in hexadecimal or binary. Longer - strings may be part hexadecimal and part binary. Very long strings will - be truncated with '...'. - - """ - length = self.len - if not length: - return '' - if length > MAX_CHARS * 4: - # Too long for hex. Truncate... - return ''.join(('0x', self._readhex(MAX_CHARS * 4, 0), '...')) - # If it's quite short and we can't do hex then use bin - if length < 32 and length % 4 != 0: - return '0b' + self.bin - # If we can use hex then do so - if not length % 4: - return '0x' + self.hex - # Otherwise first we do as much as we can in hex - # then add on 1, 2 or 3 bits on at the end - bits_at_end = length % 4 - return ''.join(('0x', self._readhex(length - bits_at_end, 0), - ', ', '0b', - self._readbin(bits_at_end, length - bits_at_end))) - - def __repr__(self): - """Return representation that could be used to recreate the bitstring. - - If the returned string is too long it will be truncated. See __str__(). - - """ - length = self.len - try: - pos = self._pos - pos_string = "" if pos == 0 else ", pos={0}".format(pos) - except AttributeError: - pos_string = "" - if isinstance(self._datastore._rawarray, MmapByteArray): - offsetstring = '' - if self._datastore.byteoffset or self._offset: - offsetstring = ", offset=%d" % (self._datastore._rawarray.byteoffset * 8 + self._offset) - lengthstring = ", length=%d" % length - return "{0}(filename='{1}'{2}{3}{4})".format(self.__class__.__name__, - self._datastore._rawarray.source.name, - lengthstring, offsetstring, pos_string) - else: - s = self.__str__() - lengthstring = '' - if s.endswith('...'): - lengthstring = " # length={0}".format(length) - return "{0}('{1}'{2}){3}".format(self.__class__.__name__, s, pos_string, lengthstring) - - def __eq__(self, bs): - """Return True if two bitstrings have the same binary representation. - - >>> BitArray('0b1110') == '0xe' - True - - """ - try: - bs = Bits(bs) - except TypeError: - return False - return equal(self._datastore, bs._datastore) - - def __ne__(self, bs): - """Return False if two bitstrings have the same binary representation. - - >>> BitArray('0b111') == '0x7' - False - - """ - return not self.__eq__(bs) - - def __invert__(self): - """Return bitstring with every bit inverted. - - Raises Error if the bitstring is empty. - - """ - if not self.len: - raise Error("Cannot invert empty bitstring.") - s = self._copy() - s._invert_all() - return s - - def __lshift__(self, n): - """Return bitstring with bits shifted by n to the left. - - n -- the number of bits to shift. Must be >= 0. - - """ - if n < 0: - raise ValueError("Cannot shift by a negative amount.") - if not self.len: - raise ValueError("Cannot shift an empty bitstring.") - n = min(n, self.len) - s = self._slice(n, self.len) - s._addright(Bits(n)) - return s - - def __rshift__(self, n): - """Return bitstring with bits shifted by n to the right. - - n -- the number of bits to shift. Must be >= 0. - - """ - if n < 0: - raise ValueError("Cannot shift by a negative amount.") - if not self.len: - raise ValueError("Cannot shift an empty bitstring.") - if not n: - return self._copy() - s = self.__class__(length=min(n, self.len)) - s._addright(self[:-n]) - return s - - def __mul__(self, n): - """Return bitstring consisting of n concatenations of self. - - Called for expression of the form 'a = b*3'. - n -- The number of concatenations. Must be >= 0. - - """ - if n < 0: - raise ValueError("Cannot multiply by a negative integer.") - if not n: - return self.__class__() - s = self._copy() - s._imul(n) - return s - - def __rmul__(self, n): - """Return bitstring consisting of n concatenations of self. - - Called for expressions of the form 'a = 3*b'. - n -- The number of concatenations. Must be >= 0. - - """ - return self.__mul__(n) - - def __and__(self, bs): - """Bit-wise 'and' between two bitstrings. Returns new bitstring. - - bs -- The bitstring to '&' with. - - Raises ValueError if the two bitstrings have differing lengths. - - """ - bs = Bits(bs) - if self.len != bs.len: - raise ValueError("Bitstrings must have the same length " - "for & operator.") - s = self._copy() - s._iand(bs) - return s - - def __rand__(self, bs): - """Bit-wise 'and' between two bitstrings. Returns new bitstring. - - bs -- the bitstring to '&' with. - - Raises ValueError if the two bitstrings have differing lengths. - - """ - return self.__and__(bs) - - def __or__(self, bs): - """Bit-wise 'or' between two bitstrings. Returns new bitstring. - - bs -- The bitstring to '|' with. - - Raises ValueError if the two bitstrings have differing lengths. - - """ - bs = Bits(bs) - if self.len != bs.len: - raise ValueError("Bitstrings must have the same length " - "for | operator.") - s = self._copy() - s._ior(bs) - return s - - def __ror__(self, bs): - """Bit-wise 'or' between two bitstrings. Returns new bitstring. - - bs -- The bitstring to '|' with. - - Raises ValueError if the two bitstrings have differing lengths. - - """ - return self.__or__(bs) - - def __xor__(self, bs): - """Bit-wise 'xor' between two bitstrings. Returns new bitstring. - - bs -- The bitstring to '^' with. - - Raises ValueError if the two bitstrings have differing lengths. - - """ - bs = Bits(bs) - if self.len != bs.len: - raise ValueError("Bitstrings must have the same length " - "for ^ operator.") - s = self._copy() - s._ixor(bs) - return s - - def __rxor__(self, bs): - """Bit-wise 'xor' between two bitstrings. Returns new bitstring. - - bs -- The bitstring to '^' with. - - Raises ValueError if the two bitstrings have differing lengths. - - """ - return self.__xor__(bs) - - def __contains__(self, bs): - """Return whether bs is contained in the current bitstring. - - bs -- The bitstring to search for. - - """ - # Don't want to change pos - try: - pos = self._pos - except AttributeError: - pass - found = Bits.find(self, bs, bytealigned=False) - try: - self._pos = pos - except UnboundLocalError: - pass - return bool(found) - - def __hash__(self): - """Return an integer hash of the object.""" - # We can't in general hash the whole bitstring (it could take hours!) - # So instead take some bits from the start and end. - if self.len <= 160: - # Use the whole bitstring. - shorter = self - else: - # Take 10 bytes from start and end - shorter = self[:80] + self[-80:] - h = 0 - for byte in shorter.tobytes(): - try: - h = (h << 4) + ord(byte) - except TypeError: - # Python 3 - h = (h << 4) + byte - g = h & 0xf0000000 - if g & (1 << 31): - h ^= (g >> 24) - h ^= g - return h % 1442968193 - - # This is only used in Python 2.x... - def __nonzero__(self): - """Return True if any bits are set to 1, otherwise return False.""" - return self.any(True) - - # ...whereas this is used in Python 3.x - __bool__ = __nonzero__ - - if _debug is True: - def _assertsanity(self): - """Check internal self consistency as a debugging aid.""" - assert self.len >= 0 - assert 0 <= self._offset, "offset={0}".format(self._offset) - assert (self.len + self._offset + 7) // 8 == self._datastore.bytelength + self._datastore.byteoffset, "len={0}, offset={1}, bytelength={2}, byteoffset={3}".format(self.len, self._offset, self._datastore.bytelength, self._datastore.byteoffset) - return True - else: - @staticmethod - def _assertsanity(): - return True - - @classmethod - def _init_with_token(cls, name, token_length, value): - if token_length is not None: - token_length = int(token_length) - if token_length == 0: - return cls() - # For pad token just return the length in zero bits - if name == 'pad': - return cls(token_length) - - if value is None: - if token_length is None: - error = "Token has no value ({0}=???).".format(name) - else: - error = "Token has no value ({0}:{1}=???).".format(name, token_length) - raise ValueError(error) - try: - b = cls(**{_tokenname_to_initialiser[name]: value}) - except KeyError: - if name in ('se', 'ue', 'sie', 'uie'): - b = cls(**{name: int(value)}) - elif name in ('uint', 'int', 'uintbe', 'intbe', 'uintle', 'intle', 'uintne', 'intne'): - b = cls(**{name: int(value), 'length': token_length}) - elif name in ('float', 'floatbe', 'floatle', 'floatne'): - b = cls(**{name: float(value), 'length': token_length}) - elif name == 'bool': - if value in (1, 'True', '1'): - b = cls(bool=True) - elif value in (0, 'False', '0'): - b = cls(bool=False) - else: - raise CreationError("bool token can only be 'True' or 'False'.") - else: - raise CreationError("Can't parse token name {0}.", name) - if token_length is not None and b.len != token_length: - msg = "Token with length {0} packed with value of length {1} ({2}:{3}={4})." - raise CreationError(msg, token_length, b.len, name, token_length, value) - return b - - def _clear(self): - """Reset the bitstring to an empty state.""" - self._datastore = ByteStore(bytearray(0)) - - def _setauto(self, s, length, offset): - """Set bitstring from a bitstring, file, bool, integer, array, iterable or string.""" - # As s can be so many different things it's important to do the checks - # in the correct order, as some types are also other allowed types. - # So basestring must be checked before Iterable - # and bytes/bytearray before Iterable but after basestring! - if isinstance(s, Bits): - if length is None: - length = s.len - offset - self._setbytes_unsafe(s._datastore.rawbytes, length, s._offset + offset) - return - - if isinstance(s, io.BytesIO): - if offset is None: - offset = 0 - if length is None: - length = s.seek(0, 2) * 8 - offset - byteoffset, offset = divmod(offset, 8) - bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset - if length + byteoffset * 8 + offset > s.seek(0, 2) * 8: - raise CreationError("BytesIO object is not long enough for specified " - "length and offset.") - self._datastore = ConstByteStore(bytearray(s.getvalue()[byteoffset: byteoffset + bytelength]), length, offset) - return - - if isinstance(s, file): - if offset is None: - offset = 0 - if length is None: - length = os.path.getsize(s.name) * 8 - offset - byteoffset, offset = divmod(offset, 8) - bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset - m = MmapByteArray(s, bytelength, byteoffset) - if length + byteoffset * 8 + offset > m.filelength * 8: - raise CreationError("File is not long enough for specified " - "length and offset.") - self._datastore = ConstByteStore(m, length, offset) - return - - if length is not None: - raise CreationError("The length keyword isn't applicable to this initialiser.") - if offset: - raise CreationError("The offset keyword isn't applicable to this initialiser.") - if isinstance(s, basestring): - bs = self._converttobitstring(s) - assert bs._offset == 0 - self._setbytes_unsafe(bs._datastore.rawbytes, bs.length, 0) - return - if isinstance(s, (bytes, bytearray)): - self._setbytes_unsafe(bytearray(s), len(s) * 8, 0) - return - if isinstance(s, array.array): - try: - b = s.tobytes() - except AttributeError: - b = s.tostring() # Python 2.7 - self._setbytes_unsafe(bytearray(b), len(b) * 8, 0) - return - if isinstance(s, numbers.Integral): - # Initialise with s zero bits. - if s < 0: - msg = "Can't create bitstring of negative length {0}." - raise CreationError(msg, s) - data = bytearray((s + 7) // 8) - self._datastore = ByteStore(data, s, 0) - return - if isinstance(s, collectionsAbc.Iterable): - # Evaluate each item as True or False and set bits to 1 or 0. - self._setbin_unsafe(''.join(str(int(bool(x))) for x in s)) - return - raise TypeError("Cannot initialise bitstring from {0}.".format(type(s))) - - def _setfile(self, filename, length, offset): - """Use file as source of bits.""" - with open(filename, 'rb') as source: - if offset is None: - offset = 0 - if length is None: - length = os.path.getsize(source.name) * 8 - offset - byteoffset, offset = divmod(offset, 8) - bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset - m = MmapByteArray(source, bytelength, byteoffset) - if length + byteoffset * 8 + offset > m.filelength * 8: - raise CreationError("File is not long enough for specified " - "length and offset.") - self._datastore = ConstByteStore(m, length, offset) - - def _setbytes_safe(self, data, length=None, offset=0): - """Set the data from a string.""" - data = bytearray(data) - if length is None: - # Use to the end of the data - length = len(data)*8 - offset - self._datastore = ByteStore(data, length, offset) - else: - if length + offset > len(data) * 8: - msg = "Not enough data present. Need {0} bits, have {1}." - raise CreationError(msg, length + offset, len(data) * 8) - if length == 0: - self._datastore = ByteStore(bytearray(0)) - else: - self._datastore = ByteStore(data, length, offset) - - def _setbytes_unsafe(self, data, length, offset): - """Unchecked version of _setbytes_safe.""" - self._datastore = type(self._datastore)(data[:], length, offset) - assert self._assertsanity() - - def _readbytes(self, length, start): - """Read bytes and return them. Note that length is in bits.""" - assert length % 8 == 0 - assert start + length <= self.len - if not (start + self._offset) % 8: - return bytes(self._datastore.getbyteslice((start + self._offset) // 8, - (start + self._offset + length) // 8)) - return self._slice(start, start + length).tobytes() - - def _getbytes(self): - """Return the data as an ordinary string.""" - if self.len % 8: - raise InterpretError("Cannot interpret as bytes unambiguously - " - "not multiple of 8 bits.") - return self._readbytes(self.len, 0) - - def _setuint(self, uint, length=None): - """Reset the bitstring to have given unsigned int interpretation.""" - try: - if length is None: - # Use the whole length. Deliberately not using .len here. - length = self._datastore.bitlength - except AttributeError: - # bitstring doesn't have a _datastore as it hasn't been created! - pass - if length is None or length == 0: - raise CreationError("A non-zero length must be specified with a " - "uint initialiser.") - if uint >= (1 << length): - msg = "{0} is too large an unsigned integer for a bitstring of length {1}. "\ - "The allowed range is [0, {2}]." - raise CreationError(msg, uint, length, (1 << length) - 1) - if uint < 0: - raise CreationError("uint cannot be initialised by a negative number.") - s = hex(uint)[2:] - s = s.rstrip('L') - if len(s) & 1: - s = '0' + s - try: - data = bytes.fromhex(s) - except AttributeError: - # the Python 2.x way - data = binascii.unhexlify(s) - # Now add bytes as needed to get the right length. - extrabytes = ((length + 7) // 8) - len(data) - if extrabytes > 0: - data = b'\x00' * extrabytes + data - offset = 8 - (length % 8) - if offset == 8: - offset = 0 - self._setbytes_unsafe(bytearray(data), length, offset) - - def _readuint_lsb0(self, length, start): - # TODO: This needs a complete rewrite - can't delegate to _readuint_msb0 - return self._readuint_msb0(length, self.len - start - length) - - def _readuint_msb0(self, length, start): - """Read bits and interpret as an unsigned int.""" - if not length: - raise InterpretError("Cannot interpret a zero length bitstring " - "as an integer.") - offset = self._offset - startbyte = (start + offset) // 8 - endbyte = (start + offset + length - 1) // 8 - - b = binascii.hexlify(bytes(self._datastore.getbyteslice(startbyte, endbyte + 1))) - assert b - i = int(b, 16) - final_bits = 8 - ((start + offset + length) % 8) - if final_bits != 8: - i >>= final_bits - i &= (1 << length) - 1 - return i - - def _getuint(self): - """Return data as an unsigned int.""" - return self._readuint(self.len, 0) - - def _setint(self, int_, length=None): - """Reset the bitstring to have given signed int interpretation.""" - # If no length given, and we've previously been given a length, use it. - if length is None and hasattr(self, 'len') and self.len != 0: - length = self.len - if length is None or length == 0: - raise CreationError("A non-zero length must be specified with an int initialiser.") - if int_ >= (1 << (length - 1)) or int_ < -(1 << (length - 1)): - raise CreationError("{0} is too large a signed integer for a bitstring of length {1}. " - "The allowed range is [{2}, {3}].", int_, length, -(1 << (length - 1)), - (1 << (length - 1)) - 1) - if int_ >= 0: - self._setuint(int_, length) - return - # Do the 2's complement thing. Add one, set to minus number, then flip bits. - self._setuint((-int_ - 1) ^ ((1 << length) - 1), length) - - def _readint(self, length, start): - """Read bits and interpret as a signed int""" - ui = self._readuint(length, start) - if not ui >> (length - 1): - # Top bit not set, number is positive - return ui - # Top bit is set, so number is negative - tmp = (~(ui - 1)) & ((1 << length) - 1) - return -tmp - - def _getint(self): - """Return data as a two's complement signed int.""" - return self._readint(self.len, 0) - - def _setuintbe(self, uintbe, length=None): - """Set the bitstring to a big-endian unsigned int interpretation.""" - if length is not None and length % 8 != 0: - raise CreationError("Big-endian integers must be whole-byte. " - "Length = {0} bits.", length) - self._setuint(uintbe, length) - - def _readuintbe(self, length, start): - """Read bits and interpret as a big-endian unsigned int.""" - if length % 8: - raise InterpretError("Big-endian integers must be whole-byte. " - "Length = {0} bits.", length) - return self._readuint(length, start) - - def _getuintbe(self): - """Return data as a big-endian two's complement unsigned int.""" - return self._readuintbe(self.len, 0) - - def _setintbe(self, intbe, length=None): - """Set bitstring to a big-endian signed int interpretation.""" - if length is not None and length % 8 != 0: - raise CreationError("Big-endian integers must be whole-byte. " - "Length = {0} bits.", length) - self._setint(intbe, length) - - def _readintbe(self, length, start): - """Read bits and interpret as a big-endian signed int.""" - if length % 8: - raise InterpretError("Big-endian integers must be whole-byte. " - "Length = {0} bits.", length) - return self._readint(length, start) - - def _getintbe(self): - """Return data as a big-endian two's complement signed int.""" - return self._readintbe(self.len, 0) - - def _setuintle(self, uintle, length=None): - if length is not None and length % 8 != 0: - raise CreationError("Little-endian integers must be whole-byte. " - "Length = {0} bits.", length) - self._setuint(uintle, length) - self._datastore._rawarray = self._datastore._rawarray[::-1] - - def _readuintle(self, length, start): - """Read bits and interpret as a little-endian unsigned int.""" - if length % 8: - raise InterpretError("Little-endian integers must be whole-byte. " - "Length = {0} bits.", length) - assert start + length <= self.len - absolute_pos = start + self._offset - startbyte, offset = divmod(absolute_pos, 8) - val = 0 - if not offset: - endbyte = (absolute_pos + length - 1) // 8 - chunksize = 4 # for 'L' format - while endbyte - chunksize + 1 >= startbyte: - val <<= 8 * chunksize - val += struct.unpack('> (length - 1): - # Top bit not set, number is positive - return ui - # Top bit is set, so number is negative - tmp = (~(ui - 1)) & ((1 << length) - 1) - return -tmp - - def _getintle(self): - return self._readintle(self.len, 0) - - def _setfloat(self, f, length=None): - # If no length given, and we've previously been given a length, use it. - if length is None and hasattr(self, 'len') and self.len != 0: - length = self.len - if length is None or length == 0: - raise CreationError("A non-zero length must be specified with a " - "float initialiser.") - if length == 32: - b = struct.pack('>f', f) - elif length == 64: - b = struct.pack('>d', f) - else: - raise CreationError("floats can only be 32 or 64 bits long, " - "not {0} bits", length) - self._setbytes_unsafe(bytearray(b), length, 0) - - def _readfloat(self, length, start): - """Read bits and interpret as a float.""" - if not (start + self._offset) % 8: - startbyte = (start + self._offset) // 8 - if length == 32: - f, = struct.unpack('>f', bytes(self._datastore.getbyteslice(startbyte, startbyte + 4))) - elif length == 64: - f, = struct.unpack('>d', bytes(self._datastore.getbyteslice(startbyte, startbyte + 8))) - else: - if length == 32: - f, = struct.unpack('>f', self._readbytes(32, start)) - elif length == 64: - f, = struct.unpack('>d', self._readbytes(64, start)) - try: - return f - except NameError: - raise InterpretError("floats can only be 32 or 64 bits long, not {0} bits", length) - - def _getfloat(self): - """Interpret the whole bitstring as a float.""" - return self._readfloat(self.len, 0) - - def _setfloatle(self, f, length=None): - # If no length given, and we've previously been given a length, use it. - if length is None and hasattr(self, 'len') and self.len != 0: - length = self.len - if length is None or length == 0: - raise CreationError("A non-zero length must be specified with a " - "float initialiser.") - if length == 32: - b = struct.pack(' 0: - tmp >>= 1 - leadingzeros += 1 - remainingpart = i + 1 - (1 << leadingzeros) - binstring = '0' * leadingzeros + '1' + Bits(uint=remainingpart, - length=leadingzeros).bin - self._setbin_unsafe(binstring) - - def _readue(self, pos): - """Return interpretation of next bits as unsigned exponential-Golomb code. - - Raises ReadError if the end of the bitstring is encountered while - reading the code. - - """ - oldpos = pos - try: - while not self[pos]: - pos += 1 - except IndexError: - raise ReadError("Read off end of bitstring trying to read code.") - leadingzeros = pos - oldpos - codenum = (1 << leadingzeros) - 1 - if leadingzeros > 0: - if pos + leadingzeros + 1 > self.len: - raise ReadError("Read off end of bitstring trying to read code.") - codenum += self._readuint(leadingzeros, pos + 1) - pos += leadingzeros + 1 - else: - assert codenum == 0 - pos += 1 - return codenum, pos - - def _getue(self): - """Return data as unsigned exponential-Golomb code. - - Raises InterpretError if bitstring is not a single exponential-Golomb code. - - """ - try: - value, newpos = self._readue(0) - if value is None or newpos != self.len: - raise ReadError - except ReadError: - raise InterpretError("Bitstring is not a single exponential-Golomb code.") - return value - - def _setse(self, i): - """Initialise bitstring with signed exponential-Golomb code for integer i.""" - if i > 0: - u = (i * 2) - 1 - else: - u = -2 * i - self._setue(u) - - def _getse(self): - """Return data as signed exponential-Golomb code. - - Raises InterpretError if bitstring is not a single exponential-Golomb code. - - """ - try: - value, newpos = self._readse(0) - if value is None or newpos != self.len: - raise ReadError - except ReadError: - raise InterpretError("Bitstring is not a single exponential-Golomb code.") - return value - - def _readse(self, pos): - """Return interpretation of next bits as a signed exponential-Golomb code. - - Advances position to after the read code. - - Raises ReadError if the end of the bitstring is encountered while - reading the code. - - """ - codenum, pos = self._readue(pos) - m = (codenum + 1) // 2 - if not codenum % 2: - return -m, pos - else: - return m, pos - - def _setuie(self, i): - """Initialise bitstring with unsigned interleaved exponential-Golomb code for integer i. - - Raises CreationError if i < 0. - - """ - if i < 0: - raise CreationError("Cannot use negative initialiser for unsigned " - "interleaved exponential-Golomb.") - self._setbin_unsafe('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1') - - def _readuie(self, pos): - """Return interpretation of next bits as unsigned interleaved exponential-Golomb code. - - Raises ReadError if the end of the bitstring is encountered while - reading the code. - - """ - try: - codenum = 1 - while not self[pos]: - pos += 1 - codenum <<= 1 - codenum += self[pos] - pos += 1 - pos += 1 - except IndexError: - raise ReadError("Read off end of bitstring trying to read code.") - codenum -= 1 - return codenum, pos - - def _getuie(self): - """Return data as unsigned interleaved exponential-Golomb code. - - Raises InterpretError if bitstring is not a single exponential-Golomb code. - - """ - try: - value, newpos = self._readuie(0) - if value is None or newpos != self.len: - raise ReadError - except ReadError: - raise InterpretError("Bitstring is not a single interleaved exponential-Golomb code.") - return value - - def _setsie(self, i): - """Initialise bitstring with signed interleaved exponential-Golomb code for integer i.""" - if not i: - self._setbin_unsafe('1') - else: - self._setuie(abs(i)) - self._addright(Bits([i < 0])) - - def _getsie(self): - """Return data as signed interleaved exponential-Golomb code. - - Raises InterpretError if bitstring is not a single exponential-Golomb code. - - """ - try: - value, newpos = self._readsie(0) - if value is None or newpos != self.len: - raise ReadError - except ReadError: - raise InterpretError("Bitstring is not a single interleaved exponential-Golomb code.") - return value - - def _readsie(self, pos): - """Return interpretation of next bits as a signed interleaved exponential-Golomb code. - - Advances position to after the read code. - - Raises ReadError if the end of the bitstring is encountered while - reading the code. - - """ - codenum, pos = self._readuie(pos) - if not codenum: - return 0, pos - try: - if self[pos]: - return -codenum, pos + 1 - else: - return codenum, pos + 1 - except IndexError: - raise ReadError("Read off end of bitstring trying to read code.") - - def _setbool(self, value): - # We deliberately don't want to have implicit conversions to bool here. - # If we did then it would be difficult to deal with the 'False' string. - if value in (1, 'True'): - self._setbytes_unsafe(bytearray(b'\x80'), 1, 0) - elif value in (0, 'False'): - self._setbytes_unsafe(bytearray(b'\x00'), 1, 0) - else: - raise CreationError('Cannot initialise boolean with {0}.', value) - - def _getbool(self): - if self.length != 1: - msg = "For a bool interpretation a bitstring must be 1 bit long, not {0} bits." - raise InterpretError(msg, self.length) - return self[0] - - def _readbool(self, pos): - return self[pos], pos + 1 - - def _setbin_safe(self, binstring): - """Reset the bitstring to the value given in binstring.""" - binstring = tidy_input_string(binstring) - # remove any 0b if present - binstring = binstring.replace('0b', '') - self._setbin_unsafe(binstring) - - def _setbin_unsafe(self, binstring): - """Same as _setbin_safe, but input isn't sanity checked. binstring mustn't start with '0b'.""" - length = len(binstring) - # pad with zeros up to byte boundary if needed - boundary = ((length + 7) // 8) * 8 - padded_binstring = binstring + '0' * (boundary - length)\ - if len(binstring) < boundary else binstring - try: - bytelist = [int(padded_binstring[x:x + 8], 2) - for x in xrange(0, len(padded_binstring), 8)] - except ValueError: - raise CreationError("Invalid character in bin initialiser {0}.", binstring) - self._setbytes_unsafe(bytearray(bytelist), length, 0) - - def _readbin(self, length, start): - """Read bits and interpret as a binary string.""" - if not length: - return '' - # Get the byte slice containing our bit slice - startbyte, startoffset = divmod(start + self._offset, 8) - endbyte = (start + self._offset + length - 1) // 8 - b = self._datastore.getbyteslice(startbyte, endbyte + 1) - # Convert to a string of '0' and '1's (via a hex string an and int!) - c = "{:0{}b}".format(int(binascii.hexlify(b), 16), 8*len(b)) - # Finally chop off any extra bits. - return c[startoffset:startoffset + length] - - def _getbin(self): - """Return interpretation as a binary string.""" - return self._readbin(self.len, 0) - - def _setoct(self, octstring): - """Reset the bitstring to have the value given in octstring.""" - octstring = tidy_input_string(octstring) - # remove any 0o if present - octstring = octstring.replace('0o', '') - binlist = [] - for i in octstring: - try: - binlist.append(OCT_TO_BITS[int(i)]) - except (ValueError, IndexError): - raise CreationError("Invalid symbol '{0}' in oct initialiser.", i) - - self._setbin_unsafe(''.join(binlist)) - - def _readoct(self, length, start): - """Read bits and interpret as an octal string.""" - if length % 3: - raise InterpretError("Cannot convert to octal unambiguously - " - "not multiple of 3 bits.") - if not length: - return '' - # Get main octal bit by converting from int. - # Strip starting 0 or 0o depending on Python version. - end = oct(self._readuint(length, start))[LEADING_OCT_CHARS:] - if end.endswith('L'): - end = end[:-1] - middle = '0' * (length // 3 - len(end)) - return middle + end - - def _getoct(self): - """Return interpretation as an octal string.""" - return self._readoct(self.len, 0) - - def _sethex(self, hexstring): - """Reset the bitstring to have the value given in hexstring.""" - hexstring = tidy_input_string(hexstring) - # remove any 0x if present - hexstring = hexstring.replace('0x', '') - length = len(hexstring) - if length % 2: - hexstring += '0' - try: - data = bytearray.fromhex(hexstring) - except ValueError: - raise CreationError("Invalid symbol in hex initialiser.") - self._setbytes_unsafe(data, length * 4, 0) - - def _readhex(self, length, start): - """Read bits and interpret as a hex string.""" - if length % 4: - raise InterpretError("Cannot convert to hex unambiguously - " - "not multiple of 4 bits.") - if not length: - return '' - s = self._slice(start, start + length).tobytes() - try: - s = s.hex() # Available in Python 3.5+ - except AttributeError: - # This monstrosity is the only thing I could get to work for both 2.6 and 3.1. - s = str(binascii.hexlify(s).decode('utf-8')) - # If there's one nibble too many then cut it off - return s[:-1] if (length // 4) % 2 else s - - def _gethex(self): - """Return the hexadecimal representation as a string prefixed with '0x'. - - Raises an InterpretError if the bitstring's length is not a multiple of 4. - - """ - return self._readhex(self.len, 0) - - def _getoffset(self): - return self._datastore.offset - - def _getlength(self): - """Return the length of the bitstring in bits.""" - return self._datastore.bitlength - - def _ensureinmemory(self): - """Ensure the data is held in memory, not in a file.""" - self._setbytes_unsafe(self._datastore.getbyteslice(0, self._datastore.bytelength), - self.len, self._offset) - - @classmethod - def _converttobitstring(cls, bs, offset=0, cache={}): - """Convert bs to a bitstring and return it. - - offset gives the suggested bit offset of first significant - bit, to optimise append etc. - - """ - if isinstance(bs, Bits): - return bs - try: - return cache[(bs, offset)] - except KeyError: - if isinstance(bs, basestring): - b = cls() - try: - _, tokens = tokenparser(bs) - except ValueError as e: - raise CreationError(*e.args) - if tokens: - b._addright(Bits._init_with_token(*tokens[0])) - b._datastore = offsetcopy(b._datastore, offset) - for token in tokens[1:]: - b._addright(Bits._init_with_token(*token)) - assert b._assertsanity() - assert b.len == 0 or b._offset == offset - if len(cache) < CACHE_SIZE: - cache[(bs, offset)] = b - return b - except TypeError: - # Unhashable type - pass - return cls(bs) - - def _copy(self): - """Create and return a new copy of the Bits (always in memory).""" - s_copy = self.__class__() - s_copy._setbytes_unsafe(self._datastore.getbyteslice(0, self._datastore.bytelength), - self.len, self._offset) - return s_copy - - def _slice_lsb0(self, start, end): - """Used internally to get a slice, without error checking (LSB0).""" - return self._slice_msb0(self.length - end, self.length - start) - - def _slice_msb0(self, start, end): - """Used internally to get a slice, without error checking.""" - if end == start: - return self.__class__() - assert start < end, "start={0}, end={1}".format(start, end) - offset = self._offset - startbyte, newoffset = divmod(start + offset, 8) - endbyte = (end + offset - 1) // 8 - bs = self.__class__() - bs._setbytes_unsafe(self._datastore.getbyteslice(startbyte, endbyte + 1), end - start, newoffset) - return bs - - def _readtoken(self, name, pos, length): - """Reads a token from the bitstring and returns the result.""" - if length is not None and int(length) > self.length - pos: - raise ReadError("Reading off the end of the data. " - "Tried to read {0} bits when only {1} available.".format(int(length), self.length - pos)) - try: - val = name_to_read[name](self, length, pos) - return val, pos + length - except KeyError: - if name == 'pad': - return None, pos + length - raise ValueError("Can't parse token {0}:{1}".format(name, length)) - except TypeError: - # This is for the 'ue', 'se' and 'bool' tokens. They will also return the new pos. - return name_to_read[name](self, pos) - - def _addright(self, bs): - """Add a bitstring to the RHS of the current bitstring.""" - self._datastore._appendstore(bs._datastore) - - def _addleft(self, bs): - """Prepend a bitstring to the current bitstring.""" - self._datastore._prependstore(bs._datastore) - - def _reverse(self): - """Reverse all bits in-place.""" - # Reverse the contents of each byte - n = [BYTE_REVERSAL_DICT[b] for b in self._datastore.rawbytes] - # Then reverse the order of the bytes - n.reverse() - # The new offset is the number of bits that were unused at the end. - newoffset = 8 - (self._offset + self.len) % 8 - if newoffset == 8: - newoffset = 0 - self._setbytes_unsafe(bytearray().join(n), self.length, newoffset) - - def _truncateleft(self, bits): - """Truncate bits from the start of the bitstring.""" - assert 0 <= bits <= self.len - if not bits: - return Bits() - truncated_bits = self._slice_msb0(0, bits) - if bits == self.len: - self._clear() - return truncated_bits - bytepos, offset = divmod(self._offset + bits, 8) - self._setbytes_unsafe(self._datastore.getbyteslice(bytepos, self._datastore.bytelength), self.len - bits, - offset) - assert self._assertsanity() - return truncated_bits - - def _truncateright(self, bits): - """Truncate bits from the end of the bitstring.""" - assert 0 <= bits <= self.len - if not bits: - return Bits() - truncated_bits = self._slice_lsb0(0, bits) - if bits == self.len: - self._clear() - return truncated_bits - newlength_in_bytes = (self._offset + self.len - bits + 7) // 8 - self._setbytes_unsafe(self._datastore.getbyteslice(0, newlength_in_bytes), self.len - bits, - self._offset) - assert self._assertsanity() - return truncated_bits - - def _insert_lsb0(self, bs, pos): - """Insert bs at pos (LSB0).""" - self._insert_msb0(bs, self.len - pos) - - def _insert_msb0(self, bs, pos): - """Insert bs at pos.""" - assert 0 <= pos <= self.len - if pos > self.len // 2: - # Inserting nearer end, so cut off end. - # end = self._slice(pos, self.len) - end = self._truncateright(self.len - pos) - self._addright(bs) - self._addright(end) - else: - # Inserting nearer start, so cut off start. - start = self._slice(0, pos) - self._truncateleft(pos) - self._addleft(bs) - self._addleft(start) - try: - self._pos = pos + bs.len - except AttributeError: - pass - assert self._assertsanity() - - def _overwrite_lsb0(self, bs, pos): - """Overwrite with bs at pos (LSB0).""" - self._overwrite_msb0(bs, self.len - pos - bs.len) - - def _overwrite_msb0(self, bs, pos): - """Overwrite with bs at pos.""" - assert 0 <= pos < self.len - if bs is self: - # Just overwriting with self, so do nothing. - assert pos == 0 - return - firstbytepos = (self._offset + pos) // 8 - lastbytepos = (self._offset + pos + bs.len - 1) // 8 - bytepos, bitoffset = divmod(self._offset + pos, 8) - if firstbytepos == lastbytepos: - mask = ((1 << bs.len) - 1) << (8 - bs.len - bitoffset) - self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) & (~mask)) - d = offsetcopy(bs._datastore, bitoffset) - self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) | (d.getbyte(0) & mask)) - else: - # Do first byte - mask = (1 << (8 - bitoffset)) - 1 - self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) & (~mask)) - d = offsetcopy(bs._datastore, bitoffset) - self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) | (d.getbyte(0) & mask)) - # Now do all the full bytes - self._datastore.setbyteslice(firstbytepos + 1, lastbytepos, d.getbyteslice(1, lastbytepos - firstbytepos)) - # and finally the last byte - bitsleft = (self._offset + pos + bs.len) % 8 - if not bitsleft: - bitsleft = 8 - mask = (1 << (8 - bitsleft)) - 1 - self._datastore.setbyte(lastbytepos, self._datastore.getbyte(lastbytepos) & mask) - self._datastore.setbyte(lastbytepos, - self._datastore.getbyte(lastbytepos) | (d.getbyte(d.bytelength - 1) & ~mask)) - assert self._assertsanity() - - def _delete_lsb0(self, bits, pos): - """Delete bits at pos (LSB0).""" - self._delete_msb0(bits, self.len - pos - bits) - - def _delete_msb0(self, bits, pos): - """Delete bits at pos.""" - assert 0 <= pos <= self.len - assert pos + bits <= self.len, "pos={}, bits={}, len={}".format(pos, bits, self.len) - if not pos: - # Cutting bits off at the start. - self._truncateleft(bits) - return - if pos + bits == self.len: - # Cutting bits off at the end. - self._truncateright(bits) - return - if pos > self.len - pos - bits: - # More bits before cut point than after it, so do bit shifting - # on the final bits. - end = self._slice_msb0(pos + bits, self.len) - assert self.len - pos > 0 - self._truncateright(self.len - pos) - self._addright(end) - return - # More bits after the cut point than before it. - start = self._slice_msb0(0, pos) - self._truncateleft(pos + bits) - self._addleft(start) - return - - def _reversebytes(self, start, end): - """Reverse bytes in-place.""" - # Make the start occur on a byte boundary - # TODO: We could be cleverer here to avoid changing the offset. - newoffset = 8 - (start % 8) - if newoffset == 8: - newoffset = 0 - self._datastore = offsetcopy(self._datastore, newoffset) - # Now just reverse the byte data - toreverse = bytearray(self._datastore.getbyteslice((newoffset + start) // 8, (newoffset + end) // 8)) - toreverse.reverse() - self._datastore.setbyteslice((newoffset + start) // 8, (newoffset + end) // 8, toreverse) - - def _set(self, pos): - """Set bit at pos to 1.""" - assert 0 <= pos < self.len - self._datastore.setbit(pos) - - def _unset(self, pos): - """Set bit at pos to 0.""" - assert 0 <= pos < self.len - self._datastore.unsetbit(pos) - - def _invert(self, pos): - """Flip bit at pos 1<->0.""" - assert 0 <= pos < self.len - self._datastore.invertbit(pos) - - def _invert_all(self): - """Invert every bit.""" - for p in xrange(self._datastore.byteoffset, self._datastore.byteoffset + self._datastore.bytelength): - self._datastore._rawarray[p] = 256 + ~self._datastore._rawarray[p] - - def _ilshift(self, n): - """Shift bits by n to the left in place. Return self.""" - assert 0 < n <= self.len - self._addright(Bits(n)) - self._truncateleft(n) - return self - - def _irshift(self, n): - """Shift bits by n to the right in place. Return self.""" - assert 0 < n <= self.len - self._addleft(Bits(n)) - self._truncateright(n) - return self - - def _imul(self, n): - """Concatenate n copies of self in place. Return self.""" - assert n >= 0 - if not n: - self._clear() - return self - m = 1 - old_len = self.len - while m * 2 < n: - self._addright(self) - m *= 2 - self._addright(self[0:(n - m) * old_len]) - return self - - def _inplace_logical_helper(self, bs, f): - """Helper function containing most of the __ior__, __iand__, __ixor__ code.""" - # Give the two bitstrings the same offset (modulo 8) - self_byteoffset, self_bitoffset = divmod(self._offset, 8) - bs_byteoffset, bs_bitoffset = divmod(bs._offset, 8) - if bs_bitoffset != self_bitoffset: - if not self_bitoffset: - bs._datastore = offsetcopy(bs._datastore, 0) - else: - self._datastore = offsetcopy(self._datastore, bs_bitoffset) - a = self._datastore.rawbytes - b = bs._datastore.rawbytes - for i in xrange(len(a)): - a[i] = f(a[i + self_byteoffset], b[i + bs_byteoffset]) - return self - - def _ior(self, bs): - return self._inplace_logical_helper(bs, operator.ior) - - def _iand(self, bs): - return self._inplace_logical_helper(bs, operator.iand) - - def _ixor(self, bs): - return self._inplace_logical_helper(bs, operator.xor) - - def _readbits(self, length, start): - """Read some bits from the bitstring and return newly constructed bitstring.""" - return self._slice(start, start + length) - - def _validate_slice_msb0(self, start, end): - """Validate start and end and return them as positive bit positions.""" - if start is None: - start = 0 - elif start < 0: - start += self.len - if end is None: - end = self.len - elif end < 0: - end += self.len - if not 0 <= end <= self.len: - raise ValueError("end is not a valid position in the bitstring.") - if not 0 <= start <= self.len: - raise ValueError("start is not a valid position in the bitstring.") - if end < start: - raise ValueError("end must not be less than start.") - return start, end - - def _validate_slice_lsb0(self, start, end): - start, end = self._validate_slice_msb0(start, end) - return self.len - end, self.len - start - - def unpack(self, fmt, **kwargs): - """Interpret the whole bitstring using fmt and return list. - - fmt -- A single string or a list of strings with comma separated tokens - describing how to interpret the bits in the bitstring. Items - can also be integers, for reading new bitstring of the given length. - kwargs -- A dictionary or keyword-value pairs - the keywords used in the - format string will be replaced with their given value. - - Raises ValueError if the format is not understood. If not enough bits - are available then all bits to the end of the bitstring will be used. - - See the docstring for 'read' for token examples. - - """ - return self._readlist(fmt, 0, **kwargs)[0] - - def _readlist(self, fmt, pos, **kwargs): - tokens = [] - stretchy_token = None - if isinstance(fmt, basestring): - fmt = [fmt] - # Replace integers with 'bits' tokens - for i, f in enumerate(fmt): - if isinstance(f, numbers.Integral): - fmt[i] = "bits:{0}".format(f) - for f_item in fmt: - stretchy, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys()))) - if stretchy: - if stretchy_token: - raise Error("It's not possible to have more than one 'filler' token.") - stretchy_token = stretchy - tokens.extend(tkns) - if not stretchy_token: - lst = [] - for name, length, _ in tokens: - if length in kwargs: - length = kwargs[length] - if name == 'bytes': - length *= 8 - if name in kwargs and length is None: - # Using default 'uint' - the name is really the length. - value, pos = self._readtoken('uint', pos, kwargs[name]) - lst.append(value) - continue - value, pos = self._readtoken(name, pos, length) - if value is not None: # Don't append pad tokens - lst.append(value) - return lst, pos - stretchy_token = False - bits_after_stretchy_token = 0 - for token in tokens: - name, length, _ = token - if length in kwargs: - length = kwargs[length] - if name == 'bytes': - length *= 8 - if name in kwargs and length is None: - # Default 'uint'. - length = kwargs[name] - if stretchy_token: - if name in ('se', 'ue', 'sie', 'uie'): - raise Error("It's not possible to parse a variable" - "length token after a 'filler' token.") - else: - if length is None: - raise Error("It's not possible to have more than " - "one 'filler' token.") - bits_after_stretchy_token += length - if length is None and name not in ('se', 'ue', 'sie', 'uie'): - assert not stretchy_token - stretchy_token = token - bits_left = self.len - pos - return_values = [] - for token in tokens: - name, length, _ = token - if token is stretchy_token: - # Set length to the remaining bits - length = max(bits_left - bits_after_stretchy_token, 0) - if length in kwargs: - length = kwargs[length] - if name == 'bytes': - length *= 8 - if name in kwargs and length is None: - # Default 'uint' - length = kwargs[name] - if length is not None: - bits_left -= length - value, pos = self._readtoken(name, pos, length) - if value is not None: - return_values.append(value) - return return_values, pos - - def _findbytes(self, bytes_, start, end, bytealigned): - """Quicker version of find when everything's whole byte - and byte aligned. - - """ - assert self._datastore.offset == 0 - assert bytealigned is True - # Extract data bytes from bitstring to be found. - bytepos = (start + 7) // 8 - found = False - p = bytepos - finalpos = end // 8 - increment = max(1024, len(bytes_) * 10) - buffersize = increment + len(bytes_) - while p < finalpos: - # Read in file or from memory in overlapping chunks and search the chunks. - buf = bytearray(self._datastore.getbyteslice(p, min(p + buffersize, finalpos))) - pos = buf.find(bytes_) - if pos != -1: - found = True - p += pos - break - p += increment - if not found: - return () - return (p * 8,) - - def _findregex(self, reg_ex, start, end, bytealigned): - """Find first occurrence of a compiled regular expression. - - Note that this doesn't support arbitrary regexes, in particular they - must match a known length. - - """ - p = start - length = len(reg_ex.pattern) - # We grab overlapping chunks of the binary representation and - # do an ordinary string search within that. - increment = max(4096, length * 10) - buffersize = increment + length - while p < end: - buf = self._readbin(min(buffersize, end - p), p) - # Test using regular expressions... - m = reg_ex.search(buf) - if m: - pos = m.start() - # pos = buf.find(targetbin) - # if pos != -1: - # if bytealigned then we only accept byte aligned positions. - if not bytealigned or (p + pos) % 8 == 0: - return (p + pos,) - if bytealigned: - # Advance to just beyond the non-byte-aligned match and try again... - p += pos + 1 - continue - p += increment - # Not found, return empty tuple - return () - - def find(self, bs, start=None, end=None, bytealigned=None): - """Find first occurrence of substring bs. - - Returns a single item tuple with the bit position if found, or an - empty tuple if not found. The bit position (pos property) will - also be set to the start of the substring if it is found. - - bs -- The bitstring to find. - start -- The bit position to start the search. Defaults to 0. - end -- The bit position one past the last bit to search. - Defaults to self.len. - bytealigned -- If True the bitstring will only be - found on byte boundaries. - - Raises ValueError if bs is empty, if start < 0, if end > self.len or - if end < start. - - >>> BitArray('0xc3e').find('0b1111') - (6,) - - """ - return self._find(bs, start, end, bytealigned) - - def _find_lsb0(self, bs, start=None, end=None, bytealigned=None): - bs = Bits(bs) - start, end = self._validate_slice_lsb0(start, end) - p = self.rfind(bs, start, end, bytealigned) - if p: - return (self.len - p[0] - bs.length,) - - def _find_msb0(self, bs, start=None, end=None, bytealigned=None): - bs = Bits(bs) - if not bs.len: - raise ValueError("Cannot find an empty bitstring.") - start, end = self._validate_slice(start, end) - if bytealigned is None: - bytealigned = globals()['bytealigned'] - if bytealigned and not bs.len % 8 and not self._datastore.offset: - p = self._findbytes(bs.bytes, start, end, bytealigned) - else: - p = self._findregex(re.compile(bs._getbin()), start, end, bytealigned) - # If called from a class that has a pos, set it - try: - self._pos = p[0] - except (AttributeError, IndexError): - pass - return p - - def findall(self, bs, start=None, end=None, count=None, bytealigned=None): - """Find all occurrences of bs. Return generator of bit positions. - - bs -- The bitstring to find. - start -- The bit position to start the search. Defaults to 0. - end -- The bit position one past the last bit to search. - Defaults to self.len. - count -- The maximum number of occurrences to find. - bytealigned -- If True the bitstring will only be found on - byte boundaries. - - Raises ValueError if bs is empty, if start < 0, if end > self.len or - if end < start. - - Note that all occurrences of bs are found, even if they overlap. - - """ - if count is not None and count < 0: - raise ValueError("In findall, count must be >= 0.") - bs = Bits(bs) - start, end = self._validate_slice(start, end) - if bytealigned is None: - bytealigned = globals()['bytealigned'] - c = 0 - if bytealigned and not bs.len % 8 and not self._datastore.offset: - # Use the quick find method - f = self._findbytes - x = bs._getbytes() - else: - f = self._findregex - x = re.compile(bs._getbin()) - while True: - - p = f(x, start, end, bytealigned) - if not p: - break - if count is not None and c >= count: - return - c += 1 - try: - self._pos = p[0] - except AttributeError: - pass - yield p[0] - if bytealigned: - start = p[0] + 8 - else: - start = p[0] + 1 - if start >= end: - break - return - - def rfind(self, bs, start=None, end=None, bytealigned=None): - """Find final occurrence of substring bs. - - Returns a single item tuple with the bit position if found, or an - empty tuple if not found. The bit position (pos property) will - also be set to the start of the substring if it is found. - - bs -- The bitstring to find. - start -- The bit position to end the reverse search. Defaults to 0. - end -- The bit position one past the first bit to reverse search. - Defaults to self.len. - bytealigned -- If True the bitstring will only be found on byte - boundaries. - - Raises ValueError if bs is empty, if start < 0, if end > self.len or - if end < start. - - """ - bs = Bits(bs) - start, end = self._validate_slice(start, end) - if bytealigned is None: - bytealigned = globals()['bytealigned'] - if not bs.len: - raise ValueError("Cannot find an empty bitstring.") - # Search chunks starting near the end and then moving back - # until we find bs. - increment = max(8192, bs.len * 80) - buffersize = min(increment + bs.len, end - start) - pos = max(start, end - buffersize) - while True: - found = list(self.findall(bs, start=pos, end=pos + buffersize, - bytealigned=bytealigned)) - if not found: - if pos == start: - return () - pos = max(start, pos - increment) - continue - return (found[-1],) - - def cut(self, bits, start=None, end=None, count=None): - """Return bitstring generator by cutting into bits sized chunks. - - bits -- The size in bits of the bitstring chunks to generate. - start -- The bit position to start the first cut. Defaults to 0. - end -- The bit position one past the last bit to use in the cut. - Defaults to self.len. - count -- If specified then at most count items are generated. - Default is to cut as many times as possible. - - """ - start, end = self._validate_slice(start, end) - if count is not None and count < 0: - raise ValueError("Cannot cut - count must be >= 0.") - if bits <= 0: - raise ValueError("Cannot cut - bits must be >= 0.") - c = 0 - while count is None or c < count: - c += 1 - nextchunk = self._slice(start, min(start + bits, end)) - if nextchunk.len != bits: - return - assert nextchunk._assertsanity() - yield nextchunk - start += bits - return - - def split(self, delimiter, start=None, end=None, count=None, - bytealigned=None): - """Return bitstring generator by splittling using a delimiter. - - The first item returned is the initial bitstring before the delimiter, - which may be an empty bitstring. - - delimiter -- The bitstring used as the divider. - start -- The bit position to start the split. Defaults to 0. - end -- The bit position one past the last bit to use in the split. - Defaults to self.len. - count -- If specified then at most count items are generated. - Default is to split as many times as possible. - bytealigned -- If True splits will only occur on byte boundaries. - - Raises ValueError if the delimiter is empty. - - """ - delimiter = Bits(delimiter) - if not delimiter.len: - raise ValueError("split delimiter cannot be empty.") - start, end = self._validate_slice(start, end) - if bytealigned is None: - bytealigned = globals()['bytealigned'] - if count is not None and count < 0: - raise ValueError("Cannot split - count must be >= 0.") - if count == 0: - return - if bytealigned and not delimiter.len % 8 and not self._datastore.offset: - # Use the quick find method - f = self._findbytes - x = delimiter._getbytes() - else: - f = self._findregex - x = re.compile(delimiter._getbin()) - found = f(x, start, end, bytealigned) - if not found: - # Initial bits are the whole bitstring being searched - yield self._slice(start, end) - return - # yield the bytes before the first occurrence of the delimiter, even if empty - yield self._slice(start, found[0]) - startpos = pos = found[0] - c = 1 - while count is None or c < count: - pos += delimiter.len - found = f(x, pos, end, bytealigned) - if not found: - # No more occurrences, so return the rest of the bitstring - yield self._slice(startpos, end) - return - c += 1 - yield self._slice(startpos, found[0]) - startpos = pos = found[0] - # Have generated count bitstrings, so time to quit. - return - - def join(self, sequence): - """Return concatenation of bitstrings joined by self. - - sequence -- A sequence of bitstrings. - - """ - s = self.__class__() - i = iter(sequence) - try: - s._addright(Bits(next(i))) - while True: - n = next(i) - s._addright(self) - s._addright(Bits(n)) - except StopIteration: - pass - return s - - def tobytes(self): - """Return the bitstring as bytes, padding with zero bits if needed. - - Up to seven zero bits will be added at the end to byte align. - - """ - d = offsetcopy(self._datastore, 0).rawbytes - # Need to ensure that unused bits at end are set to zero - unusedbits = 8 - self.len % 8 - if unusedbits != 8: - d[-1] &= (0xff << unusedbits) - return bytes(d) - - def tofile(self, f): - """Write the bitstring to a file object, padding with zero bits if needed. - - Up to seven zero bits will be added at the end to byte align. - - """ - # If the bitstring is file based then we don't want to read it all - # in to memory. - chunksize = 1024 * 1024 # 1 MiB chunks - if not self._offset: - a = 0 - bytelen = self._datastore.bytelength - p = self._datastore.getbyteslice(a, min(a + chunksize, bytelen - 1)) - while len(p) == chunksize: - f.write(p) - a += chunksize - p = self._datastore.getbyteslice(a, min(a + chunksize, bytelen - 1)) - f.write(p) - # Now the final byte, ensuring that unused bits at end are set to 0. - bits_in_final_byte = self.len % 8 - if not bits_in_final_byte: - bits_in_final_byte = 8 - f.write(self[-bits_in_final_byte:].tobytes()) - else: - # Really quite inefficient... - a = 0 - b = a + chunksize * 8 - while b <= self.len: - f.write(self._slice(a, b)._getbytes()) - a += chunksize * 8 - b += chunksize * 8 - if a != self.len: - f.write(self._slice(a, self.len).tobytes()) - - def startswith(self, prefix, start=None, end=None): - """Return whether the current bitstring starts with prefix. - - prefix -- The bitstring to search for. - start -- The bit position to start from. Defaults to 0. - end -- The bit position to end at. Defaults to self.len. - - """ - prefix = Bits(prefix) - start, end = self._validate_slice_msb0(start, end) # the _slice deals with msb0/lsb0 - if end < start + prefix.len: - return False - end = start + prefix.len - return self._slice(start, end) == prefix - - def endswith(self, suffix, start=None, end=None): - """Return whether the current bitstring ends with suffix. - - suffix -- The bitstring to search for. - start -- The bit position to start from. Defaults to 0. - end -- The bit position to end at. Defaults to self.len. - - """ - suffix = Bits(suffix) - start, end = self._validate_slice(start, end) - if start + suffix.len > end: - return False - start = end - suffix.len - return self._slice(start, end) == suffix - - def all(self, value, pos=None): - """Return True if one or many bits are all set to value. - - value -- If value is True then checks for bits set to 1, otherwise - checks for bits set to 0. - pos -- An iterable of bit positions. Negative numbers are treated in - the same way as slice indices. Defaults to the whole bitstring. - - """ - value = bool(value) - length = self.len - if pos is None: - pos = xrange(self.len) - for p in pos: - if p < 0: - p += length - if not 0 <= p < length: - raise IndexError("Bit position {0} out of range.".format(p)) - if not self._datastore.getbit(p) is value: - return False - return True - - def any(self, value, pos=None): - """Return True if any of one or many bits are set to value. - - value -- If value is True then checks for bits set to 1, otherwise - checks for bits set to 0. - pos -- An iterable of bit positions. Negative numbers are treated in - the same way as slice indices. Defaults to the whole bitstring. - - """ - value = bool(value) - length = self.len - if pos is None: - pos = xrange(self.len) - for p in pos: - if p < 0: - p += length - if not 0 <= p < length: - raise IndexError("Bit position {0} out of range.".format(p)) - if self._datastore.getbit(p) is value: - return True - return False - - def count(self, value): - """Return count of total number of either zero or one bits. - - value -- If True then bits set to 1 are counted, otherwise bits set - to 0 are counted. - - >>> Bits('0xef').count(1) - 7 - - """ - if not self.len: - return 0 - # count the number of 1s (from which it's easy to work out the 0s). - # Don't count the final byte yet. - count = sum(BIT_COUNT[self._datastore.getbyte(i)] for i in xrange(self._datastore.bytelength - 1)) - # adjust for bits at start that aren't part of the bitstring - if self._offset: - count -= BIT_COUNT[self._datastore.getbyte(0) >> (8 - self._offset)] - # and count the last 1 - 8 bits at the end. - endbits = self._datastore.bytelength * 8 - (self._offset + self.len) - count += BIT_COUNT[self._datastore.getbyte(self._datastore.bytelength - 1) >> endbits] - return count if value else self.len - count - - # Create native-endian functions as aliases depending on the byteorder - if byteorder == 'little': - _setfloatne = _setfloatle - _readfloatne = _readfloatle - _getfloatne = _getfloatle - _setuintne = _setuintle - _readuintne = _readuintle - _getuintne = _getuintle - _setintne = _setintle - _readintne = _readintle - _getintne = _getintle - else: - _setfloatne = _setfloat - _readfloatne = _readfloat - _getfloatne = _getfloat - _setuintne = _setuintbe - _readuintne = _readuintbe - _getuintne = _getuintbe - _setintne = _setintbe - _readintne = _readintbe - _getintne = _getintbe - - _offset = property(_getoffset) - - len = property(_getlength, - doc="""The length of the bitstring in bits. Read only. - """) - length = property(_getlength, - doc="""The length of the bitstring in bits. Read only. - """) - bool = property(_getbool, - doc="""The bitstring as a bool (True or False). Read only. - """) - hex = property(_gethex, - doc="""The bitstring as a hexadecimal string. Read only. - """) - bin = property(_getbin, - doc="""The bitstring as a binary string. Read only. - """) - oct = property(_getoct, - doc="""The bitstring as an octal string. Read only. - """) - bytes = property(_getbytes, - doc="""The bitstring as a bytes object. Read only. - """) - int = property(_getint, - doc="""The bitstring as a two's complement signed int. Read only. - """) - uint = property(_getuint, - doc="""The bitstring as a two's complement unsigned int. Read only. - """) - float = property(_getfloat, - doc="""The bitstring as a floating point number. Read only. - """) - intbe = property(_getintbe, - doc="""The bitstring as a two's complement big-endian signed int. Read only. - """) - uintbe = property(_getuintbe, - doc="""The bitstring as a two's complement big-endian unsigned int. Read only. - """) - floatbe = property(_getfloat, - doc="""The bitstring as a big-endian floating point number. Read only. - """) - intle = property(_getintle, - doc="""The bitstring as a two's complement little-endian signed int. Read only. - """) - uintle = property(_getuintle, - doc="""The bitstring as a two's complement little-endian unsigned int. Read only. - """) - floatle = property(_getfloatle, - doc="""The bitstring as a little-endian floating point number. Read only. - """) - intne = property(_getintne, - doc="""The bitstring as a two's complement native-endian signed int. Read only. - """) - uintne = property(_getuintne, - doc="""The bitstring as a two's complement native-endian unsigned int. Read only. - """) - floatne = property(_getfloatne, - doc="""The bitstring as a native-endian floating point number. Read only. - """) - ue = property(_getue, - doc="""The bitstring as an unsigned exponential-Golomb code. Read only. - """) - se = property(_getse, - doc="""The bitstring as a signed exponential-Golomb code. Read only. - """) - uie = property(_getuie, - doc="""The bitstring as an unsigned interleaved exponential-Golomb code. Read only. - """) - sie = property(_getsie, - doc="""The bitstring as a signed interleaved exponential-Golomb code. Read only. - """) - - - - - -class BitArray(Bits): - """A container holding a mutable sequence of bits. - - Subclass of the immutable Bits class. Inherits all of its - methods (except __hash__) and adds mutating methods. - - Mutating methods: - - append() -- Append a bitstring. - byteswap() -- Change byte endianness in-place. - insert() -- Insert a bitstring. - invert() -- Flip bit(s) between one and zero. - overwrite() -- Overwrite a section with a new bitstring. - prepend() -- Prepend a bitstring. - replace() -- Replace occurrences of one bitstring with another. - reverse() -- Reverse bits in-place. - rol() -- Rotate bits to the left. - ror() -- Rotate bits to the right. - set() -- Set bit(s) to 1 or 0. - - Methods inherited from Bits: - - all() -- Check if all specified bits are set to 1 or 0. - any() -- Check if any of specified bits are set to 1 or 0. - count() -- Count the number of bits set to 1 or 0. - cut() -- Create generator of constant sized chunks. - endswith() -- Return whether the bitstring ends with a sub-string. - find() -- Find a sub-bitstring in the current bitstring. - findall() -- Find all occurrences of a sub-bitstring in the current bitstring. - join() -- Join bitstrings together using current bitstring. - rfind() -- Seek backwards to find a sub-bitstring. - split() -- Create generator of chunks split by a delimiter. - startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobytes() -- Return bitstring as bytes, padding if needed. - tofile() -- Write bitstring to file, padding if needed. - unpack() -- Interpret bits using format string. - - Special methods: - - Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= - in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^. - - Properties: - - bin -- The bitstring as a binary string. - bool -- For single bit bitstrings, interpret as True or False. - bytepos -- The current byte position in the bitstring. - bytes -- The bitstring as a bytes object. - float -- Interpret as a floating point number. - floatbe -- Interpret as a big-endian floating point number. - floatle -- Interpret as a little-endian floating point number. - floatne -- Interpret as a native-endian floating point number. - hex -- The bitstring as a hexadecimal string. - int -- Interpret as a two's complement signed integer. - intbe -- Interpret as a big-endian signed integer. - intle -- Interpret as a little-endian signed integer. - intne -- Interpret as a native-endian signed integer. - len -- Length of the bitstring in bits. - oct -- The bitstring as an octal string. - pos -- The current bit position in the bitstring. - se -- Interpret as a signed exponential-Golomb code. - ue -- Interpret as an unsigned exponential-Golomb code. - sie -- Interpret as a signed interleaved exponential-Golomb code. - uie -- Interpret as an unsigned interleaved exponential-Golomb code. - uint -- Interpret as a two's complement unsigned integer. - uintbe -- Interpret as a big-endian unsigned integer. - uintle -- Interpret as a little-endian unsigned integer. - uintne -- Interpret as a native-endian unsigned integer. - - """ - - __slots__ = () - - # As BitArray objects are mutable, we shouldn't allow them to be hashed. - __hash__ = None - - def __init__(self, auto=None, length=None, offset=None, **kwargs): - """Either specify an 'auto' initialiser: - auto -- a string of comma separated tokens, an integer, a file object, - a bytearray, a boolean iterable or another bitstring. - - Or initialise via **kwargs with one (and only one) of: - bytes -- raw data as a string, for example read from a binary file. - bin -- binary string representation, e.g. '0b001010'. - hex -- hexadecimal string representation, e.g. '0x2ef' - oct -- octal string representation, e.g. '0o777'. - uint -- an unsigned integer. - int -- a signed integer. - float -- a floating point number. - uintbe -- an unsigned big-endian whole byte integer. - intbe -- a signed big-endian whole byte integer. - floatbe - a big-endian floating point number. - uintle -- an unsigned little-endian whole byte integer. - intle -- a signed little-endian whole byte integer. - floatle -- a little-endian floating point number. - uintne -- an unsigned native-endian whole byte integer. - intne -- a signed native-endian whole byte integer. - floatne -- a native-endian floating point number. - se -- a signed exponential-Golomb code. - ue -- an unsigned exponential-Golomb code. - sie -- a signed interleaved exponential-Golomb code. - uie -- an unsigned interleaved exponential-Golomb code. - bool -- a boolean (True or False). - filename -- a file which will be opened in binary read-only mode. - - Other keyword arguments: - length -- length of the bitstring in bits, if needed and appropriate. - It must be supplied for all integer and float initialisers. - offset -- bit offset to the data. These offset bits are - ignored and this is intended for use when - initialising using 'bytes' or 'filename'. - - """ - # For mutable BitArrays we always read in files to memory: - if not isinstance(self._datastore, ByteStore): - self._ensureinmemory() - - def __new__(cls, auto=None, length=None, offset=None, **kwargs): - x = super(BitArray, cls).__new__(cls) - y = Bits.__new__(BitArray, auto, length, offset, **kwargs) - x._datastore = ByteStore(y._datastore._rawarray[:], - y._datastore.bitlength, - y._datastore.offset) - return x - - def __iadd__(self, bs): - """Append bs to current bitstring. Return self. - - bs -- the bitstring to append. - - """ - self._append(bs) - return self - - def __copy__(self): - """Return a new copy of the BitArray.""" - s_copy = BitArray() - if not isinstance(self._datastore, ByteStore): - # Let them both point to the same (invariant) array. - # If either gets modified then at that point they'll be read into memory. - s_copy._datastore = self._datastore - else: - s_copy._datastore = copy.copy(self._datastore) - return s_copy - - def __setitem__(self, key, value): - try: - # A slice - start, step = 0, 1 - if key.step is not None: - step = key.step - except AttributeError: - # single element - if key < 0: - key += self.len - if not 0 <= key < self.len: - raise IndexError("Slice index out of range.") - if isinstance(value, numbers.Integral): - if not value: - self._unset(key) - return - if value in (1, -1): - self._set(key) - return - raise ValueError("Cannot set a single bit with integer {0}.".format(value)) - value = Bits(value) - if value.len == 1: - if value[0]: - self._set(key) - else: - self._unset(key) - else: - self._delete(1, key) - self._insert(value, key) - return - else: - if step != 1: - # convert to binary string and use string slicing - # TODO: Horribly inefficient - temp = list(self._getbin()) - v = list(Bits(value)._getbin()) - temp.__setitem__(key, v) - self._setbin_unsafe(''.join(temp)) - return - - # If value is an integer then we want to set the slice to that - # value rather than initialise a new bitstring of that length. - if not isinstance(value, numbers.Integral): - try: - value = Bits(value) - except TypeError: - raise TypeError("Bitstring, integer or string expected. " - "Got {0}.".format(type(value))) - if key.start is not None: - start = key.start - if key.start < 0: - start += self.len - if start < 0: - start = 0 - stop = self.len - if key.stop is not None: - stop = key.stop - if key.stop < 0: - stop += self.len - if start > stop: - # The standard behaviour for lists is to just insert at the - # start position if stop < start and step == 1. - stop = start - if isinstance(value, numbers.Integral): - if value >= 0: - value = self.__class__(uint=value, length=stop - start) - else: - value = self.__class__(int=value, length=stop - start) - stop = min(stop, self.len) - start = max(start, 0) - start = min(start, stop) - if (stop - start) == value.len: - if not value.len: - return - if step >= 0: - self._overwrite(value, start) - else: - self._overwrite(value.__getitem__(slice(None, None, 1)), start) - else: - # TODO: A delete then insert is wasteful - it could do unneeded shifts. - # Could be either overwrite + insert or overwrite + delete. - self._delete(stop - start, start) - if step >= 0: - self._insert(value, start) - else: - self._insert(value.__getitem__(slice(None, None, 1)), start) - # pos is now after the inserted piece. - return - - def __delitem__(self, key): - """Delete item or range. - - Indices are in units of the step parameter (default 1 bit). - Stepping is used to specify the number of bits in each item. - - >>> a = BitArray('0x001122') - >>> del a[1:2:8] - >>> print a - 0x0022 - - """ - try: - # A slice - start = 0 - step = key.step if key.step is not None else 1 - except AttributeError: - # single element - if key < 0: - key += self.len - if not 0 <= key < self.len: - raise IndexError("Slice index out of range.") - self._delete(1, key) - return - else: - if step != 1: - # convert to binary string and use string slicing - # TODO: Horribly inefficient - temp = list(self._getbin()) - temp.__delitem__(key) - self._setbin_unsafe(''.join(temp)) - return - if key.start is not None: - start = key.start - if key.start < 0: - start += self.len - if start < 0: - start = 0 - stop = self.len - if key.stop is not None: - stop = key.stop - if key.stop < 0: - stop += self.len - if start > stop: - return - stop = min(stop, self.len) - start = max(start, 0) - start = min(start, stop) - self._delete(stop - start, start) - return - - def __ilshift__(self, n): - """Shift bits by n to the left in place. Return self. - - n -- the number of bits to shift. Must be >= 0. - - """ - if n < 0: - raise ValueError("Cannot shift by a negative amount.") - if not self.len: - raise ValueError("Cannot shift an empty bitstring.") - if not n: - return self - n = min(n, self.len) - return self._ilshift(n) - - def __irshift__(self, n): - """Shift bits by n to the right in place. Return self. - - n -- the number of bits to shift. Must be >= 0. - - """ - if n < 0: - raise ValueError("Cannot shift by a negative amount.") - if not self.len: - raise ValueError("Cannot shift an empty bitstring.") - if not n: - return self - n = min(n, self.len) - return self._irshift(n) - - def __imul__(self, n): - """Concatenate n copies of self in place. Return self. - - Called for expressions of the form 'a *= 3'. - n -- The number of concatenations. Must be >= 0. - - """ - if n < 0: - raise ValueError("Cannot multiply by a negative integer.") - return self._imul(n) - - def __ior__(self, bs): - bs = Bits(bs) - if self.len != bs.len: - raise ValueError("Bitstrings must have the same length " - "for |= operator.") - return self._ior(bs) - - def __iand__(self, bs): - bs = Bits(bs) - if self.len != bs.len: - raise ValueError("Bitstrings must have the same length " - "for &= operator.") - return self._iand(bs) - - def __ixor__(self, bs): - bs = Bits(bs) - if self.len != bs.len: - raise ValueError("Bitstrings must have the same length " - "for ^= operator.") - return self._ixor(bs) - - def replace(self, old, new, start=None, end=None, count=None, - bytealigned=None): - """Replace all occurrences of old with new in place. - - Returns number of replacements made. - - old -- The bitstring to replace. - new -- The replacement bitstring. - start -- Any occurrences that start before this will not be replaced. - Defaults to 0. - end -- Any occurrences that finish after this will not be replaced. - Defaults to self.len. - count -- The maximum number of replacements to make. Defaults to - replace all occurrences. - bytealigned -- If True replacements will only be made on byte - boundaries. - - Raises ValueError if old is empty or if start or end are - out of range. - - """ - old = Bits(old) - new = Bits(new) - if not old.len: - raise ValueError("Empty bitstring cannot be replaced.") - start, end = self._validate_slice(start, end) - if bytealigned is None: - bytealigned = globals()['bytealigned'] - # Adjust count for use in split() - if count is not None: - count += 1 - sections = self.split(old, start, end, count, bytealigned) - lengths = [s.len for s in sections] - if len(lengths) == 1: - # Didn't find anything to replace. - return 0 # no replacements done - if new is self: - # Prevent self assignment woes - new = copy.copy(self) - positions = [lengths[0] + start] - for l in lengths[1:-1]: - # Next position is the previous one plus the length of the next section. - positions.append(positions[-1] + l) - # We have all the positions that need replacements. We do them - # in reverse order so that they won't move around as we replace. - positions.reverse() - try: - # Need to calculate new pos, if this is a bitstream - newpos = self._pos - for p in positions: - self[p:p + old.len] = new - if old.len != new.len: - diff = new.len - old.len - for p in positions: - if p >= newpos: - continue - if p + old.len <= newpos: - newpos += diff - else: - newpos = p - self._pos = newpos - except AttributeError: - for p in positions: - self[p:p + old.len] = new - assert self._assertsanity() - return len(lengths) - 1 - - def insert(self, bs, pos=None): - """Insert bs at bit position pos. - - bs -- The bitstring to insert. - pos -- The bit position to insert at. - - Raises ValueError if pos < 0 or pos > self.len. - - """ - bs = Bits(bs) - if not bs.len: - return self - if bs is self: - bs = self.__copy__() - if pos is None: - try: - pos = self._pos - except AttributeError: - raise TypeError("insert needs a bit position specified when used on a BitArray.") - if pos < 0: - pos += self.len - if not 0 <= pos <= self.len: - raise ValueError("Invalid insert position.") - self._insert(bs, pos) - - def overwrite(self, bs, pos=None): - """Overwrite with bs at bit position pos. - - bs -- The bitstring to overwrite with. - pos -- The bit position to begin overwriting from. - - Raises ValueError if pos < 0 or pos + bs.len > self.len - - """ - bs = Bits(bs) - if not bs.len: - return - if pos is None: - try: - pos = self._pos - except AttributeError: - raise TypeError("overwrite needs a bit position specified when used on a BitArray.") - if pos < 0: - pos += self.len - if pos < 0 or pos + bs.len > self.len: - raise ValueError("Overwrite exceeds boundary of bitstring.") - self._overwrite(bs, pos) - try: - self._pos = pos + bs.len - except AttributeError: - pass - - def append(self, bs): - """Append a bitstring to the current bitstring. - - bs -- The bitstring to append. - - """ - self._append(bs) - - def prepend(self, bs): - """Prepend a bitstring to the current bitstring. - - bs -- The bitstring to prepend. - - """ - self._prepend(bs) - - def _append_msb0(self, bs): - # The offset is a hint to make bs easily appendable. - bs = self._converttobitstring(bs, offset=(self.len + self._offset) % 8) - self._addright(bs) - - def _append_lsb0(self, bs): - bs = Bits(bs) - self._addleft(bs) - - def reverse(self, start=None, end=None): - """Reverse bits in-place. - - start -- Position of first bit to reverse. Defaults to 0. - end -- One past the position of the last bit to reverse. - Defaults to self.len. - - Using on an empty bitstring will have no effect. - - Raises ValueError if start < 0, end > self.len or end < start. - - """ - start, end = self._validate_slice(start, end) - if start == 0 and end == self.len: - self._reverse() - return - s = self._slice(start, end) - s._reverse() - self[start:end] = s - - def set(self, value, pos=None): - """Set one or many bits to 1 or 0. - - value -- If True bits are set to 1, otherwise they are set to 0. - pos -- Either a single bit position or an iterable of bit positions. - Negative numbers are treated in the same way as slice indices. - Defaults to the entire bitstring. - - Raises IndexError if pos < -self.len or pos >= self.len. - - """ - f = self._set if value else self._unset - if pos is None: - pos = xrange(self.len) - try: - length = self.len - for p in pos: - if p < 0: - p += length - if not 0 <= p < length: - raise IndexError("Bit position {0} out of range.".format(p)) - f(p) - except TypeError: - # Single pos - if pos < 0: - pos += self.len - if not 0 <= pos < length: - raise IndexError("Bit position {0} out of range.".format(pos)) - f(pos) - - def invert(self, pos=None): - """Invert one or many bits from 0 to 1 or vice versa. - - pos -- Either a single bit position or an iterable of bit positions. - Negative numbers are treated in the same way as slice indices. - - Raises IndexError if pos < -self.len or pos >= self.len. - - """ - if pos is None: - self._invert_all() - return - if not isinstance(pos, collectionsAbc.Iterable): - pos = (pos,) - length = self.len - - for p in pos: - if p < 0: - p += length - if not 0 <= p < length: - raise IndexError("Bit position {0} out of range.".format(p)) - self._invert(p) - - def ror(self, bits, start=None, end=None): - """Rotate bits to the right in-place. - - bits -- The number of bits to rotate by. - start -- Start of slice to rotate. Defaults to 0. - end -- End of slice to rotate. Defaults to self.len. - - Raises ValueError if bits < 0. - - """ - if not self.len: - raise Error("Cannot rotate an empty bitstring.") - if bits < 0: - raise ValueError("Cannot rotate by negative amount.") - self._ror(bits, start, end) - - def _ror_msb0(self, bits, start=None, end=None): - start, end = self._validate_slice_msb0(start, end) # the _slice deals with msb0/lsb0 - bits %= (end - start) - if not bits: - return - rhs = self._slice(end - bits, end) - self._delete(bits, end - bits) - self._insert(rhs, start) - - def rol(self, bits, start=None, end=None): - """Rotate bits to the left in-place. - - bits -- The number of bits to rotate by. - start -- Start of slice to rotate. Defaults to 0. - end -- End of slice to rotate. Defaults to self.len. - - Raises ValueError if bits < 0. - - """ - if not self.len: - raise Error("Cannot rotate an empty bitstring.") - if bits < 0: - raise ValueError("Cannot rotate by negative amount.") - self._rol(bits, start, end) - - def _rol_msb0(self, bits, start=None, end=None): - start, end = self._validate_slice_msb0(start, end) - bits %= (end - start) - if not bits: - return - lhs = self._slice(start, start + bits) - self._delete(bits, start) - self._insert(lhs, end - bits) - - def byteswap(self, fmt=None, start=None, end=None, repeat=True): - """Change the endianness in-place. Return number of repeats of fmt done. - - fmt -- A compact structure string, an integer number of bytes or - an iterable of integers. Defaults to 0, which byte reverses the - whole bitstring. - start -- Start bit position, defaults to 0. - end -- End bit position, defaults to self.len. - repeat -- If True (the default) the byte swapping pattern is repeated - as much as possible. - - """ - start, end = self._validate_slice(start, end) - if fmt is None or fmt == 0: - # reverse all of the whole bytes. - bytesizes = [(end - start) // 8] - elif isinstance(fmt, numbers.Integral): - if fmt < 0: - raise ValueError("Improper byte length {0}.".format(fmt)) - bytesizes = [fmt] - elif isinstance(fmt, basestring): - m = STRUCT_PACK_RE.match(fmt) - if not m: - raise ValueError("Cannot parse format string {0}.".format(fmt)) - # Split the format string into a list of 'q', '4h' etc. - formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt')) - # Now deal with multiplicative factors, 4h -> hhhh etc. - bytesizes = [] - for f in formatlist: - if len(f) == 1: - bytesizes.append(PACK_CODE_SIZE[f]) - else: - bytesizes.extend([PACK_CODE_SIZE[f[-1]]] * int(f[:-1])) - elif isinstance(fmt, collectionsAbc.Iterable): - bytesizes = fmt - for bytesize in bytesizes: - if not isinstance(bytesize, numbers.Integral) or bytesize < 0: - raise ValueError("Improper byte length {0}.".format(bytesize)) - else: - raise TypeError("Format must be an integer, string or iterable.") - - repeats = 0 - totalbitsize = 8 * sum(bytesizes) - if not totalbitsize: - return 0 - if repeat: - # Try to repeat up to the end of the bitstring. - finalbit = end - else: - # Just try one (set of) byteswap(s). - finalbit = start + totalbitsize - for patternend in xrange(start + totalbitsize, finalbit + 1, totalbitsize): - bytestart = patternend - totalbitsize - for bytesize in bytesizes: - byteend = bytestart + bytesize * 8 - self._reversebytes(bytestart, byteend) - bytestart += bytesize * 8 - repeats += 1 - return repeats - - def clear(self): - """Remove all bits, reset to zero length.""" - self._clear() - - def copy(self): - """Return a copy of the bitstring.""" - return self._copy() - - int = property(Bits._getint, Bits._setint, - doc="""The bitstring as a two's complement signed int. Read and write. - """) - uint = property(Bits._getuint, Bits._setuint, - doc="""The bitstring as a two's complement unsigned int. Read and write. - """) - float = property(Bits._getfloat, Bits._setfloat, - doc="""The bitstring as a floating point number. Read and write. - """) - intbe = property(Bits._getintbe, Bits._setintbe, - doc="""The bitstring as a two's complement big-endian signed int. Read and write. - """) - uintbe = property(Bits._getuintbe, Bits._setuintbe, - doc="""The bitstring as a two's complement big-endian unsigned int. Read and write. - """) - floatbe = property(Bits._getfloat, Bits._setfloat, - doc="""The bitstring as a big-endian floating point number. Read and write. - """) - intle = property(Bits._getintle, Bits._setintle, - doc="""The bitstring as a two's complement little-endian signed int. Read and write. - """) - uintle = property(Bits._getuintle, Bits._setuintle, - doc="""The bitstring as a two's complement little-endian unsigned int. Read and write. - """) - floatle = property(Bits._getfloatle, Bits._setfloatle, - doc="""The bitstring as a little-endian floating point number. Read and write. - """) - intne = property(Bits._getintne, Bits._setintne, - doc="""The bitstring as a two's complement native-endian signed int. Read and write. - """) - uintne = property(Bits._getuintne, Bits._setuintne, - doc="""The bitstring as a two's complement native-endian unsigned int. Read and write. - """) - floatne = property(Bits._getfloatne, Bits._setfloatne, - doc="""The bitstring as a native-endian floating point number. Read and write. - """) - ue = property(Bits._getue, Bits._setue, - doc="""The bitstring as an unsigned exponential-Golomb code. Read and write. - """) - se = property(Bits._getse, Bits._setse, - doc="""The bitstring as a signed exponential-Golomb code. Read and write. - """) - uie = property(Bits._getuie, Bits._setuie, - doc="""The bitstring as an unsigned interleaved exponential-Golomb code. Read and write. - """) - sie = property(Bits._getsie, Bits._setsie, - doc="""The bitstring as a signed interleaved exponential-Golomb code. Read and write. - """) - hex = property(Bits._gethex, Bits._sethex, - doc="""The bitstring as a hexadecimal string. Read and write. - """) - bin = property(Bits._getbin, Bits._setbin_safe, - doc="""The bitstring as a binary string. Read and write. - """) - oct = property(Bits._getoct, Bits._setoct, - doc="""The bitstring as an octal string. Read and write. - """) - bool = property(Bits._getbool, Bits._setbool, - doc="""The bitstring as a bool (True or False). Read and write. - """) - bytes = property(Bits._getbytes, Bits._setbytes_safe, - doc="""The bitstring as a ordinary string. Read and write. - """) - - - -class ConstBitStream(Bits): - """A container or stream holding an immutable sequence of bits. - - For a mutable container use the BitStream class instead. - - Methods inherited from Bits: - - all() -- Check if all specified bits are set to 1 or 0. - any() -- Check if any of specified bits are set to 1 or 0. - count() -- Count the number of bits set to 1 or 0. - cut() -- Create generator of constant sized chunks. - endswith() -- Return whether the bitstring ends with a sub-string. - find() -- Find a sub-bitstring in the current bitstring. - findall() -- Find all occurrences of a sub-bitstring in the current bitstring. - join() -- Join bitstrings together using current bitstring. - rfind() -- Seek backwards to find a sub-bitstring. - split() -- Create generator of chunks split by a delimiter. - startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobytes() -- Return bitstring as bytes, padding if needed. - tofile() -- Write bitstring to file, padding if needed. - unpack() -- Interpret bits using format string. - - Other methods: - - bytealign() -- Align to next byte boundary. - peek() -- Peek at and interpret next bits as a single item. - peeklist() -- Peek at and interpret next bits as a list of items. - read() -- Read and interpret next bits as a single item. - readlist() -- Read and interpret next bits as a list of items. - - Special methods: - - Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^. - - Properties: - - bin -- The bitstring as a binary string. - bool -- For single bit bitstrings, interpret as True or False. - bytepos -- The current byte position in the bitstring. - bytes -- The bitstring as a bytes object. - float -- Interpret as a floating point number. - floatbe -- Interpret as a big-endian floating point number. - floatle -- Interpret as a little-endian floating point number. - floatne -- Interpret as a native-endian floating point number. - hex -- The bitstring as a hexadecimal string. - int -- Interpret as a two's complement signed integer. - intbe -- Interpret as a big-endian signed integer. - intle -- Interpret as a little-endian signed integer. - intne -- Interpret as a native-endian signed integer. - len -- Length of the bitstring in bits. - oct -- The bitstring as an octal string. - pos -- The current bit position in the bitstring. - se -- Interpret as a signed exponential-Golomb code. - ue -- Interpret as an unsigned exponential-Golomb code. - sie -- Interpret as a signed interleaved exponential-Golomb code. - uie -- Interpret as an unsigned interleaved exponential-Golomb code. - uint -- Interpret as a two's complement unsigned integer. - uintbe -- Interpret as a big-endian unsigned integer. - uintle -- Interpret as a little-endian unsigned integer. - uintne -- Interpret as a native-endian unsigned integer. - - """ - - __slots__ = ('_pos',) - - def __init__(self, auto=None, length=None, offset=None, pos=0, **kwargs): - """Either specify an 'auto' initialiser: - auto -- a string of comma separated tokens, an integer, a file object, - a bytearray, a boolean iterable or another bitstring. - - Or initialise via **kwargs with one (and only one) of: - bytes -- raw data as a string, for example read from a binary file. - bin -- binary string representation, e.g. '0b001010'. - hex -- hexadecimal string representation, e.g. '0x2ef' - oct -- octal string representation, e.g. '0o777'. - uint -- an unsigned integer. - int -- a signed integer. - float -- a floating point number. - uintbe -- an unsigned big-endian whole byte integer. - intbe -- a signed big-endian whole byte integer. - floatbe - a big-endian floating point number. - uintle -- an unsigned little-endian whole byte integer. - intle -- a signed little-endian whole byte integer. - floatle -- a little-endian floating point number. - uintne -- an unsigned native-endian whole byte integer. - intne -- a signed native-endian whole byte integer. - floatne -- a native-endian floating point number. - se -- a signed exponential-Golomb code. - ue -- an unsigned exponential-Golomb code. - sie -- a signed interleaved exponential-Golomb code. - uie -- an unsigned interleaved exponential-Golomb code. - bool -- a boolean (True or False). - filename -- a file which will be opened in binary read-only mode. - - Other keyword arguments: - length -- length of the bitstring in bits, if needed and appropriate. - It must be supplied for all integer and float initialisers. - offset -- bit offset to the data. These offset bits are - ignored and this is intended for use when - initialising using 'bytes' or 'filename'. - pos -- Initial bit position, defaults to 0. - - """ - pass - - def __new__(cls, auto=None, length=None, offset=None, pos=0, **kwargs): - x = super(ConstBitStream, cls).__new__(cls) - x._initialise(auto, length, offset, **kwargs) - x._pos = x._datastore.bitlength + pos if pos < 0 else pos - if x._pos < 0 or x._pos > x._datastore.bitlength: - raise CreationError("Cannot set pos to {0} when length is {1}".format(pos, x._datastore.bitlength)) - return x - - def _setbytepos(self, bytepos): - """Move to absolute byte-aligned position in stream.""" - self._setbitpos(bytepos * 8) - - def _getbytepos(self): - """Return the current position in the stream in bytes. Must be byte aligned.""" - if self._pos % 8: - raise ByteAlignError("Not byte aligned in _getbytepos().") - return self._pos // 8 - - def _setbitpos(self, pos): - """Move to absolute position bit in bitstream.""" - if pos < 0: - raise ValueError("Bit position cannot be negative.") - if pos > self.len: - raise ValueError("Cannot seek past the end of the data.") - self._pos = pos - - def _getbitpos(self): - """Return the current position in the stream in bits.""" - return self._pos - - def _clear(self): - Bits._clear(self) - self._pos = 0 - - def __copy__(self): - """Return a new copy of the ConstBitStream for the copy module.""" - # Note that if you want a new copy (different ID), use _copy instead. - # The copy can use the same datastore as it's immutable. - s = ConstBitStream() - s._datastore = self._datastore - # Reset the bit position, don't copy it. - s._pos = 0 - return s - - def __add__(self, bs): - """Concatenate bitstrings and return new bitstring. - - bs -- the bitstring to append. - - """ - s = Bits.__add__(self, bs) - s._pos = 0 - return s - - def read(self, fmt): - """Interpret next bits according to the format string and return result. - - fmt -- Token string describing how to interpret the next bits. - - Token examples: 'int:12' : 12 bits as a signed integer - 'uint:8' : 8 bits as an unsigned integer - 'float:64' : 8 bytes as a big-endian float - 'intbe:16' : 2 bytes as a big-endian signed integer - 'uintbe:16' : 2 bytes as a big-endian unsigned integer - 'intle:32' : 4 bytes as a little-endian signed integer - 'uintle:32' : 4 bytes as a little-endian unsigned integer - 'floatle:64': 8 bytes as a little-endian float - 'intne:24' : 3 bytes as a native-endian signed integer - 'uintne:24' : 3 bytes as a native-endian unsigned integer - 'floatne:32': 4 bytes as a native-endian float - 'hex:80' : 80 bits as a hex string - 'oct:9' : 9 bits as an octal string - 'bin:1' : single bit binary string - 'ue' : next bits as unsigned exp-Golomb code - 'se' : next bits as signed exp-Golomb code - 'uie' : next bits as unsigned interleaved exp-Golomb code - 'sie' : next bits as signed interleaved exp-Golomb code - 'bits:5' : 5 bits as a bitstring - 'bytes:10' : 10 bytes as a bytes object - 'bool' : 1 bit as a bool - 'pad:3' : 3 bits of padding to ignore - returns None - - fmt may also be an integer, which will be treated like the 'bits' token. - - The position in the bitstring is advanced to after the read items. - - Raises ReadError if not enough bits are available. - Raises ValueError if the format is not understood. - - """ - if isinstance(fmt, numbers.Integral): - if fmt < 0: - raise ValueError("Cannot read negative amount.") - if fmt > self.len - self._pos: - raise ReadError("Cannot read {0} bits, only {1} available.", - fmt, self.len - self._pos) - bs = self._slice(self._pos, self._pos + fmt) - self._pos += fmt - return bs - p = self._pos - _, token = tokenparser(fmt) - if len(token) != 1: - self._pos = p - raise ValueError("Format string should be a single token, not {0} " - "tokens - use readlist() instead.".format(len(token))) - name, length, _ = token[0] - if length is None: - length = self.len - self._pos - value, self._pos = self._readtoken(name, self._pos, length) - return value - - def readlist(self, fmt, **kwargs): - """Interpret next bits according to format string(s) and return list. - - fmt -- A single string or list of strings with comma separated tokens - describing how to interpret the next bits in the bitstring. Items - can also be integers, for reading new bitstring of the given length. - kwargs -- A dictionary or keyword-value pairs - the keywords used in the - format string will be replaced with their given value. - - The position in the bitstring is advanced to after the read items. - - Raises ReadError is not enough bits are available. - Raises ValueError if the format is not understood. - - See the docstring for 'read' for token examples. 'pad' tokens are skipped - and not added to the returned list. - - >>> h, b1, b2 = s.readlist('hex:20, bin:5, bin:3') - >>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10]) - - """ - value, self._pos = self._readlist(fmt, self._pos, **kwargs) - return value - - def readto(self, bs, bytealigned=None): - """Read up to and including next occurrence of bs and return result. - - bs -- The bitstring to find. An integer is not permitted. - bytealigned -- If True the bitstring will only be - found on byte boundaries. - - Raises ValueError if bs is empty. - Raises ReadError if bs is not found. - - """ - if isinstance(bs, numbers.Integral): - raise ValueError("Integers cannot be searched for") - bs = Bits(bs) - oldpos = self._pos - p = self.find(bs, self._pos, bytealigned=bytealigned) - if not p: - raise ReadError("Substring not found") - self._pos += bs.len - return self._slice(oldpos, self._pos) - - def peek(self, fmt): - """Interpret next bits according to format string and return result. - - fmt -- Token string describing how to interpret the next bits. - - The position in the bitstring is not changed. If not enough bits are - available then all bits to the end of the bitstring will be used. - - Raises ReadError if not enough bits are available. - Raises ValueError if the format is not understood. - - See the docstring for 'read' for token examples. - - """ - pos_before = self._pos - value = self.read(fmt) - self._pos = pos_before - return value - - def peeklist(self, fmt, **kwargs): - """Interpret next bits according to format string(s) and return list. - - fmt -- One or more strings with comma separated tokens describing - how to interpret the next bits in the bitstring. - kwargs -- A dictionary or keyword-value pairs - the keywords used in the - format string will be replaced with their given value. - - The position in the bitstring is not changed. If not enough bits are - available then all bits to the end of the bitstring will be used. - - Raises ReadError if not enough bits are available. - Raises ValueError if the format is not understood. - - See the docstring for 'read' for token examples. - - """ - pos = self._pos - return_values = self.readlist(fmt, **kwargs) - self._pos = pos - return return_values - - def bytealign(self): - """Align to next byte and return number of skipped bits. - - Raises ValueError if the end of the bitstring is reached before - aligning to the next byte. - - """ - skipped = (8 - (self._pos % 8)) % 8 - self.pos += self._offset + skipped - assert self._assertsanity() - return skipped - - pos = property(_getbitpos, _setbitpos, - doc="""The position in the bitstring in bits. Read and write. - """) - bitpos = property(_getbitpos, _setbitpos, - doc="""The position in the bitstring in bits. Read and write. - """) - bytepos = property(_getbytepos, _setbytepos, - doc="""The position in the bitstring in bytes. Read and write. - """) - - -class BitStream(ConstBitStream, BitArray): - """A container or stream holding a mutable sequence of bits - - Subclass of the ConstBitStream and BitArray classes. Inherits all of - their methods. - - Methods: - - all() -- Check if all specified bits are set to 1 or 0. - any() -- Check if any of specified bits are set to 1 or 0. - append() -- Append a bitstring. - bytealign() -- Align to next byte boundary. - byteswap() -- Change byte endianness in-place. - count() -- Count the number of bits set to 1 or 0. - cut() -- Create generator of constant sized chunks. - endswith() -- Return whether the bitstring ends with a sub-string. - find() -- Find a sub-bitstring in the current bitstring. - findall() -- Find all occurrences of a sub-bitstring in the current bitstring. - insert() -- Insert a bitstring. - invert() -- Flip bit(s) between one and zero. - join() -- Join bitstrings together using current bitstring. - overwrite() -- Overwrite a section with a new bitstring. - peek() -- Peek at and interpret next bits as a single item. - peeklist() -- Peek at and interpret next bits as a list of items. - prepend() -- Prepend a bitstring. - read() -- Read and interpret next bits as a single item. - readlist() -- Read and interpret next bits as a list of items. - replace() -- Replace occurrences of one bitstring with another. - reverse() -- Reverse bits in-place. - rfind() -- Seek backwards to find a sub-bitstring. - rol() -- Rotate bits to the left. - ror() -- Rotate bits to the right. - set() -- Set bit(s) to 1 or 0. - split() -- Create generator of chunks split by a delimiter. - startswith() -- Return whether the bitstring starts with a sub-bitstring. - tobytes() -- Return bitstring as bytes, padding if needed. - tofile() -- Write bitstring to file, padding if needed. - unpack() -- Interpret bits using format string. - - Special methods: - - Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^= - in addition to [], ==, !=, +, *, ~, <<, >>, &, | and ^. - - Properties: - - bin -- The bitstring as a binary string. - bool -- For single bit bitstrings, interpret as True or False. - bytepos -- The current byte position in the bitstring. - bytes -- The bitstring as a bytes object. - float -- Interpret as a floating point number. - floatbe -- Interpret as a big-endian floating point number. - floatle -- Interpret as a little-endian floating point number. - floatne -- Interpret as a native-endian floating point number. - hex -- The bitstring as a hexadecimal string. - int -- Interpret as a two's complement signed integer. - intbe -- Interpret as a big-endian signed integer. - intle -- Interpret as a little-endian signed integer. - intne -- Interpret as a native-endian signed integer. - len -- Length of the bitstring in bits. - oct -- The bitstring as an octal string. - pos -- The current bit position in the bitstring. - se -- Interpret as a signed exponential-Golomb code. - ue -- Interpret as an unsigned exponential-Golomb code. - sie -- Interpret as a signed interleaved exponential-Golomb code. - uie -- Interpret as an unsigned interleaved exponential-Golomb code. - uint -- Interpret as a two's complement unsigned integer. - uintbe -- Interpret as a big-endian unsigned integer. - uintle -- Interpret as a little-endian unsigned integer. - uintne -- Interpret as a native-endian unsigned integer. - - """ - - __slots__ = () - - # As BitStream objects are mutable, we shouldn't allow them to be hashed. - __hash__ = None - - def __init__(self, auto=None, length=None, offset=None, pos=0, **kwargs): - """Either specify an 'auto' initialiser: - auto -- a string of comma separated tokens, an integer, a file object, - a bytearray, a boolean iterable or another bitstring. - - Or initialise via **kwargs with one (and only one) of: - bytes -- raw data as a string, for example read from a binary file. - bin -- binary string representation, e.g. '0b001010'. - hex -- hexadecimal string representation, e.g. '0x2ef' - oct -- octal string representation, e.g. '0o777'. - uint -- an unsigned integer. - int -- a signed integer. - float -- a floating point number. - uintbe -- an unsigned big-endian whole byte integer. - intbe -- a signed big-endian whole byte integer. - floatbe - a big-endian floating point number. - uintle -- an unsigned little-endian whole byte integer. - intle -- a signed little-endian whole byte integer. - floatle -- a little-endian floating point number. - uintne -- an unsigned native-endian whole byte integer. - intne -- a signed native-endian whole byte integer. - floatne -- a native-endian floating point number. - se -- a signed exponential-Golomb code. - ue -- an unsigned exponential-Golomb code. - sie -- a signed interleaved exponential-Golomb code. - uie -- an unsigned interleaved exponential-Golomb code. - bool -- a boolean (True or False). - filename -- a file which will be opened in binary read-only mode. - - Other keyword arguments: - length -- length of the bitstring in bits, if needed and appropriate. - It must be supplied for all integer and float initialisers. - offset -- bit offset to the data. These offset bits are - ignored and this is intended for use when - initialising using 'bytes' or 'filename'. - pos -- Initial bit position, defaults to 0. - - """ - # For mutable BitStreams we always read in files to memory: - if not isinstance(self._datastore, (ByteStore, ConstByteStore)): - self._ensureinmemory() - - def __new__(cls, auto=None, length=None, offset=None, pos=0, **kwargs): - x = super(BitStream, cls).__new__(cls) - y = ConstBitStream.__new__(BitStream, auto, length, offset, pos, **kwargs) - x._datastore = ByteStore(y._datastore._rawarray[:], - y._datastore.bitlength, - y._datastore.offset) - x._pos = y._pos - return x - - def __copy__(self): - """Return a new copy of the BitStream.""" - s_copy = BitStream() - s_copy._pos = 0 - if not isinstance(self._datastore, ByteStore): - # Let them both point to the same (invariant) array. - # If either gets modified then at that point they'll be read into memory. - s_copy._datastore = self._datastore - else: - s_copy._datastore = ByteStore(self._datastore._rawarray[:], - self._datastore.bitlength, - self._datastore.offset) - return s_copy - - def prepend(self, bs): - """Prepend a bitstring to the current bitstring. - - bs -- The bitstring to prepend. - - """ - bs = self._converttobitstring(bs) - self._addleft(bs) - self._pos += bs.len - - -def pack(fmt, *values, **kwargs): - """Pack the values according to the format string and return a new BitStream. - - fmt -- A single string or a list of strings with comma separated tokens - describing how to create the BitStream. - values -- Zero or more values to pack according to the format. - kwargs -- A dictionary or keyword-value pairs - the keywords used in the - format string will be replaced with their given value. - - Token examples: 'int:12' : 12 bits as a signed integer - 'uint:8' : 8 bits as an unsigned integer - 'float:64' : 8 bytes as a big-endian float - 'intbe:16' : 2 bytes as a big-endian signed integer - 'uintbe:16' : 2 bytes as a big-endian unsigned integer - 'intle:32' : 4 bytes as a little-endian signed integer - 'uintle:32' : 4 bytes as a little-endian unsigned integer - 'floatle:64': 8 bytes as a little-endian float - 'intne:24' : 3 bytes as a native-endian signed integer - 'uintne:24' : 3 bytes as a native-endian unsigned integer - 'floatne:32': 4 bytes as a native-endian float - 'hex:80' : 80 bits as a hex string - 'oct:9' : 9 bits as an octal string - 'bin:1' : single bit binary string - 'ue' / 'uie': next bits as unsigned exp-Golomb code - 'se' / 'sie': next bits as signed exp-Golomb code - 'bits:5' : 5 bits as a bitstring object - 'bytes:10' : 10 bytes as a bytes object - 'bool' : 1 bit as a bool - 'pad:3' : 3 zero bits as padding - - >>> s = pack('uint:12, bits', 100, '0xffe') - >>> t = pack(['bits', 'bin:3'], s, '111') - >>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44) - - """ - tokens = [] - if isinstance(fmt, basestring): - fmt = [fmt] - try: - for f_item in fmt: - _, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys()))) - tokens.extend(tkns) - except ValueError as e: - raise CreationError(*e.args) - value_iter = iter(values) - s = BitStream() - try: - for name, length, value in tokens: - # If the value is in the kwd dictionary then it takes precedence. - if value in kwargs: - value = kwargs[value] - # If the length is in the kwd dictionary then use that too. - if length in kwargs: - length = kwargs[length] - # Also if we just have a dictionary name then we want to use it - if name in kwargs and length is None and value is None: - s._append(kwargs[name]) - continue - if length is not None: - length = int(length) - if value is None and name != 'pad': - # Take the next value from the ones provided - value = next(value_iter) - s._addright(BitStream._init_with_token(name, length, value)) - except StopIteration: - raise CreationError("Not enough parameters present to pack according to the " - "format. {0} values are needed.", len(tokens)) - try: - next(value_iter) - except StopIteration: - # Good, we've used up all the *values. - return s - raise CreationError("Too many parameters present to pack according to the format.") - - -# Whether to label the Least Significant Bit as bit 0. Default is False. Experimental feature. -_lsb0 = False - -# Dictionary that maps token names to the function that reads them. Is set in next function. -name_to_read = {} - - -def _switch_lsb0_methods(lsb0): - global _lsb0 - _lsb0 = lsb0 - if lsb0: - ConstByteStore.getbit = ConstByteStore._getbit_lsb0 - Bits._find = Bits._find_lsb0 - Bits._slice = Bits._slice_lsb0 - BitArray._overwrite = BitArray._overwrite_lsb0 - BitArray._insert = BitArray._insert_lsb0 - BitArray._delete = BitArray._delete_lsb0 - BitArray._ror = BitArray._rol_msb0 - BitArray._rol = BitArray._ror_msb0 - ByteStore.setbit = ByteStore._setbit_lsb0 - ByteStore.unsetbit = ByteStore._unsetbit_lsb0 - ByteStore.invertbit = ByteStore._invertbit_lsb0 - BitArray._append = BitArray._append_lsb0 - BitArray._prepend = BitArray._append_msb0 # An LSB0 prepend is an MSB0 append - Bits._readuint = Bits._readuint_lsb0 - Bits._truncatestart = Bits._truncateright - Bits._truncateend = Bits._truncateleft - Bits._validate_slice = Bits._validate_slice_lsb0 - else: - ConstByteStore.getbit = ConstByteStore._getbit_msb0 - Bits._find = Bits._find_msb0 - Bits._slice = Bits._slice_msb0 - BitArray._overwrite = BitArray._overwrite_msb0 - BitArray._insert = BitArray._insert_msb0 - BitArray._delete = BitArray._delete_msb0 - BitArray._ror = BitArray._ror_msb0 - BitArray._rol = BitArray._rol_msb0 - ByteStore.setbit = ByteStore._setbit_msb0 - ByteStore.unsetbit = ByteStore._unsetbit_msb0 - ByteStore.invertbit = ByteStore._invertbit_msb0 - BitArray._append = BitArray._append_msb0 - BitArray._prepend = BitArray._append_lsb0 - Bits._readuint = Bits._readuint_msb0 - Bits._truncatestart = Bits._truncateleft - Bits._truncateend = Bits._truncateright - Bits._validate_slice = Bits._validate_slice_msb0 - - global name_to_read - name_to_read = {'uint': Bits._readuint, - 'uintle': Bits._readuintle, - 'uintbe': Bits._readuintbe, - 'uintne': Bits._readuintne, - 'int': Bits._readint, - 'intle': Bits._readintle, - 'intbe': Bits._readintbe, - 'intne': Bits._readintne, - 'float': Bits._readfloat, - 'floatbe': Bits._readfloat, # floatbe is a synonym for float - 'floatle': Bits._readfloatle, - 'floatne': Bits._readfloatne, - 'hex': Bits._readhex, - 'oct': Bits._readoct, - 'bin': Bits._readbin, - 'bits': Bits._readbits, - 'bytes': Bits._readbytes, - 'ue': Bits._readue, - 'se': Bits._readse, - 'uie': Bits._readuie, - 'sie': Bits._readsie, - 'bool': Bits._readbool, - } - - -def set_lsb0(v=True): - """Experimental method changing the bit numbering so that the least significant bit is bit 0""" - _switch_lsb0_methods(v) - - -def set_msb0(v=True): - """Experimental method to reset the bit numbering so that the most significant bit is bit 0""" - set_lsb0(not v) - - -# Initialise the default behaviour -set_msb0() - - -# Dictionaries for mapping init keywords with init functions. -init_with_length_and_offset = {'bytes': Bits._setbytes_safe, - 'filename': Bits._setfile, - } - -init_with_length_only = {'uint': Bits._setuint, - 'int': Bits._setint, - 'float': Bits._setfloat, - 'uintbe': Bits._setuintbe, - 'intbe': Bits._setintbe, - 'floatbe': Bits._setfloat, - 'uintle': Bits._setuintle, - 'intle': Bits._setintle, - 'floatle': Bits._setfloatle, - 'uintne': Bits._setuintne, - 'intne': Bits._setintne, - 'floatne': Bits._setfloatne, - } - -init_without_length_or_offset = {'bin': Bits._setbin_safe, - 'hex': Bits._sethex, - 'oct': Bits._setoct, - 'ue': Bits._setue, - 'se': Bits._setse, - 'uie': Bits._setuie, - 'sie': Bits._setsie, - 'bool': Bits._setbool, - } - - -# Aliases for backward compatibility -ConstBitArray = Bits -BitString = BitStream - -__all__ = ['ConstBitArray', 'ConstBitStream', 'BitStream', 'BitArray', - 'Bits', 'BitString', 'pack', 'Error', 'ReadError', 'InterpretError', - 'ByteAlignError', 'CreationError', 'bytealigned', 'set_lsb0', 'set_msb0'] - - -def main(): - # check if final parameter is an interpretation string - fp = sys.argv[-1] - if fp == '--help' or len(sys.argv) == 1: - print("""Create and interpret a bitstring from command-line parameters. - -Command-line parameters are concatenated and a bitstring created -from them. If the final parameter is either an interpretation string -or ends with a '.' followed by an interpretation string then that -interpretation of the bitstring will be used when printing it. - -Typical usage might be invoking the Python module from a console -as a one-off calculation: - -$ python -m bitstring int:16=-400 -0xfe70 -$ python -m bitstring float:32=0.2 bin -00111110010011001100110011001101 -$ python -m bitstring 0xff 3*0b01,0b11 uint -65367 -$ python -m bitstring hex=01, uint:12=352.hex -01160 - -This feature is experimental and is subject to change or removal. - """) - elif fp in name_to_read.keys(): - # concatenate all other parameters and interpret using the final one - b1 = Bits(','.join(sys.argv[1: -1])) - print(b1._readtoken(fp, 0, b1.__len__())[0]) - else: - # does final parameter end with a dot then an interpretation string? - interp = fp[fp.rfind('.') + 1:] - if interp in name_to_read.keys(): - sys.argv[-1] = fp[:fp.rfind('.')] - b1 = Bits(','.join(sys.argv[1:])) - print(b1._readtoken(interp, 0, b1.__len__())[0]) - else: - # No interpretation - just use default print - b1 = Bits(','.join(sys.argv[1:])) - print(b1) - - -if __name__ == '__main__': - main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE deleted file mode 100644 index e7057454d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ - -Except when otherwise stated (look for LICENSE files in directories or -information at the beginning of each file) all software and -documentation is licensed as follows: - - The MIT License - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA deleted file mode 100644 index 538e67914..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/METADATA +++ /dev/null @@ -1,34 +0,0 @@ -Metadata-Version: 2.1 -Name: cffi -Version: 1.15.1 -Summary: Foreign Function Interface for Python calling C code. -Home-page: http://cffi.readthedocs.org -Author: Armin Rigo, Maciej Fijalkowski -Author-email: python-cffi@googlegroups.com -License: MIT -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: License :: OSI Approved :: MIT License -License-File: LICENSE -Requires-Dist: pycparser - - -CFFI -==== - -Foreign Function Interface for Python calling C code. -Please see the `Documentation `_. - -Contact -------- - -`Mailing list `_ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD deleted file mode 100644 index 1ca4bfb0f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/RECORD +++ /dev/null @@ -1,44 +0,0 @@ -_cffi_backend.cp310-win_amd64.pyd,sha256=IJPn5PU1mzjwgZve-DFP2jMqFCfyLgmvxBbh7dWRD-E,181248 -cffi-1.15.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cffi-1.15.1.dist-info/LICENSE,sha256=esEZUOct9bRcUXFqeyLnuzSzJNZ_Bl4pOBUt1HLEgV8,1320 -cffi-1.15.1.dist-info/METADATA,sha256=KP4G3WmavRgDGwD2b8Y_eDsM1YeV6ckcG6Alz3-D8VY,1144 -cffi-1.15.1.dist-info/RECORD,, -cffi-1.15.1.dist-info/WHEEL,sha256=W26pYN7HLsBT1jrDSL9udgf_mdNKJmYmL23sIP-FcgM,102 -cffi-1.15.1.dist-info/entry_points.txt,sha256=y6jTxnyeuLnL-XJcDv8uML3n6wyYiGRg8MTp_QGJ9Ho,75 -cffi-1.15.1.dist-info/top_level.txt,sha256=rE7WR3rZfNKxWI9-jn6hsHCAl7MDkB-FmuQbxWjFehQ,19 -cffi/__init__.py,sha256=uABQQ4lgzvhAvVhd1_ZA_oSO9T-O93qMod-rs0Ihjb8,527 -cffi/__pycache__/__init__.cpython-310.pyc,, -cffi/__pycache__/api.cpython-310.pyc,, -cffi/__pycache__/backend_ctypes.cpython-310.pyc,, -cffi/__pycache__/cffi_opcode.cpython-310.pyc,, -cffi/__pycache__/commontypes.cpython-310.pyc,, -cffi/__pycache__/cparser.cpython-310.pyc,, -cffi/__pycache__/error.cpython-310.pyc,, -cffi/__pycache__/ffiplatform.cpython-310.pyc,, -cffi/__pycache__/lock.cpython-310.pyc,, -cffi/__pycache__/model.cpython-310.pyc,, -cffi/__pycache__/pkgconfig.cpython-310.pyc,, -cffi/__pycache__/recompiler.cpython-310.pyc,, -cffi/__pycache__/setuptools_ext.cpython-310.pyc,, -cffi/__pycache__/vengine_cpy.cpython-310.pyc,, -cffi/__pycache__/vengine_gen.cpython-310.pyc,, -cffi/__pycache__/verifier.cpython-310.pyc,, -cffi/_cffi_errors.h,sha256=G0bGOb-6SNIO0UY8KEN3cM40Yd1JuR5bETQ8Ni5PxWY,4057 -cffi/_cffi_include.h,sha256=H7cgdZR-POwmUFrIup4jOGzmje8YoQHhN99gVFg7w08,15185 -cffi/_embedding.h,sha256=zo5hCU0uCLgUeTOPdfXbXc5ABXjOffCMHyfUKBjCQ5E,18208 -cffi/api.py,sha256=Xs_dAN5x1ehfnn_F9ZTdA3Ce0bmPrqeIOkO4Ya1tfbQ,43029 -cffi/backend_ctypes.py,sha256=BHN3q2giL2_Y8wMDST2CIcc_qoMrs65qV9Ob5JvxBZ4,43575 -cffi/cffi_opcode.py,sha256=57P2NHLZkuTWueZybu5iosWljb6ocQmUXzGrCplrnyE,5911 -cffi/commontypes.py,sha256=mEZD4g0qtadnv6O6CEXvMQaJ1K6SRbG5S1h4YvVZHOU,2769 -cffi/cparser.py,sha256=CwVk2V3ATYlCoywG6zN35w6UQ7zj2EWX68KjoJp2Mzk,45237 -cffi/error.py,sha256=Bka7fSV22aIglTQDPIDfpnxTc1aWZLMQdQOJY-h_PUA,908 -cffi/ffiplatform.py,sha256=qioydJeC63dEvrQ3ht5_BPmSs7wzzzuWnZAJtfhic7I,4173 -cffi/lock.py,sha256=vnbsel7392Ib8gGBifIfAfc7MHteSwd3nP725pvc25Q,777 -cffi/model.py,sha256=HRD0WEYHF2Vr6RjS-4wyncElrZxU2256zY0fbMkSKec,22385 -cffi/parse_c_type.h,sha256=fKYNqWNX5f9kZNNhbXcRLTOlpRGRhh8eCLyHmTXIZnQ,6157 -cffi/pkgconfig.py,sha256=9zDcDf0XKIJaxFHLg7e-W8-Xb8Yq5hdhqH7kLg-ugRo,4495 -cffi/recompiler.py,sha256=lV6Hz-F1RXPk8YjafaIe3a-UNNVfLIeEQk0FwmXqg3s,66179 -cffi/setuptools_ext.py,sha256=8y14TOlRAkgdczmwtPOahyFXJHNyIqhLjUHMYQmjOHs,9150 -cffi/vengine_cpy.py,sha256=ukugKCIsURxJzHxlxS265tGjQfPTFDbThwsqBrwKh-A,44396 -cffi/vengine_gen.py,sha256=mykUhLFJIcV6AyQ5cMJ3n_7dbqw0a9WEjXW0E-WfgiI,27359 -cffi/verifier.py,sha256=AZuuR7MxjMYZc8IsZjGsF8mdGajCsOY60AZLwZZ_Z2Y,11560 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL deleted file mode 100644 index 93f1ef46a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.1) -Root-Is-Purelib: false -Tag: cp310-cp310-win_amd64 - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt deleted file mode 100644 index 4b0274f23..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[distutils.setup_keywords] -cffi_modules = cffi.setuptools_ext:cffi_modules diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt deleted file mode 100644 index f64577957..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -_cffi_backend -cffi diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py deleted file mode 100644 index b95553706..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -__all__ = ['FFI', 'VerificationError', 'VerificationMissing', 'CDefError', - 'FFIError'] - -from .api import FFI -from .error import CDefError, FFIError, VerificationError, VerificationMissing -from .error import PkgConfigError - -__version__ = "1.15.1" -__version_info__ = (1, 15, 1) - -# The verifier module file names are based on the CRC32 of a string that -# contains the following version number. It may be older than __version__ -# if nothing is clearly incompatible. -__version_verifier_modules__ = "0.8.6" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h deleted file mode 100644 index d9f7ad955..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_errors.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef CFFI_MESSAGEBOX -# ifdef _MSC_VER -# define CFFI_MESSAGEBOX 1 -# else -# define CFFI_MESSAGEBOX 0 -# endif -#endif - - -#if CFFI_MESSAGEBOX -/* Windows only: logic to take the Python-CFFI embedding logic - initialization errors and display them in a background thread - with MessageBox. The idea is that if the whole program closes - as a result of this problem, then likely it is already a console - program and you can read the stderr output in the console too. - If it is not a console program, then it will likely show its own - dialog to complain, or generally not abruptly close, and for this - case the background thread should stay alive. -*/ -static void *volatile _cffi_bootstrap_text; - -static PyObject *_cffi_start_error_capture(void) -{ - PyObject *result = NULL; - PyObject *x, *m, *bi; - - if (InterlockedCompareExchangePointer(&_cffi_bootstrap_text, - (void *)1, NULL) != NULL) - return (PyObject *)1; - - m = PyImport_AddModule("_cffi_error_capture"); - if (m == NULL) - goto error; - - result = PyModule_GetDict(m); - if (result == NULL) - goto error; - -#if PY_MAJOR_VERSION >= 3 - bi = PyImport_ImportModule("builtins"); -#else - bi = PyImport_ImportModule("__builtin__"); -#endif - if (bi == NULL) - goto error; - PyDict_SetItemString(result, "__builtins__", bi); - Py_DECREF(bi); - - x = PyRun_String( - "import sys\n" - "class FileLike:\n" - " def write(self, x):\n" - " try:\n" - " of.write(x)\n" - " except: pass\n" - " self.buf += x\n" - " def flush(self):\n" - " pass\n" - "fl = FileLike()\n" - "fl.buf = ''\n" - "of = sys.stderr\n" - "sys.stderr = fl\n" - "def done():\n" - " sys.stderr = of\n" - " return fl.buf\n", /* make sure the returned value stays alive */ - Py_file_input, - result, result); - Py_XDECREF(x); - - error: - if (PyErr_Occurred()) - { - PyErr_WriteUnraisable(Py_None); - PyErr_Clear(); - } - return result; -} - -#pragma comment(lib, "user32.lib") - -static DWORD WINAPI _cffi_bootstrap_dialog(LPVOID ignored) -{ - Sleep(666); /* may be interrupted if the whole process is closing */ -#if PY_MAJOR_VERSION >= 3 - MessageBoxW(NULL, (wchar_t *)_cffi_bootstrap_text, - L"Python-CFFI error", - MB_OK | MB_ICONERROR); -#else - MessageBoxA(NULL, (char *)_cffi_bootstrap_text, - "Python-CFFI error", - MB_OK | MB_ICONERROR); -#endif - _cffi_bootstrap_text = NULL; - return 0; -} - -static void _cffi_stop_error_capture(PyObject *ecap) -{ - PyObject *s; - void *text; - - if (ecap == (PyObject *)1) - return; - - if (ecap == NULL) - goto error; - - s = PyRun_String("done()", Py_eval_input, ecap, ecap); - if (s == NULL) - goto error; - - /* Show a dialog box, but in a background thread, and - never show multiple dialog boxes at once. */ -#if PY_MAJOR_VERSION >= 3 - text = PyUnicode_AsWideCharString(s, NULL); -#else - text = PyString_AsString(s); -#endif - - _cffi_bootstrap_text = text; - - if (text != NULL) - { - HANDLE h; - h = CreateThread(NULL, 0, _cffi_bootstrap_dialog, - NULL, 0, NULL); - if (h != NULL) - CloseHandle(h); - } - /* decref the string, but it should stay alive as 'fl.buf' - in the small module above. It will really be freed only if - we later get another similar error. So it's a leak of at - most one copy of the small module. That's fine for this - situation which is usually a "fatal error" anyway. */ - Py_DECREF(s); - PyErr_Clear(); - return; - - error: - _cffi_bootstrap_text = NULL; - PyErr_Clear(); -} - -#else - -static PyObject *_cffi_start_error_capture(void) { return NULL; } -static void _cffi_stop_error_capture(PyObject *ecap) { } - -#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h deleted file mode 100644 index e0033e5f6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_cffi_include.h +++ /dev/null @@ -1,385 +0,0 @@ -#define _CFFI_ - -/* We try to define Py_LIMITED_API before including Python.h. - - Mess: we can only define it if Py_DEBUG, Py_TRACE_REFS and - Py_REF_DEBUG are not defined. This is a best-effort approximation: - we can learn about Py_DEBUG from pyconfig.h, but it is unclear if - the same works for the other two macros. Py_DEBUG implies them, - but not the other way around. - - The implementation is messy (issue #350): on Windows, with _MSC_VER, - we have to define Py_LIMITED_API even before including pyconfig.h. - In that case, we guess what pyconfig.h will do to the macros above, - and check our guess after the #include. - - Note that on Windows, with CPython 3.x, you need >= 3.5 and virtualenv - version >= 16.0.0. With older versions of either, you don't get a - copy of PYTHON3.DLL in the virtualenv. We can't check the version of - CPython *before* we even include pyconfig.h. ffi.set_source() puts - a ``#define _CFFI_NO_LIMITED_API'' at the start of this file if it is - running on Windows < 3.5, as an attempt at fixing it, but that's - arguably wrong because it may not be the target version of Python. - Still better than nothing I guess. As another workaround, you can - remove the definition of Py_LIMITED_API here. - - See also 'py_limited_api' in cffi/setuptools_ext.py. -*/ -#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API) -# ifdef _MSC_VER -# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# include - /* sanity-check: Py_LIMITED_API will cause crashes if any of these - are also defined. Normally, the Python file PC/pyconfig.h does not - cause any of these to be defined, with the exception that _DEBUG - causes Py_DEBUG. Double-check that. */ -# ifdef Py_LIMITED_API -# if defined(Py_DEBUG) -# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set" -# endif -# if defined(Py_TRACE_REFS) -# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set" -# endif -# if defined(Py_REF_DEBUG) -# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set" -# endif -# endif -# else -# include -# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG) && !defined(_CFFI_NO_LIMITED_API) -# define Py_LIMITED_API -# endif -# endif -#endif - -#include -#ifdef __cplusplus -extern "C" { -#endif -#include -#include "parse_c_type.h" - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -#endif - -#ifdef __GNUC__ -# define _CFFI_UNUSED_FN __attribute__((unused)) -#else -# define _CFFI_UNUSED_FN /* nothing */ -#endif - -#ifdef __cplusplus -# ifndef _Bool - typedef bool _Bool; /* semi-hackish: C++ has no _Bool; bool is builtin */ -# endif -#endif - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, struct _cffi_ctypedescr *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - not used any more -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, struct _cffi_ctypedescr *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((_cffi_wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(_cffi_wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(struct _cffi_ctypedescr *, \ - PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, struct _cffi_ctypedescr *, PyObject *))_cffi_exports[24]) -#define _CFFI_CPIDX 25 -#define _cffi_call_python \ - ((void(*)(struct _cffi_externpy_s *, char *))_cffi_exports[_CFFI_CPIDX]) -#define _cffi_to_c_wchar3216_t \ - ((int(*)(PyObject *))_cffi_exports[26]) -#define _cffi_from_c_wchar3216_t \ - ((PyObject *(*)(int))_cffi_exports[27]) -#define _CFFI_NUM_EXPORTS 28 - -struct _cffi_ctypedescr; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; - -#define _cffi_type(index) ( \ - assert((((uintptr_t)_cffi_types[index]) & 1) == 0), \ - (struct _cffi_ctypedescr *)_cffi_types[index]) - -static PyObject *_cffi_init(const char *module_name, Py_ssize_t version, - const struct _cffi_type_context_s *ctx) -{ - PyObject *module, *o_arg, *new_module; - void *raw[] = { - (void *)module_name, - (void *)version, - (void *)_cffi_exports, - (void *)ctx, - }; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - o_arg = PyLong_FromVoidPtr((void *)raw); - if (o_arg == NULL) - goto failure; - - new_module = PyObject_CallMethod( - module, (char *)"_init_cffi_1_0_external_module", (char *)"O", o_arg); - - Py_DECREF(o_arg); - Py_DECREF(module); - return new_module; - - failure: - Py_XDECREF(module); - return NULL; -} - - -#ifdef HAVE_WCHAR_H -typedef wchar_t _cffi_wchar_t; -#else -typedef uint16_t _cffi_wchar_t; /* same random pick as _cffi_backend.c */ -#endif - -_CFFI_UNUSED_FN static uint16_t _cffi_to_c_char16_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 2) - return (uint16_t)_cffi_to_c_wchar_t(o); - else - return (uint16_t)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char16_t(uint16_t x) -{ - if (sizeof(_cffi_wchar_t) == 2) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -_CFFI_UNUSED_FN static int _cffi_to_c_char32_t(PyObject *o) -{ - if (sizeof(_cffi_wchar_t) == 4) - return (int)_cffi_to_c_wchar_t(o); - else - return (int)_cffi_to_c_wchar3216_t(o); -} - -_CFFI_UNUSED_FN static PyObject *_cffi_from_c_char32_t(unsigned int x) -{ - if (sizeof(_cffi_wchar_t) == 4) - return _cffi_from_c_wchar_t((_cffi_wchar_t)x); - else - return _cffi_from_c_wchar3216_t((int)x); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -_CFFI_UNUSED_FN static int -_cffi_convert_array_argument(struct _cffi_ctypedescr *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -_CFFI_UNUSED_FN static void -_cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -/********** end CPython-specific section **********/ -#else -_CFFI_UNUSED_FN -static void (*_cffi_call_python_org)(struct _cffi_externpy_s *, char *); -# define _cffi_call_python _cffi_call_python_org -#endif - - -#define _cffi_array_len(array) (sizeof(array) / sizeof((array)[0])) - -#define _cffi_prim_int(size, sign) \ - ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8 : _CFFI_PRIM_UINT8) : \ - (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \ - (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \ - (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \ - _CFFI__UNKNOWN_PRIM) - -#define _cffi_prim_float(size) \ - ((size) == sizeof(float) ? _CFFI_PRIM_FLOAT : \ - (size) == sizeof(double) ? _CFFI_PRIM_DOUBLE : \ - (size) == sizeof(long double) ? _CFFI__UNKNOWN_LONG_DOUBLE : \ - _CFFI__UNKNOWN_FLOAT_PRIM) - -#define _cffi_check_int(got, got_nonpos, expected) \ - ((got_nonpos) == (expected <= 0) && \ - (got) == (unsigned long long)expected) - -#ifdef MS_WIN32 -# define _cffi_stdcall __stdcall -#else -# define _cffi_stdcall /* nothing */ -#endif - -#ifdef __cplusplus -} -#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h deleted file mode 100644 index 6238f7284..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/_embedding.h +++ /dev/null @@ -1,528 +0,0 @@ - -/***** Support code for embedding *****/ - -#ifdef __cplusplus -extern "C" { -#endif - - -#if defined(_WIN32) -# define CFFI_DLLEXPORT __declspec(dllexport) -#elif defined(__GNUC__) -# define CFFI_DLLEXPORT __attribute__((visibility("default"))) -#else -# define CFFI_DLLEXPORT /* nothing */ -#endif - - -/* There are two global variables of type _cffi_call_python_fnptr: - - * _cffi_call_python, which we declare just below, is the one called - by ``extern "Python"`` implementations. - - * _cffi_call_python_org, which on CPython is actually part of the - _cffi_exports[] array, is the function pointer copied from - _cffi_backend. If _cffi_start_python() fails, then this is set - to NULL; otherwise, it should never be NULL. - - After initialization is complete, both are equal. However, the - first one remains equal to &_cffi_start_and_call_python until the - very end of initialization, when we are (or should be) sure that - concurrent threads also see a completely initialized world, and - only then is it changed. -*/ -#undef _cffi_call_python -typedef void (*_cffi_call_python_fnptr)(struct _cffi_externpy_s *, char *); -static void _cffi_start_and_call_python(struct _cffi_externpy_s *, char *); -static _cffi_call_python_fnptr _cffi_call_python = &_cffi_start_and_call_python; - - -#ifndef _MSC_VER - /* --- Assuming a GCC not infinitely old --- */ -# define cffi_compare_and_swap(l,o,n) __sync_bool_compare_and_swap(l,o,n) -# define cffi_write_barrier() __sync_synchronize() -# if !defined(__amd64__) && !defined(__x86_64__) && \ - !defined(__i386__) && !defined(__i386) -# define cffi_read_barrier() __sync_synchronize() -# else -# define cffi_read_barrier() (void)0 -# endif -#else - /* --- Windows threads version --- */ -# include -# define cffi_compare_and_swap(l,o,n) \ - (InterlockedCompareExchangePointer(l,n,o) == (o)) -# define cffi_write_barrier() InterlockedCompareExchange(&_cffi_dummy,0,0) -# define cffi_read_barrier() (void)0 -static volatile LONG _cffi_dummy; -#endif - -#ifdef WITH_THREAD -# ifndef _MSC_VER -# include - static pthread_mutex_t _cffi_embed_startup_lock; -# else - static CRITICAL_SECTION _cffi_embed_startup_lock; -# endif - static char _cffi_embed_startup_lock_ready = 0; -#endif - -static void _cffi_acquire_reentrant_mutex(void) -{ - static void *volatile lock = NULL; - - while (!cffi_compare_and_swap(&lock, NULL, (void *)1)) { - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: pthread_mutex_init() should be very fast, and - this is only run at start-up anyway. */ - } - -#ifdef WITH_THREAD - if (!_cffi_embed_startup_lock_ready) { -# ifndef _MSC_VER - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&_cffi_embed_startup_lock, &attr); -# else - InitializeCriticalSection(&_cffi_embed_startup_lock); -# endif - _cffi_embed_startup_lock_ready = 1; - } -#endif - - while (!cffi_compare_and_swap(&lock, (void *)1, NULL)) - ; - -#ifndef _MSC_VER - pthread_mutex_lock(&_cffi_embed_startup_lock); -#else - EnterCriticalSection(&_cffi_embed_startup_lock); -#endif -} - -static void _cffi_release_reentrant_mutex(void) -{ -#ifndef _MSC_VER - pthread_mutex_unlock(&_cffi_embed_startup_lock); -#else - LeaveCriticalSection(&_cffi_embed_startup_lock); -#endif -} - - -/********** CPython-specific section **********/ -#ifndef PYPY_VERSION - -#include "_cffi_errors.h" - - -#define _cffi_call_python_org _cffi_exports[_CFFI_CPIDX] - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(void); /* forward */ - -static void _cffi_py_initialize(void) -{ - /* XXX use initsigs=0, which "skips initialization registration of - signal handlers, which might be useful when Python is - embedded" according to the Python docs. But review and think - if it should be a user-controllable setting. - - XXX we should also give a way to write errors to a buffer - instead of to stderr. - - XXX if importing 'site' fails, CPython (any version) calls - exit(). Should we try to work around this behavior here? - */ - Py_InitializeEx(0); -} - -static int _cffi_initialize_python(void) -{ - /* This initializes Python, imports _cffi_backend, and then the - present .dll/.so is set up as a CPython C extension module. - */ - int result; - PyGILState_STATE state; - PyObject *pycode=NULL, *global_dict=NULL, *x; - PyObject *builtins; - - state = PyGILState_Ensure(); - - /* Call the initxxx() function from the present module. It will - create and initialize us as a CPython extension module, instead - of letting the startup Python code do it---it might reimport - the same .dll/.so and get maybe confused on some platforms. - It might also have troubles locating the .dll/.so again for all - I know. - */ - (void)_CFFI_PYTHON_STARTUP_FUNC(); - if (PyErr_Occurred()) - goto error; - - /* Now run the Python code provided to ffi.embedding_init_code(). - */ - pycode = Py_CompileString(_CFFI_PYTHON_STARTUP_CODE, - "", - Py_file_input); - if (pycode == NULL) - goto error; - global_dict = PyDict_New(); - if (global_dict == NULL) - goto error; - builtins = PyEval_GetBuiltins(); - if (builtins == NULL) - goto error; - if (PyDict_SetItemString(global_dict, "__builtins__", builtins) < 0) - goto error; - x = PyEval_EvalCode( -#if PY_MAJOR_VERSION < 3 - (PyCodeObject *) -#endif - pycode, global_dict, global_dict); - if (x == NULL) - goto error; - Py_DECREF(x); - - /* Done! Now if we've been called from - _cffi_start_and_call_python() in an ``extern "Python"``, we can - only hope that the Python code did correctly set up the - corresponding @ffi.def_extern() function. Otherwise, the - general logic of ``extern "Python"`` functions (inside the - _cffi_backend module) will find that the reference is still - missing and print an error. - */ - result = 0; - done: - Py_XDECREF(pycode); - Py_XDECREF(global_dict); - PyGILState_Release(state); - return result; - - error:; - { - /* Print as much information as potentially useful. - Debugging load-time failures with embedding is not fun - */ - PyObject *ecap; - PyObject *exception, *v, *tb, *f, *modules, *mod; - PyErr_Fetch(&exception, &v, &tb); - ecap = _cffi_start_error_capture(); - f = PySys_GetObject((char *)"stderr"); - if (f != NULL && f != Py_None) { - PyFile_WriteString( - "Failed to initialize the Python-CFFI embedding logic:\n\n", f); - } - - if (exception != NULL) { - PyErr_NormalizeException(&exception, &v, &tb); - PyErr_Display(exception, v, tb); - } - Py_XDECREF(exception); - Py_XDECREF(v); - Py_XDECREF(tb); - - if (f != NULL && f != Py_None) { - PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME - "\ncompiled with cffi version: 1.15.1" - "\n_cffi_backend module: ", f); - modules = PyImport_GetModuleDict(); - mod = PyDict_GetItemString(modules, "_cffi_backend"); - if (mod == NULL) { - PyFile_WriteString("not loaded", f); - } - else { - v = PyObject_GetAttrString(mod, "__file__"); - PyFile_WriteObject(v, f, 0); - Py_XDECREF(v); - } - PyFile_WriteString("\nsys.path: ", f); - PyFile_WriteObject(PySys_GetObject((char *)"path"), f, 0); - PyFile_WriteString("\n\n", f); - } - _cffi_stop_error_capture(ecap); - } - result = -1; - goto done; -} - -#if PY_VERSION_HEX < 0x03080000 -PyAPI_DATA(char *) _PyParser_TokenNames[]; /* from CPython */ -#endif - -static int _cffi_carefully_make_gil(void) -{ - /* This does the basic initialization of Python. It can be called - completely concurrently from unrelated threads. It assumes - that we don't hold the GIL before (if it exists), and we don't - hold it afterwards. - - (What it really does used to be completely different in Python 2 - and Python 3, with the Python 2 solution avoiding the spin-lock - around the Py_InitializeEx() call. However, after recent changes - to CPython 2.7 (issue #358) it no longer works. So we use the - Python 3 solution everywhere.) - - This initializes Python by calling Py_InitializeEx(). - Important: this must not be called concurrently at all. - So we use a global variable as a simple spin lock. This global - variable must be from 'libpythonX.Y.so', not from this - cffi-based extension module, because it must be shared from - different cffi-based extension modules. - - In Python < 3.8, we choose - _PyParser_TokenNames[0] as a completely arbitrary pointer value - that is never written to. The default is to point to the - string "ENDMARKER". We change it temporarily to point to the - next character in that string. (Yes, I know it's REALLY - obscure.) - - In Python >= 3.8, this string array is no longer writable, so - instead we pick PyCapsuleType.tp_version_tag. We can't change - Python < 3.8 because someone might use a mixture of cffi - embedded modules, some of which were compiled before this file - changed. - */ - -#ifdef WITH_THREAD -# if PY_VERSION_HEX < 0x03080000 - char *volatile *lock = (char *volatile *)_PyParser_TokenNames; - char *old_value, *locked_value; - - while (1) { /* spin loop */ - old_value = *lock; - locked_value = old_value + 1; - if (old_value[0] == 'E') { - assert(old_value[1] == 'N'); - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { - assert(old_value[0] == 'N'); - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# else - int volatile *lock = (int volatile *)&PyCapsule_Type.tp_version_tag; - int old_value, locked_value; - assert(!(PyCapsule_Type.tp_flags & Py_TPFLAGS_HAVE_VERSION_TAG)); - - while (1) { /* spin loop */ - old_value = *lock; - locked_value = -42; - if (old_value == 0) { - if (cffi_compare_and_swap(lock, old_value, locked_value)) - break; - } - else { - assert(old_value == locked_value); - /* should ideally do a spin loop instruction here, but - hard to do it portably and doesn't really matter I - think: PyEval_InitThreads() should be very fast, and - this is only run at start-up anyway. */ - } - } -# endif -#endif - - /* call Py_InitializeEx() */ - if (!Py_IsInitialized()) { - _cffi_py_initialize(); -#if PY_VERSION_HEX < 0x03070000 - PyEval_InitThreads(); -#endif - PyEval_SaveThread(); /* release the GIL */ - /* the returned tstate must be the one that has been stored into the - autoTLSkey by _PyGILState_Init() called from Py_Initialize(). */ - } - else { -#if PY_VERSION_HEX < 0x03070000 - /* PyEval_InitThreads() is always a no-op from CPython 3.7 */ - PyGILState_STATE state = PyGILState_Ensure(); - PyEval_InitThreads(); - PyGILState_Release(state); -#endif - } - -#ifdef WITH_THREAD - /* release the lock */ - while (!cffi_compare_and_swap(lock, locked_value, old_value)) - ; -#endif - - return 0; -} - -/********** end CPython-specific section **********/ - - -#else - - -/********** PyPy-specific section **********/ - -PyMODINIT_FUNC _CFFI_PYTHON_STARTUP_FUNC(const void *[]); /* forward */ - -static struct _cffi_pypy_init_s { - const char *name; - void *func; /* function pointer */ - const char *code; -} _cffi_pypy_init = { - _CFFI_MODULE_NAME, - _CFFI_PYTHON_STARTUP_FUNC, - _CFFI_PYTHON_STARTUP_CODE, -}; - -extern int pypy_carefully_make_gil(const char *); -extern int pypy_init_embedded_cffi_module(int, struct _cffi_pypy_init_s *); - -static int _cffi_carefully_make_gil(void) -{ - return pypy_carefully_make_gil(_CFFI_MODULE_NAME); -} - -static int _cffi_initialize_python(void) -{ - return pypy_init_embedded_cffi_module(0xB011, &_cffi_pypy_init); -} - -/********** end PyPy-specific section **********/ - - -#endif - - -#ifdef __GNUC__ -__attribute__((noinline)) -#endif -static _cffi_call_python_fnptr _cffi_start_python(void) -{ - /* Delicate logic to initialize Python. This function can be - called multiple times concurrently, e.g. when the process calls - its first ``extern "Python"`` functions in multiple threads at - once. It can also be called recursively, in which case we must - ignore it. We also have to consider what occurs if several - different cffi-based extensions reach this code in parallel - threads---it is a different copy of the code, then, and we - can't have any shared global variable unless it comes from - 'libpythonX.Y.so'. - - Idea: - - * _cffi_carefully_make_gil(): "carefully" call - PyEval_InitThreads() (possibly with Py_InitializeEx() first). - - * then we use a (local) custom lock to make sure that a call to this - cffi-based extension will wait if another call to the *same* - extension is running the initialization in another thread. - It is reentrant, so that a recursive call will not block, but - only one from a different thread. - - * then we grab the GIL and (Python 2) we call Py_InitializeEx(). - At this point, concurrent calls to Py_InitializeEx() are not - possible: we have the GIL. - - * do the rest of the specific initialization, which may - temporarily release the GIL but not the custom lock. - Only release the custom lock when we are done. - */ - static char called = 0; - - if (_cffi_carefully_make_gil() != 0) - return NULL; - - _cffi_acquire_reentrant_mutex(); - - /* Here the GIL exists, but we don't have it. We're only protected - from concurrency by the reentrant mutex. */ - - /* This file only initializes the embedded module once, the first - time this is called, even if there are subinterpreters. */ - if (!called) { - called = 1; /* invoke _cffi_initialize_python() only once, - but don't set '_cffi_call_python' right now, - otherwise concurrent threads won't call - this function at all (we need them to wait) */ - if (_cffi_initialize_python() == 0) { - /* now initialization is finished. Switch to the fast-path. */ - - /* We would like nobody to see the new value of - '_cffi_call_python' without also seeing the rest of the - data initialized. However, this is not possible. But - the new value of '_cffi_call_python' is the function - 'cffi_call_python()' from _cffi_backend. So: */ - cffi_write_barrier(); - /* ^^^ we put a write barrier here, and a corresponding - read barrier at the start of cffi_call_python(). This - ensures that after that read barrier, we see everything - done here before the write barrier. - */ - - assert(_cffi_call_python_org != NULL); - _cffi_call_python = (_cffi_call_python_fnptr)_cffi_call_python_org; - } - else { - /* initialization failed. Reset this to NULL, even if it was - already set to some other value. Future calls to - _cffi_start_python() are still forced to occur, and will - always return NULL from now on. */ - _cffi_call_python_org = NULL; - } - } - - _cffi_release_reentrant_mutex(); - - return (_cffi_call_python_fnptr)_cffi_call_python_org; -} - -static -void _cffi_start_and_call_python(struct _cffi_externpy_s *externpy, char *args) -{ - _cffi_call_python_fnptr fnptr; - int current_err = errno; -#ifdef _MSC_VER - int current_lasterr = GetLastError(); -#endif - fnptr = _cffi_start_python(); - if (fnptr == NULL) { - fprintf(stderr, "function %s() called, but initialization code " - "failed. Returning 0.\n", externpy->name); - memset(args, 0, externpy->size_of_result); - } -#ifdef _MSC_VER - SetLastError(current_lasterr); -#endif - errno = current_err; - - if (fnptr != NULL) - fnptr(externpy, args); -} - - -/* The cffi_start_python() function makes sure Python is initialized - and our cffi module is set up. It can be called manually from the - user C code. The same effect is obtained automatically from any - dll-exported ``extern "Python"`` function. This function returns - -1 if initialization failed, 0 if all is OK. */ -_CFFI_UNUSED_FN -static int cffi_start_python(void) -{ - if (_cffi_call_python == &_cffi_start_and_call_python) { - if (_cffi_start_python() == NULL) - return -1; - } - cffi_read_barrier(); - return 0; -} - -#undef cffi_compare_and_swap -#undef cffi_write_barrier -#undef cffi_read_barrier - -#ifdef __cplusplus -} -#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py deleted file mode 100644 index 10090fe8f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/api.py +++ /dev/null @@ -1,965 +0,0 @@ -import sys, types -from .lock import allocate_lock -from .error import CDefError -from . import model - -try: - callable -except NameError: - # Python 3.1 - from collections import Callable - callable = lambda x: isinstance(x, Callable) - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -_unspecified = object() - - - -class FFI(object): - r''' - The main top-level class that you instantiate once, or once per module. - - Example usage: - - ffi = FFI() - ffi.cdef(""" - int printf(const char *, ...); - """) - - C = ffi.dlopen(None) # standard library - -or- - C = ffi.verify() # use a C compiler: verify the decl above is right - - C.printf("hello, %s!\n", ffi.new("char[]", "world")) - ''' - - def __init__(self, backend=None): - """Create an FFI instance. The 'backend' argument is used to - select a non-default backend, mostly for tests. - """ - if backend is None: - # You need PyPy (>= 2.0 beta), or a CPython (>= 2.6) with - # _cffi_backend.so compiled. - import _cffi_backend as backend - from . import __version__ - if backend.__version__ != __version__: - # bad version! Try to be as explicit as possible. - if hasattr(backend, '__file__'): - # CPython - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. When we import the top-level '_cffi_backend' extension module, we get version %s, located in %r. The two versions should be equal; check your installation." % ( - __version__, __file__, - backend.__version__, backend.__file__)) - else: - # PyPy - raise Exception("Version mismatch: this is the 'cffi' package version %s, located in %r. This interpreter comes with a built-in '_cffi_backend' module, which is version %s. The two versions should be equal; check your installation." % ( - __version__, __file__, backend.__version__)) - # (If you insist you can also try to pass the option - # 'backend=backend_ctypes.CTypesBackend()', but don't - # rely on it! It's probably not going to work well.) - - from . import cparser - self._backend = backend - self._lock = allocate_lock() - self._parser = cparser.Parser() - self._cached_btypes = {} - self._parsed_types = types.ModuleType('parsed_types').__dict__ - self._new_types = types.ModuleType('new_types').__dict__ - self._function_caches = [] - self._libraries = [] - self._cdefsources = [] - self._included_ffis = [] - self._windows_unicode = None - self._init_once_cache = {} - self._cdef_version = None - self._embedding = None - self._typecache = model.get_typecache(backend) - if hasattr(backend, 'set_ffi'): - backend.set_ffi(self) - for name in list(backend.__dict__): - if name.startswith('RTLD_'): - setattr(self, name, getattr(backend, name)) - # - with self._lock: - self.BVoidP = self._get_cached_btype(model.voidp_type) - self.BCharA = self._get_cached_btype(model.char_array_type) - if isinstance(backend, types.ModuleType): - # _cffi_backend: attach these constants to the class - if not hasattr(FFI, 'NULL'): - FFI.NULL = self.cast(self.BVoidP, 0) - FFI.CData, FFI.CType = backend._get_types() - else: - # ctypes backend: attach these constants to the instance - self.NULL = self.cast(self.BVoidP, 0) - self.CData, self.CType = backend._get_types() - self.buffer = backend.buffer - - def cdef(self, csource, override=False, packed=False, pack=None): - """Parse the given C source. This registers all declared functions, - types, and global variables. The functions and global variables can - then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'. - The types can be used in 'ffi.new()' and other functions. - If 'packed' is specified as True, all structs declared inside this - cdef are packed, i.e. laid out without any field alignment at all. - Alternatively, 'pack' can be a small integer, and requests for - alignment greater than that are ignored (pack=1 is equivalent to - packed=True). - """ - self._cdef(csource, override=override, packed=packed, pack=pack) - - def embedding_api(self, csource, packed=False, pack=None): - self._cdef(csource, packed=packed, pack=pack, dllexport=True) - if self._embedding is None: - self._embedding = '' - - def _cdef(self, csource, override=False, **options): - if not isinstance(csource, str): # unicode, on Python 2 - if not isinstance(csource, basestring): - raise TypeError("cdef() argument must be a string") - csource = csource.encode('ascii') - with self._lock: - self._cdef_version = object() - self._parser.parse(csource, override=override, **options) - self._cdefsources.append(csource) - if override: - for cache in self._function_caches: - cache.clear() - finishlist = self._parser._recomplete - if finishlist: - self._parser._recomplete = [] - for tp in finishlist: - tp.finish_backend_type(self, finishlist) - - def dlopen(self, name, flags=0): - """Load and return a dynamic library identified by 'name'. - The standard C library can be loaded by passing None. - Note that functions and types declared by 'ffi.cdef()' are not - linked to a particular library, just like C headers; in the - library we only look for the actual (untyped) symbols. - """ - if not (isinstance(name, basestring) or - name is None or - isinstance(name, self.CData)): - raise TypeError("dlopen(name): name must be a file name, None, " - "or an already-opened 'void *' handle") - with self._lock: - lib, function_cache = _make_ffi_library(self, name, flags) - self._function_caches.append(function_cache) - self._libraries.append(lib) - return lib - - def dlclose(self, lib): - """Close a library obtained with ffi.dlopen(). After this call, - access to functions or variables from the library will fail - (possibly with a segmentation fault). - """ - type(lib).__cffi_close__(lib) - - def _typeof_locked(self, cdecl): - # call me with the lock! - key = cdecl - if key in self._parsed_types: - return self._parsed_types[key] - # - if not isinstance(cdecl, str): # unicode, on Python 2 - cdecl = cdecl.encode('ascii') - # - type = self._parser.parse_type(cdecl) - really_a_function_type = type.is_raw_function - if really_a_function_type: - type = type.as_function_pointer() - btype = self._get_cached_btype(type) - result = btype, really_a_function_type - self._parsed_types[key] = result - return result - - def _typeof(self, cdecl, consider_function_as_funcptr=False): - # string -> ctype object - try: - result = self._parsed_types[cdecl] - except KeyError: - with self._lock: - result = self._typeof_locked(cdecl) - # - btype, really_a_function_type = result - if really_a_function_type and not consider_function_as_funcptr: - raise CDefError("the type %r is a function type, not a " - "pointer-to-function type" % (cdecl,)) - return btype - - def typeof(self, cdecl): - """Parse the C type given as a string and return the - corresponding object. - It can also be used on 'cdata' instance to get its C type. - """ - if isinstance(cdecl, basestring): - return self._typeof(cdecl) - if isinstance(cdecl, self.CData): - return self._backend.typeof(cdecl) - if isinstance(cdecl, types.BuiltinFunctionType): - res = _builtin_function_type(cdecl) - if res is not None: - return res - if (isinstance(cdecl, types.FunctionType) - and hasattr(cdecl, '_cffi_base_type')): - with self._lock: - return self._get_cached_btype(cdecl._cffi_base_type) - raise TypeError(type(cdecl)) - - def sizeof(self, cdecl): - """Return the size in bytes of the argument. It can be a - string naming a C type, or a 'cdata' instance. - """ - if isinstance(cdecl, basestring): - BType = self._typeof(cdecl) - return self._backend.sizeof(BType) - else: - return self._backend.sizeof(cdecl) - - def alignof(self, cdecl): - """Return the natural alignment size in bytes of the C type - given as a string. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.alignof(cdecl) - - def offsetof(self, cdecl, *fields_or_indexes): - """Return the offset of the named field inside the given - structure or array, which must be given as a C type name. - You can give several field names in case of nested structures. - You can also give numeric values which correspond to array - items, in case of an array type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._typeoffsetof(cdecl, *fields_or_indexes)[1] - - def new(self, cdecl, init=None): - """Allocate an instance according to the specified C type and - return a pointer to it. The specified C type must be either a - pointer or an array: ``new('X *')`` allocates an X and returns - a pointer to it, whereas ``new('X[n]')`` allocates an array of - n X'es and returns an array referencing it (which works - mostly like a pointer, like in C). You can also use - ``new('X[]', n)`` to allocate an array of a non-constant - length n. - - The memory is initialized following the rules of declaring a - global variable in C: by default it is zero-initialized, but - an explicit initializer can be given which can be used to - fill all or part of the memory. - - When the returned object goes out of scope, the memory - is freed. In other words the returned object has - ownership of the value of type 'cdecl' that it points to. This - means that the raw data can be used as long as this object is - kept alive, but must not be used for a longer time. Be careful - about that when copying the pointer to the memory somewhere - else, e.g. into another structure. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.newp(cdecl, init) - - def new_allocator(self, alloc=None, free=None, - should_clear_after_alloc=True): - """Return a new allocator, i.e. a function that behaves like ffi.new() - but uses the provided low-level 'alloc' and 'free' functions. - - 'alloc' is called with the size as argument. If it returns NULL, a - MemoryError is raised. 'free' is called with the result of 'alloc' - as argument. Both can be either Python function or directly C - functions. If 'free' is None, then no free function is called. - If both 'alloc' and 'free' are None, the default is used. - - If 'should_clear_after_alloc' is set to False, then the memory - returned by 'alloc' is assumed to be already cleared (or you are - fine with garbage); otherwise CFFI will clear it. - """ - compiled_ffi = self._backend.FFI() - allocator = compiled_ffi.new_allocator(alloc, free, - should_clear_after_alloc) - def allocate(cdecl, init=None): - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return allocator(cdecl, init) - return allocate - - def cast(self, cdecl, source): - """Similar to a C cast: returns an instance of the named C - type initialized with the given 'source'. The source is - casted between integers or pointers of any type. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.cast(cdecl, source) - - def string(self, cdata, maxlen=-1): - """Return a Python string (or unicode string) from the 'cdata'. - If 'cdata' is a pointer or array of characters or bytes, returns - the null-terminated string. The returned string extends until - the first null character, or at most 'maxlen' characters. If - 'cdata' is an array then 'maxlen' defaults to its length. - - If 'cdata' is a pointer or array of wchar_t, returns a unicode - string following the same rules. - - If 'cdata' is a single character or byte or a wchar_t, returns - it as a string or unicode string. - - If 'cdata' is an enum, returns the value of the enumerator as a - string, or 'NUMBER' if the value is out of range. - """ - return self._backend.string(cdata, maxlen) - - def unpack(self, cdata, length): - """Unpack an array of C data of the given length, - returning a Python string/unicode/list. - - If 'cdata' is a pointer to 'char', returns a byte string. - It does not stop at the first null. This is equivalent to: - ffi.buffer(cdata, length)[:] - - If 'cdata' is a pointer to 'wchar_t', returns a unicode string. - 'length' is measured in wchar_t's; it is not the size in bytes. - - If 'cdata' is a pointer to anything else, returns a list of - 'length' items. This is a faster equivalent to: - [cdata[i] for i in range(length)] - """ - return self._backend.unpack(cdata, length) - - #def buffer(self, cdata, size=-1): - # """Return a read-write buffer object that references the raw C data - # pointed to by the given 'cdata'. The 'cdata' must be a pointer or - # an array. Can be passed to functions expecting a buffer, or directly - # manipulated with: - # - # buf[:] get a copy of it in a regular string, or - # buf[idx] as a single character - # buf[:] = ... - # buf[idx] = ... change the content - # """ - # note that 'buffer' is a type, set on this instance by __init__ - - def from_buffer(self, cdecl, python_buffer=_unspecified, - require_writable=False): - """Return a cdata of the given type pointing to the data of the - given Python object, which must support the buffer interface. - Note that this is not meant to be used on the built-in types - str or unicode (you can build 'char[]' arrays explicitly) - but only on objects containing large quantities of raw data - in some other format, like 'array.array' or numpy arrays. - - The first argument is optional and default to 'char[]'. - """ - if python_buffer is _unspecified: - cdecl, python_buffer = self.BCharA, cdecl - elif isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - return self._backend.from_buffer(cdecl, python_buffer, - require_writable) - - def memmove(self, dest, src, n): - """ffi.memmove(dest, src, n) copies n bytes of memory from src to dest. - - Like the C function memmove(), the memory areas may overlap; - apart from that it behaves like the C function memcpy(). - - 'src' can be any cdata ptr or array, or any Python buffer object. - 'dest' can be any cdata ptr or array, or a writable Python buffer - object. The size to copy, 'n', is always measured in bytes. - - Unlike other methods, this one supports all Python buffer including - byte strings and bytearrays---but it still does not support - non-contiguous buffers. - """ - return self._backend.memmove(dest, src, n) - - def callback(self, cdecl, python_callable=None, error=None, onerror=None): - """Return a callback object or a decorator making such a - callback object. 'cdecl' must name a C function pointer type. - The callback invokes the specified 'python_callable' (which may - be provided either directly or via a decorator). Important: the - callback object must be manually kept alive for as long as the - callback may be invoked from the C level. - """ - def callback_decorator_wrap(python_callable): - if not callable(python_callable): - raise TypeError("the 'python_callable' argument " - "is not callable") - return self._backend.callback(cdecl, python_callable, - error, onerror) - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl, consider_function_as_funcptr=True) - if python_callable is None: - return callback_decorator_wrap # decorator mode - else: - return callback_decorator_wrap(python_callable) # direct mode - - def getctype(self, cdecl, replace_with=''): - """Return a string giving the C type 'cdecl', which may be itself - a string or a object. If 'replace_with' is given, it gives - extra text to append (or insert for more complicated C types), like - a variable name, or '*' to get actually the C type 'pointer-to-cdecl'. - """ - if isinstance(cdecl, basestring): - cdecl = self._typeof(cdecl) - replace_with = replace_with.strip() - if (replace_with.startswith('*') - and '&[' in self._backend.getcname(cdecl, '&')): - replace_with = '(%s)' % replace_with - elif replace_with and not replace_with[0] in '[(': - replace_with = ' ' + replace_with - return self._backend.getcname(cdecl, replace_with) - - def gc(self, cdata, destructor, size=0): - """Return a new cdata object that points to the same - data. Later, when this new cdata object is garbage-collected, - 'destructor(old_cdata_object)' will be called. - - The optional 'size' gives an estimate of the size, used to - trigger the garbage collection more eagerly. So far only used - on PyPy. It tells the GC that the returned object keeps alive - roughly 'size' bytes of external memory. - """ - return self._backend.gcp(cdata, destructor, size) - - def _get_cached_btype(self, type): - assert self._lock.acquire(False) is False - # call me with the lock! - try: - BType = self._cached_btypes[type] - except KeyError: - finishlist = [] - BType = type.get_cached_btype(self, finishlist) - for type in finishlist: - type.finish_backend_type(self, finishlist) - return BType - - def verify(self, source='', tmpdir=None, **kwargs): - """Verify that the current ffi signatures compile on this - machine, and return a dynamic library object. The dynamic - library can be used to call functions and access global - variables declared in this 'ffi'. The library is compiled - by the C compiler: it gives you C-level API compatibility - (including calling macros). This is unlike 'ffi.dlopen()', - which requires binary compatibility in the signatures. - """ - from .verifier import Verifier, _caller_dir_pycache - # - # If set_unicode(True) was called, insert the UNICODE and - # _UNICODE macro declarations - if self._windows_unicode: - self._apply_windows_unicode(kwargs) - # - # Set the tmpdir here, and not in Verifier.__init__: it picks - # up the caller's directory, which we want to be the caller of - # ffi.verify(), as opposed to the caller of Veritier(). - tmpdir = tmpdir or _caller_dir_pycache() - # - # Make a Verifier() and use it to load the library. - self.verifier = Verifier(self, source, tmpdir, **kwargs) - lib = self.verifier.load_library() - # - # Save the loaded library for keep-alive purposes, even - # if the caller doesn't keep it alive itself (it should). - self._libraries.append(lib) - return lib - - def _get_errno(self): - return self._backend.get_errno() - def _set_errno(self, errno): - self._backend.set_errno(errno) - errno = property(_get_errno, _set_errno, None, - "the value of 'errno' from/to the C calls") - - def getwinerror(self, code=-1): - return self._backend.getwinerror(code) - - def _pointer_to(self, ctype): - with self._lock: - return model.pointer_cache(self, ctype) - - def addressof(self, cdata, *fields_or_indexes): - """Return the address of a . - If 'fields_or_indexes' are given, returns the address of that - field or array item in the structure or array, recursively in - case of nested structures. - """ - try: - ctype = self._backend.typeof(cdata) - except TypeError: - if '__addressof__' in type(cdata).__dict__: - return type(cdata).__addressof__(cdata, *fields_or_indexes) - raise - if fields_or_indexes: - ctype, offset = self._typeoffsetof(ctype, *fields_or_indexes) - else: - if ctype.kind == "pointer": - raise TypeError("addressof(pointer)") - offset = 0 - ctypeptr = self._pointer_to(ctype) - return self._backend.rawaddressof(ctypeptr, cdata, offset) - - def _typeoffsetof(self, ctype, field_or_index, *fields_or_indexes): - ctype, offset = self._backend.typeoffsetof(ctype, field_or_index) - for field1 in fields_or_indexes: - ctype, offset1 = self._backend.typeoffsetof(ctype, field1, 1) - offset += offset1 - return ctype, offset - - def include(self, ffi_to_include): - """Includes the typedefs, structs, unions and enums defined - in another FFI instance. Usage is similar to a #include in C, - where a part of the program might include types defined in - another part for its own usage. Note that the include() - method has no effect on functions, constants and global - variables, which must anyway be accessed directly from the - lib object returned by the original FFI instance. - """ - if not isinstance(ffi_to_include, FFI): - raise TypeError("ffi.include() expects an argument that is also of" - " type cffi.FFI, not %r" % ( - type(ffi_to_include).__name__,)) - if ffi_to_include is self: - raise ValueError("self.include(self)") - with ffi_to_include._lock: - with self._lock: - self._parser.include(ffi_to_include._parser) - self._cdefsources.append('[') - self._cdefsources.extend(ffi_to_include._cdefsources) - self._cdefsources.append(']') - self._included_ffis.append(ffi_to_include) - - def new_handle(self, x): - return self._backend.newp_handle(self.BVoidP, x) - - def from_handle(self, x): - return self._backend.from_handle(x) - - def release(self, x): - self._backend.release(x) - - def set_unicode(self, enabled_flag): - """Windows: if 'enabled_flag' is True, enable the UNICODE and - _UNICODE defines in C, and declare the types like TCHAR and LPTCSTR - to be (pointers to) wchar_t. If 'enabled_flag' is False, - declare these types to be (pointers to) plain 8-bit characters. - This is mostly for backward compatibility; you usually want True. - """ - if self._windows_unicode is not None: - raise ValueError("set_unicode() can only be called once") - enabled_flag = bool(enabled_flag) - if enabled_flag: - self.cdef("typedef wchar_t TBYTE;" - "typedef wchar_t TCHAR;" - "typedef const wchar_t *LPCTSTR;" - "typedef const wchar_t *PCTSTR;" - "typedef wchar_t *LPTSTR;" - "typedef wchar_t *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - else: - self.cdef("typedef char TBYTE;" - "typedef char TCHAR;" - "typedef const char *LPCTSTR;" - "typedef const char *PCTSTR;" - "typedef char *LPTSTR;" - "typedef char *PTSTR;" - "typedef TBYTE *PTBYTE;" - "typedef TCHAR *PTCHAR;") - self._windows_unicode = enabled_flag - - def _apply_windows_unicode(self, kwds): - defmacros = kwds.get('define_macros', ()) - if not isinstance(defmacros, (list, tuple)): - raise TypeError("'define_macros' must be a list or tuple") - defmacros = list(defmacros) + [('UNICODE', '1'), - ('_UNICODE', '1')] - kwds['define_macros'] = defmacros - - def _apply_embedding_fix(self, kwds): - # must include an argument like "-lpython2.7" for the compiler - def ensure(key, value): - lst = kwds.setdefault(key, []) - if value not in lst: - lst.append(value) - # - if '__pypy__' in sys.builtin_module_names: - import os - if sys.platform == "win32": - # we need 'libpypy-c.lib'. Current distributions of - # pypy (>= 4.1) contain it as 'libs/python27.lib'. - pythonlib = "python{0[0]}{0[1]}".format(sys.version_info) - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'libs')) - else: - # we need 'libpypy-c.{so,dylib}', which should be by - # default located in 'sys.prefix/bin' for installed - # systems. - if sys.version_info < (3,): - pythonlib = "pypy-c" - else: - pythonlib = "pypy3-c" - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'bin')) - # On uninstalled pypy's, the libpypy-c is typically found in - # .../pypy/goal/. - if hasattr(sys, 'prefix'): - ensure('library_dirs', os.path.join(sys.prefix, 'pypy', 'goal')) - else: - if sys.platform == "win32": - template = "python%d%d" - if hasattr(sys, 'gettotalrefcount'): - template += '_d' - else: - try: - import sysconfig - except ImportError: # 2.6 - from distutils import sysconfig - template = "python%d.%d" - if sysconfig.get_config_var('DEBUG_EXT'): - template += sysconfig.get_config_var('DEBUG_EXT') - pythonlib = (template % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - if hasattr(sys, 'abiflags'): - pythonlib += sys.abiflags - ensure('libraries', pythonlib) - if sys.platform == "win32": - ensure('extra_link_args', '/MANIFEST') - - def set_source(self, module_name, source, source_extension='.c', **kwds): - import os - if hasattr(self, '_assigned_source'): - raise ValueError("set_source() cannot be called several times " - "per ffi object") - if not isinstance(module_name, basestring): - raise TypeError("'module_name' must be a string") - if os.sep in module_name or (os.altsep and os.altsep in module_name): - raise ValueError("'module_name' must not contain '/': use a dotted " - "name to make a 'package.module' location") - self._assigned_source = (str(module_name), source, - source_extension, kwds) - - def set_source_pkgconfig(self, module_name, pkgconfig_libs, source, - source_extension='.c', **kwds): - from . import pkgconfig - if not isinstance(pkgconfig_libs, list): - raise TypeError("the pkgconfig_libs argument must be a list " - "of package names") - kwds2 = pkgconfig.flags_from_pkgconfig(pkgconfig_libs) - pkgconfig.merge_flags(kwds, kwds2) - self.set_source(module_name, source, source_extension, **kwds) - - def distutils_extension(self, tmpdir='build', verbose=True): - from distutils.dir_util import mkpath - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - if hasattr(self, 'verifier'): # fallback, 'tmpdir' ignored - return self.verifier.get_extension() - raise ValueError("set_source() must be called before" - " distutils_extension()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("distutils_extension() is only for C extension " - "modules, not for dlopen()-style pure Python " - "modules") - mkpath(tmpdir) - ext, updated = recompile(self, module_name, - source, tmpdir=tmpdir, extradir=tmpdir, - source_extension=source_extension, - call_c_compiler=False, **kwds) - if verbose: - if updated: - sys.stderr.write("regenerated: %r\n" % (ext.sources[0],)) - else: - sys.stderr.write("not modified: %r\n" % (ext.sources[0],)) - return ext - - def emit_c_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is None: - raise TypeError("emit_c_code() is only for C extension modules, " - "not for dlopen()-style pure Python modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, **kwds) - - def emit_python_code(self, filename): - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before emit_c_code()") - module_name, source, source_extension, kwds = self._assigned_source - if source is not None: - raise TypeError("emit_python_code() is only for dlopen()-style " - "pure Python modules, not for C extension modules") - recompile(self, module_name, source, - c_file=filename, call_c_compiler=False, **kwds) - - def compile(self, tmpdir='.', verbose=0, target=None, debug=None): - """The 'target' argument gives the final file name of the - compiled DLL. Use '*' to force distutils' choice, suitable for - regular CPython C API modules. Use a file name ending in '.*' - to ask for the system's default extension for dynamic libraries - (.so/.dll/.dylib). - - The default is '*' when building a non-embedded C API extension, - and (module_name + '.*') when building an embedded library. - """ - from .recompiler import recompile - # - if not hasattr(self, '_assigned_source'): - raise ValueError("set_source() must be called before compile()") - module_name, source, source_extension, kwds = self._assigned_source - return recompile(self, module_name, source, tmpdir=tmpdir, - target=target, source_extension=source_extension, - compiler_verbose=verbose, debug=debug, **kwds) - - def init_once(self, func, tag): - # Read _init_once_cache[tag], which is either (False, lock) if - # we're calling the function now in some thread, or (True, result). - # Don't call setdefault() in most cases, to avoid allocating and - # immediately freeing a lock; but still use setdefaut() to avoid - # races. - try: - x = self._init_once_cache[tag] - except KeyError: - x = self._init_once_cache.setdefault(tag, (False, allocate_lock())) - # Common case: we got (True, result), so we return the result. - if x[0]: - return x[1] - # Else, it's a lock. Acquire it to serialize the following tests. - with x[1]: - # Read again from _init_once_cache the current status. - x = self._init_once_cache[tag] - if x[0]: - return x[1] - # Call the function and store the result back. - result = func() - self._init_once_cache[tag] = (True, result) - return result - - def embedding_init_code(self, pysource): - if self._embedding: - raise ValueError("embedding_init_code() can only be called once") - # fix 'pysource' before it gets dumped into the C file: - # - remove empty lines at the beginning, so it starts at "line 1" - # - dedent, if all non-empty lines are indented - # - check for SyntaxErrors - import re - match = re.match(r'\s*\n', pysource) - if match: - pysource = pysource[match.end():] - lines = pysource.splitlines() or [''] - prefix = re.match(r'\s*', lines[0]).group() - for i in range(1, len(lines)): - line = lines[i] - if line.rstrip(): - while not line.startswith(prefix): - prefix = prefix[:-1] - i = len(prefix) - lines = [line[i:]+'\n' for line in lines] - pysource = ''.join(lines) - # - compile(pysource, "cffi_init", "exec") - # - self._embedding = pysource - - def def_extern(self, *args, **kwds): - raise ValueError("ffi.def_extern() is only available on API-mode FFI " - "objects") - - def list_types(self): - """Returns the user type names known to this FFI instance. - This returns a tuple containing three lists of names: - (typedef_names, names_of_structs, names_of_unions) - """ - typedefs = [] - structs = [] - unions = [] - for key in self._parser._declarations: - if key.startswith('typedef '): - typedefs.append(key[8:]) - elif key.startswith('struct '): - structs.append(key[7:]) - elif key.startswith('union '): - unions.append(key[6:]) - typedefs.sort() - structs.sort() - unions.sort() - return (typedefs, structs, unions) - - -def _load_backend_lib(backend, name, flags): - import os - if not isinstance(name, basestring): - if sys.platform != "win32" or name is not None: - return backend.load_library(name, flags) - name = "c" # Windows: load_library(None) fails, but this works - # on Python 2 (backward compatibility hack only) - first_error = None - if '.' in name or '/' in name or os.sep in name: - try: - return backend.load_library(name, flags) - except OSError as e: - first_error = e - import ctypes.util - path = ctypes.util.find_library(name) - if path is None: - if name == "c" and sys.platform == "win32" and sys.version_info >= (3,): - raise OSError("dlopen(None) cannot work on Windows for Python 3 " - "(see http://bugs.python.org/issue23606)") - msg = ("ctypes.util.find_library() did not manage " - "to locate a library called %r" % (name,)) - if first_error is not None: - msg = "%s. Additionally, %s" % (first_error, msg) - raise OSError(msg) - return backend.load_library(path, flags) - -def _make_ffi_library(ffi, libname, flags): - backend = ffi._backend - backendlib = _load_backend_lib(backend, libname, flags) - # - def accessor_function(name): - key = 'function ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - value = backendlib.load_function(BType, name) - library.__dict__[name] = value - # - def accessor_variable(name): - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - read_variable = backendlib.read_variable - write_variable = backendlib.write_variable - setattr(FFILibrary, name, property( - lambda self: read_variable(BType, name), - lambda self, value: write_variable(BType, name, value))) - # - def addressof_var(name): - try: - return addr_variables[name] - except KeyError: - with ffi._lock: - if name not in addr_variables: - key = 'variable ' + name - tp, _ = ffi._parser._declarations[key] - BType = ffi._get_cached_btype(tp) - if BType.kind != 'array': - BType = model.pointer_cache(ffi, BType) - p = backendlib.load_function(BType, name) - addr_variables[name] = p - return addr_variables[name] - # - def accessor_constant(name): - raise NotImplementedError("non-integer constant '%s' cannot be " - "accessed from a dlopen() library" % (name,)) - # - def accessor_int_constant(name): - library.__dict__[name] = ffi._parser._int_constants[name] - # - accessors = {} - accessors_version = [False] - addr_variables = {} - # - def update_accessors(): - if accessors_version[0] is ffi._cdef_version: - return - # - for key, (tp, _) in ffi._parser._declarations.items(): - if not isinstance(tp, model.EnumType): - tag, name = key.split(' ', 1) - if tag == 'function': - accessors[name] = accessor_function - elif tag == 'variable': - accessors[name] = accessor_variable - elif tag == 'constant': - accessors[name] = accessor_constant - else: - for i, enumname in enumerate(tp.enumerators): - def accessor_enum(name, tp=tp, i=i): - tp.check_not_partial() - library.__dict__[name] = tp.enumvalues[i] - accessors[enumname] = accessor_enum - for name in ffi._parser._int_constants: - accessors.setdefault(name, accessor_int_constant) - accessors_version[0] = ffi._cdef_version - # - def make_accessor(name): - with ffi._lock: - if name in library.__dict__ or name in FFILibrary.__dict__: - return # added by another thread while waiting for the lock - if name not in accessors: - update_accessors() - if name not in accessors: - raise AttributeError(name) - accessors[name](name) - # - class FFILibrary(object): - def __getattr__(self, name): - make_accessor(name) - return getattr(self, name) - def __setattr__(self, name, value): - try: - property = getattr(self.__class__, name) - except AttributeError: - make_accessor(name) - setattr(self, name, value) - else: - property.__set__(self, value) - def __dir__(self): - with ffi._lock: - update_accessors() - return accessors.keys() - def __addressof__(self, name): - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - make_accessor(name) - if name in library.__dict__: - return library.__dict__[name] - if name in FFILibrary.__dict__: - return addressof_var(name) - raise AttributeError("cffi library has no function or " - "global variable named '%s'" % (name,)) - def __cffi_close__(self): - backendlib.close_lib() - self.__dict__.clear() - # - if isinstance(libname, basestring): - try: - if not isinstance(libname, str): # unicode, on Python 2 - libname = libname.encode('utf-8') - FFILibrary.__name__ = 'FFILibrary_%s' % libname - except UnicodeError: - pass - library = FFILibrary() - return library, library.__dict__ - -def _builtin_function_type(func): - # a hack to make at least ffi.typeof(builtin_function) work, - # if the builtin function was obtained by 'vengine_cpy'. - import sys - try: - module = sys.modules[func.__module__] - ffi = module._cffi_original_ffi - types_of_builtin_funcs = module._cffi_types_of_builtin_funcs - tp = types_of_builtin_funcs[func] - except (KeyError, AttributeError, TypeError): - return None - else: - with ffi._lock: - return ffi._get_cached_btype(tp) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py deleted file mode 100644 index 3368a2ac6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/backend_ctypes.py +++ /dev/null @@ -1,1121 +0,0 @@ -import ctypes, ctypes.util, operator, sys -from . import model - -if sys.version_info < (3,): - bytechr = chr -else: - unicode = str - long = int - xrange = range - bytechr = lambda num: bytes([num]) - -class CTypesType(type): - pass - -class CTypesData(object): - __metaclass__ = CTypesType - __slots__ = ['__weakref__'] - __name__ = '' - - def __init__(self, *args): - raise TypeError("cannot instantiate %r" % (self.__class__,)) - - @classmethod - def _newp(cls, init): - raise TypeError("expected a pointer or array ctype, got '%s'" - % (cls._get_c_name(),)) - - @staticmethod - def _to_ctypes(value): - raise TypeError - - @classmethod - def _arg_to_ctypes(cls, *value): - try: - ctype = cls._ctype - except AttributeError: - raise TypeError("cannot create an instance of %r" % (cls,)) - if value: - res = cls._to_ctypes(*value) - if not isinstance(res, ctype): - res = cls._ctype(res) - else: - res = cls._ctype() - return res - - @classmethod - def _create_ctype_obj(cls, init): - if init is None: - return cls._arg_to_ctypes() - else: - return cls._arg_to_ctypes(init) - - @staticmethod - def _from_ctypes(ctypes_value): - raise TypeError - - @classmethod - def _get_c_name(cls, replace_with=''): - return cls._reftypename.replace(' &', replace_with) - - @classmethod - def _fix_class(cls): - cls.__name__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__qualname__ = 'CData<%s>' % (cls._get_c_name(),) - cls.__module__ = 'ffi' - - def _get_own_repr(self): - raise NotImplementedError - - def _addr_repr(self, address): - if address == 0: - return 'NULL' - else: - if address < 0: - address += 1 << (8*ctypes.sizeof(ctypes.c_void_p)) - return '0x%x' % address - - def __repr__(self, c_name=None): - own = self._get_own_repr() - return '' % (c_name or self._get_c_name(), own) - - def _convert_to_address(self, BClass): - if BClass is None: - raise TypeError("cannot convert %r to an address" % ( - self._get_c_name(),)) - else: - raise TypeError("cannot convert %r to %r" % ( - self._get_c_name(), BClass._get_c_name())) - - @classmethod - def _get_size(cls): - return ctypes.sizeof(cls._ctype) - - def _get_size_of_instance(self): - return ctypes.sizeof(self._ctype) - - @classmethod - def _cast_from(cls, source): - raise TypeError("cannot cast to %r" % (cls._get_c_name(),)) - - def _cast_to_integer(self): - return self._convert_to_address(None) - - @classmethod - def _alignment(cls): - return ctypes.alignment(cls._ctype) - - def __iter__(self): - raise TypeError("cdata %r does not support iteration" % ( - self._get_c_name()),) - - def _make_cmp(name): - cmpfunc = getattr(operator, name) - def cmp(self, other): - v_is_ptr = not isinstance(self, CTypesGenericPrimitive) - w_is_ptr = (isinstance(other, CTypesData) and - not isinstance(other, CTypesGenericPrimitive)) - if v_is_ptr and w_is_ptr: - return cmpfunc(self._convert_to_address(None), - other._convert_to_address(None)) - elif v_is_ptr or w_is_ptr: - return NotImplemented - else: - if isinstance(self, CTypesGenericPrimitive): - self = self._value - if isinstance(other, CTypesGenericPrimitive): - other = other._value - return cmpfunc(self, other) - cmp.func_name = name - return cmp - - __eq__ = _make_cmp('__eq__') - __ne__ = _make_cmp('__ne__') - __lt__ = _make_cmp('__lt__') - __le__ = _make_cmp('__le__') - __gt__ = _make_cmp('__gt__') - __ge__ = _make_cmp('__ge__') - - def __hash__(self): - return hash(self._convert_to_address(None)) - - def _to_string(self, maxlen): - raise TypeError("string(): %r" % (self,)) - - -class CTypesGenericPrimitive(CTypesData): - __slots__ = [] - - def __hash__(self): - return hash(self._value) - - def _get_own_repr(self): - return repr(self._from_ctypes(self._value)) - - -class CTypesGenericArray(CTypesData): - __slots__ = [] - - @classmethod - def _newp(cls, init): - return cls(init) - - def __iter__(self): - for i in xrange(len(self)): - yield self[i] - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - -class CTypesGenericPtr(CTypesData): - __slots__ = ['_address', '_as_ctype_ptr'] - _automatic_casts = False - kind = "pointer" - - @classmethod - def _newp(cls, init): - return cls(init) - - @classmethod - def _cast_from(cls, source): - if source is None: - address = 0 - elif isinstance(source, CTypesData): - address = source._cast_to_integer() - elif isinstance(source, (int, long)): - address = source - else: - raise TypeError("bad type for cast to %r: %r" % - (cls, type(source).__name__)) - return cls._new_pointer_at(address) - - @classmethod - def _new_pointer_at(cls, address): - self = cls.__new__(cls) - self._address = address - self._as_ctype_ptr = ctypes.cast(address, cls._ctype) - return self - - def _get_own_repr(self): - try: - return self._addr_repr(self._address) - except AttributeError: - return '???' - - def _cast_to_integer(self): - return self._address - - def __nonzero__(self): - return bool(self._address) - __bool__ = __nonzero__ - - @classmethod - def _to_ctypes(cls, value): - if not isinstance(value, CTypesData): - raise TypeError("unexpected %s object" % type(value).__name__) - address = value._convert_to_address(cls) - return ctypes.cast(address, cls._ctype) - - @classmethod - def _from_ctypes(cls, ctypes_ptr): - address = ctypes.cast(ctypes_ptr, ctypes.c_void_p).value or 0 - return cls._new_pointer_at(address) - - @classmethod - def _initialize(cls, ctypes_ptr, value): - if value: - ctypes_ptr.contents = cls._to_ctypes(value).contents - - def _convert_to_address(self, BClass): - if (BClass in (self.__class__, None) or BClass._automatic_casts - or self._automatic_casts): - return self._address - else: - return CTypesData._convert_to_address(self, BClass) - - -class CTypesBaseStructOrUnion(CTypesData): - __slots__ = ['_blob'] - - @classmethod - def _create_ctype_obj(cls, init): - # may be overridden - raise TypeError("cannot instantiate opaque type %s" % (cls,)) - - def _get_own_repr(self): - return self._addr_repr(ctypes.addressof(self._blob)) - - @classmethod - def _offsetof(cls, fieldname): - return getattr(cls._ctype, fieldname).offset - - def _convert_to_address(self, BClass): - if getattr(BClass, '_BItem', None) is self.__class__: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @classmethod - def _from_ctypes(cls, ctypes_struct_or_union): - self = cls.__new__(cls) - self._blob = ctypes_struct_or_union - return self - - @classmethod - def _to_ctypes(cls, value): - return value._blob - - def __repr__(self, c_name=None): - return CTypesData.__repr__(self, c_name or self._get_c_name(' &')) - - -class CTypesBackend(object): - - PRIMITIVE_TYPES = { - 'char': ctypes.c_char, - 'short': ctypes.c_short, - 'int': ctypes.c_int, - 'long': ctypes.c_long, - 'long long': ctypes.c_longlong, - 'signed char': ctypes.c_byte, - 'unsigned char': ctypes.c_ubyte, - 'unsigned short': ctypes.c_ushort, - 'unsigned int': ctypes.c_uint, - 'unsigned long': ctypes.c_ulong, - 'unsigned long long': ctypes.c_ulonglong, - 'float': ctypes.c_float, - 'double': ctypes.c_double, - '_Bool': ctypes.c_bool, - } - - for _name in ['unsigned long long', 'unsigned long', - 'unsigned int', 'unsigned short', 'unsigned char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['uint%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['uintptr_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['size_t'] = PRIMITIVE_TYPES[_name] - - for _name in ['long long', 'long', 'int', 'short', 'signed char']: - _size = ctypes.sizeof(PRIMITIVE_TYPES[_name]) - PRIMITIVE_TYPES['int%d_t' % (8*_size)] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_void_p): - PRIMITIVE_TYPES['intptr_t'] = PRIMITIVE_TYPES[_name] - PRIMITIVE_TYPES['ptrdiff_t'] = PRIMITIVE_TYPES[_name] - if _size == ctypes.sizeof(ctypes.c_size_t): - PRIMITIVE_TYPES['ssize_t'] = PRIMITIVE_TYPES[_name] - - - def __init__(self): - self.RTLD_LAZY = 0 # not supported anyway by ctypes - self.RTLD_NOW = 0 - self.RTLD_GLOBAL = ctypes.RTLD_GLOBAL - self.RTLD_LOCAL = ctypes.RTLD_LOCAL - - def set_ffi(self, ffi): - self.ffi = ffi - - def _get_types(self): - return CTypesData, CTypesType - - def load_library(self, path, flags=0): - cdll = ctypes.CDLL(path, flags) - return CTypesLibrary(self, cdll) - - def new_void_type(self): - class CTypesVoid(CTypesData): - __slots__ = [] - _reftypename = 'void &' - @staticmethod - def _from_ctypes(novalue): - return None - @staticmethod - def _to_ctypes(novalue): - if novalue is not None: - raise TypeError("None expected, got %s object" % - (type(novalue).__name__,)) - return None - CTypesVoid._fix_class() - return CTypesVoid - - def new_primitive_type(self, name): - if name == 'wchar_t': - raise NotImplementedError(name) - ctype = self.PRIMITIVE_TYPES[name] - if name == 'char': - kind = 'char' - elif name in ('float', 'double'): - kind = 'float' - else: - if name in ('signed char', 'unsigned char'): - kind = 'byte' - elif name == '_Bool': - kind = 'bool' - else: - kind = 'int' - is_signed = (ctype(-1).value == -1) - # - def _cast_source_to_int(source): - if isinstance(source, (int, long, float)): - source = int(source) - elif isinstance(source, CTypesData): - source = source._cast_to_integer() - elif isinstance(source, bytes): - source = ord(source) - elif source is None: - source = 0 - else: - raise TypeError("bad type for cast to %r: %r" % - (CTypesPrimitive, type(source).__name__)) - return source - # - kind1 = kind - class CTypesPrimitive(CTypesGenericPrimitive): - __slots__ = ['_value'] - _ctype = ctype - _reftypename = '%s &' % name - kind = kind1 - - def __init__(self, value): - self._value = value - - @staticmethod - def _create_ctype_obj(init): - if init is None: - return ctype() - return ctype(CTypesPrimitive._to_ctypes(init)) - - if kind == 'int' or kind == 'byte': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = ctype(source).value # cast within range - return cls(source) - def __int__(self): - return self._value - - if kind == 'bool': - @classmethod - def _cast_from(cls, source): - if not isinstance(source, (int, long, float)): - source = _cast_source_to_int(source) - return cls(bool(source)) - def __int__(self): - return int(self._value) - - if kind == 'char': - @classmethod - def _cast_from(cls, source): - source = _cast_source_to_int(source) - source = bytechr(source & 0xFF) - return cls(source) - def __int__(self): - return ord(self._value) - - if kind == 'float': - @classmethod - def _cast_from(cls, source): - if isinstance(source, float): - pass - elif isinstance(source, CTypesGenericPrimitive): - if hasattr(source, '__float__'): - source = float(source) - else: - source = int(source) - else: - source = _cast_source_to_int(source) - source = ctype(source).value # fix precision - return cls(source) - def __int__(self): - return int(self._value) - def __float__(self): - return self._value - - _cast_to_integer = __int__ - - if kind == 'int' or kind == 'byte' or kind == 'bool': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long)): - if isinstance(x, CTypesData): - x = int(x) - else: - raise TypeError("integer expected, got %s" % - type(x).__name__) - if ctype(x).value != x: - if not is_signed and x < 0: - raise OverflowError("%s: negative integer" % name) - else: - raise OverflowError("%s: integer out of bounds" - % name) - return x - - if kind == 'char': - @staticmethod - def _to_ctypes(x): - if isinstance(x, bytes) and len(x) == 1: - return x - if isinstance(x, CTypesPrimitive): # > - return x._value - raise TypeError("character expected, got %s" % - type(x).__name__) - def __nonzero__(self): - return ord(self._value) != 0 - else: - def __nonzero__(self): - return self._value != 0 - __bool__ = __nonzero__ - - if kind == 'float': - @staticmethod - def _to_ctypes(x): - if not isinstance(x, (int, long, float, CTypesData)): - raise TypeError("float expected, got %s" % - type(x).__name__) - return ctype(x).value - - @staticmethod - def _from_ctypes(value): - return getattr(value, 'value', value) - - @staticmethod - def _initialize(blob, init): - blob.value = CTypesPrimitive._to_ctypes(init) - - if kind == 'char': - def _to_string(self, maxlen): - return self._value - if kind == 'byte': - def _to_string(self, maxlen): - return chr(self._value & 0xff) - # - CTypesPrimitive._fix_class() - return CTypesPrimitive - - def new_pointer_type(self, BItem): - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'charp' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'bytep' - elif BItem is getbtype(model.void_type): - kind = 'voidp' - else: - kind = 'generic' - # - class CTypesPtr(CTypesGenericPtr): - __slots__ = ['_own'] - if kind == 'charp': - __slots__ += ['__as_strbuf'] - _BItem = BItem - if hasattr(BItem, '_ctype'): - _ctype = ctypes.POINTER(BItem._ctype) - _bitem_size = ctypes.sizeof(BItem._ctype) - else: - _ctype = ctypes.c_void_p - if issubclass(BItem, CTypesGenericArray): - _reftypename = BItem._get_c_name('(* &)') - else: - _reftypename = BItem._get_c_name(' * &') - - def __init__(self, init): - ctypeobj = BItem._create_ctype_obj(init) - if kind == 'charp': - self.__as_strbuf = ctypes.create_string_buffer( - ctypeobj.value + b'\x00') - self._as_ctype_ptr = ctypes.cast( - self.__as_strbuf, self._ctype) - else: - self._as_ctype_ptr = ctypes.pointer(ctypeobj) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own = True - - def __add__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address + - other * self._bitem_size) - else: - return NotImplemented - - def __sub__(self, other): - if isinstance(other, (int, long)): - return self._new_pointer_at(self._address - - other * self._bitem_size) - elif type(self) is type(other): - return (self._address - other._address) // self._bitem_size - else: - return NotImplemented - - def __getitem__(self, index): - if getattr(self, '_own', False) and index != 0: - raise IndexError - return BItem._from_ctypes(self._as_ctype_ptr[index]) - - def __setitem__(self, index, value): - self._as_ctype_ptr[index] = BItem._to_ctypes(value) - - if kind == 'charp' or kind == 'voidp': - @classmethod - def _arg_to_ctypes(cls, *value): - if value and isinstance(value[0], bytes): - return ctypes.c_char_p(value[0]) - else: - return super(CTypesPtr, cls)._arg_to_ctypes(*value) - - if kind == 'charp' or kind == 'bytep': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = sys.maxsize - p = ctypes.cast(self._as_ctype_ptr, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % ( - ctypes.sizeof(self._as_ctype_ptr.contents),) - return super(CTypesPtr, self)._get_own_repr() - # - if (BItem is self.ffi._get_cached_btype(model.void_type) or - BItem is self.ffi._get_cached_btype(model.PrimitiveType('char'))): - CTypesPtr._automatic_casts = True - # - CTypesPtr._fix_class() - return CTypesPtr - - def new_array_type(self, CTypesPtr, length): - if length is None: - brackets = ' &[]' - else: - brackets = ' &[%d]' % length - BItem = CTypesPtr._BItem - getbtype = self.ffi._get_cached_btype - if BItem is getbtype(model.PrimitiveType('char')): - kind = 'char' - elif BItem in (getbtype(model.PrimitiveType('signed char')), - getbtype(model.PrimitiveType('unsigned char'))): - kind = 'byte' - else: - kind = 'generic' - # - class CTypesArray(CTypesGenericArray): - __slots__ = ['_blob', '_own'] - if length is not None: - _ctype = BItem._ctype * length - else: - __slots__.append('_ctype') - _reftypename = BItem._get_c_name(brackets) - _declared_length = length - _CTPtr = CTypesPtr - - def __init__(self, init): - if length is None: - if isinstance(init, (int, long)): - len1 = init - init = None - elif kind == 'char' and isinstance(init, bytes): - len1 = len(init) + 1 # extra null - else: - init = tuple(init) - len1 = len(init) - self._ctype = BItem._ctype * len1 - self._blob = self._ctype() - self._own = True - if init is not None: - self._initialize(self._blob, init) - - @staticmethod - def _initialize(blob, init): - if isinstance(init, bytes): - init = [init[i:i+1] for i in range(len(init))] - else: - if isinstance(init, CTypesGenericArray): - if (len(init) != len(blob) or - not isinstance(init, CTypesArray)): - raise TypeError("length/type mismatch: %s" % (init,)) - init = tuple(init) - if len(init) > len(blob): - raise IndexError("too many initializers") - addr = ctypes.cast(blob, ctypes.c_void_p).value - PTR = ctypes.POINTER(BItem._ctype) - itemsize = ctypes.sizeof(BItem._ctype) - for i, value in enumerate(init): - p = ctypes.cast(addr + i * itemsize, PTR) - BItem._initialize(p.contents, value) - - def __len__(self): - return len(self._blob) - - def __getitem__(self, index): - if not (0 <= index < len(self._blob)): - raise IndexError - return BItem._from_ctypes(self._blob[index]) - - def __setitem__(self, index, value): - if not (0 <= index < len(self._blob)): - raise IndexError - self._blob[index] = BItem._to_ctypes(value) - - if kind == 'char' or kind == 'byte': - def _to_string(self, maxlen): - if maxlen < 0: - maxlen = len(self._blob) - p = ctypes.cast(self._blob, - ctypes.POINTER(ctypes.c_char)) - n = 0 - while n < maxlen and p[n] != b'\x00': - n += 1 - return b''.join([p[i] for i in range(n)]) - - def _get_own_repr(self): - if getattr(self, '_own', False): - return 'owning %d bytes' % (ctypes.sizeof(self._blob),) - return super(CTypesArray, self)._get_own_repr() - - def _convert_to_address(self, BClass): - if BClass in (CTypesPtr, None) or BClass._automatic_casts: - return ctypes.addressof(self._blob) - else: - return CTypesData._convert_to_address(self, BClass) - - @staticmethod - def _from_ctypes(ctypes_array): - self = CTypesArray.__new__(CTypesArray) - self._blob = ctypes_array - return self - - @staticmethod - def _arg_to_ctypes(value): - return CTypesPtr._arg_to_ctypes(value) - - def __add__(self, other): - if isinstance(other, (int, long)): - return CTypesPtr._new_pointer_at( - ctypes.addressof(self._blob) + - other * ctypes.sizeof(BItem._ctype)) - else: - return NotImplemented - - @classmethod - def _cast_from(cls, source): - raise NotImplementedError("casting to %r" % ( - cls._get_c_name(),)) - # - CTypesArray._fix_class() - return CTypesArray - - def _new_struct_or_union(self, kind, name, base_ctypes_class): - # - class struct_or_union(base_ctypes_class): - pass - struct_or_union.__name__ = '%s_%s' % (kind, name) - kind1 = kind - # - class CTypesStructOrUnion(CTypesBaseStructOrUnion): - __slots__ = ['_blob'] - _ctype = struct_or_union - _reftypename = '%s &' % (name,) - _kind = kind = kind1 - # - CTypesStructOrUnion._fix_class() - return CTypesStructOrUnion - - def new_struct_type(self, name): - return self._new_struct_or_union('struct', name, ctypes.Structure) - - def new_union_type(self, name): - return self._new_struct_or_union('union', name, ctypes.Union) - - def complete_struct_or_union(self, CTypesStructOrUnion, fields, tp, - totalsize=-1, totalalignment=-1, sflags=0, - pack=0): - if totalsize >= 0 or totalalignment >= 0: - raise NotImplementedError("the ctypes backend of CFFI does not support " - "structures completed by verify(); please " - "compile and install the _cffi_backend module.") - struct_or_union = CTypesStructOrUnion._ctype - fnames = [fname for (fname, BField, bitsize) in fields] - btypes = [BField for (fname, BField, bitsize) in fields] - bitfields = [bitsize for (fname, BField, bitsize) in fields] - # - bfield_types = {} - cfields = [] - for (fname, BField, bitsize) in fields: - if bitsize < 0: - cfields.append((fname, BField._ctype)) - bfield_types[fname] = BField - else: - cfields.append((fname, BField._ctype, bitsize)) - bfield_types[fname] = Ellipsis - if sflags & 8: - struct_or_union._pack_ = 1 - elif pack: - struct_or_union._pack_ = pack - struct_or_union._fields_ = cfields - CTypesStructOrUnion._bfield_types = bfield_types - # - @staticmethod - def _create_ctype_obj(init): - result = struct_or_union() - if init is not None: - initialize(result, init) - return result - CTypesStructOrUnion._create_ctype_obj = _create_ctype_obj - # - def initialize(blob, init): - if is_union: - if len(init) > 1: - raise ValueError("union initializer: %d items given, but " - "only one supported (use a dict if needed)" - % (len(init),)) - if not isinstance(init, dict): - if isinstance(init, (bytes, unicode)): - raise TypeError("union initializer: got a str") - init = tuple(init) - if len(init) > len(fnames): - raise ValueError("too many values for %s initializer" % - CTypesStructOrUnion._get_c_name()) - init = dict(zip(fnames, init)) - addr = ctypes.addressof(blob) - for fname, value in init.items(): - BField, bitsize = name2fieldtype[fname] - assert bitsize < 0, \ - "not implemented: initializer with bit fields" - offset = CTypesStructOrUnion._offsetof(fname) - PTR = ctypes.POINTER(BField._ctype) - p = ctypes.cast(addr + offset, PTR) - BField._initialize(p.contents, value) - is_union = CTypesStructOrUnion._kind == 'union' - name2fieldtype = dict(zip(fnames, zip(btypes, bitfields))) - # - for fname, BField, bitsize in fields: - if fname == '': - raise NotImplementedError("nested anonymous structs/unions") - if hasattr(CTypesStructOrUnion, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - if bitsize < 0: - def getter(self, fname=fname, BField=BField, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BField._from_ctypes(p.contents) - def setter(self, value, fname=fname, BField=BField): - setattr(self._blob, fname, BField._to_ctypes(value)) - # - if issubclass(BField, CTypesGenericArray): - setter = None - if BField._declared_length == 0: - def getter(self, fname=fname, BFieldPtr=BField._CTPtr, - offset=CTypesStructOrUnion._offsetof(fname), - PTR=ctypes.POINTER(BField._ctype)): - addr = ctypes.addressof(self._blob) - p = ctypes.cast(addr + offset, PTR) - return BFieldPtr._from_ctypes(p) - # - else: - def getter(self, fname=fname, BField=BField): - return BField._from_ctypes(getattr(self._blob, fname)) - def setter(self, value, fname=fname, BField=BField): - # xxx obscure workaround - value = BField._to_ctypes(value) - oldvalue = getattr(self._blob, fname) - setattr(self._blob, fname, value) - if value != getattr(self._blob, fname): - setattr(self._blob, fname, oldvalue) - raise OverflowError("value too large for bitfield") - setattr(CTypesStructOrUnion, fname, property(getter, setter)) - # - CTypesPtr = self.ffi._get_cached_btype(model.PointerType(tp)) - for fname in fnames: - if hasattr(CTypesPtr, fname): - raise ValueError("the field name %r conflicts in " - "the ctypes backend" % fname) - def getter(self, fname=fname): - return getattr(self[0], fname) - def setter(self, value, fname=fname): - setattr(self[0], fname, value) - setattr(CTypesPtr, fname, property(getter, setter)) - - def new_function_type(self, BArgs, BResult, has_varargs): - nameargs = [BArg._get_c_name() for BArg in BArgs] - if has_varargs: - nameargs.append('...') - nameargs = ', '.join(nameargs) - # - class CTypesFunctionPtr(CTypesGenericPtr): - __slots__ = ['_own_callback', '_name'] - _ctype = ctypes.CFUNCTYPE(getattr(BResult, '_ctype', None), - *[BArg._ctype for BArg in BArgs], - use_errno=True) - _reftypename = BResult._get_c_name('(* &)(%s)' % (nameargs,)) - - def __init__(self, init, error=None): - # create a callback to the Python callable init() - import traceback - assert not has_varargs, "varargs not supported for callbacks" - if getattr(BResult, '_ctype', None) is not None: - error = BResult._from_ctypes( - BResult._create_ctype_obj(error)) - else: - error = None - def callback(*args): - args2 = [] - for arg, BArg in zip(args, BArgs): - args2.append(BArg._from_ctypes(arg)) - try: - res2 = init(*args2) - res2 = BResult._to_ctypes(res2) - except: - traceback.print_exc() - res2 = error - if issubclass(BResult, CTypesGenericPtr): - if res2: - res2 = ctypes.cast(res2, ctypes.c_void_p).value - # .value: http://bugs.python.org/issue1574593 - else: - res2 = None - #print repr(res2) - return res2 - if issubclass(BResult, CTypesGenericPtr): - # The only pointers callbacks can return are void*s: - # http://bugs.python.org/issue5710 - callback_ctype = ctypes.CFUNCTYPE( - ctypes.c_void_p, - *[BArg._ctype for BArg in BArgs], - use_errno=True) - else: - callback_ctype = CTypesFunctionPtr._ctype - self._as_ctype_ptr = callback_ctype(callback) - self._address = ctypes.cast(self._as_ctype_ptr, - ctypes.c_void_p).value - self._own_callback = init - - @staticmethod - def _initialize(ctypes_ptr, value): - if value: - raise NotImplementedError("ctypes backend: not supported: " - "initializers for function pointers") - - def __repr__(self): - c_name = getattr(self, '_name', None) - if c_name: - i = self._reftypename.index('(* &)') - if self._reftypename[i-1] not in ' )*': - c_name = ' ' + c_name - c_name = self._reftypename.replace('(* &)', c_name) - return CTypesData.__repr__(self, c_name) - - def _get_own_repr(self): - if getattr(self, '_own_callback', None) is not None: - return 'calling %r' % (self._own_callback,) - return super(CTypesFunctionPtr, self)._get_own_repr() - - def __call__(self, *args): - if has_varargs: - assert len(args) >= len(BArgs) - extraargs = args[len(BArgs):] - args = args[:len(BArgs)] - else: - assert len(args) == len(BArgs) - ctypes_args = [] - for arg, BArg in zip(args, BArgs): - ctypes_args.append(BArg._arg_to_ctypes(arg)) - if has_varargs: - for i, arg in enumerate(extraargs): - if arg is None: - ctypes_args.append(ctypes.c_void_p(0)) # NULL - continue - if not isinstance(arg, CTypesData): - raise TypeError( - "argument %d passed in the variadic part " - "needs to be a cdata object (got %s)" % - (1 + len(BArgs) + i, type(arg).__name__)) - ctypes_args.append(arg._arg_to_ctypes(arg)) - result = self._as_ctype_ptr(*ctypes_args) - return BResult._from_ctypes(result) - # - CTypesFunctionPtr._fix_class() - return CTypesFunctionPtr - - def new_enum_type(self, name, enumerators, enumvalues, CTypesInt): - assert isinstance(name, str) - reverse_mapping = dict(zip(reversed(enumvalues), - reversed(enumerators))) - # - class CTypesEnum(CTypesInt): - __slots__ = [] - _reftypename = '%s &' % name - - def _get_own_repr(self): - value = self._value - try: - return '%d: %s' % (value, reverse_mapping[value]) - except KeyError: - return str(value) - - def _to_string(self, maxlen): - value = self._value - try: - return reverse_mapping[value] - except KeyError: - return str(value) - # - CTypesEnum._fix_class() - return CTypesEnum - - def get_errno(self): - return ctypes.get_errno() - - def set_errno(self, value): - ctypes.set_errno(value) - - def string(self, b, maxlen=-1): - return b._to_string(maxlen) - - def buffer(self, bptr, size=-1): - raise NotImplementedError("buffer() with ctypes backend") - - def sizeof(self, cdata_or_BType): - if isinstance(cdata_or_BType, CTypesData): - return cdata_or_BType._get_size_of_instance() - else: - assert issubclass(cdata_or_BType, CTypesData) - return cdata_or_BType._get_size() - - def alignof(self, BType): - assert issubclass(BType, CTypesData) - return BType._alignment() - - def newp(self, BType, source): - if not issubclass(BType, CTypesData): - raise TypeError - return BType._newp(source) - - def cast(self, BType, source): - return BType._cast_from(source) - - def callback(self, BType, source, error, onerror): - assert onerror is None # XXX not implemented - return BType(source, error) - - _weakref_cache_ref = None - - def gcp(self, cdata, destructor, size=0): - if self._weakref_cache_ref is None: - import weakref - class MyRef(weakref.ref): - def __eq__(self, other): - myref = self() - return self is other or ( - myref is not None and myref is other()) - def __ne__(self, other): - return not (self == other) - def __hash__(self): - try: - return self._hash - except AttributeError: - self._hash = hash(self()) - return self._hash - self._weakref_cache_ref = {}, MyRef - weak_cache, MyRef = self._weakref_cache_ref - - if destructor is None: - try: - del weak_cache[MyRef(cdata)] - except KeyError: - raise TypeError("Can remove destructor only on a object " - "previously returned by ffi.gc()") - return None - - def remove(k): - cdata, destructor = weak_cache.pop(k, (None, None)) - if destructor is not None: - destructor(cdata) - - new_cdata = self.cast(self.typeof(cdata), cdata) - assert new_cdata is not cdata - weak_cache[MyRef(new_cdata, remove)] = (cdata, destructor) - return new_cdata - - typeof = type - - def getcname(self, BType, replace_with): - return BType._get_c_name(replace_with) - - def typeoffsetof(self, BType, fieldname, num=0): - if isinstance(fieldname, str): - if num == 0 and issubclass(BType, CTypesGenericPtr): - BType = BType._BItem - if not issubclass(BType, CTypesBaseStructOrUnion): - raise TypeError("expected a struct or union ctype") - BField = BType._bfield_types[fieldname] - if BField is Ellipsis: - raise TypeError("not supported for bitfields") - return (BField, BType._offsetof(fieldname)) - elif isinstance(fieldname, (int, long)): - if issubclass(BType, CTypesGenericArray): - BType = BType._CTPtr - if not issubclass(BType, CTypesGenericPtr): - raise TypeError("expected an array or ptr ctype") - BItem = BType._BItem - offset = BItem._get_size() * fieldname - if offset > sys.maxsize: - raise OverflowError - return (BItem, offset) - else: - raise TypeError(type(fieldname)) - - def rawaddressof(self, BTypePtr, cdata, offset=None): - if isinstance(cdata, CTypesBaseStructOrUnion): - ptr = ctypes.pointer(type(cdata)._to_ctypes(cdata)) - elif isinstance(cdata, CTypesGenericPtr): - if offset is None or not issubclass(type(cdata)._BItem, - CTypesBaseStructOrUnion): - raise TypeError("unexpected cdata type") - ptr = type(cdata)._to_ctypes(cdata) - elif isinstance(cdata, CTypesGenericArray): - ptr = type(cdata)._to_ctypes(cdata) - else: - raise TypeError("expected a ") - if offset: - ptr = ctypes.cast( - ctypes.c_void_p( - ctypes.cast(ptr, ctypes.c_void_p).value + offset), - type(ptr)) - return BTypePtr._from_ctypes(ptr) - - -class CTypesLibrary(object): - - def __init__(self, backend, cdll): - self.backend = backend - self.cdll = cdll - - def load_function(self, BType, name): - c_func = getattr(self.cdll, name) - funcobj = BType._from_ctypes(c_func) - funcobj._name = name - return funcobj - - def read_variable(self, BType, name): - try: - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - except AttributeError as e: - raise NotImplementedError(e) - return BType._from_ctypes(ctypes_obj) - - def write_variable(self, BType, name, value): - new_ctypes_obj = BType._to_ctypes(value) - ctypes_obj = BType._ctype.in_dll(self.cdll, name) - ctypes.memmove(ctypes.addressof(ctypes_obj), - ctypes.addressof(new_ctypes_obj), - ctypes.sizeof(BType._ctype)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py deleted file mode 100644 index b26ccc965..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/cffi_opcode.py +++ /dev/null @@ -1,187 +0,0 @@ -from .error import VerificationError - -class CffiOp(object): - def __init__(self, op, arg): - self.op = op - self.arg = arg - - def as_c_expr(self): - if self.op is None: - assert isinstance(self.arg, str) - return '(_cffi_opcode_t)(%s)' % (self.arg,) - classname = CLASS_NAME[self.op] - return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg) - - def as_python_bytes(self): - if self.op is None and self.arg.isdigit(): - value = int(self.arg) # non-negative: '-' not in self.arg - if value >= 2**31: - raise OverflowError("cannot emit %r: limited to 2**31-1" - % (self.arg,)) - return format_four_bytes(value) - if isinstance(self.arg, str): - raise VerificationError("cannot emit to Python: %r" % (self.arg,)) - return format_four_bytes((self.arg << 8) | self.op) - - def __str__(self): - classname = CLASS_NAME.get(self.op, self.op) - return '(%s %s)' % (classname, self.arg) - -def format_four_bytes(num): - return '\\x%02X\\x%02X\\x%02X\\x%02X' % ( - (num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - (num ) & 0xFF) - -OP_PRIMITIVE = 1 -OP_POINTER = 3 -OP_ARRAY = 5 -OP_OPEN_ARRAY = 7 -OP_STRUCT_UNION = 9 -OP_ENUM = 11 -OP_FUNCTION = 13 -OP_FUNCTION_END = 15 -OP_NOOP = 17 -OP_BITFIELD = 19 -OP_TYPENAME = 21 -OP_CPYTHON_BLTN_V = 23 # varargs -OP_CPYTHON_BLTN_N = 25 # noargs -OP_CPYTHON_BLTN_O = 27 # O (i.e. a single arg) -OP_CONSTANT = 29 -OP_CONSTANT_INT = 31 -OP_GLOBAL_VAR = 33 -OP_DLOPEN_FUNC = 35 -OP_DLOPEN_CONST = 37 -OP_GLOBAL_VAR_F = 39 -OP_EXTERN_PYTHON = 41 - -PRIM_VOID = 0 -PRIM_BOOL = 1 -PRIM_CHAR = 2 -PRIM_SCHAR = 3 -PRIM_UCHAR = 4 -PRIM_SHORT = 5 -PRIM_USHORT = 6 -PRIM_INT = 7 -PRIM_UINT = 8 -PRIM_LONG = 9 -PRIM_ULONG = 10 -PRIM_LONGLONG = 11 -PRIM_ULONGLONG = 12 -PRIM_FLOAT = 13 -PRIM_DOUBLE = 14 -PRIM_LONGDOUBLE = 15 - -PRIM_WCHAR = 16 -PRIM_INT8 = 17 -PRIM_UINT8 = 18 -PRIM_INT16 = 19 -PRIM_UINT16 = 20 -PRIM_INT32 = 21 -PRIM_UINT32 = 22 -PRIM_INT64 = 23 -PRIM_UINT64 = 24 -PRIM_INTPTR = 25 -PRIM_UINTPTR = 26 -PRIM_PTRDIFF = 27 -PRIM_SIZE = 28 -PRIM_SSIZE = 29 -PRIM_INT_LEAST8 = 30 -PRIM_UINT_LEAST8 = 31 -PRIM_INT_LEAST16 = 32 -PRIM_UINT_LEAST16 = 33 -PRIM_INT_LEAST32 = 34 -PRIM_UINT_LEAST32 = 35 -PRIM_INT_LEAST64 = 36 -PRIM_UINT_LEAST64 = 37 -PRIM_INT_FAST8 = 38 -PRIM_UINT_FAST8 = 39 -PRIM_INT_FAST16 = 40 -PRIM_UINT_FAST16 = 41 -PRIM_INT_FAST32 = 42 -PRIM_UINT_FAST32 = 43 -PRIM_INT_FAST64 = 44 -PRIM_UINT_FAST64 = 45 -PRIM_INTMAX = 46 -PRIM_UINTMAX = 47 -PRIM_FLOATCOMPLEX = 48 -PRIM_DOUBLECOMPLEX = 49 -PRIM_CHAR16 = 50 -PRIM_CHAR32 = 51 - -_NUM_PRIM = 52 -_UNKNOWN_PRIM = -1 -_UNKNOWN_FLOAT_PRIM = -2 -_UNKNOWN_LONG_DOUBLE = -3 - -_IO_FILE_STRUCT = -1 - -PRIMITIVE_TO_INDEX = { - 'char': PRIM_CHAR, - 'short': PRIM_SHORT, - 'int': PRIM_INT, - 'long': PRIM_LONG, - 'long long': PRIM_LONGLONG, - 'signed char': PRIM_SCHAR, - 'unsigned char': PRIM_UCHAR, - 'unsigned short': PRIM_USHORT, - 'unsigned int': PRIM_UINT, - 'unsigned long': PRIM_ULONG, - 'unsigned long long': PRIM_ULONGLONG, - 'float': PRIM_FLOAT, - 'double': PRIM_DOUBLE, - 'long double': PRIM_LONGDOUBLE, - 'float _Complex': PRIM_FLOATCOMPLEX, - 'double _Complex': PRIM_DOUBLECOMPLEX, - '_Bool': PRIM_BOOL, - 'wchar_t': PRIM_WCHAR, - 'char16_t': PRIM_CHAR16, - 'char32_t': PRIM_CHAR32, - 'int8_t': PRIM_INT8, - 'uint8_t': PRIM_UINT8, - 'int16_t': PRIM_INT16, - 'uint16_t': PRIM_UINT16, - 'int32_t': PRIM_INT32, - 'uint32_t': PRIM_UINT32, - 'int64_t': PRIM_INT64, - 'uint64_t': PRIM_UINT64, - 'intptr_t': PRIM_INTPTR, - 'uintptr_t': PRIM_UINTPTR, - 'ptrdiff_t': PRIM_PTRDIFF, - 'size_t': PRIM_SIZE, - 'ssize_t': PRIM_SSIZE, - 'int_least8_t': PRIM_INT_LEAST8, - 'uint_least8_t': PRIM_UINT_LEAST8, - 'int_least16_t': PRIM_INT_LEAST16, - 'uint_least16_t': PRIM_UINT_LEAST16, - 'int_least32_t': PRIM_INT_LEAST32, - 'uint_least32_t': PRIM_UINT_LEAST32, - 'int_least64_t': PRIM_INT_LEAST64, - 'uint_least64_t': PRIM_UINT_LEAST64, - 'int_fast8_t': PRIM_INT_FAST8, - 'uint_fast8_t': PRIM_UINT_FAST8, - 'int_fast16_t': PRIM_INT_FAST16, - 'uint_fast16_t': PRIM_UINT_FAST16, - 'int_fast32_t': PRIM_INT_FAST32, - 'uint_fast32_t': PRIM_UINT_FAST32, - 'int_fast64_t': PRIM_INT_FAST64, - 'uint_fast64_t': PRIM_UINT_FAST64, - 'intmax_t': PRIM_INTMAX, - 'uintmax_t': PRIM_UINTMAX, - } - -F_UNION = 0x01 -F_CHECK_FIELDS = 0x02 -F_PACKED = 0x04 -F_EXTERNAL = 0x08 -F_OPAQUE = 0x10 - -G_FLAGS = dict([('_CFFI_' + _key, globals()[_key]) - for _key in ['F_UNION', 'F_CHECK_FIELDS', 'F_PACKED', - 'F_EXTERNAL', 'F_OPAQUE']]) - -CLASS_NAME = {} -for _name, _value in list(globals().items()): - if _name.startswith('OP_') and isinstance(_value, int): - CLASS_NAME[_value] = _name[3:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py deleted file mode 100644 index e5045ee2c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/commontypes.py +++ /dev/null @@ -1,80 +0,0 @@ -import sys -from . import model -from .error import FFIError - - -COMMON_TYPES = {} - -try: - # fetch "bool" and all simple Windows types - from _cffi_backend import _get_common_types - _get_common_types(COMMON_TYPES) -except ImportError: - pass - -COMMON_TYPES['FILE'] = model.unknown_type('FILE', '_IO_FILE') -COMMON_TYPES['bool'] = '_Bool' # in case we got ImportError above - -for _type in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - if _type.endswith('_t'): - COMMON_TYPES[_type] = _type -del _type - -_CACHE = {} - -def resolve_common_type(parser, commontype): - try: - return _CACHE[commontype] - except KeyError: - cdecl = COMMON_TYPES.get(commontype, commontype) - if not isinstance(cdecl, str): - result, quals = cdecl, 0 # cdecl is already a BaseType - elif cdecl in model.PrimitiveType.ALL_PRIMITIVE_TYPES: - result, quals = model.PrimitiveType(cdecl), 0 - elif cdecl == 'set-unicode-needed': - raise FFIError("The Windows type %r is only available after " - "you call ffi.set_unicode()" % (commontype,)) - else: - if commontype == cdecl: - raise FFIError( - "Unsupported type: %r. Please look at " - "http://cffi.readthedocs.io/en/latest/cdef.html#ffi-cdef-limitations " - "and file an issue if you think this type should really " - "be supported." % (commontype,)) - result, quals = parser.parse_type_and_quals(cdecl) # recursive - - assert isinstance(result, model.BaseTypeByIdentity) - _CACHE[commontype] = result, quals - return result, quals - - -# ____________________________________________________________ -# extra types for Windows (most of them are in commontypes.c) - - -def win_common_types(): - return { - "UNICODE_STRING": model.StructType( - "_UNICODE_STRING", - ["Length", - "MaximumLength", - "Buffer"], - [model.PrimitiveType("unsigned short"), - model.PrimitiveType("unsigned short"), - model.PointerType(model.PrimitiveType("wchar_t"))], - [-1, -1, -1]), - "PUNICODE_STRING": "UNICODE_STRING *", - "PCUNICODE_STRING": "const UNICODE_STRING *", - - "TBYTE": "set-unicode-needed", - "TCHAR": "set-unicode-needed", - "LPCTSTR": "set-unicode-needed", - "PCTSTR": "set-unicode-needed", - "LPTSTR": "set-unicode-needed", - "PTSTR": "set-unicode-needed", - "PTBYTE": "set-unicode-needed", - "PTCHAR": "set-unicode-needed", - } - -if sys.platform == 'win32': - COMMON_TYPES.update(win_common_types()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py deleted file mode 100644 index c4acd23aa..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/cparser.py +++ /dev/null @@ -1,1006 +0,0 @@ -from . import model -from .commontypes import COMMON_TYPES, resolve_common_type -from .error import FFIError, CDefError -try: - from . import _pycparser as pycparser -except ImportError: - import pycparser -import weakref, re, sys - -try: - if sys.version_info < (3,): - import thread as _thread - else: - import _thread - lock = _thread.allocate_lock() -except ImportError: - lock = None - -def _workaround_for_static_import_finders(): - # Issue #392: packaging tools like cx_Freeze can not find these - # because pycparser uses exec dynamic import. This is an obscure - # workaround. This function is never called. - import pycparser.yacctab - import pycparser.lextab - -CDEF_SOURCE_STRING = "" -_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", - re.DOTALL | re.MULTILINE) -_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" - r"\b((?:[^\n\\]|\\.)*?)$", - re.DOTALL | re.MULTILINE) -_r_line_directive = re.compile(r"^[ \t]*#[ \t]*(?:line|\d+)\b.*$", re.MULTILINE) -_r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") -_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") -_r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") -_r_words = re.compile(r"\w+|\S") -_parser_cache = None -_r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) -_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") -_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") -_r_cdecl = re.compile(r"\b__cdecl\b") -_r_extern_python = re.compile(r'\bextern\s*"' - r'(Python|Python\s*\+\s*C|C\s*\+\s*Python)"\s*.') -_r_star_const_space = re.compile( # matches "* const " - r"[*]\s*((const|volatile|restrict)\b\s*)+") -_r_int_dotdotdot = re.compile(r"(\b(int|long|short|signed|unsigned|char)\s*)+" - r"\.\.\.") -_r_float_dotdotdot = re.compile(r"\b(double|float)\s*\.\.\.") - -def _get_parser(): - global _parser_cache - if _parser_cache is None: - _parser_cache = pycparser.CParser() - return _parser_cache - -def _workaround_for_old_pycparser(csource): - # Workaround for a pycparser issue (fixed between pycparser 2.10 and - # 2.14): "char*const***" gives us a wrong syntax tree, the same as - # for "char***(*const)". This means we can't tell the difference - # afterwards. But "char(*const(***))" gives us the right syntax - # tree. The issue only occurs if there are several stars in - # sequence with no parenthesis inbetween, just possibly qualifiers. - # Attempt to fix it by adding some parentheses in the source: each - # time we see "* const" or "* const *", we add an opening - # parenthesis before each star---the hard part is figuring out where - # to close them. - parts = [] - while True: - match = _r_star_const_space.search(csource) - if not match: - break - #print repr(''.join(parts)+csource), '=>', - parts.append(csource[:match.start()]) - parts.append('('); closing = ')' - parts.append(match.group()) # e.g. "* const " - endpos = match.end() - if csource.startswith('*', endpos): - parts.append('('); closing += ')' - level = 0 - i = endpos - while i < len(csource): - c = csource[i] - if c == '(': - level += 1 - elif c == ')': - if level == 0: - break - level -= 1 - elif c in ',;=': - if level == 0: - break - i += 1 - csource = csource[endpos:i] + closing + csource[i:] - #print repr(''.join(parts)+csource) - parts.append(csource) - return ''.join(parts) - -def _preprocess_extern_python(csource): - # input: `extern "Python" int foo(int);` or - # `extern "Python" { int foo(int); }` - # output: - # void __cffi_extern_python_start; - # int foo(int); - # void __cffi_extern_python_stop; - # - # input: `extern "Python+C" int foo(int);` - # output: - # void __cffi_extern_python_plus_c_start; - # int foo(int); - # void __cffi_extern_python_stop; - parts = [] - while True: - match = _r_extern_python.search(csource) - if not match: - break - endpos = match.end() - 1 - #print - #print ''.join(parts)+csource - #print '=>' - parts.append(csource[:match.start()]) - if 'C' in match.group(1): - parts.append('void __cffi_extern_python_plus_c_start; ') - else: - parts.append('void __cffi_extern_python_start; ') - if csource[endpos] == '{': - # grouping variant - closing = csource.find('}', endpos) - if closing < 0: - raise CDefError("'extern \"Python\" {': no '}' found") - if csource.find('{', endpos + 1, closing) >= 0: - raise NotImplementedError("cannot use { } inside a block " - "'extern \"Python\" { ... }'") - parts.append(csource[endpos+1:closing]) - csource = csource[closing+1:] - else: - # non-grouping variant - semicolon = csource.find(';', endpos) - if semicolon < 0: - raise CDefError("'extern \"Python\": no ';' found") - parts.append(csource[endpos:semicolon+1]) - csource = csource[semicolon+1:] - parts.append(' void __cffi_extern_python_stop;') - #print ''.join(parts)+csource - #print - parts.append(csource) - return ''.join(parts) - -def _warn_for_string_literal(csource): - if '"' not in csource: - return - for line in csource.splitlines(): - if '"' in line and not line.lstrip().startswith('#'): - import warnings - warnings.warn("String literal found in cdef() or type source. " - "String literals are ignored here, but you should " - "remove them anyway because some character sequences " - "confuse pre-parsing.") - break - -def _warn_for_non_extern_non_static_global_variable(decl): - if not decl.storage: - import warnings - warnings.warn("Global variable '%s' in cdef(): for consistency " - "with C it should have a storage class specifier " - "(usually 'extern')" % (decl.name,)) - -def _remove_line_directives(csource): - # _r_line_directive matches whole lines, without the final \n, if they - # start with '#line' with some spacing allowed, or '#NUMBER'. This - # function stores them away and replaces them with exactly the string - # '#line@N', where N is the index in the list 'line_directives'. - line_directives = [] - def replace(m): - i = len(line_directives) - line_directives.append(m.group()) - return '#line@%d' % i - csource = _r_line_directive.sub(replace, csource) - return csource, line_directives - -def _put_back_line_directives(csource, line_directives): - def replace(m): - s = m.group() - if not s.startswith('#line@'): - raise AssertionError("unexpected #line directive " - "(should have been processed and removed") - return line_directives[int(s[6:])] - return _r_line_directive.sub(replace, csource) - -def _preprocess(csource): - # First, remove the lines of the form '#line N "filename"' because - # the "filename" part could confuse the rest - csource, line_directives = _remove_line_directives(csource) - # Remove comments. NOTE: this only work because the cdef() section - # should not contain any string literals (except in line directives)! - def replace_keeping_newlines(m): - return ' ' + m.group().count('\n') * '\n' - csource = _r_comment.sub(replace_keeping_newlines, csource) - # Remove the "#define FOO x" lines - macros = {} - for match in _r_define.finditer(csource): - macroname, macrovalue = match.groups() - macrovalue = macrovalue.replace('\\\n', '').strip() - macros[macroname] = macrovalue - csource = _r_define.sub('', csource) - # - if pycparser.__version__ < '2.14': - csource = _workaround_for_old_pycparser(csource) - # - # BIG HACK: replace WINAPI or __stdcall with "volatile const". - # It doesn't make sense for the return type of a function to be - # "volatile volatile const", so we abuse it to detect __stdcall... - # Hack number 2 is that "int(volatile *fptr)();" is not valid C - # syntax, so we place the "volatile" before the opening parenthesis. - csource = _r_stdcall2.sub(' volatile volatile const(', csource) - csource = _r_stdcall1.sub(' volatile volatile const ', csource) - csource = _r_cdecl.sub(' ', csource) - # - # Replace `extern "Python"` with start/end markers - csource = _preprocess_extern_python(csource) - # - # Now there should not be any string literal left; warn if we get one - _warn_for_string_literal(csource) - # - # Replace "[...]" with "[__dotdotdotarray__]" - csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) - # - # Replace "...}" with "__dotdotdotNUM__}". This construction should - # occur only at the end of enums; at the end of structs we have "...;}" - # and at the end of vararg functions "...);". Also replace "=...[,}]" - # with ",__dotdotdotNUM__[,}]": this occurs in the enums too, when - # giving an unknown value. - matches = list(_r_partial_enum.finditer(csource)) - for number, match in enumerate(reversed(matches)): - p = match.start() - if csource[p] == '=': - p2 = csource.find('...', p, match.end()) - assert p2 > p - csource = '%s,__dotdotdot%d__ %s' % (csource[:p], number, - csource[p2+3:]) - else: - assert csource[p:p+3] == '...' - csource = '%s __dotdotdot%d__ %s' % (csource[:p], number, - csource[p+3:]) - # Replace "int ..." or "unsigned long int..." with "__dotdotdotint__" - csource = _r_int_dotdotdot.sub(' __dotdotdotint__ ', csource) - # Replace "float ..." or "double..." with "__dotdotdotfloat__" - csource = _r_float_dotdotdot.sub(' __dotdotdotfloat__ ', csource) - # Replace all remaining "..." with the same name, "__dotdotdot__", - # which is declared with a typedef for the purpose of C parsing. - csource = csource.replace('...', ' __dotdotdot__ ') - # Finally, put back the line directives - csource = _put_back_line_directives(csource, line_directives) - return csource, macros - -def _common_type_names(csource): - # Look in the source for what looks like usages of types from the - # list of common types. A "usage" is approximated here as the - # appearance of the word, minus a "definition" of the type, which - # is the last word in a "typedef" statement. Approximative only - # but should be fine for all the common types. - look_for_words = set(COMMON_TYPES) - look_for_words.add(';') - look_for_words.add(',') - look_for_words.add('(') - look_for_words.add(')') - look_for_words.add('typedef') - words_used = set() - is_typedef = False - paren = 0 - previous_word = '' - for word in _r_words.findall(csource): - if word in look_for_words: - if word == ';': - if is_typedef: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - is_typedef = False - elif word == 'typedef': - is_typedef = True - paren = 0 - elif word == '(': - paren += 1 - elif word == ')': - paren -= 1 - elif word == ',': - if is_typedef and paren == 0: - words_used.discard(previous_word) - look_for_words.discard(previous_word) - else: # word in COMMON_TYPES - words_used.add(word) - previous_word = word - return words_used - - -class Parser(object): - - def __init__(self): - self._declarations = {} - self._included_declarations = set() - self._anonymous_counter = 0 - self._structnode2type = weakref.WeakKeyDictionary() - self._options = {} - self._int_constants = {} - self._recomplete = [] - self._uses_new_feature = None - - def _parse(self, csource): - csource, macros = _preprocess(csource) - # XXX: for more efficiency we would need to poke into the - # internals of CParser... the following registers the - # typedefs, because their presence or absence influences the - # parsing itself (but what they are typedef'ed to plays no role) - ctn = _common_type_names(csource) - typenames = [] - for name in sorted(self._declarations): - if name.startswith('typedef '): - name = name[8:] - typenames.append(name) - ctn.discard(name) - typenames += sorted(ctn) - # - csourcelines = [] - csourcelines.append('# 1 ""') - for typename in typenames: - csourcelines.append('typedef int %s;' % typename) - csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,' - ' __dotdotdot__;') - # this forces pycparser to consider the following in the file - # called from line 1 - csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,)) - csourcelines.append(csource) - fullcsource = '\n'.join(csourcelines) - if lock is not None: - lock.acquire() # pycparser is not thread-safe... - try: - ast = _get_parser().parse(fullcsource) - except pycparser.c_parser.ParseError as e: - self.convert_pycparser_error(e, csource) - finally: - if lock is not None: - lock.release() - # csource will be used to find buggy source text - return ast, macros, csource - - def _convert_pycparser_error(self, e, csource): - # xxx look for ":NUM:" at the start of str(e) - # and interpret that as a line number. This will not work if - # the user gives explicit ``# NUM "FILE"`` directives. - line = None - msg = str(e) - match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg) - if match: - linenum = int(match.group(1), 10) - csourcelines = csource.splitlines() - if 1 <= linenum <= len(csourcelines): - line = csourcelines[linenum-1] - return line - - def convert_pycparser_error(self, e, csource): - line = self._convert_pycparser_error(e, csource) - - msg = str(e) - if line: - msg = 'cannot parse "%s"\n%s' % (line.strip(), msg) - else: - msg = 'parse error\n%s' % (msg,) - raise CDefError(msg) - - def parse(self, csource, override=False, packed=False, pack=None, - dllexport=False): - if packed: - if packed != True: - raise ValueError("'packed' should be False or True; use " - "'pack' to give another value") - if pack: - raise ValueError("cannot give both 'pack' and 'packed'") - pack = 1 - elif pack: - if pack & (pack - 1): - raise ValueError("'pack' must be a power of two, not %r" % - (pack,)) - else: - pack = 0 - prev_options = self._options - try: - self._options = {'override': override, - 'packed': pack, - 'dllexport': dllexport} - self._internal_parse(csource) - finally: - self._options = prev_options - - def _internal_parse(self, csource): - ast, macros, csource = self._parse(csource) - # add the macros - self._process_macros(macros) - # find the first "__dotdotdot__" and use that as a separator - # between the repeated typedefs and the real csource - iterator = iter(ast.ext) - for decl in iterator: - if decl.name == '__dotdotdot__': - break - else: - assert 0 - current_decl = None - # - try: - self._inside_extern_python = '__cffi_extern_python_stop' - for decl in iterator: - current_decl = decl - if isinstance(decl, pycparser.c_ast.Decl): - self._parse_decl(decl) - elif isinstance(decl, pycparser.c_ast.Typedef): - if not decl.name: - raise CDefError("typedef does not declare any name", - decl) - quals = 0 - if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType) and - decl.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_type(decl) - elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and - isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and - isinstance(decl.type.type.type, - pycparser.c_ast.IdentifierType) and - decl.type.type.type.names[-1].startswith('__dotdotdot')): - realtype = self._get_unknown_ptr_type(decl) - else: - realtype, quals = self._get_type_and_quals( - decl.type, name=decl.name, partial_length_ok=True, - typedef_example="*(%s *)0" % (decl.name,)) - self._declare('typedef ' + decl.name, realtype, quals=quals) - elif decl.__class__.__name__ == 'Pragma': - pass # skip pragma, only in pycparser 2.15 - else: - raise CDefError("unexpected <%s>: this construct is valid " - "C but not valid in cdef()" % - decl.__class__.__name__, decl) - except CDefError as e: - if len(e.args) == 1: - e.args = e.args + (current_decl,) - raise - except FFIError as e: - msg = self._convert_pycparser_error(e, csource) - if msg: - e.args = (e.args[0] + "\n *** Err: %s" % msg,) - raise - - def _add_constants(self, key, val): - if key in self._int_constants: - if self._int_constants[key] == val: - return # ignore identical double declarations - raise FFIError( - "multiple declarations of constant: %s" % (key,)) - self._int_constants[key] = val - - def _add_integer_constant(self, name, int_str): - int_str = int_str.lower().rstrip("ul") - neg = int_str.startswith('-') - if neg: - int_str = int_str[1:] - # "010" is not valid oct in py3 - if (int_str.startswith("0") and int_str != '0' - and not int_str.startswith("0x")): - int_str = "0o" + int_str[1:] - pyvalue = int(int_str, 0) - if neg: - pyvalue = -pyvalue - self._add_constants(name, pyvalue) - self._declare('macro ' + name, pyvalue) - - def _process_macros(self, macros): - for key, value in macros.items(): - value = value.strip() - if _r_int_literal.match(value): - self._add_integer_constant(key, value) - elif value == '...': - self._declare('macro ' + key, value) - else: - raise CDefError( - 'only supports one of the following syntax:\n' - ' #define %s ... (literally dot-dot-dot)\n' - ' #define %s NUMBER (with NUMBER an integer' - ' constant, decimal/hex/octal)\n' - 'got:\n' - ' #define %s %s' - % (key, key, key, value)) - - def _declare_function(self, tp, quals, decl): - tp = self._get_type_pointer(tp, quals) - if self._options.get('dllexport'): - tag = 'dllexport_python ' - elif self._inside_extern_python == '__cffi_extern_python_start': - tag = 'extern_python ' - elif self._inside_extern_python == '__cffi_extern_python_plus_c_start': - tag = 'extern_python_plus_c ' - else: - tag = 'function ' - self._declare(tag + decl.name, tp) - - def _parse_decl(self, decl): - node = decl.type - if isinstance(node, pycparser.c_ast.FuncDecl): - tp, quals = self._get_type_and_quals(node, name=decl.name) - assert isinstance(tp, model.RawFunctionType) - self._declare_function(tp, quals, decl) - else: - if isinstance(node, pycparser.c_ast.Struct): - self._get_struct_union_enum_type('struct', node) - elif isinstance(node, pycparser.c_ast.Union): - self._get_struct_union_enum_type('union', node) - elif isinstance(node, pycparser.c_ast.Enum): - self._get_struct_union_enum_type('enum', node) - elif not decl.name: - raise CDefError("construct does not declare any variable", - decl) - # - if decl.name: - tp, quals = self._get_type_and_quals(node, - partial_length_ok=True) - if tp.is_raw_function: - self._declare_function(tp, quals, decl) - elif (tp.is_integer_type() and - hasattr(decl, 'init') and - hasattr(decl.init, 'value') and - _r_int_literal.match(decl.init.value)): - self._add_integer_constant(decl.name, decl.init.value) - elif (tp.is_integer_type() and - isinstance(decl.init, pycparser.c_ast.UnaryOp) and - decl.init.op == '-' and - hasattr(decl.init.expr, 'value') and - _r_int_literal.match(decl.init.expr.value)): - self._add_integer_constant(decl.name, - '-' + decl.init.expr.value) - elif (tp is model.void_type and - decl.name.startswith('__cffi_extern_python_')): - # hack: `extern "Python"` in the C source is replaced - # with "void __cffi_extern_python_start;" and - # "void __cffi_extern_python_stop;" - self._inside_extern_python = decl.name - else: - if self._inside_extern_python !='__cffi_extern_python_stop': - raise CDefError( - "cannot declare constants or " - "variables with 'extern \"Python\"'") - if (quals & model.Q_CONST) and not tp.is_array_type: - self._declare('constant ' + decl.name, tp, quals=quals) - else: - _warn_for_non_extern_non_static_global_variable(decl) - self._declare('variable ' + decl.name, tp, quals=quals) - - def parse_type(self, cdecl): - return self.parse_type_and_quals(cdecl)[0] - - def parse_type_and_quals(self, cdecl): - ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] - assert not macros - exprnode = ast.ext[-1].type.args.params[0] - if isinstance(exprnode, pycparser.c_ast.ID): - raise CDefError("unknown identifier '%s'" % (exprnode.name,)) - return self._get_type_and_quals(exprnode.type) - - def _declare(self, name, obj, included=False, quals=0): - if name in self._declarations: - prevobj, prevquals = self._declarations[name] - if prevobj is obj and prevquals == quals: - return - if not self._options.get('override'): - raise FFIError( - "multiple declarations of %s (for interactive usage, " - "try cdef(xx, override=True))" % (name,)) - assert '__dotdotdot__' not in name.split() - self._declarations[name] = (obj, quals) - if included: - self._included_declarations.add(obj) - - def _extract_quals(self, type): - quals = 0 - if isinstance(type, (pycparser.c_ast.TypeDecl, - pycparser.c_ast.PtrDecl)): - if 'const' in type.quals: - quals |= model.Q_CONST - if 'volatile' in type.quals: - quals |= model.Q_VOLATILE - if 'restrict' in type.quals: - quals |= model.Q_RESTRICT - return quals - - def _get_type_pointer(self, type, quals, declname=None): - if isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - if (isinstance(type, model.StructOrUnionOrEnum) and - type.name.startswith('$') and type.name[1:].isdigit() and - type.forcename is None and declname is not None): - return model.NamedPointerType(type, declname, quals) - return model.PointerType(type, quals) - - def _get_type_and_quals(self, typenode, name=None, partial_length_ok=False, - typedef_example=None): - # first, dereference typedefs, if we have it already parsed, we're good - if (isinstance(typenode, pycparser.c_ast.TypeDecl) and - isinstance(typenode.type, pycparser.c_ast.IdentifierType) and - len(typenode.type.names) == 1 and - ('typedef ' + typenode.type.names[0]) in self._declarations): - tp, quals = self._declarations['typedef ' + typenode.type.names[0]] - quals |= self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.ArrayDecl): - # array type - if typenode.dim is None: - length = None - else: - length = self._parse_constant( - typenode.dim, partial_length_ok=partial_length_ok) - # a hack: in 'typedef int foo_t[...][...];', don't use '...' as - # the length but use directly the C expression that would be - # generated by recompiler.py. This lets the typedef be used in - # many more places within recompiler.py - if typedef_example is not None: - if length == '...': - length = '_cffi_array_len(%s)' % (typedef_example,) - typedef_example = "*" + typedef_example - # - tp, quals = self._get_type_and_quals(typenode.type, - partial_length_ok=partial_length_ok, - typedef_example=typedef_example) - return model.ArrayType(tp, length), quals - # - if isinstance(typenode, pycparser.c_ast.PtrDecl): - # pointer type - itemtype, itemquals = self._get_type_and_quals(typenode.type) - tp = self._get_type_pointer(itemtype, itemquals, declname=name) - quals = self._extract_quals(typenode) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.TypeDecl): - quals = self._extract_quals(typenode) - type = typenode.type - if isinstance(type, pycparser.c_ast.IdentifierType): - # assume a primitive type. get it from .names, but reduce - # synonyms to a single chosen combination - names = list(type.names) - if names != ['signed', 'char']: # keep this unmodified - prefixes = {} - while names: - name = names[0] - if name in ('short', 'long', 'signed', 'unsigned'): - prefixes[name] = prefixes.get(name, 0) + 1 - del names[0] - else: - break - # ignore the 'signed' prefix below, and reorder the others - newnames = [] - for prefix in ('unsigned', 'short', 'long'): - for i in range(prefixes.get(prefix, 0)): - newnames.append(prefix) - if not names: - names = ['int'] # implicitly - if names == ['int']: # but kill it if 'short' or 'long' - if 'short' in prefixes or 'long' in prefixes: - names = [] - names = newnames + names - ident = ' '.join(names) - if ident == 'void': - return model.void_type, quals - if ident == '__dotdotdot__': - raise FFIError(':%d: bad usage of "..."' % - typenode.coord.line) - tp0, quals0 = resolve_common_type(self, ident) - return tp0, (quals | quals0) - # - if isinstance(type, pycparser.c_ast.Struct): - # 'struct foobar' - tp = self._get_struct_union_enum_type('struct', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Union): - # 'union foobar' - tp = self._get_struct_union_enum_type('union', type, name) - return tp, quals - # - if isinstance(type, pycparser.c_ast.Enum): - # 'enum foobar' - tp = self._get_struct_union_enum_type('enum', type, name) - return tp, quals - # - if isinstance(typenode, pycparser.c_ast.FuncDecl): - # a function type - return self._parse_function_type(typenode, name), 0 - # - # nested anonymous structs or unions end up here - if isinstance(typenode, pycparser.c_ast.Struct): - return self._get_struct_union_enum_type('struct', typenode, name, - nested=True), 0 - if isinstance(typenode, pycparser.c_ast.Union): - return self._get_struct_union_enum_type('union', typenode, name, - nested=True), 0 - # - raise FFIError(":%d: bad or unsupported type declaration" % - typenode.coord.line) - - def _parse_function_type(self, typenode, funcname=None): - params = list(getattr(typenode.args, 'params', [])) - for i, arg in enumerate(params): - if not hasattr(arg, 'type'): - raise CDefError("%s arg %d: unknown type '%s'" - " (if you meant to use the old C syntax of giving" - " untyped arguments, it is not supported)" - % (funcname or 'in expression', i + 1, - getattr(arg, 'name', '?'))) - ellipsis = ( - len(params) > 0 and - isinstance(params[-1].type, pycparser.c_ast.TypeDecl) and - isinstance(params[-1].type.type, - pycparser.c_ast.IdentifierType) and - params[-1].type.type.names == ['__dotdotdot__']) - if ellipsis: - params.pop() - if not params: - raise CDefError( - "%s: a function with only '(...)' as argument" - " is not correct C" % (funcname or 'in expression')) - args = [self._as_func_arg(*self._get_type_and_quals(argdeclnode.type)) - for argdeclnode in params] - if not ellipsis and args == [model.void_type]: - args = [] - result, quals = self._get_type_and_quals(typenode.type) - # the 'quals' on the result type are ignored. HACK: we absure them - # to detect __stdcall functions: we textually replace "__stdcall" - # with "volatile volatile const" above. - abi = None - if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway - if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']: - abi = '__stdcall' - return model.RawFunctionType(tuple(args), result, ellipsis, abi) - - def _as_func_arg(self, type, quals): - if isinstance(type, model.ArrayType): - return model.PointerType(type.item, quals) - elif isinstance(type, model.RawFunctionType): - return type.as_function_pointer() - else: - return type - - def _get_struct_union_enum_type(self, kind, type, name=None, nested=False): - # First, a level of caching on the exact 'type' node of the AST. - # This is obscure, but needed because pycparser "unrolls" declarations - # such as "typedef struct { } foo_t, *foo_p" and we end up with - # an AST that is not a tree, but a DAG, with the "type" node of the - # two branches foo_t and foo_p of the trees being the same node. - # It's a bit silly but detecting "DAG-ness" in the AST tree seems - # to be the only way to distinguish this case from two independent - # structs. See test_struct_with_two_usages. - try: - return self._structnode2type[type] - except KeyError: - pass - # - # Note that this must handle parsing "struct foo" any number of - # times and always return the same StructType object. Additionally, - # one of these times (not necessarily the first), the fields of - # the struct can be specified with "struct foo { ...fields... }". - # If no name is given, then we have to create a new anonymous struct - # with no caching; in this case, the fields are either specified - # right now or never. - # - force_name = name - name = type.name - # - # get the type or create it if needed - if name is None: - # 'force_name' is used to guess a more readable name for - # anonymous structs, for the common case "typedef struct { } foo". - if force_name is not None: - explicit_name = '$%s' % force_name - else: - self._anonymous_counter += 1 - explicit_name = '$%d' % self._anonymous_counter - tp = None - else: - explicit_name = name - key = '%s %s' % (kind, name) - tp, _ = self._declarations.get(key, (None, None)) - # - if tp is None: - if kind == 'struct': - tp = model.StructType(explicit_name, None, None, None) - elif kind == 'union': - tp = model.UnionType(explicit_name, None, None, None) - elif kind == 'enum': - if explicit_name == '__dotdotdot__': - raise CDefError("Enums cannot be declared with ...") - tp = self._build_enum_type(explicit_name, type.values) - else: - raise AssertionError("kind = %r" % (kind,)) - if name is not None: - self._declare(key, tp) - else: - if kind == 'enum' and type.values is not None: - raise NotImplementedError( - "enum %s: the '{}' declaration should appear on the first " - "time the enum is mentioned, not later" % explicit_name) - if not tp.forcename: - tp.force_the_name(force_name) - if tp.forcename and '$' in tp.name: - self._declare('anonymous %s' % tp.forcename, tp) - # - self._structnode2type[type] = tp - # - # enums: done here - if kind == 'enum': - return tp - # - # is there a 'type.decls'? If yes, then this is the place in the - # C sources that declare the fields. If no, then just return the - # existing type, possibly still incomplete. - if type.decls is None: - return tp - # - if tp.fldnames is not None: - raise CDefError("duplicate declaration of struct %s" % name) - fldnames = [] - fldtypes = [] - fldbitsize = [] - fldquals = [] - for decl in type.decls: - if (isinstance(decl.type, pycparser.c_ast.IdentifierType) and - ''.join(decl.type.names) == '__dotdotdot__'): - # XXX pycparser is inconsistent: 'names' should be a list - # of strings, but is sometimes just one string. Use - # str.join() as a way to cope with both. - self._make_partial(tp, nested) - continue - if decl.bitsize is None: - bitsize = -1 - else: - bitsize = self._parse_constant(decl.bitsize) - self._partial_length = False - type, fqual = self._get_type_and_quals(decl.type, - partial_length_ok=True) - if self._partial_length: - self._make_partial(tp, nested) - if isinstance(type, model.StructType) and type.partial: - self._make_partial(tp, nested) - fldnames.append(decl.name or '') - fldtypes.append(type) - fldbitsize.append(bitsize) - fldquals.append(fqual) - tp.fldnames = tuple(fldnames) - tp.fldtypes = tuple(fldtypes) - tp.fldbitsize = tuple(fldbitsize) - tp.fldquals = tuple(fldquals) - if fldbitsize != [-1] * len(fldbitsize): - if isinstance(tp, model.StructType) and tp.partial: - raise NotImplementedError("%s: using both bitfields and '...;'" - % (tp,)) - tp.packed = self._options.get('packed') - if tp.completed: # must be re-completed: it is not opaque any more - tp.completed = 0 - self._recomplete.append(tp) - return tp - - def _make_partial(self, tp, nested): - if not isinstance(tp, model.StructOrUnion): - raise CDefError("%s cannot be partial" % (tp,)) - if not tp.has_c_name() and not nested: - raise NotImplementedError("%s is partial but has no C name" %(tp,)) - tp.partial = True - - def _parse_constant(self, exprnode, partial_length_ok=False): - # for now, limited to expressions that are an immediate number - # or positive/negative number - if isinstance(exprnode, pycparser.c_ast.Constant): - s = exprnode.value - if '0' <= s[0] <= '9': - s = s.rstrip('uUlL') - try: - if s.startswith('0'): - return int(s, 8) - else: - return int(s, 10) - except ValueError: - if len(s) > 1: - if s.lower()[0:2] == '0x': - return int(s, 16) - elif s.lower()[0:2] == '0b': - return int(s, 2) - raise CDefError("invalid constant %r" % (s,)) - elif s[0] == "'" and s[-1] == "'" and ( - len(s) == 3 or (len(s) == 4 and s[1] == "\\")): - return ord(s[-2]) - else: - raise CDefError("invalid constant %r" % (s,)) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '+'): - return self._parse_constant(exprnode.expr) - # - if (isinstance(exprnode, pycparser.c_ast.UnaryOp) and - exprnode.op == '-'): - return -self._parse_constant(exprnode.expr) - # load previously defined int constant - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name in self._int_constants): - return self._int_constants[exprnode.name] - # - if (isinstance(exprnode, pycparser.c_ast.ID) and - exprnode.name == '__dotdotdotarray__'): - if partial_length_ok: - self._partial_length = True - return '...' - raise FFIError(":%d: unsupported '[...]' here, cannot derive " - "the actual array length in this context" - % exprnode.coord.line) - # - if isinstance(exprnode, pycparser.c_ast.BinaryOp): - left = self._parse_constant(exprnode.left) - right = self._parse_constant(exprnode.right) - if exprnode.op == '+': - return left + right - elif exprnode.op == '-': - return left - right - elif exprnode.op == '*': - return left * right - elif exprnode.op == '/': - return self._c_div(left, right) - elif exprnode.op == '%': - return left - self._c_div(left, right) * right - elif exprnode.op == '<<': - return left << right - elif exprnode.op == '>>': - return left >> right - elif exprnode.op == '&': - return left & right - elif exprnode.op == '|': - return left | right - elif exprnode.op == '^': - return left ^ right - # - raise FFIError(":%d: unsupported expression: expected a " - "simple numeric constant" % exprnode.coord.line) - - def _c_div(self, a, b): - result = a // b - if ((a < 0) ^ (b < 0)) and (a % b) != 0: - result += 1 - return result - - def _build_enum_type(self, explicit_name, decls): - if decls is not None: - partial = False - enumerators = [] - enumvalues = [] - nextenumvalue = 0 - for enum in decls.enumerators: - if _r_enum_dotdotdot.match(enum.name): - partial = True - continue - if enum.value is not None: - nextenumvalue = self._parse_constant(enum.value) - enumerators.append(enum.name) - enumvalues.append(nextenumvalue) - self._add_constants(enum.name, nextenumvalue) - nextenumvalue += 1 - enumerators = tuple(enumerators) - enumvalues = tuple(enumvalues) - tp = model.EnumType(explicit_name, enumerators, enumvalues) - tp.partial = partial - else: # opaque enum - tp = model.EnumType(explicit_name, (), ()) - return tp - - def include(self, other): - for name, (tp, quals) in other._declarations.items(): - if name.startswith('anonymous $enum_$'): - continue # fix for test_anonymous_enum_include - kind = name.split(' ', 1)[0] - if kind in ('struct', 'union', 'enum', 'anonymous', 'typedef'): - self._declare(name, tp, included=True, quals=quals) - for k, v in other._int_constants.items(): - self._add_constants(k, v) - - def _get_unknown_type(self, decl): - typenames = decl.type.type.names - if typenames == ['__dotdotdot__']: - return model.unknown_type(decl.name) - - if typenames == ['__dotdotdotint__']: - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef int... %s'" % decl.name - return model.UnknownIntegerType(decl.name) - - if typenames == ['__dotdotdotfloat__']: - # note: not for 'long double' so far - if self._uses_new_feature is None: - self._uses_new_feature = "'typedef float... %s'" % decl.name - return model.UnknownFloatType(decl.name) - - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) - - def _get_unknown_ptr_type(self, decl): - if decl.type.type.type.names == ['__dotdotdot__']: - return model.unknown_ptr_type(decl.name) - raise FFIError(':%d: unsupported usage of "..." in typedef' - % decl.coord.line) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py deleted file mode 100644 index 0f8f406f5..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/error.py +++ /dev/null @@ -1,31 +0,0 @@ - -class FFIError(Exception): - __module__ = 'cffi' - -class CDefError(Exception): - __module__ = 'cffi' - def __str__(self): - try: - current_decl = self.args[1] - filename = current_decl.coord.file - linenum = current_decl.coord.line - prefix = '%s:%d: ' % (filename, linenum) - except (AttributeError, TypeError, IndexError): - prefix = '' - return '%s%s' % (prefix, self.args[0]) - -class VerificationError(Exception): - """ An error raised when verification fails - """ - __module__ = 'cffi' - -class VerificationMissing(Exception): - """ An error raised when incomplete structures are passed into - cdef, but no verification has been done - """ - __module__ = 'cffi' - -class PkgConfigError(Exception): - """ An error raised for missing modules in pkg-config - """ - __module__ = 'cffi' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py deleted file mode 100644 index 0feab6ee9..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/ffiplatform.py +++ /dev/null @@ -1,127 +0,0 @@ -import sys, os -from .error import VerificationError - - -LIST_OF_FILE_NAMES = ['sources', 'include_dirs', 'library_dirs', - 'extra_objects', 'depends'] - -def get_extension(srcfilename, modname, sources=(), **kwds): - _hack_at_distutils() - from distutils.core import Extension - allsources = [srcfilename] - for src in sources: - allsources.append(os.path.normpath(src)) - return Extension(name=modname, sources=allsources, **kwds) - -def compile(tmpdir, ext, compiler_verbose=0, debug=None): - """Compile a C extension module using distutils.""" - - _hack_at_distutils() - saved_environ = os.environ.copy() - try: - outputfilename = _build(tmpdir, ext, compiler_verbose, debug) - outputfilename = os.path.abspath(outputfilename) - finally: - # workaround for a distutils bugs where some env vars can - # become longer and longer every time it is used - for key, value in saved_environ.items(): - if os.environ.get(key) != value: - os.environ[key] = value - return outputfilename - -def _build(tmpdir, ext, compiler_verbose=0, debug=None): - # XXX compact but horrible :-( - from distutils.core import Distribution - import distutils.errors, distutils.log - # - dist = Distribution({'ext_modules': [ext]}) - dist.parse_config_files() - options = dist.get_option_dict('build_ext') - if debug is None: - debug = sys.flags.debug - options['debug'] = ('ffiplatform', debug) - options['force'] = ('ffiplatform', True) - options['build_lib'] = ('ffiplatform', tmpdir) - options['build_temp'] = ('ffiplatform', tmpdir) - # - try: - old_level = distutils.log.set_threshold(0) or 0 - try: - distutils.log.set_verbosity(compiler_verbose) - dist.run_command('build_ext') - cmd_obj = dist.get_command_obj('build_ext') - [soname] = cmd_obj.get_outputs() - finally: - distutils.log.set_threshold(old_level) - except (distutils.errors.CompileError, - distutils.errors.LinkError) as e: - raise VerificationError('%s: %s' % (e.__class__.__name__, e)) - # - return soname - -try: - from os.path import samefile -except ImportError: - def samefile(f1, f2): - return os.path.abspath(f1) == os.path.abspath(f2) - -def maybe_relative_path(path): - if not os.path.isabs(path): - return path # already relative - dir = path - names = [] - while True: - prevdir = dir - dir, name = os.path.split(prevdir) - if dir == prevdir or not dir: - return path # failed to make it relative - names.append(name) - try: - if samefile(dir, os.curdir): - names.reverse() - return os.path.join(*names) - except OSError: - pass - -# ____________________________________________________________ - -try: - int_or_long = (int, long) - import cStringIO -except NameError: - int_or_long = int # Python 3 - import io as cStringIO - -def _flatten(x, f): - if isinstance(x, str): - f.write('%ds%s' % (len(x), x)) - elif isinstance(x, dict): - keys = sorted(x.keys()) - f.write('%dd' % len(keys)) - for key in keys: - _flatten(key, f) - _flatten(x[key], f) - elif isinstance(x, (list, tuple)): - f.write('%dl' % len(x)) - for value in x: - _flatten(value, f) - elif isinstance(x, int_or_long): - f.write('%di' % (x,)) - else: - raise TypeError( - "the keywords to verify() contains unsupported object %r" % (x,)) - -def flatten(x): - f = cStringIO.StringIO() - _flatten(x, f) - return f.getvalue() - -def _hack_at_distutils(): - # Windows-only workaround for some configurations: see - # https://bugs.python.org/issue23246 (Python 2.7 with - # a specific MS compiler suite download) - if sys.platform == "win32": - try: - import setuptools # for side-effects, patches distutils - except ImportError: - pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py deleted file mode 100644 index 2e40ed857..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/lock.py +++ /dev/null @@ -1,30 +0,0 @@ -import sys - -if sys.version_info < (3,): - try: - from thread import allocate_lock - except ImportError: - from dummy_thread import allocate_lock -else: - try: - from _thread import allocate_lock - except ImportError: - from _dummy_thread import allocate_lock - - -##import sys -##l1 = allocate_lock - -##class allocate_lock(object): -## def __init__(self): -## self._real = l1() -## def __enter__(self): -## for i in range(4, 0, -1): -## print sys._getframe(i).f_code -## print -## return self._real.__enter__() -## def __exit__(self, *args): -## return self._real.__exit__(*args) -## def acquire(self, f): -## assert f is False -## return self._real.acquire(f) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py deleted file mode 100644 index 065e8c0a7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/model.py +++ /dev/null @@ -1,617 +0,0 @@ -import types -import weakref - -from .lock import allocate_lock -from .error import CDefError, VerificationError, VerificationMissing - -# type qualifiers -Q_CONST = 0x01 -Q_RESTRICT = 0x02 -Q_VOLATILE = 0x04 - -def qualify(quals, replace_with): - if quals & Q_CONST: - replace_with = ' const ' + replace_with.lstrip() - if quals & Q_VOLATILE: - replace_with = ' volatile ' + replace_with.lstrip() - if quals & Q_RESTRICT: - # It seems that __restrict is supported by gcc and msvc. - # If you hit some different compiler, add a #define in - # _cffi_include.h for it (and in its copies, documented there) - replace_with = ' __restrict ' + replace_with.lstrip() - return replace_with - - -class BaseTypeByIdentity(object): - is_array_type = False - is_raw_function = False - - def get_c_name(self, replace_with='', context='a C file', quals=0): - result = self.c_name_with_marker - assert result.count('&') == 1 - # some logic duplication with ffi.getctype()... :-( - replace_with = replace_with.strip() - if replace_with: - if replace_with.startswith('*') and '&[' in result: - replace_with = '(%s)' % replace_with - elif not replace_with[0] in '[(': - replace_with = ' ' + replace_with - replace_with = qualify(quals, replace_with) - result = result.replace('&', replace_with) - if '$' in result: - raise VerificationError( - "cannot generate '%s' in %s: unknown type name" - % (self._get_c_name(), context)) - return result - - def _get_c_name(self): - return self.c_name_with_marker.replace('&', '') - - def has_c_name(self): - return '$' not in self._get_c_name() - - def is_integer_type(self): - return False - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - try: - BType = ffi._cached_btypes[self] - except KeyError: - BType = self.build_backend_type(ffi, finishlist) - BType2 = ffi._cached_btypes.setdefault(self, BType) - assert BType2 is BType - return BType - - def __repr__(self): - return '<%s>' % (self._get_c_name(),) - - def _get_items(self): - return [(name, getattr(self, name)) for name in self._attrs_] - - -class BaseType(BaseTypeByIdentity): - - def __eq__(self, other): - return (self.__class__ == other.__class__ and - self._get_items() == other._get_items()) - - def __ne__(self, other): - return not self == other - - def __hash__(self): - return hash((self.__class__, tuple(self._get_items()))) - - -class VoidType(BaseType): - _attrs_ = () - - def __init__(self): - self.c_name_with_marker = 'void&' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_void_type') - -void_type = VoidType() - - -class BasePrimitiveType(BaseType): - def is_complex_type(self): - return False - - -class PrimitiveType(BasePrimitiveType): - _attrs_ = ('name',) - - ALL_PRIMITIVE_TYPES = { - 'char': 'c', - 'short': 'i', - 'int': 'i', - 'long': 'i', - 'long long': 'i', - 'signed char': 'i', - 'unsigned char': 'i', - 'unsigned short': 'i', - 'unsigned int': 'i', - 'unsigned long': 'i', - 'unsigned long long': 'i', - 'float': 'f', - 'double': 'f', - 'long double': 'f', - 'float _Complex': 'j', - 'double _Complex': 'j', - '_Bool': 'i', - # the following types are not primitive in the C sense - 'wchar_t': 'c', - 'char16_t': 'c', - 'char32_t': 'c', - 'int8_t': 'i', - 'uint8_t': 'i', - 'int16_t': 'i', - 'uint16_t': 'i', - 'int32_t': 'i', - 'uint32_t': 'i', - 'int64_t': 'i', - 'uint64_t': 'i', - 'int_least8_t': 'i', - 'uint_least8_t': 'i', - 'int_least16_t': 'i', - 'uint_least16_t': 'i', - 'int_least32_t': 'i', - 'uint_least32_t': 'i', - 'int_least64_t': 'i', - 'uint_least64_t': 'i', - 'int_fast8_t': 'i', - 'uint_fast8_t': 'i', - 'int_fast16_t': 'i', - 'uint_fast16_t': 'i', - 'int_fast32_t': 'i', - 'uint_fast32_t': 'i', - 'int_fast64_t': 'i', - 'uint_fast64_t': 'i', - 'intptr_t': 'i', - 'uintptr_t': 'i', - 'intmax_t': 'i', - 'uintmax_t': 'i', - 'ptrdiff_t': 'i', - 'size_t': 'i', - 'ssize_t': 'i', - } - - def __init__(self, name): - assert name in self.ALL_PRIMITIVE_TYPES - self.name = name - self.c_name_with_marker = name + '&' - - def is_char_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' - def is_integer_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' - def is_float_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' - def is_complex_type(self): - return self.ALL_PRIMITIVE_TYPES[self.name] == 'j' - - def build_backend_type(self, ffi, finishlist): - return global_cache(self, ffi, 'new_primitive_type', self.name) - - -class UnknownIntegerType(BasePrimitiveType): - _attrs_ = ('name',) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def is_integer_type(self): - return True - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("integer type '%s' can only be used after " - "compilation" % self.name) - -class UnknownFloatType(BasePrimitiveType): - _attrs_ = ('name', ) - - def __init__(self, name): - self.name = name - self.c_name_with_marker = name + '&' - - def build_backend_type(self, ffi, finishlist): - raise NotImplementedError("float type '%s' can only be used after " - "compilation" % self.name) - - -class BaseFunctionType(BaseType): - _attrs_ = ('args', 'result', 'ellipsis', 'abi') - - def __init__(self, args, result, ellipsis, abi=None): - self.args = args - self.result = result - self.ellipsis = ellipsis - self.abi = abi - # - reprargs = [arg._get_c_name() for arg in self.args] - if self.ellipsis: - reprargs.append('...') - reprargs = reprargs or ['void'] - replace_with = self._base_pattern % (', '.join(reprargs),) - if abi is not None: - replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] - self.c_name_with_marker = ( - self.result.c_name_with_marker.replace('&', replace_with)) - - -class RawFunctionType(BaseFunctionType): - # Corresponds to a C type like 'int(int)', which is the C type of - # a function, but not a pointer-to-function. The backend has no - # notion of such a type; it's used temporarily by parsing. - _base_pattern = '(&)(%s)' - is_raw_function = True - - def build_backend_type(self, ffi, finishlist): - raise CDefError("cannot render the type %r: it is a function " - "type, not a pointer-to-function type" % (self,)) - - def as_function_pointer(self): - return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) - - -class FunctionPtrType(BaseFunctionType): - _base_pattern = '(*&)(%s)' - - def build_backend_type(self, ffi, finishlist): - result = self.result.get_cached_btype(ffi, finishlist) - args = [] - for tp in self.args: - args.append(tp.get_cached_btype(ffi, finishlist)) - abi_args = () - if self.abi == "__stdcall": - if not self.ellipsis: # __stdcall ignored for variadic funcs - try: - abi_args = (ffi._backend.FFI_STDCALL,) - except AttributeError: - pass - return global_cache(self, ffi, 'new_function_type', - tuple(args), result, self.ellipsis, *abi_args) - - def as_raw_function(self): - return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) - - -class PointerType(BaseType): - _attrs_ = ('totype', 'quals') - - def __init__(self, totype, quals=0): - self.totype = totype - self.quals = quals - extra = qualify(quals, " *&") - if totype.is_array_type: - extra = "(%s)" % (extra.lstrip(),) - self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) - - def build_backend_type(self, ffi, finishlist): - BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) - return global_cache(self, ffi, 'new_pointer_type', BItem) - -voidp_type = PointerType(void_type) - -def ConstPointerType(totype): - return PointerType(totype, Q_CONST) - -const_voidp_type = ConstPointerType(void_type) - - -class NamedPointerType(PointerType): - _attrs_ = ('totype', 'name') - - def __init__(self, totype, name, quals=0): - PointerType.__init__(self, totype, quals) - self.name = name - self.c_name_with_marker = name + '&' - - -class ArrayType(BaseType): - _attrs_ = ('item', 'length') - is_array_type = True - - def __init__(self, item, length): - self.item = item - self.length = length - # - if length is None: - brackets = '&[]' - elif length == '...': - brackets = '&[/*...*/]' - else: - brackets = '&[%s]' % length - self.c_name_with_marker = ( - self.item.c_name_with_marker.replace('&', brackets)) - - def length_is_unknown(self): - return isinstance(self.length, str) - - def resolve_length(self, newlength): - return ArrayType(self.item, newlength) - - def build_backend_type(self, ffi, finishlist): - if self.length_is_unknown(): - raise CDefError("cannot render the type %r: unknown length" % - (self,)) - self.item.get_cached_btype(ffi, finishlist) # force the item BType - BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) - return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) - -char_array_type = ArrayType(PrimitiveType('char'), None) - - -class StructOrUnionOrEnum(BaseTypeByIdentity): - _attrs_ = ('name',) - forcename = None - - def build_c_name_with_marker(self): - name = self.forcename or '%s %s' % (self.kind, self.name) - self.c_name_with_marker = name + '&' - - def force_the_name(self, forcename): - self.forcename = forcename - self.build_c_name_with_marker() - - def get_official_name(self): - assert self.c_name_with_marker.endswith('&') - return self.c_name_with_marker[:-1] - - -class StructOrUnion(StructOrUnionOrEnum): - fixedlayout = None - completed = 0 - partial = False - packed = 0 - - def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): - self.name = name - self.fldnames = fldnames - self.fldtypes = fldtypes - self.fldbitsize = fldbitsize - self.fldquals = fldquals - self.build_c_name_with_marker() - - def anonymous_struct_fields(self): - if self.fldtypes is not None: - for name, type in zip(self.fldnames, self.fldtypes): - if name == '' and isinstance(type, StructOrUnion): - yield type - - def enumfields(self, expand_anonymous_struct_union=True): - fldquals = self.fldquals - if fldquals is None: - fldquals = (0,) * len(self.fldnames) - for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, - self.fldbitsize, fldquals): - if (name == '' and isinstance(type, StructOrUnion) - and expand_anonymous_struct_union): - # nested anonymous struct/union - for result in type.enumfields(): - yield result - else: - yield (name, type, bitsize, quals) - - def force_flatten(self): - # force the struct or union to have a declaration that lists - # directly all fields returned by enumfields(), flattening - # nested anonymous structs/unions. - names = [] - types = [] - bitsizes = [] - fldquals = [] - for name, type, bitsize, quals in self.enumfields(): - names.append(name) - types.append(type) - bitsizes.append(bitsize) - fldquals.append(quals) - self.fldnames = tuple(names) - self.fldtypes = tuple(types) - self.fldbitsize = tuple(bitsizes) - self.fldquals = tuple(fldquals) - - def get_cached_btype(self, ffi, finishlist, can_delay=False): - BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, - can_delay) - if not can_delay: - self.finish_backend_type(ffi, finishlist) - return BType - - def finish_backend_type(self, ffi, finishlist): - if self.completed: - if self.completed != 2: - raise NotImplementedError("recursive structure declaration " - "for '%s'" % (self.name,)) - return - BType = ffi._cached_btypes[self] - # - self.completed = 1 - # - if self.fldtypes is None: - pass # not completing it: it's an opaque struct - # - elif self.fixedlayout is None: - fldtypes = [tp.get_cached_btype(ffi, finishlist) - for tp in self.fldtypes] - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) - extra_flags = () - if self.packed: - if self.packed == 1: - extra_flags = (8,) # SF_PACKED - else: - extra_flags = (0, self.packed) - ffi._backend.complete_struct_or_union(BType, lst, self, - -1, -1, *extra_flags) - # - else: - fldtypes = [] - fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout - for i in range(len(self.fldnames)): - fsize = fieldsize[i] - ftype = self.fldtypes[i] - # - if isinstance(ftype, ArrayType) and ftype.length_is_unknown(): - # fix the length to match the total size - BItemType = ftype.item.get_cached_btype(ffi, finishlist) - nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) - if nrest != 0: - self._verification_error( - "field '%s.%s' has a bogus size?" % ( - self.name, self.fldnames[i] or '{}')) - ftype = ftype.resolve_length(nlen) - self.fldtypes = (self.fldtypes[:i] + (ftype,) + - self.fldtypes[i+1:]) - # - BFieldType = ftype.get_cached_btype(ffi, finishlist) - if isinstance(ftype, ArrayType) and ftype.length is None: - assert fsize == 0 - else: - bitemsize = ffi.sizeof(BFieldType) - if bitemsize != fsize: - self._verification_error( - "field '%s.%s' is declared as %d bytes, but is " - "really %d bytes" % (self.name, - self.fldnames[i] or '{}', - bitemsize, fsize)) - fldtypes.append(BFieldType) - # - lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) - ffi._backend.complete_struct_or_union(BType, lst, self, - totalsize, totalalignment) - self.completed = 2 - - def _verification_error(self, msg): - raise VerificationError(msg) - - def check_not_partial(self): - if self.partial and self.fixedlayout is None: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - finishlist.append(self) - # - return global_cache(self, ffi, 'new_%s_type' % self.kind, - self.get_official_name(), key=self) - - -class StructType(StructOrUnion): - kind = 'struct' - - -class UnionType(StructOrUnion): - kind = 'union' - - -class EnumType(StructOrUnionOrEnum): - kind = 'enum' - partial = False - partial_resolved = False - - def __init__(self, name, enumerators, enumvalues, baseinttype=None): - self.name = name - self.enumerators = enumerators - self.enumvalues = enumvalues - self.baseinttype = baseinttype - self.build_c_name_with_marker() - - def force_the_name(self, forcename): - StructOrUnionOrEnum.force_the_name(self, forcename) - if self.forcename is None: - name = self.get_official_name() - self.forcename = '$' + name.replace(' ', '_') - - def check_not_partial(self): - if self.partial and not self.partial_resolved: - raise VerificationMissing(self._get_c_name()) - - def build_backend_type(self, ffi, finishlist): - self.check_not_partial() - base_btype = self.build_baseinttype(ffi, finishlist) - return global_cache(self, ffi, 'new_enum_type', - self.get_official_name(), - self.enumerators, self.enumvalues, - base_btype, key=self) - - def build_baseinttype(self, ffi, finishlist): - if self.baseinttype is not None: - return self.baseinttype.get_cached_btype(ffi, finishlist) - # - if self.enumvalues: - smallest_value = min(self.enumvalues) - largest_value = max(self.enumvalues) - else: - import warnings - try: - # XXX! The goal is to ensure that the warnings.warn() - # will not suppress the warning. We want to get it - # several times if we reach this point several times. - __warningregistry__.clear() - except NameError: - pass - warnings.warn("%r has no values explicitly defined; " - "guessing that it is equivalent to 'unsigned int'" - % self._get_c_name()) - smallest_value = largest_value = 0 - if smallest_value < 0: # needs a signed type - sign = 1 - candidate1 = PrimitiveType("int") - candidate2 = PrimitiveType("long") - else: - sign = 0 - candidate1 = PrimitiveType("unsigned int") - candidate2 = PrimitiveType("unsigned long") - btype1 = candidate1.get_cached_btype(ffi, finishlist) - btype2 = candidate2.get_cached_btype(ffi, finishlist) - size1 = ffi.sizeof(btype1) - size2 = ffi.sizeof(btype2) - if (smallest_value >= ((-1) << (8*size1-1)) and - largest_value < (1 << (8*size1-sign))): - return btype1 - if (smallest_value >= ((-1) << (8*size2-1)) and - largest_value < (1 << (8*size2-sign))): - return btype2 - raise CDefError("%s values don't all fit into either 'long' " - "or 'unsigned long'" % self._get_c_name()) - -def unknown_type(name, structname=None): - if structname is None: - structname = '$%s' % name - tp = StructType(structname, None, None, None) - tp.force_the_name(name) - tp.origin = "unknown_type" - return tp - -def unknown_ptr_type(name, structname=None): - if structname is None: - structname = '$$%s' % name - tp = StructType(structname, None, None, None) - return NamedPointerType(tp, name) - - -global_lock = allocate_lock() -_typecache_cffi_backend = weakref.WeakValueDictionary() - -def get_typecache(backend): - # returns _typecache_cffi_backend if backend is the _cffi_backend - # module, or type(backend).__typecache if backend is an instance of - # CTypesBackend (or some FakeBackend class during tests) - if isinstance(backend, types.ModuleType): - return _typecache_cffi_backend - with global_lock: - if not hasattr(type(backend), '__typecache'): - type(backend).__typecache = weakref.WeakValueDictionary() - return type(backend).__typecache - -def global_cache(srctype, ffi, funcname, *args, **kwds): - key = kwds.pop('key', (funcname, args)) - assert not kwds - try: - return ffi._typecache[key] - except KeyError: - pass - try: - res = getattr(ffi._backend, funcname)(*args) - except NotImplementedError as e: - raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) - # note that setdefault() on WeakValueDictionary is not atomic - # and contains a rare bug (http://bugs.python.org/issue19542); - # we have to use a lock and do it ourselves - cache = ffi._typecache - with global_lock: - res1 = cache.get(key) - if res1 is None: - cache[key] = res - return res - else: - return res1 - -def pointer_cache(ffi, BType): - return global_cache('?', ffi, 'new_pointer_type', BType) - -def attach_exception_info(e, name): - if e.args and type(e.args[0]) is str: - e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h b/dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h deleted file mode 100644 index ea1aa24ee..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/parse_c_type.h +++ /dev/null @@ -1,181 +0,0 @@ - -/* This part is from file 'cffi/parse_c_type.h'. It is copied at the - beginning of C sources generated by CFFI's ffi.set_source(). */ - -typedef void *_cffi_opcode_t; - -#define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) -#define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) -#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) - -#define _CFFI_OP_PRIMITIVE 1 -#define _CFFI_OP_POINTER 3 -#define _CFFI_OP_ARRAY 5 -#define _CFFI_OP_OPEN_ARRAY 7 -#define _CFFI_OP_STRUCT_UNION 9 -#define _CFFI_OP_ENUM 11 -#define _CFFI_OP_FUNCTION 13 -#define _CFFI_OP_FUNCTION_END 15 -#define _CFFI_OP_NOOP 17 -#define _CFFI_OP_BITFIELD 19 -#define _CFFI_OP_TYPENAME 21 -#define _CFFI_OP_CPYTHON_BLTN_V 23 // varargs -#define _CFFI_OP_CPYTHON_BLTN_N 25 // noargs -#define _CFFI_OP_CPYTHON_BLTN_O 27 // O (i.e. a single arg) -#define _CFFI_OP_CONSTANT 29 -#define _CFFI_OP_CONSTANT_INT 31 -#define _CFFI_OP_GLOBAL_VAR 33 -#define _CFFI_OP_DLOPEN_FUNC 35 -#define _CFFI_OP_DLOPEN_CONST 37 -#define _CFFI_OP_GLOBAL_VAR_F 39 -#define _CFFI_OP_EXTERN_PYTHON 41 - -#define _CFFI_PRIM_VOID 0 -#define _CFFI_PRIM_BOOL 1 -#define _CFFI_PRIM_CHAR 2 -#define _CFFI_PRIM_SCHAR 3 -#define _CFFI_PRIM_UCHAR 4 -#define _CFFI_PRIM_SHORT 5 -#define _CFFI_PRIM_USHORT 6 -#define _CFFI_PRIM_INT 7 -#define _CFFI_PRIM_UINT 8 -#define _CFFI_PRIM_LONG 9 -#define _CFFI_PRIM_ULONG 10 -#define _CFFI_PRIM_LONGLONG 11 -#define _CFFI_PRIM_ULONGLONG 12 -#define _CFFI_PRIM_FLOAT 13 -#define _CFFI_PRIM_DOUBLE 14 -#define _CFFI_PRIM_LONGDOUBLE 15 - -#define _CFFI_PRIM_WCHAR 16 -#define _CFFI_PRIM_INT8 17 -#define _CFFI_PRIM_UINT8 18 -#define _CFFI_PRIM_INT16 19 -#define _CFFI_PRIM_UINT16 20 -#define _CFFI_PRIM_INT32 21 -#define _CFFI_PRIM_UINT32 22 -#define _CFFI_PRIM_INT64 23 -#define _CFFI_PRIM_UINT64 24 -#define _CFFI_PRIM_INTPTR 25 -#define _CFFI_PRIM_UINTPTR 26 -#define _CFFI_PRIM_PTRDIFF 27 -#define _CFFI_PRIM_SIZE 28 -#define _CFFI_PRIM_SSIZE 29 -#define _CFFI_PRIM_INT_LEAST8 30 -#define _CFFI_PRIM_UINT_LEAST8 31 -#define _CFFI_PRIM_INT_LEAST16 32 -#define _CFFI_PRIM_UINT_LEAST16 33 -#define _CFFI_PRIM_INT_LEAST32 34 -#define _CFFI_PRIM_UINT_LEAST32 35 -#define _CFFI_PRIM_INT_LEAST64 36 -#define _CFFI_PRIM_UINT_LEAST64 37 -#define _CFFI_PRIM_INT_FAST8 38 -#define _CFFI_PRIM_UINT_FAST8 39 -#define _CFFI_PRIM_INT_FAST16 40 -#define _CFFI_PRIM_UINT_FAST16 41 -#define _CFFI_PRIM_INT_FAST32 42 -#define _CFFI_PRIM_UINT_FAST32 43 -#define _CFFI_PRIM_INT_FAST64 44 -#define _CFFI_PRIM_UINT_FAST64 45 -#define _CFFI_PRIM_INTMAX 46 -#define _CFFI_PRIM_UINTMAX 47 -#define _CFFI_PRIM_FLOATCOMPLEX 48 -#define _CFFI_PRIM_DOUBLECOMPLEX 49 -#define _CFFI_PRIM_CHAR16 50 -#define _CFFI_PRIM_CHAR32 51 - -#define _CFFI__NUM_PRIM 52 -#define _CFFI__UNKNOWN_PRIM (-1) -#define _CFFI__UNKNOWN_FLOAT_PRIM (-2) -#define _CFFI__UNKNOWN_LONG_DOUBLE (-3) - -#define _CFFI__IO_FILE_STRUCT (-1) - - -struct _cffi_global_s { - const char *name; - void *address; - _cffi_opcode_t type_op; - void *size_or_direct_fn; // OP_GLOBAL_VAR: size, or 0 if unknown - // OP_CPYTHON_BLTN_*: addr of direct function -}; - -struct _cffi_getconst_s { - unsigned long long value; - const struct _cffi_type_context_s *ctx; - int gindex; -}; - -struct _cffi_struct_union_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_STRUCT_UNION - int flags; // _CFFI_F_* flags below - size_t size; - int alignment; - int first_field_index; // -> _cffi_fields array - int num_fields; -}; -#define _CFFI_F_UNION 0x01 // is a union, not a struct -#define _CFFI_F_CHECK_FIELDS 0x02 // complain if fields are not in the - // "standard layout" or if some are missing -#define _CFFI_F_PACKED 0x04 // for CHECK_FIELDS, assume a packed struct -#define _CFFI_F_EXTERNAL 0x08 // in some other ffi.include() -#define _CFFI_F_OPAQUE 0x10 // opaque - -struct _cffi_field_s { - const char *name; - size_t field_offset; - size_t field_size; - _cffi_opcode_t field_type_op; -}; - -struct _cffi_enum_s { - const char *name; - int type_index; // -> _cffi_types, on a OP_ENUM - int type_prim; // _CFFI_PRIM_xxx - const char *enumerators; // comma-delimited string -}; - -struct _cffi_typename_s { - const char *name; - int type_index; /* if opaque, points to a possibly artificial - OP_STRUCT which is itself opaque */ -}; - -struct _cffi_type_context_s { - _cffi_opcode_t *types; - const struct _cffi_global_s *globals; - const struct _cffi_field_s *fields; - const struct _cffi_struct_union_s *struct_unions; - const struct _cffi_enum_s *enums; - const struct _cffi_typename_s *typenames; - int num_globals; - int num_struct_unions; - int num_enums; - int num_typenames; - const char *const *includes; - int num_types; - int flags; /* future extension */ -}; - -struct _cffi_parse_info_s { - const struct _cffi_type_context_s *ctx; - _cffi_opcode_t *output; - unsigned int output_size; - size_t error_location; - const char *error_message; -}; - -struct _cffi_externpy_s { - const char *name; - size_t size_of_result; - void *reserved1, *reserved2; -}; - -#ifdef _CFFI_INTERNAL -static int parse_c_type(struct _cffi_parse_info_s *info, const char *input); -static int search_in_globals(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -static int search_in_struct_unions(const struct _cffi_type_context_s *ctx, - const char *search, size_t search_len); -#endif diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py deleted file mode 100644 index 89708a504..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/pkgconfig.py +++ /dev/null @@ -1,121 +0,0 @@ -# pkg-config, https://www.freedesktop.org/wiki/Software/pkg-config/ integration for cffi -import sys, os, subprocess - -from .error import PkgConfigError - - -def merge_flags(cfg1, cfg2): - """Merge values from cffi config flags cfg2 to cf1 - - Example: - merge_flags({"libraries": ["one"]}, {"libraries": ["two"]}) - {"libraries": ["one", "two"]} - """ - for key, value in cfg2.items(): - if key not in cfg1: - cfg1[key] = value - else: - if not isinstance(cfg1[key], list): - raise TypeError("cfg1[%r] should be a list of strings" % (key,)) - if not isinstance(value, list): - raise TypeError("cfg2[%r] should be a list of strings" % (key,)) - cfg1[key].extend(value) - return cfg1 - - -def call(libname, flag, encoding=sys.getfilesystemencoding()): - """Calls pkg-config and returns the output if found - """ - a = ["pkg-config", "--print-errors"] - a.append(flag) - a.append(libname) - try: - pc = subprocess.Popen(a, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - except EnvironmentError as e: - raise PkgConfigError("cannot run pkg-config: %s" % (str(e).strip(),)) - - bout, berr = pc.communicate() - if pc.returncode != 0: - try: - berr = berr.decode(encoding) - except Exception: - pass - raise PkgConfigError(berr.strip()) - - if sys.version_info >= (3,) and not isinstance(bout, str): # Python 3.x - try: - bout = bout.decode(encoding) - except UnicodeDecodeError: - raise PkgConfigError("pkg-config %s %s returned bytes that cannot " - "be decoded with encoding %r:\n%r" % - (flag, libname, encoding, bout)) - - if os.altsep != '\\' and '\\' in bout: - raise PkgConfigError("pkg-config %s %s returned an unsupported " - "backslash-escaped output:\n%r" % - (flag, libname, bout)) - return bout - - -def flags_from_pkgconfig(libs): - r"""Return compiler line flags for FFI.set_source based on pkg-config output - - Usage - ... - ffibuilder.set_source("_foo", pkgconfig = ["libfoo", "libbar >= 1.8.3"]) - - If pkg-config is installed on build machine, then arguments include_dirs, - library_dirs, libraries, define_macros, extra_compile_args and - extra_link_args are extended with an output of pkg-config for libfoo and - libbar. - - Raises PkgConfigError in case the pkg-config call fails. - """ - - def get_include_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-I")] - - def get_library_dirs(string): - return [x[2:] for x in string.split() if x.startswith("-L")] - - def get_libraries(string): - return [x[2:] for x in string.split() if x.startswith("-l")] - - # convert -Dfoo=bar to list of tuples [("foo", "bar")] expected by distutils - def get_macros(string): - def _macro(x): - x = x[2:] # drop "-D" - if '=' in x: - return tuple(x.split("=", 1)) # "-Dfoo=bar" => ("foo", "bar") - else: - return (x, None) # "-Dfoo" => ("foo", None) - return [_macro(x) for x in string.split() if x.startswith("-D")] - - def get_other_cflags(string): - return [x for x in string.split() if not x.startswith("-I") and - not x.startswith("-D")] - - def get_other_libs(string): - return [x for x in string.split() if not x.startswith("-L") and - not x.startswith("-l")] - - # return kwargs for given libname - def kwargs(libname): - fse = sys.getfilesystemencoding() - all_cflags = call(libname, "--cflags") - all_libs = call(libname, "--libs") - return { - "include_dirs": get_include_dirs(all_cflags), - "library_dirs": get_library_dirs(all_libs), - "libraries": get_libraries(all_libs), - "define_macros": get_macros(all_cflags), - "extra_compile_args": get_other_cflags(all_cflags), - "extra_link_args": get_other_libs(all_libs), - } - - # merge all arguments together - ret = {} - for libname in libs: - lib_flags = kwargs(libname) - merge_flags(ret, lib_flags) - return ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py deleted file mode 100644 index 4e33dde21..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/recompiler.py +++ /dev/null @@ -1,1581 +0,0 @@ -import os, sys, io -from . import ffiplatform, model -from .error import VerificationError -from .cffi_opcode import * - -VERSION_BASE = 0x2601 -VERSION_EMBEDDED = 0x2701 -VERSION_CHAR16CHAR32 = 0x2801 - -USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or - sys.version_info >= (3, 5)) - - -class GlobalExpr: - def __init__(self, name, address, type_op, size=0, check_value=0): - self.name = name - self.address = address - self.type_op = type_op - self.size = size - self.check_value = check_value - - def as_c_expr(self): - return ' { "%s", (void *)%s, %s, (void *)%s },' % ( - self.name, self.address, self.type_op.as_c_expr(), self.size) - - def as_python_expr(self): - return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, - self.check_value) - -class FieldExpr: - def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): - self.name = name - self.field_offset = field_offset - self.field_size = field_size - self.fbitsize = fbitsize - self.field_type_op = field_type_op - - def as_c_expr(self): - spaces = " " * len(self.name) - return (' { "%s", %s,\n' % (self.name, self.field_offset) + - ' %s %s,\n' % (spaces, self.field_size) + - ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) - - def as_python_expr(self): - raise NotImplementedError - - def as_field_python_expr(self): - if self.field_type_op.op == OP_NOOP: - size_expr = '' - elif self.field_type_op.op == OP_BITFIELD: - size_expr = format_four_bytes(self.fbitsize) - else: - raise NotImplementedError - return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), - size_expr, - self.name) - -class StructUnionExpr: - def __init__(self, name, type_index, flags, size, alignment, comment, - first_field_index, c_fields): - self.name = name - self.type_index = type_index - self.flags = flags - self.size = size - self.alignment = alignment - self.comment = comment - self.first_field_index = first_field_index - self.c_fields = c_fields - - def as_c_expr(self): - return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) - + '\n %s, %s, ' % (self.size, self.alignment) - + '%d, %d ' % (self.first_field_index, len(self.c_fields)) - + ('/* %s */ ' % self.comment if self.comment else '') - + '},') - - def as_python_expr(self): - flags = eval(self.flags, G_FLAGS) - fields_expr = [c_field.as_field_python_expr() - for c_field in self.c_fields] - return "(b'%s%s%s',%s)" % ( - format_four_bytes(self.type_index), - format_four_bytes(flags), - self.name, - ','.join(fields_expr)) - -class EnumExpr: - def __init__(self, name, type_index, size, signed, allenums): - self.name = name - self.type_index = type_index - self.size = size - self.signed = signed - self.allenums = allenums - - def as_c_expr(self): - return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' - ' "%s" },' % (self.name, self.type_index, - self.size, self.signed, self.allenums)) - - def as_python_expr(self): - prim_index = { - (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, - (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, - (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, - (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, - }[self.size, self.signed] - return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), - format_four_bytes(prim_index), - self.name, self.allenums) - -class TypenameExpr: - def __init__(self, name, type_index): - self.name = name - self.type_index = type_index - - def as_c_expr(self): - return ' { "%s", %d },' % (self.name, self.type_index) - - def as_python_expr(self): - return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) - - -# ____________________________________________________________ - - -class Recompiler: - _num_externpy = 0 - - def __init__(self, ffi, module_name, target_is_python=False): - self.ffi = ffi - self.module_name = module_name - self.target_is_python = target_is_python - self._version = VERSION_BASE - - def needs_version(self, ver): - self._version = max(self._version, ver) - - def collect_type_table(self): - self._typesdict = {} - self._generate("collecttype") - # - all_decls = sorted(self._typesdict, key=str) - # - # prepare all FUNCTION bytecode sequences first - self.cffi_types = [] - for tp in all_decls: - if tp.is_raw_function: - assert self._typesdict[tp] is None - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - for tp1 in tp.args: - assert isinstance(tp1, (model.VoidType, - model.BasePrimitiveType, - model.PointerType, - model.StructOrUnionOrEnum, - model.FunctionPtrType)) - if self._typesdict[tp1] is None: - self._typesdict[tp1] = len(self.cffi_types) - self.cffi_types.append(tp1) # placeholder - self.cffi_types.append('END') # placeholder - # - # prepare all OTHER bytecode sequences - for tp in all_decls: - if not tp.is_raw_function and self._typesdict[tp] is None: - self._typesdict[tp] = len(self.cffi_types) - self.cffi_types.append(tp) # placeholder - if tp.is_array_type and tp.length is not None: - self.cffi_types.append('LEN') # placeholder - assert None not in self._typesdict.values() - # - # collect all structs and unions and enums - self._struct_unions = {} - self._enums = {} - for tp in all_decls: - if isinstance(tp, model.StructOrUnion): - self._struct_unions[tp] = None - elif isinstance(tp, model.EnumType): - self._enums[tp] = None - for i, tp in enumerate(sorted(self._struct_unions, - key=lambda tp: tp.name)): - self._struct_unions[tp] = i - for i, tp in enumerate(sorted(self._enums, - key=lambda tp: tp.name)): - self._enums[tp] = i - # - # emit all bytecode sequences now - for tp in all_decls: - method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) - method(tp, self._typesdict[tp]) - # - # consistency check - for op in self.cffi_types: - assert isinstance(op, CffiOp) - self.cffi_types = tuple(self.cffi_types) # don't change any more - - def _enum_fields(self, tp): - # When producing C, expand all anonymous struct/union fields. - # That's necessary to have C code checking the offsets of the - # individual fields contained in them. When producing Python, - # don't do it and instead write it like it is, with the - # corresponding fields having an empty name. Empty names are - # recognized at runtime when we import the generated Python - # file. - expand_anonymous_struct_union = not self.target_is_python - return tp.enumfields(expand_anonymous_struct_union) - - def _do_collect_type(self, tp): - if not isinstance(tp, model.BaseTypeByIdentity): - if isinstance(tp, tuple): - for x in tp: - self._do_collect_type(x) - return - if tp not in self._typesdict: - self._typesdict[tp] = None - if isinstance(tp, model.FunctionPtrType): - self._do_collect_type(tp.as_raw_function()) - elif isinstance(tp, model.StructOrUnion): - if tp.fldtypes is not None and ( - tp not in self.ffi._parser._included_declarations): - for name1, tp1, _, _ in self._enum_fields(tp): - self._do_collect_type(self._field_type(tp, name1, tp1)) - else: - for _, x in tp._get_items(): - self._do_collect_type(x) - - def _generate(self, step_name): - lst = self.ffi._parser._declarations.items() - for name, (tp, quals) in sorted(lst): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in recompile(): %r" % name) - try: - self._current_quals = quals - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - # ---------- - - ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] - - def collect_step_tables(self): - # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. - self._lsts = {} - for step_name in self.ALL_STEPS: - self._lsts[step_name] = [] - self._seen_struct_unions = set() - self._generate("ctx") - self._add_missing_struct_unions() - # - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if step_name != "field": - lst.sort(key=lambda entry: entry.name) - self._lsts[step_name] = tuple(lst) # don't change any more - # - # check for a possible internal inconsistency: _cffi_struct_unions - # should have been generated with exactly self._struct_unions - lst = self._lsts["struct_union"] - for tp, i in self._struct_unions.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._struct_unions) - # same with enums - lst = self._lsts["enum"] - for tp, i in self._enums.items(): - assert i < len(lst) - assert lst[i].name == tp.name - assert len(lst) == len(self._enums) - - # ---------- - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self, f, preamble): - if self.target_is_python: - assert preamble is None - self.write_py_source_to_f(f) - else: - assert preamble is not None - self.write_c_source_to_f(f, preamble) - - def _rel_readlines(self, filename): - g = open(os.path.join(os.path.dirname(__file__), filename), 'r') - lines = g.readlines() - g.close() - return lines - - def write_c_source_to_f(self, f, preamble): - self._f = f - prnt = self._prnt - if self.ffi._embedding is not None: - prnt('#define _CFFI_USE_EMBEDDING') - if not USE_LIMITED_API: - prnt('#define _CFFI_NO_LIMITED_API') - # - # first the '#include' (actually done by inlining the file's content) - lines = self._rel_readlines('_cffi_include.h') - i = lines.index('#include "parse_c_type.h"\n') - lines[i:i+1] = self._rel_readlines('parse_c_type.h') - prnt(''.join(lines)) - # - # if we have ffi._embedding != None, we give it here as a macro - # and include an extra file - base_module_name = self.module_name.split('.')[-1] - if self.ffi._embedding is not None: - prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) - prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {') - self._print_string_literal_in_array(self.ffi._embedding) - prnt('0 };') - prnt('#ifdef PYPY_VERSION') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( - base_module_name,)) - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( - base_module_name,)) - prnt('#else') - prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( - base_module_name,)) - prnt('#endif') - lines = self._rel_readlines('_embedding.h') - i = lines.index('#include "_cffi_errors.h"\n') - lines[i:i+1] = self._rel_readlines('_cffi_errors.h') - prnt(''.join(lines)) - self.needs_version(VERSION_EMBEDDED) - # - # then paste the C source given by the user, verbatim. - prnt('/************************************************************/') - prnt() - prnt(preamble) - prnt() - prnt('/************************************************************/') - prnt() - # - # the declaration of '_cffi_types' - prnt('static void *_cffi_types[] = {') - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - for i, op in enumerate(self.cffi_types): - comment = '' - if i in typeindex2type: - comment = ' // ' + typeindex2type[i]._get_c_name() - prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) - if not self.cffi_types: - prnt(' 0') - prnt('};') - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._seen_constants = set() - self._generate("decl") - # - # the declaration of '_cffi_globals' and '_cffi_typenames' - nums = {} - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - nums[step_name] = len(lst) - if nums[step_name] > 0: - prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( - step_name, step_name)) - for entry in lst: - prnt(entry.as_c_expr()) - prnt('};') - prnt() - # - # the declaration of '_cffi_includes' - if self.ffi._included_ffis: - prnt('static const char * const _cffi_includes[] = {') - for ffi_to_include in self.ffi._included_ffis: - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is None: - raise VerificationError( - "not implemented yet: ffi.include() of a Python-based " - "ffi inside a C-based ffi") - prnt(' "%s",' % (included_module_name,)) - prnt(' NULL') - prnt('};') - prnt() - # - # the declaration of '_cffi_type_context' - prnt('static const struct _cffi_type_context_s _cffi_type_context = {') - prnt(' _cffi_types,') - for step_name in self.ALL_STEPS: - if nums[step_name] > 0: - prnt(' _cffi_%ss,' % step_name) - else: - prnt(' NULL, /* no %ss */' % step_name) - for step_name in self.ALL_STEPS: - if step_name != "field": - prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) - if self.ffi._included_ffis: - prnt(' _cffi_includes,') - else: - prnt(' NULL, /* no includes */') - prnt(' %d, /* num_types */' % (len(self.cffi_types),)) - flags = 0 - if self._num_externpy > 0 or self.ffi._embedding is not None: - flags |= 1 # set to mean that we use extern "Python" - prnt(' %d, /* flags */' % flags) - prnt('};') - prnt() - # - # the init function - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility push(default) /* for -fvisibility= */') - prnt('#endif') - prnt() - prnt('#ifdef PYPY_VERSION') - prnt('PyMODINIT_FUNC') - prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) - prnt('{') - if flags & 1: - prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') - prnt(' _cffi_call_python_org = ' - '(void(*)(struct _cffi_externpy_s *, char *))p[1];') - prnt(' }') - prnt(' p[0] = (const void *)0x%x;' % self._version) - prnt(' p[1] = &_cffi_type_context;') - prnt('#if PY_MAJOR_VERSION >= 3') - prnt(' return NULL;') - prnt('#endif') - prnt('}') - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - prnt('# ifdef _MSC_VER') - prnt(' PyMODINIT_FUNC') - prnt('# if PY_MAJOR_VERSION >= 3') - prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) - prnt('# else') - prnt(' init%s(void) { }' % (base_module_name,)) - prnt('# endif') - prnt('# endif') - prnt('#elif PY_MAJOR_VERSION >= 3') - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % (base_module_name,)) - prnt('{') - prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#else') - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % (base_module_name,)) - prnt('{') - prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % ( - self.module_name, self._version)) - prnt('}') - prnt('#endif') - prnt() - prnt('#ifdef __GNUC__') - prnt('# pragma GCC visibility pop') - prnt('#endif') - self._version = None - - def _to_py(self, x): - if isinstance(x, str): - return "b'%s'" % (x,) - if isinstance(x, (list, tuple)): - rep = [self._to_py(item) for item in x] - if len(rep) == 1: - rep.append('') - return "(%s)" % (','.join(rep),) - return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. - - def write_py_source_to_f(self, f): - self._f = f - prnt = self._prnt - # - # header - prnt("# auto-generated file") - prnt("import _cffi_backend") - # - # the 'import' of the included ffis - num_includes = len(self.ffi._included_ffis or ()) - for i in range(num_includes): - ffi_to_include = self.ffi._included_ffis[i] - try: - included_module_name, included_source = ( - ffi_to_include._assigned_source[:2]) - except AttributeError: - raise VerificationError( - "ffi object %r includes %r, but the latter has not " - "been prepared with set_source()" % ( - self.ffi, ffi_to_include,)) - if included_source is not None: - raise VerificationError( - "not implemented yet: ffi.include() of a C-based " - "ffi inside a Python-based ffi") - prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) - prnt() - prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) - prnt(" _version = 0x%x," % (self._version,)) - self._version = None - # - # the '_types' keyword argument - self.cffi_types = tuple(self.cffi_types) # don't change any more - types_lst = [op.as_python_bytes() for op in self.cffi_types] - prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) - typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) - # - # the keyword arguments from ALL_STEPS - for step_name in self.ALL_STEPS: - lst = self._lsts[step_name] - if len(lst) > 0 and step_name != "field": - prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) - # - # the '_includes' keyword argument - if num_includes > 0: - prnt(' _includes = (%s,),' % ( - ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) - # - # the footer - prnt(')') - - # ---------- - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type(): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - elif isinstance(tp, model.UnknownFloatType): - # don't check with is_float_type(): it may be a 'long - # double' here, and _cffi_to_c_double would loose precision - converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) - else: - cname = tp.get_c_name('') - converter = '(%s)_cffi_to_c_%s' % (cname, - tp.name.replace(' ', '_')) - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif (isinstance(tp, model.StructOrUnionOrEnum) or - isinstance(tp, model.BasePrimitiveType)): - # a struct (not a struct pointer) as a function argument; - # or, a complex (the same code works) - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - '(%s)alloca((size_t)datasize) : NULL;' % ( - tovar, tp.get_c_name(''))) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.BasePrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif isinstance(tp, model.UnknownFloatType): - return '_cffi_from_c_double(%s)' % (var,) - elif tp.name != 'long double' and not tp.is_complex_type(): - cname = tp.name.replace(' ', '_') - if cname in ('char16_t', 'char32_t'): - self.needs_version(VERSION_CHAR16CHAR32) - return '_cffi_from_c_%s(%s)' % (cname, var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs - - def _typedef_type(self, tp, name): - return self._global_type(tp, "(*(%s *)0)" % (name,)) - - def _generate_cpy_typedef_collecttype(self, tp, name): - self._do_collect_type(self._typedef_type(tp, name)) - - def _generate_cpy_typedef_decl(self, tp, name): - pass - - def _typedef_ctx(self, tp, name): - type_index = self._typesdict[tp] - self._lsts["typename"].append(TypenameExpr(name, type_index)) - - def _generate_cpy_typedef_ctx(self, tp, name): - tp = self._typedef_type(tp, name) - self._typedef_ctx(tp, name) - if getattr(tp, "origin", None) == "unknown_type": - self._struct_ctx(tp, tp.name, approxname=None) - elif isinstance(tp, model.NamedPointerType): - self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, - named_ptr=tp) - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - self._do_collect_type(tp.as_raw_function()) - if tp.ellipsis and not self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_function_decl(self, tp, name): - assert not self.target_is_python - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_constant_decl(tp, name) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - # - # ------------------------------ - # the 'd' version of the function, only for addressof(lib, 'func') - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arguments.append(type.get_c_name(' x%d' % i, context)) - call_arguments.append('x%d' % i) - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) - prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) - prnt('{') - call_arguments = ', '.join(call_arguments) - result_code = 'return ' - if isinstance(tp.result, model.VoidType): - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, call_arguments)) - prnt('}') - # - prnt('#ifndef PYPY_VERSION') # ------------------------------ - # - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' x%d' % i, context) - prnt(' %s;' % arg) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - result_decl = ' %s;' % tp.result.get_c_name(' result', context) - prnt(result_decl) - prnt(' PyObject *pyresult;') - else: - result_decl = None - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % ( - name, len(rng), len(rng), - ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - call_arguments = ['x%d' % i for i in range(len(tp.args))] - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - # - prnt('#else') # ------------------------------ - # - # the PyPy version: need to replace struct/union arguments with - # pointers, and if the result is a struct/union, insert a first - # arg that is a pointer to the result. We also do that for - # complex args and return type. - def need_indirection(type): - return (isinstance(type, model.StructOrUnion) or - (isinstance(type, model.PrimitiveType) and - type.is_complex_type())) - difference = False - arguments = [] - call_arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - indirection = '' - if need_indirection(type): - indirection = '*' - difference = True - arg = type.get_c_name(' %sx%d' % (indirection, i), context) - arguments.append(arg) - call_arguments.append('%sx%d' % (indirection, i)) - tp_result = tp.result - if need_indirection(tp_result): - context = 'result of %s' % name - arg = tp_result.get_c_name(' *result', context) - arguments.insert(0, arg) - tp_result = model.void_type - result_decl = None - result_code = '*result = ' - difference = True - if difference: - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, - repr_arguments) - prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) - prnt('{') - if result_decl: - prnt(result_decl) - call_arguments = ', '.join(call_arguments) - prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) - if result_decl: - prnt(' return result;') - prnt('}') - else: - prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) - # - prnt('#endif') # ------------------------------ - prnt() - - def _generate_cpy_function_ctx(self, tp, name): - if tp.ellipsis and not self.target_is_python: - self._generate_cpy_constant_ctx(tp, name) - return - type_index = self._typesdict[tp.as_raw_function()] - numargs = len(tp.args) - if self.target_is_python: - meth_kind = OP_DLOPEN_FUNC - elif numargs == 0: - meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' - elif numargs == 1: - meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' - else: - meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' - self._lsts["global"].append( - GlobalExpr(name, '_cffi_f_%s' % name, - CffiOp(meth_kind, type_index), - size='_cffi_d_%s' % name)) - - # ---------- - # named structs or unions - - def _field_type(self, tp_struct, field_name, tp_field): - if isinstance(tp_field, model.ArrayType): - actual_length = tp_field.length - if actual_length == '...': - ptr_struct_name = tp_struct.get_c_name('*') - actual_length = '_cffi_array_len(((%s)0)->%s)' % ( - ptr_struct_name, field_name) - tp_item = self._field_type(tp_struct, '%s[0]' % field_name, - tp_field.item) - tp_field = model.ArrayType(tp_item, actual_length) - return tp_field - - def _struct_collecttype(self, tp): - self._do_collect_type(tp) - if self.target_is_python: - # also requires nested anon struct/unions in ABI mode, recursively - for fldtype in tp.anonymous_struct_fields(): - self._struct_collecttype(fldtype) - - def _struct_decl(self, tp, cname, approxname): - if tp.fldtypes is None: - return - prnt = self._prnt - checkfuncname = '_cffi_checkfld_%s' % (approxname,) - prnt('_CFFI_UNUSED_FN') - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in self._enum_fields(tp): - try: - if ftype.is_integer_type() or fbitsize >= 0: - # accept all integers, but complain on float or double - if fname != '': - prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " - "an integer */" % (fname, cname, fname)) - continue - # only accept exactly the type declared, except that '[]' - # is interpreted as a '*' and so will match any array length. - # (It would also match '*', but that's harder to detect...) - while (isinstance(ftype, model.ArrayType) - and (ftype.length is None or ftype.length == '...')): - ftype = ftype.item - fname = fname + '[0]' - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) - prnt() - - def _struct_ctx(self, tp, cname, approxname, named_ptr=None): - type_index = self._typesdict[tp] - reason_for_not_expanding = None - flags = [] - if isinstance(tp, model.UnionType): - flags.append("_CFFI_F_UNION") - if tp.fldtypes is None: - flags.append("_CFFI_F_OPAQUE") - reason_for_not_expanding = "opaque" - if (tp not in self.ffi._parser._included_declarations and - (named_ptr is None or - named_ptr not in self.ffi._parser._included_declarations)): - if tp.fldtypes is None: - pass # opaque - elif tp.partial or any(tp.anonymous_struct_fields()): - pass # field layout obtained silently from the C compiler - else: - flags.append("_CFFI_F_CHECK_FIELDS") - if tp.packed: - if tp.packed > 1: - raise NotImplementedError( - "%r is declared with 'pack=%r'; only 0 or 1 are " - "supported in API mode (try to use \"...;\", which " - "does not require a 'pack' declaration)" % - (tp, tp.packed)) - flags.append("_CFFI_F_PACKED") - else: - flags.append("_CFFI_F_EXTERNAL") - reason_for_not_expanding = "external" - flags = '|'.join(flags) or '0' - c_fields = [] - if reason_for_not_expanding is None: - enumfields = list(self._enum_fields(tp)) - for fldname, fldtype, fbitsize, fqual in enumfields: - fldtype = self._field_type(tp, fldname, fldtype) - self._check_not_opaque(fldtype, - "field '%s.%s'" % (tp.name, fldname)) - # cname is None for _add_missing_struct_unions() only - op = OP_NOOP - if fbitsize >= 0: - op = OP_BITFIELD - size = '%d /* bits */' % fbitsize - elif cname is None or ( - isinstance(fldtype, model.ArrayType) and - fldtype.length is None): - size = '(size_t)-1' - else: - size = 'sizeof(((%s)0)->%s)' % ( - tp.get_c_name('*') if named_ptr is None - else named_ptr.name, - fldname) - if cname is None or fbitsize >= 0: - offset = '(size_t)-1' - elif named_ptr is not None: - offset = '((char *)&((%s)0)->%s) - (char *)0' % ( - named_ptr.name, fldname) - else: - offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) - c_fields.append( - FieldExpr(fldname, offset, size, fbitsize, - CffiOp(op, self._typesdict[fldtype]))) - first_field_index = len(self._lsts["field"]) - self._lsts["field"].extend(c_fields) - # - if cname is None: # unknown name, for _add_missing_struct_unions - size = '(size_t)-2' - align = -2 - comment = "unnamed" - else: - if named_ptr is not None: - size = 'sizeof(*(%s)0)' % (named_ptr.name,) - align = '-1 /* unknown alignment */' - else: - size = 'sizeof(%s)' % (cname,) - align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) - comment = None - else: - size = '(size_t)-1' - align = -1 - first_field_index = -1 - comment = reason_for_not_expanding - self._lsts["struct_union"].append( - StructUnionExpr(tp.name, type_index, flags, size, align, comment, - first_field_index, c_fields)) - self._seen_struct_unions.add(tp) - - def _check_not_opaque(self, tp, location): - while isinstance(tp, model.ArrayType): - tp = tp.item - if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None: - raise TypeError( - "%s is of an opaque type (not declared in cdef())" % location) - - def _add_missing_struct_unions(self): - # not very nice, but some struct declarations might be missing - # because they don't have any known C name. Check that they are - # not partial (we can't complete or verify them!) and emit them - # anonymously. - lst = list(self._struct_unions.items()) - lst.sort(key=lambda tp_order: tp_order[1]) - for tp, order in lst: - if tp not in self._seen_struct_unions: - if tp.partial: - raise NotImplementedError("internal inconsistency: %r is " - "partial but was not seen at " - "this point" % (tp,)) - if tp.name.startswith('$') and tp.name[1:].isdigit(): - approxname = tp.name[1:] - elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': - approxname = 'FILE' - self._typedef_ctx(tp, 'FILE') - else: - raise NotImplementedError("internal inconsistency: %r" % - (tp,)) - self._struct_ctx(tp, None, approxname) - - def _generate_cpy_struct_collecttype(self, tp, name): - self._struct_collecttype(tp) - _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype - - def _struct_names(self, tp): - cname = tp.get_c_name('') - if ' ' in cname: - return cname, cname.replace(' ', '_') - else: - return cname, '_' + cname - - def _generate_cpy_struct_decl(self, tp, name): - self._struct_decl(tp, *self._struct_names(tp)) - _generate_cpy_union_decl = _generate_cpy_struct_decl - - def _generate_cpy_struct_ctx(self, tp, name): - self._struct_ctx(tp, *self._struct_names(tp)) - _generate_cpy_union_ctx = _generate_cpy_struct_ctx - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_cpy_anonymous_collecttype(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_collecttype(tp, name) - else: - self._struct_collecttype(tp) - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp) - else: - self._struct_decl(tp, name, 'typedef_' + name) - - def _generate_cpy_anonymous_ctx(self, tp, name): - if isinstance(tp, model.EnumType): - self._enum_ctx(tp, name) - else: - self._struct_ctx(tp, name, 'typedef_' + name) - - # ---------- - # constants, declared with "static const ..." - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - check_value=None): - if (category, name) in self._seen_constants: - raise VerificationError( - "duplicate declaration of %s '%s'" % (category, name)) - self._seen_constants.add((category, name)) - # - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - if is_int: - prnt('static int %s(unsigned long long *o)' % funcname) - prnt('{') - prnt(' int n = (%s) <= 0;' % (name,)) - prnt(' *o = (unsigned long long)((%s) | 0);' - ' /* check that %s is an integer */' % (name, name)) - if check_value is not None: - if check_value > 0: - check_value = '%dU' % (check_value,) - prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) - prnt(' n |= 2;') - prnt(' return n;') - prnt('}') - else: - assert check_value is None - prnt('static void %s(char *o)' % funcname) - prnt('{') - prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = tp.is_integer_type() - if not is_int or self.target_is_python: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - def _generate_cpy_constant_ctx(self, tp, name): - if not self.target_is_python and tp.is_integer_type(): - type_op = CffiOp(OP_CONSTANT_INT, -1) - else: - if self.target_is_python: - const_kind = OP_DLOPEN_CONST - else: - const_kind = OP_CONSTANT - type_index = self._typesdict[tp] - type_op = CffiOp(const_kind, type_index) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op)) - - # ---------- - # enums - - def _generate_cpy_enum_collecttype(self, tp, name): - self._do_collect_type(tp) - - def _generate_cpy_enum_decl(self, tp, name=None): - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator) - - def _enum_ctx(self, tp, cname): - type_index = self._typesdict[tp] - type_op = CffiOp(OP_ENUM, -1) - if self.target_is_python: - tp.check_not_partial() - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._lsts["global"].append( - GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, - check_value=enumvalue)) - # - if cname is not None and '$' not in cname and not self.target_is_python: - size = "sizeof(%s)" % cname - signed = "((%s)-1) <= 0" % cname - else: - basetp = tp.build_baseinttype(self.ffi, []) - size = self.ffi.sizeof(basetp) - signed = int(int(self.ffi.cast(basetp, -1)) < 0) - allenums = ",".join(tp.enumerators) - self._lsts["enum"].append( - EnumExpr(tp.name, type_index, size, signed, allenums)) - - def _generate_cpy_enum_ctx(self, tp, name): - self._enum_ctx(tp, tp._get_c_name()) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_collecttype(self, tp, name): - pass - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - def _generate_cpy_macro_ctx(self, tp, name): - if tp == '...': - if self.target_is_python: - raise VerificationError( - "cannot use the syntax '...' in '#define %s ...' when " - "using the ABI mode" % (name,)) - check_value = None - else: - check_value = tp # an integer - type_op = CffiOp(OP_CONSTANT_INT, -1) - self._lsts["global"].append( - GlobalExpr(name, '_cffi_const_%s' % name, type_op, - check_value=check_value)) - - # ---------- - # global variables - - def _global_type(self, tp, global_name): - if isinstance(tp, model.ArrayType): - actual_length = tp.length - if actual_length == '...': - actual_length = '_cffi_array_len(%s)' % (global_name,) - tp_item = self._global_type(tp.item, '%s[0]' % global_name) - tp = model.ArrayType(tp_item, actual_length) - return tp - - def _generate_cpy_variable_collecttype(self, tp, name): - self._do_collect_type(self._global_type(tp, name)) - - def _generate_cpy_variable_decl(self, tp, name): - prnt = self._prnt - tp = self._global_type(tp, name) - if isinstance(tp, model.ArrayType) and tp.length is None: - tp = tp.item - ampersand = '' - else: - ampersand = '&' - # This code assumes that casts from "tp *" to "void *" is a - # no-op, i.e. a function that returns a "tp *" can be called - # as if it returned a "void *". This should be generally true - # on any modern machine. The only exception to that rule (on - # uncommon architectures, and as far as I can tell) might be - # if 'tp' were a function type, but that is not possible here. - # (If 'tp' is a function _pointer_ type, then casts from "fn_t - # **" to "void *" are again no-ops, as far as I can tell.) - decl = '*_cffi_var_%s(void)' % (name,) - prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) - prnt('{') - prnt(' return %s(%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_cpy_variable_ctx(self, tp, name): - tp = self._global_type(tp, name) - type_index = self._typesdict[tp] - if self.target_is_python: - op = OP_GLOBAL_VAR - else: - op = OP_GLOBAL_VAR_F - self._lsts["global"].append( - GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) - - # ---------- - # extern "Python" - - def _generate_cpy_extern_python_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - self._do_collect_type(tp) - _generate_cpy_dllexport_python_collecttype = \ - _generate_cpy_extern_python_plus_c_collecttype = \ - _generate_cpy_extern_python_collecttype - - def _extern_python_decl(self, tp, name, tag_and_space): - prnt = self._prnt - if isinstance(tp.result, model.VoidType): - size_of_result = '0' - else: - context = 'result of %s' % name - size_of_result = '(int)sizeof(%s)' % ( - tp.result.get_c_name('', context),) - prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) - prnt(' { "%s.%s", %s, 0, 0 };' % ( - self.module_name, name, size_of_result)) - prnt() - # - arguments = [] - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - arg = type.get_c_name(' a%d' % i, context) - arguments.append(arg) - # - repr_arguments = ', '.join(arguments) - repr_arguments = repr_arguments or 'void' - name_and_arguments = '%s(%s)' % (name, repr_arguments) - if tp.abi == "__stdcall": - name_and_arguments = '_cffi_stdcall ' + name_and_arguments - # - def may_need_128_bits(tp): - return (isinstance(tp, model.PrimitiveType) and - tp.name == 'long double') - # - size_of_a = max(len(tp.args)*8, 8) - if may_need_128_bits(tp.result): - size_of_a = max(size_of_a, 16) - if isinstance(tp.result, model.StructOrUnion): - size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( - tp.result.get_c_name(''), size_of_a, - tp.result.get_c_name(''), size_of_a) - prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) - prnt('{') - prnt(' char a[%s];' % size_of_a) - prnt(' char *p = a;') - for i, type in enumerate(tp.args): - arg = 'a%d' % i - if (isinstance(type, model.StructOrUnion) or - may_need_128_bits(type)): - arg = '&' + arg - type = model.PointerType(type) - prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) - prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) - if not isinstance(tp.result, model.VoidType): - prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) - prnt('}') - prnt() - self._num_externpy += 1 - - def _generate_cpy_extern_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'static ') - - def _generate_cpy_dllexport_python_decl(self, tp, name): - self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') - - def _generate_cpy_extern_python_plus_c_decl(self, tp, name): - self._extern_python_decl(tp, name, '') - - def _generate_cpy_extern_python_ctx(self, tp, name): - if self.target_is_python: - raise VerificationError( - "cannot use 'extern \"Python\"' in the ABI mode") - if tp.ellipsis: - raise NotImplementedError("a vararg function is extern \"Python\"") - type_index = self._typesdict[tp] - type_op = CffiOp(OP_EXTERN_PYTHON, type_index) - self._lsts["global"].append( - GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) - - _generate_cpy_dllexport_python_ctx = \ - _generate_cpy_extern_python_plus_c_ctx = \ - _generate_cpy_extern_python_ctx - - def _print_string_literal_in_array(self, s): - prnt = self._prnt - prnt('// # NB. this is not a string because of a size limit in MSVC') - if not isinstance(s, bytes): # unicode - s = s.encode('utf-8') # -> bytes - else: - s.decode('utf-8') # got bytes, check for valid utf-8 - try: - s.decode('ascii') - except UnicodeDecodeError: - s = b'# -*- encoding: utf8 -*-\n' + s - for line in s.splitlines(True): - comment = line - if type('//') is bytes: # python2 - line = map(ord, line) # make a list of integers - else: # python3 - # type(line) is bytes, which enumerates like a list of integers - comment = ascii(comment)[1:-1] - prnt(('// ' + comment).rstrip()) - printed_line = '' - for c in line: - if len(printed_line) >= 76: - prnt(printed_line) - printed_line = '' - printed_line += '%d,' % (c,) - prnt(printed_line) - - # ---------- - # emitting the opcodes for individual types - - def _emit_bytecode_VoidType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) - - def _emit_bytecode_PrimitiveType(self, tp, index): - prim_index = PRIMITIVE_TO_INDEX[tp.name] - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) - - def _emit_bytecode_UnknownIntegerType(self, tp, index): - s = ('_cffi_prim_int(sizeof(%s), (\n' - ' ((%s)-1) | 0 /* check that %s is an integer type */\n' - ' ) <= 0)' % (tp.name, tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_UnknownFloatType(self, tp, index): - s = ('_cffi_prim_float(sizeof(%s) *\n' - ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' - ' )' % (tp.name, tp.name)) - self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) - - def _emit_bytecode_RawFunctionType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) - index += 1 - for tp1 in tp.args: - realindex = self._typesdict[tp1] - if index != realindex: - if isinstance(tp1, model.PrimitiveType): - self._emit_bytecode_PrimitiveType(tp1, index) - else: - self.cffi_types[index] = CffiOp(OP_NOOP, realindex) - index += 1 - flags = int(tp.ellipsis) - if tp.abi is not None: - if tp.abi == '__stdcall': - flags |= 2 - else: - raise NotImplementedError("abi=%r" % (tp.abi,)) - self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) - - def _emit_bytecode_PointerType(self, tp, index): - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) - - _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType - _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType - - def _emit_bytecode_FunctionPtrType(self, tp, index): - raw = tp.as_raw_function() - self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) - - def _emit_bytecode_ArrayType(self, tp, index): - item_index = self._typesdict[tp.item] - if tp.length is None: - self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) - elif tp.length == '...': - raise VerificationError( - "type %s badly placed: the '...' array length can only be " - "used on global arrays or on fields of structures" % ( - str(tp).replace('/*...*/', '...'),)) - else: - assert self.cffi_types[index + 1] == 'LEN' - self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) - self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) - - def _emit_bytecode_StructType(self, tp, index): - struct_index = self._struct_unions[tp] - self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) - _emit_bytecode_UnionType = _emit_bytecode_StructType - - def _emit_bytecode_EnumType(self, tp, index): - enum_index = self._enums[tp] - self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - -def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): - if verbose: - print("generating %s" % (target_file,)) - recompiler = Recompiler(ffi, module_name, - target_is_python=(preamble is None)) - recompiler.collect_type_table() - recompiler.collect_step_tables() - f = NativeIO() - recompiler.write_source_to_f(f, preamble) - output = f.getvalue() - try: - with open(target_file, 'r') as f1: - if f1.read(len(output) + 1) != output: - raise IOError - if verbose: - print("(already up-to-date)") - return False # already up-to-date - except IOError: - tmp_file = '%s.~%d' % (target_file, os.getpid()) - with open(tmp_file, 'w') as f1: - f1.write(output) - try: - os.rename(tmp_file, target_file) - except OSError: - os.unlink(target_file) - os.rename(tmp_file, target_file) - return True - -def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): - assert preamble is not None - return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, - verbose) - -def make_py_source(ffi, module_name, target_py_file, verbose=False): - return _make_c_or_py_source(ffi, module_name, None, target_py_file, - verbose) - -def _modname_to_file(outputdir, modname, extension): - parts = modname.split('.') - try: - os.makedirs(os.path.join(outputdir, *parts[:-1])) - except OSError: - pass - parts[-1] += extension - return os.path.join(outputdir, *parts), parts - - -# Aaargh. Distutils is not tested at all for the purpose of compiling -# DLLs that are not extension modules. Here are some hacks to work -# around that, in the _patch_for_*() functions... - -def _patch_meth(patchlist, cls, name, new_meth): - old = getattr(cls, name) - patchlist.append((cls, name, old)) - setattr(cls, name, new_meth) - return old - -def _unpatch_meths(patchlist): - for cls, name, old_meth in reversed(patchlist): - setattr(cls, name, old_meth) - -def _patch_for_embedding(patchlist): - if sys.platform == 'win32': - # we must not remove the manifest when building for embedding! - from distutils.msvc9compiler import MSVCCompiler - _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', - lambda self, manifest_file: manifest_file) - - if sys.platform == 'darwin': - # we must not make a '-bundle', but a '-dynamiclib' instead - from distutils.ccompiler import CCompiler - def my_link_shared_object(self, *args, **kwds): - if '-bundle' in self.linker_so: - self.linker_so = list(self.linker_so) - i = self.linker_so.index('-bundle') - self.linker_so[i] = '-dynamiclib' - return old_link_shared_object(self, *args, **kwds) - old_link_shared_object = _patch_meth(patchlist, CCompiler, - 'link_shared_object', - my_link_shared_object) - -def _patch_for_target(patchlist, target): - from distutils.command.build_ext import build_ext - # if 'target' is different from '*', we need to patch some internal - # method to just return this 'target' value, instead of having it - # built from module_name - if target.endswith('.*'): - target = target[:-2] - if sys.platform == 'win32': - target += '.dll' - elif sys.platform == 'darwin': - target += '.dylib' - else: - target += '.so' - _patch_meth(patchlist, build_ext, 'get_ext_filename', - lambda self, ext_name: target) - - -def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, - c_file=None, source_extension='.c', extradir=None, - compiler_verbose=1, target=None, debug=None, **kwds): - if not isinstance(module_name, str): - module_name = module_name.encode('ascii') - if ffi._windows_unicode: - ffi._apply_windows_unicode(kwds) - if preamble is not None: - embedding = (ffi._embedding is not None) - if embedding: - ffi._apply_embedding_fix(kwds) - if c_file is None: - c_file, parts = _modname_to_file(tmpdir, module_name, - source_extension) - if extradir: - parts = [extradir] + parts - ext_c_file = os.path.join(*parts) - else: - ext_c_file = c_file - # - if target is None: - if embedding: - target = '%s.*' % module_name - else: - target = '*' - # - ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) - updated = make_c_source(ffi, module_name, preamble, c_file, - verbose=compiler_verbose) - if call_c_compiler: - patchlist = [] - cwd = os.getcwd() - try: - if embedding: - _patch_for_embedding(patchlist) - if target != '*': - _patch_for_target(patchlist, target) - if compiler_verbose: - if tmpdir == '.': - msg = 'the current directory is' - else: - msg = 'setting the current directory to' - print('%s %r' % (msg, os.path.abspath(tmpdir))) - os.chdir(tmpdir) - outputfilename = ffiplatform.compile('.', ext, - compiler_verbose, debug) - finally: - os.chdir(cwd) - _unpatch_meths(patchlist) - return outputfilename - else: - return ext, updated - else: - if c_file is None: - c_file, _ = _modname_to_file(tmpdir, module_name, '.py') - updated = make_py_source(ffi, module_name, c_file, - verbose=compiler_verbose) - if call_c_compiler: - return c_file - else: - return None, updated - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py deleted file mode 100644 index 5df6494fe..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/setuptools_ext.py +++ /dev/null @@ -1,219 +0,0 @@ -import os -import sys - -try: - basestring -except NameError: - # Python 3.x - basestring = str - -def error(msg): - from distutils.errors import DistutilsSetupError - raise DistutilsSetupError(msg) - - -def execfile(filename, glob): - # We use execfile() (here rewritten for Python 3) instead of - # __import__() to load the build script. The problem with - # a normal import is that in some packages, the intermediate - # __init__.py files may already try to import the file that - # we are generating. - with open(filename) as f: - src = f.read() - src += '\n' # Python 2.6 compatibility - code = compile(src, filename, 'exec') - exec(code, glob, glob) - - -def add_cffi_module(dist, mod_spec): - from cffi.api import FFI - - if not isinstance(mod_spec, basestring): - error("argument to 'cffi_modules=...' must be a str or a list of str," - " not %r" % (type(mod_spec).__name__,)) - mod_spec = str(mod_spec) - try: - build_file_name, ffi_var_name = mod_spec.split(':') - except ValueError: - error("%r must be of the form 'path/build.py:ffi_variable'" % - (mod_spec,)) - if not os.path.exists(build_file_name): - ext = '' - rewritten = build_file_name.replace('.', '/') + '.py' - if os.path.exists(rewritten): - ext = ' (rewrite cffi_modules to [%r])' % ( - rewritten + ':' + ffi_var_name,) - error("%r does not name an existing file%s" % (build_file_name, ext)) - - mod_vars = {'__name__': '__cffi__', '__file__': build_file_name} - execfile(build_file_name, mod_vars) - - try: - ffi = mod_vars[ffi_var_name] - except KeyError: - error("%r: object %r not found in module" % (mod_spec, - ffi_var_name)) - if not isinstance(ffi, FFI): - ffi = ffi() # maybe it's a function instead of directly an ffi - if not isinstance(ffi, FFI): - error("%r is not an FFI instance (got %r)" % (mod_spec, - type(ffi).__name__)) - if not hasattr(ffi, '_assigned_source'): - error("%r: the set_source() method was not called" % (mod_spec,)) - module_name, source, source_extension, kwds = ffi._assigned_source - if ffi._windows_unicode: - kwds = kwds.copy() - ffi._apply_windows_unicode(kwds) - - if source is None: - _add_py_module(dist, ffi, module_name) - else: - _add_c_module(dist, ffi, module_name, source, source_extension, kwds) - -def _set_py_limited_api(Extension, kwds): - """ - Add py_limited_api to kwds if setuptools >= 26 is in use. - Do not alter the setting if it already exists. - Setuptools takes care of ignoring the flag on Python 2 and PyPy. - - CPython itself should ignore the flag in a debugging version - (by not listing .abi3.so in the extensions it supports), but - it doesn't so far, creating troubles. That's why we check - for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent - of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401) - - On Windows, with CPython <= 3.4, it's better not to use py_limited_api - because virtualenv *still* doesn't copy PYTHON3.DLL on these versions. - Recently (2020) we started shipping only >= 3.5 wheels, though. So - we'll give it another try and set py_limited_api on Windows >= 3.5. - """ - from cffi import recompiler - - if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount') - and recompiler.USE_LIMITED_API): - import setuptools - try: - setuptools_major_version = int(setuptools.__version__.partition('.')[0]) - if setuptools_major_version >= 26: - kwds['py_limited_api'] = True - except ValueError: # certain development versions of setuptools - # If we don't know the version number of setuptools, we - # try to set 'py_limited_api' anyway. At worst, we get a - # warning. - kwds['py_limited_api'] = True - return kwds - -def _add_c_module(dist, ffi, module_name, source, source_extension, kwds): - from distutils.core import Extension - # We are a setuptools extension. Need this build_ext for py_limited_api. - from setuptools.command.build_ext import build_ext - from distutils.dir_util import mkpath - from distutils import log - from cffi import recompiler - - allsources = ['$PLACEHOLDER'] - allsources.extend(kwds.pop('sources', [])) - kwds = _set_py_limited_api(Extension, kwds) - ext = Extension(name=module_name, sources=allsources, **kwds) - - def make_mod(tmpdir, pre_run=None): - c_file = os.path.join(tmpdir, module_name + source_extension) - log.info("generating cffi module %r" % c_file) - mkpath(tmpdir) - # a setuptools-only, API-only hook: called with the "ext" and "ffi" - # arguments just before we turn the ffi into C code. To use it, - # subclass the 'distutils.command.build_ext.build_ext' class and - # add a method 'def pre_run(self, ext, ffi)'. - if pre_run is not None: - pre_run(ext, ffi) - updated = recompiler.make_c_source(ffi, module_name, source, c_file) - if not updated: - log.info("already up-to-date") - return c_file - - if dist.ext_modules is None: - dist.ext_modules = [] - dist.ext_modules.append(ext) - - base_class = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class): - def run(self): - if ext.sources[0] == '$PLACEHOLDER': - pre_run = getattr(self, 'pre_run', None) - ext.sources[0] = make_mod(self.build_temp, pre_run) - base_class.run(self) - dist.cmdclass['build_ext'] = build_ext_make_mod - # NB. multiple runs here will create multiple 'build_ext_make_mod' - # classes. Even in this case the 'build_ext' command should be - # run once; but just in case, the logic above does nothing if - # called again. - - -def _add_py_module(dist, ffi, module_name): - from distutils.dir_util import mkpath - from setuptools.command.build_py import build_py - from setuptools.command.build_ext import build_ext - from distutils import log - from cffi import recompiler - - def generate_mod(py_file): - log.info("generating cffi module %r" % py_file) - mkpath(os.path.dirname(py_file)) - updated = recompiler.make_py_source(ffi, module_name, py_file) - if not updated: - log.info("already up-to-date") - - base_class = dist.cmdclass.get('build_py', build_py) - class build_py_make_mod(base_class): - def run(self): - base_class.run(self) - module_path = module_name.split('.') - module_path[-1] += '.py' - generate_mod(os.path.join(self.build_lib, *module_path)) - def get_source_files(self): - # This is called from 'setup.py sdist' only. Exclude - # the generate .py module in this case. - saved_py_modules = self.py_modules - try: - if saved_py_modules: - self.py_modules = [m for m in saved_py_modules - if m != module_name] - return base_class.get_source_files(self) - finally: - self.py_modules = saved_py_modules - dist.cmdclass['build_py'] = build_py_make_mod - - # distutils and setuptools have no notion I could find of a - # generated python module. If we don't add module_name to - # dist.py_modules, then things mostly work but there are some - # combination of options (--root and --record) that will miss - # the module. So we add it here, which gives a few apparently - # harmless warnings about not finding the file outside the - # build directory. - # Then we need to hack more in get_source_files(); see above. - if dist.py_modules is None: - dist.py_modules = [] - dist.py_modules.append(module_name) - - # the following is only for "build_ext -i" - base_class_2 = dist.cmdclass.get('build_ext', build_ext) - class build_ext_make_mod(base_class_2): - def run(self): - base_class_2.run(self) - if self.inplace: - # from get_ext_fullpath() in distutils/command/build_ext.py - module_path = module_name.split('.') - package = '.'.join(module_path[:-1]) - build_py = self.get_finalized_command('build_py') - package_dir = build_py.get_package_dir(package) - file_name = module_path[-1] + '.py' - generate_mod(os.path.join(package_dir, file_name)) - dist.cmdclass['build_ext'] = build_ext_make_mod - -def cffi_modules(dist, attr, value): - assert attr == 'cffi_modules' - if isinstance(value, basestring): - value = [value] - - for cffi_module in value: - add_cffi_module(dist, cffi_module) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py deleted file mode 100644 index 8c26db092..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_cpy.py +++ /dev/null @@ -1,1076 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, imp -from . import model -from .error import VerificationError - - -class VCPythonEngine(object): - _class_key = 'x' - _gen_python_module = True - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self._struct_pending_verification = {} - self._types_of_builtin_functions = {} - - def patch_extension_kwds(self, kwds): - pass - - def find_module(self, module_name, path, so_suffixes): - try: - f, filename, descr = imp.find_module(module_name, path) - except ImportError: - return None - if f is not None: - f.close() - # Note that after a setuptools installation, there are both .py - # and .so files with the same basename. The code here relies on - # imp.find_module() locating the .so in priority. - if descr[0] not in so_suffixes: - return None - return filename - - def collect_types(self): - self._typesdict = {} - self._generate("collecttype") - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def _gettypenum(self, type): - # a KeyError here is a bug. please report it! :-) - return self._typesdict[type] - - def _do_collect_type(self, tp): - if ((not isinstance(tp, model.PrimitiveType) - or tp.name == 'long double') - and tp not in self._typesdict): - num = len(self._typesdict) - self._typesdict[tp] = num - - def write_source_to_f(self): - self.collect_types() - # - # The new module will have a _cffi_setup() function that receives - # objects from the ffi world, and that calls some setup code in - # the module. This setup code is split in several independent - # functions, e.g. one per constant. The functions are "chained" - # by ending in a tail call to each other. - # - # This is further split in two chained lists, depending on if we - # can do it at import-time or if we must wait for _cffi_setup() to - # provide us with the objects. This is needed because we - # need the values of the enum constants in order to build the - # that we may have to pass to _cffi_setup(). - # - # The following two 'chained_list_constants' items contains - # the head of these two chained lists, as a string that gives the - # call to do, if any. - self._chained_list_constants = ['((void)lib,0)', '((void)lib,0)'] - # - prnt = self._prnt - # first paste some standard set of lines that are mostly '#define' - prnt(cffimod_header) - prnt() - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - prnt() - # - # call generate_cpy_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate("decl") - # - # implement the function _cffi_setup_custom() as calling the - # head of the chained list. - self._generate_setup_custom() - prnt() - # - # produce the method table, including the entries for the - # generated Python->C function wrappers, which are done - # by generate_cpy_function_method(). - prnt('static PyMethodDef _cffi_methods[] = {') - self._generate("method") - prnt(' {"_cffi_setup", _cffi_setup, METH_VARARGS, NULL},') - prnt(' {NULL, NULL, 0, NULL} /* Sentinel */') - prnt('};') - prnt() - # - # standard init. - modname = self.verifier.get_module_name() - constants = self._chained_list_constants[False] - prnt('#if PY_MAJOR_VERSION >= 3') - prnt() - prnt('static struct PyModuleDef _cffi_module_def = {') - prnt(' PyModuleDef_HEAD_INIT,') - prnt(' "%s",' % modname) - prnt(' NULL,') - prnt(' -1,') - prnt(' _cffi_methods,') - prnt(' NULL, NULL, NULL, NULL') - prnt('};') - prnt() - prnt('PyMODINIT_FUNC') - prnt('PyInit_%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = PyModule_Create(&_cffi_module_def);') - prnt(' if (lib == NULL)') - prnt(' return NULL;') - prnt(' if (%s < 0 || _cffi_init() < 0) {' % (constants,)) - prnt(' Py_DECREF(lib);') - prnt(' return NULL;') - prnt(' }') - prnt(' return lib;') - prnt('}') - prnt() - prnt('#else') - prnt() - prnt('PyMODINIT_FUNC') - prnt('init%s(void)' % modname) - prnt('{') - prnt(' PyObject *lib;') - prnt(' lib = Py_InitModule("%s", _cffi_methods);' % modname) - prnt(' if (lib == NULL)') - prnt(' return;') - prnt(' if (%s < 0 || _cffi_init() < 0)' % (constants,)) - prnt(' return;') - prnt(' return;') - prnt('}') - prnt() - prnt('#endif') - - def load_library(self, flags=None): - # XXX review all usages of 'self' here! - # import it as a new extension module - imp.acquire_lock() - try: - if hasattr(sys, "getdlopenflags"): - previous_flags = sys.getdlopenflags() - try: - if hasattr(sys, "setdlopenflags") and flags is not None: - sys.setdlopenflags(flags) - module = imp.load_dynamic(self.verifier.get_module_name(), - self.verifier.modulefilename) - except ImportError as e: - error = "importing %r: %s" % (self.verifier.modulefilename, e) - raise VerificationError(error) - finally: - if hasattr(sys, "setdlopenflags"): - sys.setdlopenflags(previous_flags) - finally: - imp.release_lock() - # - # call loading_cpy_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - # - # the C code will need the objects. Collect them in - # order in a list. - revmapping = dict([(value, key) - for (key, value) in self._typesdict.items()]) - lst = [revmapping[i] for i in range(len(revmapping))] - lst = list(map(self.ffi._get_cached_btype, lst)) - # - # build the FFILibrary class and instance and call _cffi_setup(). - # this will set up some fields like '_cffi_types', and only then - # it will invoke the chained list of functions that will really - # build (notably) the constant objects, as if they are - # pointers, and store them as attributes on the 'library' object. - class FFILibrary(object): - _cffi_python_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir + list(self.__dict__) - library = FFILibrary() - if module._cffi_setup(lst, VerificationError, library): - import warnings - warnings.warn("reimporting %r might overwrite older definitions" - % (self.verifier.get_module_name())) - # - # finally, call the loaded_cpy_xxx() functions. This will perform - # the final adjustments, like copying the Python->C wrapper - # functions from the module to the 'library' object, and setting - # up the FFILibrary class with properties for the global C variables. - self._load(module, 'loaded', library=library) - module._cffi_original_ffi = self.ffi - module._cffi_types_of_builtin_funcs = self._types_of_builtin_functions - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_cpy_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_cpy_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - - def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): - extraarg = '' - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - converter = '_cffi_to_c_int' - extraarg = ', %s' % tp.name - else: - converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), - tp.name.replace(' ', '_')) - errvalue = '-1' - # - elif isinstance(tp, model.PointerType): - self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, - tovar, errcode) - return - # - elif isinstance(tp, (model.StructOrUnion, model.EnumType)): - # a struct (not a struct pointer) as a function argument - self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' - % (tovar, self._gettypenum(tp), fromvar)) - self._prnt(' %s;' % errcode) - return - # - elif isinstance(tp, model.FunctionPtrType): - converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') - extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) - errvalue = 'NULL' - # - else: - raise NotImplementedError(tp) - # - self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) - self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( - tovar, tp.get_c_name(''), errvalue)) - self._prnt(' %s;' % errcode) - - def _extra_local_variables(self, tp, localvars, freelines): - if isinstance(tp, model.PointerType): - localvars.add('Py_ssize_t datasize') - localvars.add('struct _cffi_freeme_s *large_args_free = NULL') - freelines.add('if (large_args_free != NULL)' - ' _cffi_free_array_arguments(large_args_free);') - - def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): - self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') - self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( - self._gettypenum(tp), fromvar, tovar)) - self._prnt(' if (datasize != 0) {') - self._prnt(' %s = ((size_t)datasize) <= 640 ? ' - 'alloca((size_t)datasize) : NULL;' % (tovar,)) - self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, ' - '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar)) - self._prnt(' datasize, &large_args_free) < 0)') - self._prnt(' %s;' % errcode) - self._prnt(' }') - - def _convert_expr_from_c(self, tp, var, context): - if isinstance(tp, model.PrimitiveType): - if tp.is_integer_type() and tp.name != '_Bool': - return '_cffi_from_c_int(%s, %s)' % (var, tp.name) - elif tp.name != 'long double': - return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) - else: - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.ArrayType): - return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( - var, self._gettypenum(model.PointerType(tp.item))) - elif isinstance(tp, model.StructOrUnion): - if tp.fldnames is None: - raise TypeError("'%s' is used as %s, but is opaque" % ( - tp._get_c_name(), context)) - return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - elif isinstance(tp, model.EnumType): - return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( - var, self._gettypenum(tp)) - else: - raise NotImplementedError(tp) - - # ---------- - # typedefs: generates no code so far - - _generate_cpy_typedef_collecttype = _generate_nothing - _generate_cpy_typedef_decl = _generate_nothing - _generate_cpy_typedef_method = _generate_nothing - _loading_cpy_typedef = _loaded_noop - _loaded_cpy_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_cpy_function_collecttype(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - self._do_collect_type(tp) - else: - # don't call _do_collect_type(tp) in this common case, - # otherwise test_autofilled_struct_as_argument fails - for type in tp.args: - self._do_collect_type(type) - self._do_collect_type(tp.result) - - def _generate_cpy_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no CPython wrapper) - self._generate_cpy_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - if numargs == 0: - argname = 'noarg' - elif numargs == 1: - argname = 'arg0' - else: - argname = 'args' - prnt('static PyObject *') - prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) - prnt('{') - # - context = 'argument of %s' % name - for i, type in enumerate(tp.args): - prnt(' %s;' % type.get_c_name(' x%d' % i, context)) - # - localvars = set() - freelines = set() - for type in tp.args: - self._extra_local_variables(type, localvars, freelines) - for decl in sorted(localvars): - prnt(' %s;' % (decl,)) - # - if not isinstance(tp.result, model.VoidType): - result_code = 'result = ' - context = 'result of %s' % name - prnt(' %s;' % tp.result.get_c_name(' result', context)) - prnt(' PyObject *pyresult;') - else: - result_code = '' - # - if len(tp.args) > 1: - rng = range(len(tp.args)) - for i in rng: - prnt(' PyObject *arg%d;' % i) - prnt() - prnt(' if (!PyArg_ParseTuple(args, "%s:%s", %s))' % ( - 'O' * numargs, name, ', '.join(['&arg%d' % i for i in rng]))) - prnt(' return NULL;') - prnt() - # - for i, type in enumerate(tp.args): - self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, - 'return NULL') - prnt() - # - prnt(' Py_BEGIN_ALLOW_THREADS') - prnt(' _cffi_restore_errno();') - prnt(' { %s%s(%s); }' % ( - result_code, name, - ', '.join(['x%d' % i for i in range(len(tp.args))]))) - prnt(' _cffi_save_errno();') - prnt(' Py_END_ALLOW_THREADS') - prnt() - # - prnt(' (void)self; /* unused */') - if numargs == 0: - prnt(' (void)noarg; /* unused */') - if result_code: - prnt(' pyresult = %s;' % - self._convert_expr_from_c(tp.result, 'result', 'result type')) - for freeline in freelines: - prnt(' ' + freeline) - prnt(' return pyresult;') - else: - for freeline in freelines: - prnt(' ' + freeline) - prnt(' Py_INCREF(Py_None);') - prnt(' return Py_None;') - prnt('}') - prnt() - - def _generate_cpy_function_method(self, tp, name): - if tp.ellipsis: - return - numargs = len(tp.args) - if numargs == 0: - meth = 'METH_NOARGS' - elif numargs == 1: - meth = 'METH_O' - else: - meth = 'METH_VARARGS' - self._prnt(' {"%s", _cffi_f_%s, %s, NULL},' % (name, name, meth)) - - _loading_cpy_function = _loaded_noop - - def _loaded_cpy_function(self, tp, name, module, library): - if tp.ellipsis: - return - func = getattr(module, name) - setattr(library, name, func) - self._types_of_builtin_functions[func] = tp - - # ---------- - # named structs - - _generate_cpy_struct_collecttype = _generate_nothing - def _generate_cpy_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - def _generate_cpy_struct_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'struct', name) - def _loading_cpy_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - def _loaded_cpy_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - _generate_cpy_union_collecttype = _generate_nothing - def _generate_cpy_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - def _generate_cpy_union_method(self, tp, name): - self._generate_struct_or_union_method(tp, 'union', name) - def _loading_cpy_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - def _loaded_cpy_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - prnt('static PyObject *') - prnt('%s(PyObject *self, PyObject *noarg)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static Py_ssize_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' (void)self; /* unused */') - prnt(' (void)noarg; /* unused */') - prnt(' return _cffi_get_struct_layout(nums);') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _generate_struct_or_union_method(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - self._prnt(' {"%s", %s, METH_NOARGS, NULL},' % (layoutfuncname, - layoutfuncname)) - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - function = getattr(module, layoutfuncname) - layout = function() - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - _generate_cpy_anonymous_collecttype = _generate_nothing - - def _generate_cpy_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_cpy_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _generate_cpy_anonymous_method(self, tp, name): - if not isinstance(tp, model.EnumType): - self._generate_struct_or_union_method(tp, '', name) - - def _loading_cpy_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_cpy_enum(tp, name, module) - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_cpy_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_cpy_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_cpy_const(self, is_int, name, tp=None, category='const', - vartp=None, delayed=True, size_too=False, - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - prnt(' PyObject *o;') - prnt(' int res;') - if not is_int: - prnt(' %s;' % (vartp or tp).get_c_name(' i', name)) - else: - assert category == 'const' - # - if check_value is not None: - self._check_int_constant_value(name, check_value) - # - if not is_int: - if category == 'var': - realexpr = '&' + name - else: - realexpr = name - prnt(' i = (%s);' % (realexpr,)) - prnt(' o = %s;' % (self._convert_expr_from_c(tp, 'i', - 'variable type'),)) - assert delayed - else: - prnt(' o = _cffi_from_c_int_const(%s);' % name) - prnt(' if (o == NULL)') - prnt(' return -1;') - if size_too: - prnt(' {') - prnt(' PyObject *o1 = o;') - prnt(' o = Py_BuildValue("On", o1, (Py_ssize_t)sizeof(%s));' - % (name,)) - prnt(' Py_DECREF(o1);') - prnt(' if (o == NULL)') - prnt(' return -1;') - prnt(' }') - prnt(' res = PyObject_SetAttrString(lib, "%s", o);' % name) - prnt(' Py_DECREF(o);') - prnt(' if (res < 0)') - prnt(' return -1;') - prnt(' return %s;' % self._chained_list_constants[delayed]) - self._chained_list_constants[delayed] = funcname + '(lib)' - prnt('}') - prnt() - - def _generate_cpy_constant_collecttype(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - if not is_int: - self._do_collect_type(tp) - - def _generate_cpy_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_cpy_const(is_int, name, tp) - - _generate_cpy_constant_method = _generate_nothing - _loading_cpy_constant = _loaded_noop - _loaded_cpy_constant = _loaded_noop - - # ---------- - # enums - - def _check_int_constant_value(self, name, value, err_prefix=''): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' snprintf(buf, 63, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' snprintf(buf, 63, "%%lu", (unsigned long)(%s));' % - name) - prnt(' PyErr_Format(_cffi_VerificationError,') - prnt(' "%s%s has the real value %s, not %s",') - prnt(' "%s", "%s", buf, "%d");' % ( - err_prefix, name, value)) - prnt(' return -1;') - prnt(' }') - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_cpy_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_cpy_const(True, enumerator, delayed=False) - return - # - funcname = self._enum_funcname(prefix, name) - prnt = self._prnt - prnt('static int %s(PyObject *lib)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue, - "enum %s: " % name) - prnt(' return %s;' % self._chained_list_constants[True]) - self._chained_list_constants[True] = funcname + '(lib)' - prnt('}') - prnt() - - _generate_cpy_enum_collecttype = _generate_nothing - _generate_cpy_enum_method = _generate_nothing - - def _loading_cpy_enum(self, tp, name, module): - if tp.partial: - enumvalues = [getattr(module, enumerator) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - - def _loaded_cpy_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - - # ---------- - # macros: for now only for integers - - def _generate_cpy_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_cpy_const(True, name, check_value=check_value) - - _generate_cpy_macro_collecttype = _generate_nothing - _generate_cpy_macro_method = _generate_nothing - _loading_cpy_macro = _loaded_noop - _loaded_cpy_macro = _loaded_noop - - # ---------- - # global variables - - def _generate_cpy_variable_collecttype(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - else: - tp_ptr = model.PointerType(tp) - self._do_collect_type(tp_ptr) - - def _generate_cpy_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - tp_ptr = model.PointerType(tp.item) - self._generate_cpy_const(False, name, tp, vartp=tp_ptr, - size_too = tp.length_is_unknown()) - else: - tp_ptr = model.PointerType(tp) - self._generate_cpy_const(False, name, tp_ptr, category='var') - - _generate_cpy_variable_method = _generate_nothing - _loading_cpy_variable = _loaded_noop - - def _loaded_cpy_variable(self, tp, name, module, library): - value = getattr(library, name) - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - assert isinstance(value, tuple) - (value, size) = value - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - # 'value' is a which we have to replace with - # a if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - return - # remove ptr= from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - ptr = value - delattr(library, name) - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - - # ---------- - - def _generate_setup_custom(self): - prnt = self._prnt - prnt('static int _cffi_setup_custom(PyObject *lib)') - prnt('{') - prnt(' return %s;' % self._chained_list_constants[True]) - prnt('}') - -cffimod_header = r''' -#include -#include - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -#endif - -#if PY_MAJOR_VERSION < 3 -# undef PyCapsule_CheckExact -# undef PyCapsule_GetPointer -# define PyCapsule_CheckExact(capsule) (PyCObject_Check(capsule)) -# define PyCapsule_GetPointer(capsule, name) \ - (PyCObject_AsVoidPtr(capsule)) -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PyInt_FromLong PyLong_FromLong -#endif - -#define _cffi_from_c_double PyFloat_FromDouble -#define _cffi_from_c_float PyFloat_FromDouble -#define _cffi_from_c_long PyInt_FromLong -#define _cffi_from_c_ulong PyLong_FromUnsignedLong -#define _cffi_from_c_longlong PyLong_FromLongLong -#define _cffi_from_c_ulonglong PyLong_FromUnsignedLongLong -#define _cffi_from_c__Bool PyBool_FromLong - -#define _cffi_to_c_double PyFloat_AsDouble -#define _cffi_to_c_float PyFloat_AsDouble - -#define _cffi_from_c_int_const(x) \ - (((x) > 0) ? \ - ((unsigned long long)(x) <= (unsigned long long)LONG_MAX) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromUnsignedLongLong((unsigned long long)(x)) : \ - ((long long)(x) >= (long long)LONG_MIN) ? \ - PyInt_FromLong((long)(x)) : \ - PyLong_FromLongLong((long long)(x))) - -#define _cffi_from_c_int(x, type) \ - (((type)-1) > 0 ? /* unsigned */ \ - (sizeof(type) < sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - sizeof(type) == sizeof(long) ? \ - PyLong_FromUnsignedLong((unsigned long)x) : \ - PyLong_FromUnsignedLongLong((unsigned long long)x)) : \ - (sizeof(type) <= sizeof(long) ? \ - PyInt_FromLong((long)x) : \ - PyLong_FromLongLong((long long)x))) - -#define _cffi_to_c_int(o, type) \ - ((type)( \ - sizeof(type) == 1 ? (((type)-1) > 0 ? (type)_cffi_to_c_u8(o) \ - : (type)_cffi_to_c_i8(o)) : \ - sizeof(type) == 2 ? (((type)-1) > 0 ? (type)_cffi_to_c_u16(o) \ - : (type)_cffi_to_c_i16(o)) : \ - sizeof(type) == 4 ? (((type)-1) > 0 ? (type)_cffi_to_c_u32(o) \ - : (type)_cffi_to_c_i32(o)) : \ - sizeof(type) == 8 ? (((type)-1) > 0 ? (type)_cffi_to_c_u64(o) \ - : (type)_cffi_to_c_i64(o)) : \ - (Py_FatalError("unsupported size for type " #type), (type)0))) - -#define _cffi_to_c_i8 \ - ((int(*)(PyObject *))_cffi_exports[1]) -#define _cffi_to_c_u8 \ - ((int(*)(PyObject *))_cffi_exports[2]) -#define _cffi_to_c_i16 \ - ((int(*)(PyObject *))_cffi_exports[3]) -#define _cffi_to_c_u16 \ - ((int(*)(PyObject *))_cffi_exports[4]) -#define _cffi_to_c_i32 \ - ((int(*)(PyObject *))_cffi_exports[5]) -#define _cffi_to_c_u32 \ - ((unsigned int(*)(PyObject *))_cffi_exports[6]) -#define _cffi_to_c_i64 \ - ((long long(*)(PyObject *))_cffi_exports[7]) -#define _cffi_to_c_u64 \ - ((unsigned long long(*)(PyObject *))_cffi_exports[8]) -#define _cffi_to_c_char \ - ((int(*)(PyObject *))_cffi_exports[9]) -#define _cffi_from_c_pointer \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[10]) -#define _cffi_to_c_pointer \ - ((char *(*)(PyObject *, CTypeDescrObject *))_cffi_exports[11]) -#define _cffi_get_struct_layout \ - ((PyObject *(*)(Py_ssize_t[]))_cffi_exports[12]) -#define _cffi_restore_errno \ - ((void(*)(void))_cffi_exports[13]) -#define _cffi_save_errno \ - ((void(*)(void))_cffi_exports[14]) -#define _cffi_from_c_char \ - ((PyObject *(*)(char))_cffi_exports[15]) -#define _cffi_from_c_deref \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[16]) -#define _cffi_to_c \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[17]) -#define _cffi_from_c_struct \ - ((PyObject *(*)(char *, CTypeDescrObject *))_cffi_exports[18]) -#define _cffi_to_c_wchar_t \ - ((wchar_t(*)(PyObject *))_cffi_exports[19]) -#define _cffi_from_c_wchar_t \ - ((PyObject *(*)(wchar_t))_cffi_exports[20]) -#define _cffi_to_c_long_double \ - ((long double(*)(PyObject *))_cffi_exports[21]) -#define _cffi_to_c__Bool \ - ((_Bool(*)(PyObject *))_cffi_exports[22]) -#define _cffi_prepare_pointer_call_argument \ - ((Py_ssize_t(*)(CTypeDescrObject *, PyObject *, char **))_cffi_exports[23]) -#define _cffi_convert_array_from_object \ - ((int(*)(char *, CTypeDescrObject *, PyObject *))_cffi_exports[24]) -#define _CFFI_NUM_EXPORTS 25 - -typedef struct _ctypedescr CTypeDescrObject; - -static void *_cffi_exports[_CFFI_NUM_EXPORTS]; -static PyObject *_cffi_types, *_cffi_VerificationError; - -static int _cffi_setup_custom(PyObject *lib); /* forward */ - -static PyObject *_cffi_setup(PyObject *self, PyObject *args) -{ - PyObject *library; - int was_alive = (_cffi_types != NULL); - (void)self; /* unused */ - if (!PyArg_ParseTuple(args, "OOO", &_cffi_types, &_cffi_VerificationError, - &library)) - return NULL; - Py_INCREF(_cffi_types); - Py_INCREF(_cffi_VerificationError); - if (_cffi_setup_custom(library) < 0) - return NULL; - return PyBool_FromLong(was_alive); -} - -union _cffi_union_alignment_u { - unsigned char m_char; - unsigned short m_short; - unsigned int m_int; - unsigned long m_long; - unsigned long long m_longlong; - float m_float; - double m_double; - long double m_longdouble; -}; - -struct _cffi_freeme_s { - struct _cffi_freeme_s *next; - union _cffi_union_alignment_u alignment; -}; - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static int _cffi_convert_array_argument(CTypeDescrObject *ctptr, PyObject *arg, - char **output_data, Py_ssize_t datasize, - struct _cffi_freeme_s **freeme) -{ - char *p; - if (datasize < 0) - return -1; - - p = *output_data; - if (p == NULL) { - struct _cffi_freeme_s *fp = (struct _cffi_freeme_s *)PyObject_Malloc( - offsetof(struct _cffi_freeme_s, alignment) + (size_t)datasize); - if (fp == NULL) - return -1; - fp->next = *freeme; - *freeme = fp; - p = *output_data = (char *)&fp->alignment; - } - memset((void *)p, 0, (size_t)datasize); - return _cffi_convert_array_from_object(p, ctptr, arg); -} - -#ifdef __GNUC__ - __attribute__((unused)) -#endif -static void _cffi_free_array_arguments(struct _cffi_freeme_s *freeme) -{ - do { - void *p = (void *)freeme; - freeme = freeme->next; - PyObject_Free(p); - } while (freeme != NULL); -} - -static int _cffi_init(void) -{ - PyObject *module, *c_api_object = NULL; - - module = PyImport_ImportModule("_cffi_backend"); - if (module == NULL) - goto failure; - - c_api_object = PyObject_GetAttrString(module, "_C_API"); - if (c_api_object == NULL) - goto failure; - if (!PyCapsule_CheckExact(c_api_object)) { - PyErr_SetNone(PyExc_ImportError); - goto failure; - } - memcpy(_cffi_exports, PyCapsule_GetPointer(c_api_object, "cffi"), - _CFFI_NUM_EXPORTS * sizeof(void *)); - - Py_DECREF(module); - Py_DECREF(c_api_object); - return 0; - - failure: - Py_XDECREF(module); - Py_XDECREF(c_api_object); - return -1; -} - -#define _cffi_type(num) ((CTypeDescrObject *)PyList_GET_ITEM(_cffi_types, num)) - -/**********/ -''' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py deleted file mode 100644 index 72e144de0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/vengine_gen.py +++ /dev/null @@ -1,675 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os -import types - -from . import model -from .error import VerificationError - - -class VGenericEngine(object): - _class_key = 'g' - _gen_python_module = False - - def __init__(self, verifier): - self.verifier = verifier - self.ffi = verifier.ffi - self.export_symbols = [] - self._struct_pending_verification = {} - - def patch_extension_kwds(self, kwds): - # add 'export_symbols' to the dictionary. Note that we add the - # list before filling it. When we fill it, it will thus also show - # up in kwds['export_symbols']. - kwds.setdefault('export_symbols', self.export_symbols) - - def find_module(self, module_name, path, so_suffixes): - for so_suffix in so_suffixes: - basename = module_name + so_suffix - if path is None: - path = sys.path - for dirname in path: - filename = os.path.join(dirname, basename) - if os.path.isfile(filename): - return filename - - def collect_types(self): - pass # not needed in the generic engine - - def _prnt(self, what=''): - self._f.write(what + '\n') - - def write_source_to_f(self): - prnt = self._prnt - # first paste some standard set of lines that are mostly '#include' - prnt(cffimod_header) - # then paste the C source given by the user, verbatim. - prnt(self.verifier.preamble) - # - # call generate_gen_xxx_decl(), for every xxx found from - # ffi._parser._declarations. This generates all the functions. - self._generate('decl') - # - # on Windows, distutils insists on putting init_cffi_xyz in - # 'export_symbols', so instead of fighting it, just give up and - # give it one - if sys.platform == 'win32': - if sys.version_info >= (3,): - prefix = 'PyInit_' - else: - prefix = 'init' - modname = self.verifier.get_module_name() - prnt("void %s%s(void) { }\n" % (prefix, modname)) - - def load_library(self, flags=0): - # import it with the CFFI backend - backend = self.ffi._backend - # needs to make a path that contains '/', on Posix - filename = os.path.join(os.curdir, self.verifier.modulefilename) - module = backend.load_library(filename, flags) - # - # call loading_gen_struct() to get the struct layout inferred by - # the C compiler - self._load(module, 'loading') - - # build the FFILibrary class and instance, this is a module subclass - # because modules are expected to have usually-constant-attributes and - # in PyPy this means the JIT is able to treat attributes as constant, - # which we want. - class FFILibrary(types.ModuleType): - _cffi_generic_module = module - _cffi_ffi = self.ffi - _cffi_dir = [] - def __dir__(self): - return FFILibrary._cffi_dir - library = FFILibrary("") - # - # finally, call the loaded_gen_xxx() functions. This will set - # up the 'library' object. - self._load(module, 'loaded', library=library) - return library - - def _get_declarations(self): - lst = [(key, tp) for (key, (tp, qual)) in - self.ffi._parser._declarations.items()] - lst.sort() - return lst - - def _generate(self, step_name): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - try: - method = getattr(self, '_generate_gen_%s_%s' % (kind, - step_name)) - except AttributeError: - raise VerificationError( - "not implemented in verify(): %r" % name) - try: - method(tp, realname) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _load(self, module, step_name, **kwds): - for name, tp in self._get_declarations(): - kind, realname = name.split(' ', 1) - method = getattr(self, '_%s_gen_%s' % (step_name, kind)) - try: - method(tp, realname, module, **kwds) - except Exception as e: - model.attach_exception_info(e, name) - raise - - def _generate_nothing(self, tp, name): - pass - - def _loaded_noop(self, tp, name, module, **kwds): - pass - - # ---------- - # typedefs: generates no code so far - - _generate_gen_typedef_decl = _generate_nothing - _loading_gen_typedef = _loaded_noop - _loaded_gen_typedef = _loaded_noop - - # ---------- - # function declarations - - def _generate_gen_function_decl(self, tp, name): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - # cannot support vararg functions better than this: check for its - # exact type (including the fixed arguments), and build it as a - # constant function pointer (no _cffi_f_%s wrapper) - self._generate_gen_const(False, name, tp) - return - prnt = self._prnt - numargs = len(tp.args) - argnames = [] - for i, type in enumerate(tp.args): - indirection = '' - if isinstance(type, model.StructOrUnion): - indirection = '*' - argnames.append('%sx%d' % (indirection, i)) - context = 'argument of %s' % name - arglist = [type.get_c_name(' %s' % arg, context) - for type, arg in zip(tp.args, argnames)] - tpresult = tp.result - if isinstance(tpresult, model.StructOrUnion): - arglist.insert(0, tpresult.get_c_name(' *r', context)) - tpresult = model.void_type - arglist = ', '.join(arglist) or 'void' - wrappername = '_cffi_f_%s' % name - self.export_symbols.append(wrappername) - if tp.abi: - abi = tp.abi + ' ' - else: - abi = '' - funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) - context = 'result of %s' % name - prnt(tpresult.get_c_name(funcdecl, context)) - prnt('{') - # - if isinstance(tp.result, model.StructOrUnion): - result_code = '*r = ' - elif not isinstance(tp.result, model.VoidType): - result_code = 'return ' - else: - result_code = '' - prnt(' %s%s(%s);' % (result_code, name, ', '.join(argnames))) - prnt('}') - prnt() - - _loading_gen_function = _loaded_noop - - def _loaded_gen_function(self, tp, name, module, library): - assert isinstance(tp, model.FunctionPtrType) - if tp.ellipsis: - newfunction = self._load_constant(False, tp, name, module) - else: - indirections = [] - base_tp = tp - if (any(isinstance(typ, model.StructOrUnion) for typ in tp.args) - or isinstance(tp.result, model.StructOrUnion)): - indirect_args = [] - for i, typ in enumerate(tp.args): - if isinstance(typ, model.StructOrUnion): - typ = model.PointerType(typ) - indirections.append((i, typ)) - indirect_args.append(typ) - indirect_result = tp.result - if isinstance(indirect_result, model.StructOrUnion): - if indirect_result.fldtypes is None: - raise TypeError("'%s' is used as result type, " - "but is opaque" % ( - indirect_result._get_c_name(),)) - indirect_result = model.PointerType(indirect_result) - indirect_args.insert(0, indirect_result) - indirections.insert(0, ("result", indirect_result)) - indirect_result = model.void_type - tp = model.FunctionPtrType(tuple(indirect_args), - indirect_result, tp.ellipsis) - BFunc = self.ffi._get_cached_btype(tp) - wrappername = '_cffi_f_%s' % name - newfunction = module.load_function(BFunc, wrappername) - for i, typ in indirections: - newfunction = self._make_struct_wrapper(newfunction, i, typ, - base_tp) - setattr(library, name, newfunction) - type(library)._cffi_dir.append(name) - - def _make_struct_wrapper(self, oldfunc, i, tp, base_tp): - backend = self.ffi._backend - BType = self.ffi._get_cached_btype(tp) - if i == "result": - ffi = self.ffi - def newfunc(*args): - res = ffi.new(BType) - oldfunc(res, *args) - return res[0] - else: - def newfunc(*args): - args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:] - return oldfunc(*args) - newfunc._cffi_base_type = base_tp - return newfunc - - # ---------- - # named structs - - def _generate_gen_struct_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'struct', name) - - def _loading_gen_struct(self, tp, name, module): - self._loading_struct_or_union(tp, 'struct', name, module) - - def _loaded_gen_struct(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_gen_union_decl(self, tp, name): - assert name == tp.name - self._generate_struct_or_union_decl(tp, 'union', name) - - def _loading_gen_union(self, tp, name, module): - self._loading_struct_or_union(tp, 'union', name, module) - - def _loaded_gen_union(self, tp, name, module, **kwds): - self._loaded_struct_or_union(tp) - - def _generate_struct_or_union_decl(self, tp, prefix, name): - if tp.fldnames is None: - return # nothing to do with opaque structs - checkfuncname = '_cffi_check_%s_%s' % (prefix, name) - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - cname = ('%s %s' % (prefix, name)).strip() - # - prnt = self._prnt - prnt('static void %s(%s *p)' % (checkfuncname, cname)) - prnt('{') - prnt(' /* only to generate compile-time warnings or errors */') - prnt(' (void)p;') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if (isinstance(ftype, model.PrimitiveType) - and ftype.is_integer_type()) or fbitsize >= 0: - # accept all integers, but complain on float or double - prnt(' (void)((p->%s) << 1);' % fname) - else: - # only accept exactly the type declared. - try: - prnt(' { %s = &p->%s; (void)tmp; }' % ( - ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), - fname)) - except VerificationError as e: - prnt(' /* %s */' % str(e)) # cannot verify it, ignore - prnt('}') - self.export_symbols.append(layoutfuncname) - prnt('intptr_t %s(intptr_t i)' % (layoutfuncname,)) - prnt('{') - prnt(' struct _cffi_aligncheck { char x; %s y; };' % cname) - prnt(' static intptr_t nums[] = {') - prnt(' sizeof(%s),' % cname) - prnt(' offsetof(struct _cffi_aligncheck, y),') - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - prnt(' offsetof(%s, %s),' % (cname, fname)) - if isinstance(ftype, model.ArrayType) and ftype.length is None: - prnt(' 0, /* %s */' % ftype._get_c_name()) - else: - prnt(' sizeof(((%s *)0)->%s),' % (cname, fname)) - prnt(' -1') - prnt(' };') - prnt(' return nums[i];') - prnt(' /* the next line is not executed, but compiled */') - prnt(' %s(0);' % (checkfuncname,)) - prnt('}') - prnt() - - def _loading_struct_or_union(self, tp, prefix, name, module): - if tp.fldnames is None: - return # nothing to do with opaque structs - layoutfuncname = '_cffi_layout_%s_%s' % (prefix, name) - # - BFunc = self.ffi._typeof_locked("intptr_t(*)(intptr_t)")[0] - function = module.load_function(BFunc, layoutfuncname) - layout = [] - num = 0 - while True: - x = function(num) - if x < 0: break - layout.append(x) - num += 1 - if isinstance(tp, model.StructOrUnion) and tp.partial: - # use the function()'s sizes and offsets to guide the - # layout of the struct - totalsize = layout[0] - totalalignment = layout[1] - fieldofs = layout[2::2] - fieldsize = layout[3::2] - tp.force_flatten() - assert len(fieldofs) == len(fieldsize) == len(tp.fldnames) - tp.fixedlayout = fieldofs, fieldsize, totalsize, totalalignment - else: - cname = ('%s %s' % (prefix, name)).strip() - self._struct_pending_verification[tp] = layout, cname - - def _loaded_struct_or_union(self, tp): - if tp.fldnames is None: - return # nothing to do with opaque structs - self.ffi._get_cached_btype(tp) # force 'fixedlayout' to be considered - - if tp in self._struct_pending_verification: - # check that the layout sizes and offsets match the real ones - def check(realvalue, expectedvalue, msg): - if realvalue != expectedvalue: - raise VerificationError( - "%s (we have %d, but C compiler says %d)" - % (msg, expectedvalue, realvalue)) - ffi = self.ffi - BStruct = ffi._get_cached_btype(tp) - layout, cname = self._struct_pending_verification.pop(tp) - check(layout[0], ffi.sizeof(BStruct), "wrong total size") - check(layout[1], ffi.alignof(BStruct), "wrong total alignment") - i = 2 - for fname, ftype, fbitsize, fqual in tp.enumfields(): - if fbitsize >= 0: - continue # xxx ignore fbitsize for now - check(layout[i], ffi.offsetof(BStruct, fname), - "wrong offset for field %r" % (fname,)) - if layout[i+1] != 0: - BField = ffi._get_cached_btype(ftype) - check(layout[i+1], ffi.sizeof(BField), - "wrong size for field %r" % (fname,)) - i += 2 - assert i == len(layout) - - # ---------- - # 'anonymous' declarations. These are produced for anonymous structs - # or unions; the 'name' is obtained by a typedef. - - def _generate_gen_anonymous_decl(self, tp, name): - if isinstance(tp, model.EnumType): - self._generate_gen_enum_decl(tp, name, '') - else: - self._generate_struct_or_union_decl(tp, '', name) - - def _loading_gen_anonymous(self, tp, name, module): - if isinstance(tp, model.EnumType): - self._loading_gen_enum(tp, name, module, '') - else: - self._loading_struct_or_union(tp, '', name, module) - - def _loaded_gen_anonymous(self, tp, name, module, **kwds): - if isinstance(tp, model.EnumType): - self._loaded_gen_enum(tp, name, module, **kwds) - else: - self._loaded_struct_or_union(tp) - - # ---------- - # constants, likely declared with '#define' - - def _generate_gen_const(self, is_int, name, tp=None, category='const', - check_value=None): - prnt = self._prnt - funcname = '_cffi_%s_%s' % (category, name) - self.export_symbols.append(funcname) - if check_value is not None: - assert is_int - assert category == 'const' - prnt('int %s(char *out_error)' % funcname) - prnt('{') - self._check_int_constant_value(name, check_value) - prnt(' return 0;') - prnt('}') - elif is_int: - assert category == 'const' - prnt('int %s(long long *out_value)' % funcname) - prnt('{') - prnt(' *out_value = (long long)(%s);' % (name,)) - prnt(' return (%s) <= 0;' % (name,)) - prnt('}') - else: - assert tp is not None - assert check_value is None - if category == 'var': - ampersand = '&' - else: - ampersand = '' - extra = '' - if category == 'const' and isinstance(tp, model.StructOrUnion): - extra = 'const *' - ampersand = '&' - prnt(tp.get_c_name(' %s%s(void)' % (extra, funcname), name)) - prnt('{') - prnt(' return (%s%s);' % (ampersand, name)) - prnt('}') - prnt() - - def _generate_gen_constant_decl(self, tp, name): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - self._generate_gen_const(is_int, name, tp) - - _loading_gen_constant = _loaded_noop - - def _load_constant(self, is_int, tp, name, module, check_value=None): - funcname = '_cffi_const_%s' % name - if check_value is not None: - assert is_int - self._load_known_int_constant(module, funcname) - value = check_value - elif is_int: - BType = self.ffi._typeof_locked("long long*")[0] - BFunc = self.ffi._typeof_locked("int(*)(long long*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType) - negative = function(p) - value = int(p[0]) - if value < 0 and not negative: - BLongLong = self.ffi._typeof_locked("long long")[0] - value += (1 << (8*self.ffi.sizeof(BLongLong))) - else: - assert check_value is None - fntypeextra = '(*)(void)' - if isinstance(tp, model.StructOrUnion): - fntypeextra = '*' + fntypeextra - BFunc = self.ffi._typeof_locked(tp.get_c_name(fntypeextra, name))[0] - function = module.load_function(BFunc, funcname) - value = function() - if isinstance(tp, model.StructOrUnion): - value = value[0] - return value - - def _loaded_gen_constant(self, tp, name, module, library): - is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type() - value = self._load_constant(is_int, tp, name, module) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # enums - - def _check_int_constant_value(self, name, value): - prnt = self._prnt - if value <= 0: - prnt(' if ((%s) > 0 || (long)(%s) != %dL) {' % ( - name, name, value)) - else: - prnt(' if ((%s) <= 0 || (unsigned long)(%s) != %dUL) {' % ( - name, name, value)) - prnt(' char buf[64];') - prnt(' if ((%s) <= 0)' % name) - prnt(' sprintf(buf, "%%ld", (long)(%s));' % name) - prnt(' else') - prnt(' sprintf(buf, "%%lu", (unsigned long)(%s));' % - name) - prnt(' sprintf(out_error, "%s has the real value %s, not %s",') - prnt(' "%s", buf, "%d");' % (name[:100], value)) - prnt(' return -1;') - prnt(' }') - - def _load_known_int_constant(self, module, funcname): - BType = self.ffi._typeof_locked("char[]")[0] - BFunc = self.ffi._typeof_locked("int(*)(char*)")[0] - function = module.load_function(BFunc, funcname) - p = self.ffi.new(BType, 256) - if function(p) < 0: - error = self.ffi.string(p) - if sys.version_info >= (3,): - error = str(error, 'utf-8') - raise VerificationError(error) - - def _enum_funcname(self, prefix, name): - # "$enum_$1" => "___D_enum____D_1" - name = name.replace('$', '___D_') - return '_cffi_e_%s_%s' % (prefix, name) - - def _generate_gen_enum_decl(self, tp, name, prefix='enum'): - if tp.partial: - for enumerator in tp.enumerators: - self._generate_gen_const(True, enumerator) - return - # - funcname = self._enum_funcname(prefix, name) - self.export_symbols.append(funcname) - prnt = self._prnt - prnt('int %s(char *out_error)' % funcname) - prnt('{') - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - self._check_int_constant_value(enumerator, enumvalue) - prnt(' return 0;') - prnt('}') - prnt() - - def _loading_gen_enum(self, tp, name, module, prefix='enum'): - if tp.partial: - enumvalues = [self._load_constant(True, tp, enumerator, module) - for enumerator in tp.enumerators] - tp.enumvalues = tuple(enumvalues) - tp.partial_resolved = True - else: - funcname = self._enum_funcname(prefix, name) - self._load_known_int_constant(module, funcname) - - def _loaded_gen_enum(self, tp, name, module, library): - for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): - setattr(library, enumerator, enumvalue) - type(library)._cffi_dir.append(enumerator) - - # ---------- - # macros: for now only for integers - - def _generate_gen_macro_decl(self, tp, name): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - self._generate_gen_const(True, name, check_value=check_value) - - _loading_gen_macro = _loaded_noop - - def _loaded_gen_macro(self, tp, name, module, library): - if tp == '...': - check_value = None - else: - check_value = tp # an integer - value = self._load_constant(True, tp, name, module, - check_value=check_value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - - # ---------- - # global variables - - def _generate_gen_variable_decl(self, tp, name): - if isinstance(tp, model.ArrayType): - if tp.length_is_unknown(): - prnt = self._prnt - funcname = '_cffi_sizeof_%s' % (name,) - self.export_symbols.append(funcname) - prnt("size_t %s(void)" % funcname) - prnt("{") - prnt(" return sizeof(%s);" % (name,)) - prnt("}") - tp_ptr = model.PointerType(tp.item) - self._generate_gen_const(False, name, tp_ptr) - else: - tp_ptr = model.PointerType(tp) - self._generate_gen_const(False, name, tp_ptr, category='var') - - _loading_gen_variable = _loaded_noop - - def _loaded_gen_variable(self, tp, name, module, library): - if isinstance(tp, model.ArrayType): # int a[5] is "constant" in the - # sense that "a=..." is forbidden - if tp.length_is_unknown(): - funcname = '_cffi_sizeof_%s' % (name,) - BFunc = self.ffi._typeof_locked('size_t(*)(void)')[0] - function = module.load_function(BFunc, funcname) - size = function() - BItemType = self.ffi._get_cached_btype(tp.item) - length, rest = divmod(size, self.ffi.sizeof(BItemType)) - if rest != 0: - raise VerificationError( - "bad size: %r does not seem to be an array of %s" % - (name, tp.item)) - tp = tp.resolve_length(length) - tp_ptr = model.PointerType(tp.item) - value = self._load_constant(False, tp_ptr, name, module) - # 'value' is a which we have to replace with - # a if the N is actually known - if tp.length is not None: - BArray = self.ffi._get_cached_btype(tp) - value = self.ffi.cast(BArray, value) - setattr(library, name, value) - type(library)._cffi_dir.append(name) - return - # remove ptr= from the library instance, and replace - # it by a property on the class, which reads/writes into ptr[0]. - funcname = '_cffi_var_%s' % name - BFunc = self.ffi._typeof_locked(tp.get_c_name('*(*)(void)', name))[0] - function = module.load_function(BFunc, funcname) - ptr = function() - def getter(library): - return ptr[0] - def setter(library, value): - ptr[0] = value - setattr(type(library), name, property(getter, setter)) - type(library)._cffi_dir.append(name) - -cffimod_header = r''' -#include -#include -#include -#include -#include /* XXX for ssize_t on some platforms */ - -/* this block of #ifs should be kept exactly identical between - c/_cffi_backend.c, cffi/vengine_cpy.py, cffi/vengine_gen.py - and cffi/_cffi_include.h */ -#if defined(_MSC_VER) -# include /* for alloca() */ -# if _MSC_VER < 1600 /* MSVC < 2010 */ - typedef __int8 int8_t; - typedef __int16 int16_t; - typedef __int32 int32_t; - typedef __int64 int64_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int64 uint64_t; - typedef __int8 int_least8_t; - typedef __int16 int_least16_t; - typedef __int32 int_least32_t; - typedef __int64 int_least64_t; - typedef unsigned __int8 uint_least8_t; - typedef unsigned __int16 uint_least16_t; - typedef unsigned __int32 uint_least32_t; - typedef unsigned __int64 uint_least64_t; - typedef __int8 int_fast8_t; - typedef __int16 int_fast16_t; - typedef __int32 int_fast32_t; - typedef __int64 int_fast64_t; - typedef unsigned __int8 uint_fast8_t; - typedef unsigned __int16 uint_fast16_t; - typedef unsigned __int32 uint_fast32_t; - typedef unsigned __int64 uint_fast64_t; - typedef __int64 intmax_t; - typedef unsigned __int64 uintmax_t; -# else -# include -# endif -# if _MSC_VER < 1800 /* MSVC < 2013 */ -# ifndef __cplusplus - typedef unsigned char _Bool; -# endif -# endif -#else -# include -# if (defined (__SVR4) && defined (__sun)) || defined(_AIX) || defined(__hpux) -# include -# endif -#endif -''' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py b/dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py deleted file mode 100644 index 9fbfef27a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cffi/verifier.py +++ /dev/null @@ -1,307 +0,0 @@ -# -# DEPRECATED: implementation for ffi.verify() -# -import sys, os, binascii, shutil, io -from . import __version_verifier_modules__ -from . import ffiplatform -from .error import VerificationError - -if sys.version_info >= (3, 3): - import importlib.machinery - def _extension_suffixes(): - return importlib.machinery.EXTENSION_SUFFIXES[:] -else: - import imp - def _extension_suffixes(): - return [suffix for suffix, _, type in imp.get_suffixes() - if type == imp.C_EXTENSION] - - -if sys.version_info >= (3,): - NativeIO = io.StringIO -else: - class NativeIO(io.BytesIO): - def write(self, s): - if isinstance(s, unicode): - s = s.encode('ascii') - super(NativeIO, self).write(s) - - -class Verifier(object): - - def __init__(self, ffi, preamble, tmpdir=None, modulename=None, - ext_package=None, tag='', force_generic_engine=False, - source_extension='.c', flags=None, relative_to=None, **kwds): - if ffi._parser._uses_new_feature: - raise VerificationError( - "feature not supported with ffi.verify(), but only " - "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) - self.ffi = ffi - self.preamble = preamble - if not modulename: - flattened_kwds = ffiplatform.flatten(kwds) - vengine_class = _locate_engine_class(ffi, force_generic_engine) - self._vengine = vengine_class(self) - self._vengine.patch_extension_kwds(kwds) - self.flags = flags - self.kwds = self.make_relative_to(kwds, relative_to) - # - if modulename: - if tag: - raise TypeError("can't specify both 'modulename' and 'tag'") - else: - key = '\x00'.join(['%d.%d' % sys.version_info[:2], - __version_verifier_modules__, - preamble, flattened_kwds] + - ffi._cdefsources) - if sys.version_info >= (3,): - key = key.encode('utf-8') - k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) - k1 = k1.lstrip('0x').rstrip('L') - k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) - k2 = k2.lstrip('0').rstrip('L') - modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, - k1, k2) - suffix = _get_so_suffixes()[0] - self.tmpdir = tmpdir or _caller_dir_pycache() - self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) - self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) - self.ext_package = ext_package - self._has_source = False - self._has_module = False - - def write_source(self, file=None): - """Write the C source code. It is produced in 'self.sourcefilename', - which can be tweaked beforehand.""" - with self.ffi._lock: - if self._has_source and file is None: - raise VerificationError( - "source code already written") - self._write_source(file) - - def compile_module(self): - """Write the C source code (if not done already) and compile it. - This produces a dynamic link library in 'self.modulefilename'.""" - with self.ffi._lock: - if self._has_module: - raise VerificationError("module already compiled") - if not self._has_source: - self._write_source() - self._compile_module() - - def load_library(self): - """Get a C module from this Verifier instance. - Returns an instance of a FFILibrary class that behaves like the - objects returned by ffi.dlopen(), but that delegates all - operations to the C module. If necessary, the C code is written - and compiled first. - """ - with self.ffi._lock: - if not self._has_module: - self._locate_module() - if not self._has_module: - if not self._has_source: - self._write_source() - self._compile_module() - return self._load_library() - - def get_module_name(self): - basename = os.path.basename(self.modulefilename) - # kill both the .so extension and the other .'s, as introduced - # by Python 3: 'basename.cpython-33m.so' - basename = basename.split('.', 1)[0] - # and the _d added in Python 2 debug builds --- but try to be - # conservative and not kill a legitimate _d - if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): - basename = basename[:-2] - return basename - - def get_extension(self): - ffiplatform._hack_at_distutils() # backward compatibility hack - if not self._has_source: - with self.ffi._lock: - if not self._has_source: - self._write_source() - sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) - modname = self.get_module_name() - return ffiplatform.get_extension(sourcename, modname, **self.kwds) - - def generates_python_module(self): - return self._vengine._gen_python_module - - def make_relative_to(self, kwds, relative_to): - if relative_to and os.path.dirname(relative_to): - dirname = os.path.dirname(relative_to) - kwds = kwds.copy() - for key in ffiplatform.LIST_OF_FILE_NAMES: - if key in kwds: - lst = kwds[key] - if not isinstance(lst, (list, tuple)): - raise TypeError("keyword '%s' should be a list or tuple" - % (key,)) - lst = [os.path.join(dirname, fn) for fn in lst] - kwds[key] = lst - return kwds - - # ---------- - - def _locate_module(self): - if not os.path.isfile(self.modulefilename): - if self.ext_package: - try: - pkg = __import__(self.ext_package, None, None, ['__doc__']) - except ImportError: - return # cannot import the package itself, give up - # (e.g. it might be called differently before installation) - path = pkg.__path__ - else: - path = None - filename = self._vengine.find_module(self.get_module_name(), path, - _get_so_suffixes()) - if filename is None: - return - self.modulefilename = filename - self._vengine.collect_types() - self._has_module = True - - def _write_source_to(self, file): - self._vengine._f = file - try: - self._vengine.write_source_to_f() - finally: - del self._vengine._f - - def _write_source(self, file=None): - if file is not None: - self._write_source_to(file) - else: - # Write our source file to an in memory file. - f = NativeIO() - self._write_source_to(f) - source_data = f.getvalue() - - # Determine if this matches the current file - if os.path.exists(self.sourcefilename): - with open(self.sourcefilename, "r") as fp: - needs_written = not (fp.read() == source_data) - else: - needs_written = True - - # Actually write the file out if it doesn't match - if needs_written: - _ensure_dir(self.sourcefilename) - with open(self.sourcefilename, "w") as fp: - fp.write(source_data) - - # Set this flag - self._has_source = True - - def _compile_module(self): - # compile this C source - tmpdir = os.path.dirname(self.sourcefilename) - outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) - try: - same = ffiplatform.samefile(outputfilename, self.modulefilename) - except OSError: - same = False - if not same: - _ensure_dir(self.modulefilename) - shutil.move(outputfilename, self.modulefilename) - self._has_module = True - - def _load_library(self): - assert self._has_module - if self.flags is not None: - return self._vengine.load_library(self.flags) - else: - return self._vengine.load_library() - -# ____________________________________________________________ - -_FORCE_GENERIC_ENGINE = False # for tests - -def _locate_engine_class(ffi, force_generic_engine): - if _FORCE_GENERIC_ENGINE: - force_generic_engine = True - if not force_generic_engine: - if '__pypy__' in sys.builtin_module_names: - force_generic_engine = True - else: - try: - import _cffi_backend - except ImportError: - _cffi_backend = '?' - if ffi._backend is not _cffi_backend: - force_generic_engine = True - if force_generic_engine: - from . import vengine_gen - return vengine_gen.VGenericEngine - else: - from . import vengine_cpy - return vengine_cpy.VCPythonEngine - -# ____________________________________________________________ - -_TMPDIR = None - -def _caller_dir_pycache(): - if _TMPDIR: - return _TMPDIR - result = os.environ.get('CFFI_TMPDIR') - if result: - return result - filename = sys._getframe(2).f_code.co_filename - return os.path.abspath(os.path.join(os.path.dirname(filename), - '__pycache__')) - -def set_tmpdir(dirname): - """Set the temporary directory to use instead of __pycache__.""" - global _TMPDIR - _TMPDIR = dirname - -def cleanup_tmpdir(tmpdir=None, keep_so=False): - """Clean up the temporary directory by removing all files in it - called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" - tmpdir = tmpdir or _caller_dir_pycache() - try: - filelist = os.listdir(tmpdir) - except OSError: - return - if keep_so: - suffix = '.c' # only remove .c files - else: - suffix = _get_so_suffixes()[0].lower() - for fn in filelist: - if fn.lower().startswith('_cffi_') and ( - fn.lower().endswith(suffix) or fn.lower().endswith('.c')): - try: - os.unlink(os.path.join(tmpdir, fn)) - except OSError: - pass - clean_dir = [os.path.join(tmpdir, 'build')] - for dir in clean_dir: - try: - for fn in os.listdir(dir): - fn = os.path.join(dir, fn) - if os.path.isdir(fn): - clean_dir.append(fn) - else: - os.unlink(fn) - except OSError: - pass - -def _get_so_suffixes(): - suffixes = _extension_suffixes() - if not suffixes: - # bah, no C_EXTENSION available. Occurs on pypy without cpyext - if sys.platform == 'win32': - suffixes = [".pyd"] - else: - suffixes = [".so"] - - return suffixes - -def _ensure_dir(filename): - dirname = os.path.dirname(filename) - if dirname and not os.path.isdir(dirname): - os.makedirs(dirname) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE deleted file mode 100644 index 07074259b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE +++ /dev/null @@ -1,6 +0,0 @@ -This software is made available under the terms of *either* of the licenses -found in LICENSE.APACHE or LICENSE.BSD. Contributions to cryptography are made -under the terms of *both* these licenses. - -The code used in the OS random engine is derived from CPython, and is licensed -under the terms of the PSF License Agreement. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE deleted file mode 100644 index 62589edd1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - https://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD deleted file mode 100644 index ec1a29d34..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.BSD +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) Individual contributors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - 3. Neither the name of PyCA Cryptography nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF deleted file mode 100644 index 4d3a4f57d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/LICENSE.PSF +++ /dev/null @@ -1,41 +0,0 @@ -1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and - the Individual or Organization ("Licensee") accessing and otherwise using Python - 2.7.12 software in source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby - grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, - analyze, test, perform and/or display publicly, prepare derivative works, - distribute, and otherwise use Python 2.7.12 alone or in any derivative - version, provided, however, that PSF's License Agreement and PSF's notice of - copyright, i.e., "Copyright © 2001-2016 Python Software Foundation; All Rights - Reserved" are retained in Python 2.7.12 alone or in any derivative version - prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on or - incorporates Python 2.7.12 or any part thereof, and wants to make the - derivative work available to others as provided herein, then Licensee hereby - agrees to include in any such work a brief summary of the changes made to Python - 2.7.12. - -4. PSF is making Python 2.7.12 available to Licensee on an "AS IS" basis. - PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF - EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR - WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE - USE OF PYTHON 2.7.12 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.12 - FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF - MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.12, OR ANY DERIVATIVE - THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material breach of - its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any relationship - of agency, partnership, or joint venture between PSF and Licensee. This License - Agreement does not grant permission to use PSF trademarks or trade name in a - trademark sense to endorse or promote products or services of Licensee, or any - third party. - -8. By copying, installing or otherwise using Python 2.7.12, Licensee agrees - to be bound by the terms and conditions of this License Agreement. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA deleted file mode 100644 index a6b90cb1b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/METADATA +++ /dev/null @@ -1,134 +0,0 @@ -Metadata-Version: 2.1 -Name: cryptography -Version: 39.0.0 -Summary: cryptography is a package which provides cryptographic recipes and primitives to Python developers. -Home-page: https://github.com/pyca/cryptography -Author: The Python Cryptographic Authority and individual contributors -Author-email: cryptography-dev@python.org -License: (Apache-2.0 OR BSD-3-Clause) AND PSF-2.0 -Project-URL: Documentation, https://cryptography.io/ -Project-URL: Source, https://github.com/pyca/cryptography/ -Project-URL: Issues, https://github.com/pyca/cryptography/issues -Project-URL: Changelog, https://cryptography.io/en/latest/changelog/ -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: Apache Software License -Classifier: License :: OSI Approved :: BSD License -Classifier: Natural Language :: English -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: POSIX -Classifier: Operating System :: POSIX :: BSD -Classifier: Operating System :: POSIX :: Linux -Classifier: Operating System :: Microsoft :: Windows -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3 :: Only -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Security :: Cryptography -Requires-Python: >=3.6 -Description-Content-Type: text/x-rst -License-File: LICENSE -License-File: LICENSE.APACHE -License-File: LICENSE.BSD -License-File: LICENSE.PSF -Requires-Dist: cffi (>=1.12) -Provides-Extra: docs -Requires-Dist: sphinx (!=1.8.0,!=3.1.0,!=3.1.1,!=5.2.0,!=5.2.0.post0,>=1.6.5) ; extra == 'docs' -Requires-Dist: sphinx-rtd-theme ; extra == 'docs' -Provides-Extra: docstest -Requires-Dist: pyenchant (>=1.6.11) ; extra == 'docstest' -Requires-Dist: twine (>=1.12.0) ; extra == 'docstest' -Requires-Dist: sphinxcontrib-spelling (>=4.0.1) ; extra == 'docstest' -Provides-Extra: pep8test -Requires-Dist: black ; extra == 'pep8test' -Requires-Dist: ruff ; extra == 'pep8test' -Provides-Extra: sdist -Requires-Dist: setuptools-rust (>=0.11.4) ; extra == 'sdist' -Provides-Extra: ssh -Requires-Dist: bcrypt (>=3.1.5) ; extra == 'ssh' -Provides-Extra: test -Requires-Dist: pytest (>=6.2.0) ; extra == 'test' -Requires-Dist: pytest-benchmark ; extra == 'test' -Requires-Dist: pytest-cov ; extra == 'test' -Requires-Dist: pytest-subtests ; extra == 'test' -Requires-Dist: pytest-xdist ; extra == 'test' -Requires-Dist: pretend ; extra == 'test' -Requires-Dist: iso8601 ; extra == 'test' -Requires-Dist: pytz ; extra == 'test' -Requires-Dist: hypothesis (!=3.79.2,>=1.11.4) ; extra == 'test' - -pyca/cryptography -================= - -.. image:: https://img.shields.io/pypi/v/cryptography.svg - :target: https://pypi.org/project/cryptography/ - :alt: Latest Version - -.. image:: https://readthedocs.org/projects/cryptography/badge/?version=latest - :target: https://cryptography.io - :alt: Latest Docs - -.. image:: https://github.com/pyca/cryptography/workflows/CI/badge.svg?branch=main - :target: https://github.com/pyca/cryptography/actions?query=workflow%3ACI+branch%3Amain - - -``cryptography`` is a package which provides cryptographic recipes and -primitives to Python developers. Our goal is for it to be your "cryptographic -standard library". It supports Python 3.6+ and PyPy3 7.2+. - -``cryptography`` includes both high level recipes and low level interfaces to -common cryptographic algorithms such as symmetric ciphers, message digests, and -key derivation functions. For example, to encrypt something with -``cryptography``'s high level symmetric encryption recipe: - -.. code-block:: pycon - - >>> from cryptography.fernet import Fernet - >>> # Put this somewhere safe! - >>> key = Fernet.generate_key() - >>> f = Fernet(key) - >>> token = f.encrypt(b"A really secret message. Not for prying eyes.") - >>> token - b'...' - >>> f.decrypt(token) - b'A really secret message. Not for prying eyes.' - -You can find more information in the `documentation`_. - -You can install ``cryptography`` with: - -.. code-block:: console - - $ pip install cryptography - -For full details see `the installation documentation`_. - -Discussion -~~~~~~~~~~ - -If you run into bugs, you can file them in our `issue tracker`_. - -We maintain a `cryptography-dev`_ mailing list for development discussion. - -You can also join ``#pyca`` on ``irc.libera.chat`` to ask questions or get -involved. - -Security -~~~~~~~~ - -Need to report a security issue? Please consult our `security reporting`_ -documentation. - - -.. _`documentation`: https://cryptography.io/ -.. _`the installation documentation`: https://cryptography.io/en/latest/installation/ -.. _`issue tracker`: https://github.com/pyca/cryptography/issues -.. _`cryptography-dev`: https://mail.python.org/mailman/listinfo/cryptography-dev -.. _`security reporting`: https://cryptography.io/en/latest/security/ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD deleted file mode 100644 index 59e30294f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/RECORD +++ /dev/null @@ -1,180 +0,0 @@ -cryptography-39.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -cryptography-39.0.0.dist-info/LICENSE,sha256=Q9rSzHUqtyHNmp827OcPtTq3cTVR8tPYaU2OjFoG1uI,323 -cryptography-39.0.0.dist-info/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360 -cryptography-39.0.0.dist-info/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532 -cryptography-39.0.0.dist-info/LICENSE.PSF,sha256=aT7ApmKzn5laTyUrA6YiKUVHDBtvEsoCkY5O_g32S58,2415 -cryptography-39.0.0.dist-info/METADATA,sha256=9_Lv-r92FYmcasTY96FUL4KvsyXjXD16mimgcXZDS-I,5398 -cryptography-39.0.0.dist-info/RECORD,, -cryptography-39.0.0.dist-info/WHEEL,sha256=CFPxCuvaTIZS0h5c8o2xFStPFn1i6Rraxc2uR61QpoA,100 -cryptography-39.0.0.dist-info/top_level.txt,sha256=KNaT-Sn2K4uxNaEbe6mYdDn3qWDMlp4y-MtWfB73nJc,13 -cryptography/__about__.py,sha256=NhtFVw-0uEYjXuKl7COCAUliy4IsXpQnSVM0TbWb7pk,417 -cryptography/__init__.py,sha256=iQ8I-Ks1V8lhzOac6IRPpiZZak2MvW-Gvduvhl0lXg8,781 -cryptography/__pycache__/__about__.cpython-310.pyc,, -cryptography/__pycache__/__init__.cpython-310.pyc,, -cryptography/__pycache__/exceptions.cpython-310.pyc,, -cryptography/__pycache__/fernet.cpython-310.pyc,, -cryptography/__pycache__/utils.cpython-310.pyc,, -cryptography/exceptions.py,sha256=sN_VVTF_LuKMM6R-lIASFFuzAmz1uZ2Qbcdko9WyS64,1471 -cryptography/fernet.py,sha256=qO4sQurx79k-5yOh4UnUZGm51zod0wRXJchz0l063To,6851 -cryptography/hazmat/__init__.py,sha256=OYlvgprzULzZlsf3yYTsd6VUVyQmpsbHjgJdNnsyRwE,418 -cryptography/hazmat/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/__pycache__/_oid.cpython-310.pyc,, -cryptography/hazmat/_oid.py,sha256=rCvnwb0z0VCKn7Y92IEQAoPErrANWREydYflZSNRrao,14155 -cryptography/hazmat/backends/__init__.py,sha256=bgrjB1SX2vXX-rmfG7A4PqGkq-isqQVXGaZtjWHAgj0,324 -cryptography/hazmat/backends/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__init__.py,sha256=oCa7eZbqvHsQ1pBeD_OOfnGxVaZbCfWnAKnHqOyPf1c,270 -cryptography/hazmat/backends/openssl/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/aead.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/backend.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/ciphers.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/cmac.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/decode_asn1.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/dh.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/dsa.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/ec.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/ed25519.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/ed448.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/hashes.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/hmac.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/poly1305.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/rsa.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/utils.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/x25519.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/__pycache__/x448.cpython-310.pyc,, -cryptography/hazmat/backends/openssl/aead.py,sha256=uS6dkj7hKQzrQXyktJUXnBK2212sW18RSlXzGp4X5W8,9830 -cryptography/hazmat/backends/openssl/backend.py,sha256=8XELtLZx3twWXGbS41StTpYU9BkWTUA4ms2d966qT70,95375 -cryptography/hazmat/backends/openssl/ciphers.py,sha256=wGMLOZ2wuULqERxWajmb9QpmUewQU4XkI9Un0JtLazU,10341 -cryptography/hazmat/backends/openssl/cmac.py,sha256=cFZtDpqN5PNzo1X9tm8N8WDV5X81GRFXuXRUsjyFtF4,3005 -cryptography/hazmat/backends/openssl/decode_asn1.py,sha256=nSqtgO5MJVf_UUkvw9tez10zhGnsGHq24OP1X2GKOe4,1113 -cryptography/hazmat/backends/openssl/dh.py,sha256=dmWD1JK5mJenrVRJqd_u4hkOI4Y5Zalhu8HtwEDYUxI,12216 -cryptography/hazmat/backends/openssl/dsa.py,sha256=SQwoCTiNHrWjDQOFag3GznWG5K9CWM1AizqJ4usTRbY,8927 -cryptography/hazmat/backends/openssl/ec.py,sha256=kgxwW508FTXDwGG-7pSywBLlICZKKfo4bcTHnNpsvJY,11103 -cryptography/hazmat/backends/openssl/ed25519.py,sha256=adWaawleloe9T0BctejcclybE51dwb-CmL_b0f6zBiU,5921 -cryptography/hazmat/backends/openssl/ed448.py,sha256=Ja_GMzDBcs_8N2PpmU2dd6sszbJh3xP-TrN88MkQLBI,5875 -cryptography/hazmat/backends/openssl/hashes.py,sha256=yFuHeO8qDPRbH2B9JJtW51wEVfhu11SFs3lhHBHGyPA,3240 -cryptography/hazmat/backends/openssl/hmac.py,sha256=mN7irlzO6Rbc3UIDqlySwaW5KoCn28N8gKS3lh9WEUg,3094 -cryptography/hazmat/backends/openssl/poly1305.py,sha256=Oivx5k9DcAU_BSySxEQiw5tE1pcz-ljmFpmXAPZqJrI,2513 -cryptography/hazmat/backends/openssl/rsa.py,sha256=bTVNj5ODSDvgCl_OdTolCMZC25okI_AU2g7qAr5qlfk,21626 -cryptography/hazmat/backends/openssl/utils.py,sha256=7Ara81KkY0QCLPqW6kUG9dEsp52cZ3kOUJczwEpecJ0,1977 -cryptography/hazmat/backends/openssl/x25519.py,sha256=Fu8e-z3iV69oVIXz962vu0VRKRTsuFAUpuquEfSm9tY,4753 -cryptography/hazmat/backends/openssl/x448.py,sha256=6tZgh44ipS_UWJ6amueXxc8xIXdIfFtdpvnhri-oxXs,4339 -cryptography/hazmat/bindings/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/bindings/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/bindings/_openssl.pyd,sha256=WTSmmo8FNePv2ZJo2JSuUtmqcBGKX_66Pi_k-vzktGQ,3964416 -cryptography/hazmat/bindings/_openssl.pyi,sha256=mpNJLuYLbCVrd5i33FBTmWwL_55Dw7JPkSLlSX9Q7oI,230 -cryptography/hazmat/bindings/_rust.pyd,sha256=6bMZ866dhCecXoJ1uXlcaWhdNEjWM96UyCSoEgM24BE,1686528 -cryptography/hazmat/bindings/_rust/__init__.pyi,sha256=CHojGtYxYjj16E8tJiVJy950XuGMATdGq6PPkztHQxs,983 -cryptography/hazmat/bindings/_rust/asn1.pyi,sha256=9CyI-grOsLQB_hfnhJPoG9dNOdJ7Zg6B0iUpzCowh44,592 -cryptography/hazmat/bindings/_rust/ocsp.pyi,sha256=Y6ZY8P6xlz5WFedNj1U4nxcaFFXFTHUVnGFB6LA9b9M,909 -cryptography/hazmat/bindings/_rust/pkcs7.pyi,sha256=VkTC78wjJgb_qrboOYIFPuFZ3W46zsr6zsxnlrOMwao,460 -cryptography/hazmat/bindings/_rust/x509.pyi,sha256=jwTin2QHfdX7XfhZPwMp0JVw_UbyB-YG2GGwFG15a74,1751 -cryptography/hazmat/bindings/openssl/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/bindings/openssl/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/bindings/openssl/__pycache__/_conditional.cpython-310.pyc,, -cryptography/hazmat/bindings/openssl/__pycache__/binding.cpython-310.pyc,, -cryptography/hazmat/bindings/openssl/_conditional.py,sha256=M7T58AwF0LqrUWzMofCB8t8WKITd2-UJqMdZAc_bL9A,9893 -cryptography/hazmat/bindings/openssl/binding.py,sha256=ItRz2bAUPbtHaQFtH7dGmazO5kspBh6gjBdRVzcfo8k,8738 -cryptography/hazmat/primitives/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/primitives/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/_asymmetric.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/_cipheralgorithm.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/_serialization.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/cmac.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/constant_time.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/hashes.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/hmac.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/keywrap.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/padding.cpython-310.pyc,, -cryptography/hazmat/primitives/__pycache__/poly1305.cpython-310.pyc,, -cryptography/hazmat/primitives/_asymmetric.py,sha256=QacvnyA1fcXWbSAASCiodHVcTYwkaMdzq6KUIlaO7H0,496 -cryptography/hazmat/primitives/_cipheralgorithm.py,sha256=3VSLRa30MqRs9qeNwopLG3_7bIQAp7Q77EJk6i9yJEs,1063 -cryptography/hazmat/primitives/_serialization.py,sha256=HssBsIm3rNVPct1nZTACJzbymZc2WaZAWdkg1l5slD0,5196 -cryptography/hazmat/primitives/asymmetric/__init__.py,sha256=s9oKCQ2ycFdXoERdS1imafueSkBsL9kvbyfghaauZ9Y,180 -cryptography/hazmat/primitives/asymmetric/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/dh.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/dsa.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ec.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ed25519.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/ed448.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/padding.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/rsa.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/types.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/utils.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/x25519.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/__pycache__/x448.cpython-310.pyc,, -cryptography/hazmat/primitives/asymmetric/dh.py,sha256=swBaY7eQWFb6EEc8mxygeryA7pEXkbjjU9-fx-0G2gE,6627 -cryptography/hazmat/primitives/asymmetric/dsa.py,sha256=JufsxrrxeJQlsiWMmx_44l90FNRw19o9kcKtk4rO8TU,7885 -cryptography/hazmat/primitives/asymmetric/ec.py,sha256=CdxppDV1lV2QlrQ0EhniqvFi8wp8PDYsvFWdpzyyVIY,12725 -cryptography/hazmat/primitives/asymmetric/ed25519.py,sha256=7Btmrsamd1joaFbjNOMekA4VtWfKJD-paJcubJzyRPc,2727 -cryptography/hazmat/primitives/asymmetric/ed448.py,sha256=oR-j4jGcWUnGxWi1GygHxVZbgkSOKHsR6y1E3Lf6wYM,2647 -cryptography/hazmat/primitives/asymmetric/padding.py,sha256=EkKuY9e6UFqSuQ0LvyKYKl_L19tOfNCTlHWEiKgHeUc,2690 -cryptography/hazmat/primitives/asymmetric/rsa.py,sha256=njFky5AkSrsBh47PeVLjj81SOLOiZaxAUSzGWD2Znxw,11479 -cryptography/hazmat/primitives/asymmetric/types.py,sha256=A-jXO0if3rZbKONGkYvisMiLntNx6P4g_xCNpxi50W8,1813 -cryptography/hazmat/primitives/asymmetric/utils.py,sha256=p6nF7EzF0sp5GYFTw1HEhPYYjuTik53WTUkvuPIfDRk,755 -cryptography/hazmat/primitives/asymmetric/x25519.py,sha256=-nbaGlgT1sufO9Ic-urwKDql8Da0U3GL6hZJIMqHgVc,2588 -cryptography/hazmat/primitives/asymmetric/x448.py,sha256=V3lxb1VOiRTa3bzVUC3uZat2ogfExUOdktCIGUUMZ2Y,2556 -cryptography/hazmat/primitives/ciphers/__init__.py,sha256=2K5I_haxK0BLNqSZcQUqcjf8FmHY8xV1U-XjfgUmkM8,645 -cryptography/hazmat/primitives/ciphers/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/aead.cpython-310.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/algorithms.cpython-310.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/base.cpython-310.pyc,, -cryptography/hazmat/primitives/ciphers/__pycache__/modes.cpython-310.pyc,, -cryptography/hazmat/primitives/ciphers/aead.py,sha256=vteCHw01e57SKCpfkbiPZw4CLswb9-40Ozhbqh_YmHI,11940 -cryptography/hazmat/primitives/ciphers/algorithms.py,sha256=J1qeJK97fpSaX1E0ENsWvJG_qKGoOZvm57baBGOQofQ,5135 -cryptography/hazmat/primitives/ciphers/base.py,sha256=HFC7opE-LpjxBI3g7v2Q1EGb3VhKj-JBKNFsL_-AlnY,8270 -cryptography/hazmat/primitives/ciphers/modes.py,sha256=OnoG6UsqsLzV_zfIjJySoE-cOakfiloclOa99t-nNWM,8358 -cryptography/hazmat/primitives/cmac.py,sha256=ZbpwI87EhO3maiwqzttN1z0ObsAO1ufnl2Px5b9uJ1c,2036 -cryptography/hazmat/primitives/constant_time.py,sha256=6bkW00QjhKusdgsQbexXhMlGX0XRN59XNmxWS2W38NA,387 -cryptography/hazmat/primitives/hashes.py,sha256=RuDy0vgDOZh8BAH-2RTiETWvlJR2giBHgAYYFCVgxQo,6043 -cryptography/hazmat/primitives/hmac.py,sha256=pKiyxmJVcixW7Xk7w4ofde6Z7F8UohqGZa01PoxRotc,2122 -cryptography/hazmat/primitives/kdf/__init__.py,sha256=DcZhzfLG8d8IYBH771lGTVU5S87OQDpu3nrfOwZnsmA,715 -cryptography/hazmat/primitives/kdf/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/concatkdf.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/hkdf.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/kbkdf.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/pbkdf2.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/scrypt.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/__pycache__/x963kdf.cpython-310.pyc,, -cryptography/hazmat/primitives/kdf/concatkdf.py,sha256=nz7Paa4oBXXPpCJO-6-tm_zxvOwxpwtjaXUzF5Ylf9A,3759 -cryptography/hazmat/primitives/kdf/hkdf.py,sha256=eQrZVEuv_GcSXpxSYR2GH3H8UJJXKsk4EZ2euJA4srw,3018 -cryptography/hazmat/primitives/kdf/kbkdf.py,sha256=Ys2ITSbEw49V1v_DagQBd17owQr2A2iyPue4mot4Z_g,9196 -cryptography/hazmat/primitives/kdf/pbkdf2.py,sha256=wEMH4CJfPccCg9apQLXyWUWBrZLTpYLLnoZEnzvaHQo,2032 -cryptography/hazmat/primitives/kdf/scrypt.py,sha256=Wt7jj51vsedNtQX-LZI41geqUZnBFYnrhOXpoheLsOM,2227 -cryptography/hazmat/primitives/kdf/x963kdf.py,sha256=H401RIRI2cIu52v8IM6ZCqDdCO2zuJPQogS2w_yIGpc,2005 -cryptography/hazmat/primitives/keywrap.py,sha256=TWqyG9K7k-Ymq4kcIw7u3NIKUPVDtv6bimwxIJYTe20,5643 -cryptography/hazmat/primitives/padding.py,sha256=xruasOE5Cd8KEQ-yp9W6v9WKPvKH-GudHCPKQ7A8HfI,6207 -cryptography/hazmat/primitives/poly1305.py,sha256=QvxPMrqjgKJt0mOZSeZKk4NcxsNCd2kgfI-X1CmyUW4,1837 -cryptography/hazmat/primitives/serialization/__init__.py,sha256=pKGAzJ0YIIOutw1sC1paGCiqKSXhUwu7dBkAxwjacQU,1196 -cryptography/hazmat/primitives/serialization/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/base.cpython-310.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/pkcs12.cpython-310.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/pkcs7.cpython-310.pyc,, -cryptography/hazmat/primitives/serialization/__pycache__/ssh.cpython-310.pyc,, -cryptography/hazmat/primitives/serialization/base.py,sha256=yYluZmJzaOsClGO0aUk5QZ3tcFDDFcNaoK6EevJcrAw,1967 -cryptography/hazmat/primitives/serialization/pkcs12.py,sha256=mEd4_K-5YF4BVqmAMFFEMir6wCFA0j_rKlu_Ef4QLRQ,6716 -cryptography/hazmat/primitives/serialization/pkcs7.py,sha256=97USeLUaVfGsJp6mKpoDC4SCLNYmJTcyW2vOzNG0b4w,7055 -cryptography/hazmat/primitives/serialization/ssh.py,sha256=EZ9a1FiAaxznp6b8L99lt-BspSnleHGlU-3XRwLVvfc,23966 -cryptography/hazmat/primitives/twofactor/__init__.py,sha256=ZHo4zwWidFP2RWFl8luiNuYkVMZPghzx54izPNSCtD4,222 -cryptography/hazmat/primitives/twofactor/__pycache__/__init__.cpython-310.pyc,, -cryptography/hazmat/primitives/twofactor/__pycache__/hotp.cpython-310.pyc,, -cryptography/hazmat/primitives/twofactor/__pycache__/totp.cpython-310.pyc,, -cryptography/hazmat/primitives/twofactor/hotp.py,sha256=DT4RoUzQtiFwBw30GnDpe9L0c2W9w6jirsR74QtS_rA,2989 -cryptography/hazmat/primitives/twofactor/totp.py,sha256=Pr2Zh9KHqYGyix2C9KCAEXAjqOgkqBrF_BdGLv9INcI,1449 -cryptography/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -cryptography/utils.py,sha256=lBhYLBbA8MXX_yyZmhY3lOQ5Cyp3iZWBD18l49WAXCE,4095 -cryptography/x509/__init__.py,sha256=4WL6dxMLEWM9Wa9f_SpNbwcQNg76dut5zigRcHpRoTA,7719 -cryptography/x509/__pycache__/__init__.cpython-310.pyc,, -cryptography/x509/__pycache__/base.cpython-310.pyc,, -cryptography/x509/__pycache__/certificate_transparency.cpython-310.pyc,, -cryptography/x509/__pycache__/extensions.cpython-310.pyc,, -cryptography/x509/__pycache__/general_name.cpython-310.pyc,, -cryptography/x509/__pycache__/name.cpython-310.pyc,, -cryptography/x509/__pycache__/ocsp.cpython-310.pyc,, -cryptography/x509/__pycache__/oid.cpython-310.pyc,, -cryptography/x509/base.py,sha256=6oZ_nqLqO-eya_KTTCcTRP7Pr8d2YUvZTN3xxyLnZzw,34442 -cryptography/x509/certificate_transparency.py,sha256=jkjOvVu8bS5ljHov2AWdWScENQxylmDgESk01koC0Rs,2226 -cryptography/x509/extensions.py,sha256=ZWp47YmQ30KcLB25j7tWLcAeyEHw4DeQleeCmfcZqE0,65289 -cryptography/x509/general_name.py,sha256=976U2AwKWDXJEwas2JgByeSpfG2IKyWREL9oB5m6Azo,7907 -cryptography/x509/name.py,sha256=lIYhyGvkcKiFxBSqgahtdc_zy6MeshgQ9O0YdXxXIRM,14837 -cryptography/x509/ocsp.py,sha256=Lq23RjbLj0tUrsQbvMF8Mt1vD2s9-f4PhxrBXSECJ4s,18507 -cryptography/x509/oid.py,sha256=dAllMplMi_Kc_lEiQKnSM-rTN5w--a1UZucV-HvQOb0,793 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL deleted file mode 100644 index cd6a22a16..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.38.4) -Root-Is-Purelib: false -Tag: cp36-abi3-win_amd64 - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt deleted file mode 100644 index 0d38bc5ea..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -cryptography diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py deleted file mode 100644 index 83439a962..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__about__.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -__all__ = [ - "__version__", - "__author__", - "__copyright__", -] - -__version__ = "39.0.0" - -__author__ = "The Python Cryptographic Authority and individual contributors" -__copyright__ = "Copyright 2013-2022 {}".format(__author__) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py deleted file mode 100644 index 07c894ea3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import sys -import warnings - -from cryptography.__about__ import __author__, __copyright__, __version__ -from cryptography.utils import CryptographyDeprecationWarning - -__all__ = [ - "__version__", - "__author__", - "__copyright__", -] - -if sys.version_info[:2] == (3, 6): - warnings.warn( - "Python 3.6 is no longer supported by the Python core team. " - "Therefore, support for it is deprecated in cryptography. The next " - "release of cryptography (40.0) will be the last to support Python " - "3.6.", - CryptographyDeprecationWarning, - stacklevel=2, - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py deleted file mode 100644 index a315703c3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/exceptions.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils - -if typing.TYPE_CHECKING: - from cryptography.hazmat.bindings.openssl.binding import ( - _OpenSSLErrorWithText, - ) - - -class _Reasons(utils.Enum): - BACKEND_MISSING_INTERFACE = 0 - UNSUPPORTED_HASH = 1 - UNSUPPORTED_CIPHER = 2 - UNSUPPORTED_PADDING = 3 - UNSUPPORTED_MGF = 4 - UNSUPPORTED_PUBLIC_KEY_ALGORITHM = 5 - UNSUPPORTED_ELLIPTIC_CURVE = 6 - UNSUPPORTED_SERIALIZATION = 7 - UNSUPPORTED_X509 = 8 - UNSUPPORTED_EXCHANGE_ALGORITHM = 9 - UNSUPPORTED_DIFFIE_HELLMAN = 10 - UNSUPPORTED_MAC = 11 - - -class UnsupportedAlgorithm(Exception): - def __init__( - self, message: str, reason: typing.Optional[_Reasons] = None - ) -> None: - super(UnsupportedAlgorithm, self).__init__(message) - self._reason = reason - - -class AlreadyFinalized(Exception): - pass - - -class AlreadyUpdated(Exception): - pass - - -class NotYetFinalized(Exception): - pass - - -class InvalidTag(Exception): - pass - - -class InvalidSignature(Exception): - pass - - -class InternalError(Exception): - def __init__( - self, msg: str, err_code: typing.List["_OpenSSLErrorWithText"] - ) -> None: - super(InternalError, self).__init__(msg) - self.err_code = err_code - - -class InvalidKey(Exception): - pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py deleted file mode 100644 index a2601f80f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/fernet.py +++ /dev/null @@ -1,220 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import base64 -import binascii -import os -import time -import typing - -from cryptography import utils -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import hashes, padding -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives.hmac import HMAC - - -class InvalidToken(Exception): - pass - - -_MAX_CLOCK_SKEW = 60 - - -class Fernet: - def __init__( - self, - key: typing.Union[bytes, str], - backend: typing.Any = None, - ) -> None: - try: - key = base64.urlsafe_b64decode(key) - except binascii.Error as exc: - raise ValueError( - "Fernet key must be 32 url-safe base64-encoded bytes." - ) from exc - if len(key) != 32: - raise ValueError( - "Fernet key must be 32 url-safe base64-encoded bytes." - ) - - self._signing_key = key[:16] - self._encryption_key = key[16:] - - @classmethod - def generate_key(cls) -> bytes: - return base64.urlsafe_b64encode(os.urandom(32)) - - def encrypt(self, data: bytes) -> bytes: - return self.encrypt_at_time(data, int(time.time())) - - def encrypt_at_time(self, data: bytes, current_time: int) -> bytes: - iv = os.urandom(16) - return self._encrypt_from_parts(data, current_time, iv) - - def _encrypt_from_parts( - self, data: bytes, current_time: int, iv: bytes - ) -> bytes: - utils._check_bytes("data", data) - - padder = padding.PKCS7(algorithms.AES.block_size).padder() - padded_data = padder.update(data) + padder.finalize() - encryptor = Cipher( - algorithms.AES(self._encryption_key), - modes.CBC(iv), - ).encryptor() - ciphertext = encryptor.update(padded_data) + encryptor.finalize() - - basic_parts = ( - b"\x80" - + current_time.to_bytes(length=8, byteorder="big") - + iv - + ciphertext - ) - - h = HMAC(self._signing_key, hashes.SHA256()) - h.update(basic_parts) - hmac = h.finalize() - return base64.urlsafe_b64encode(basic_parts + hmac) - - def decrypt( - self, token: typing.Union[bytes, str], ttl: typing.Optional[int] = None - ) -> bytes: - timestamp, data = Fernet._get_unverified_token_data(token) - if ttl is None: - time_info = None - else: - time_info = (ttl, int(time.time())) - return self._decrypt_data(data, timestamp, time_info) - - def decrypt_at_time( - self, token: typing.Union[bytes, str], ttl: int, current_time: int - ) -> bytes: - if ttl is None: - raise ValueError( - "decrypt_at_time() can only be used with a non-None ttl" - ) - timestamp, data = Fernet._get_unverified_token_data(token) - return self._decrypt_data(data, timestamp, (ttl, current_time)) - - def extract_timestamp(self, token: typing.Union[bytes, str]) -> int: - timestamp, data = Fernet._get_unverified_token_data(token) - # Verify the token was not tampered with. - self._verify_signature(data) - return timestamp - - @staticmethod - def _get_unverified_token_data( - token: typing.Union[bytes, str] - ) -> typing.Tuple[int, bytes]: - if not isinstance(token, (str, bytes)): - raise TypeError("token must be bytes or str") - - try: - data = base64.urlsafe_b64decode(token) - except (TypeError, binascii.Error): - raise InvalidToken - - if not data or data[0] != 0x80: - raise InvalidToken - - if len(data) < 9: - raise InvalidToken - - timestamp = int.from_bytes(data[1:9], byteorder="big") - return timestamp, data - - def _verify_signature(self, data: bytes) -> None: - h = HMAC(self._signing_key, hashes.SHA256()) - h.update(data[:-32]) - try: - h.verify(data[-32:]) - except InvalidSignature: - raise InvalidToken - - def _decrypt_data( - self, - data: bytes, - timestamp: int, - time_info: typing.Optional[typing.Tuple[int, int]], - ) -> bytes: - if time_info is not None: - ttl, current_time = time_info - if timestamp + ttl < current_time: - raise InvalidToken - - if current_time + _MAX_CLOCK_SKEW < timestamp: - raise InvalidToken - - self._verify_signature(data) - - iv = data[9:25] - ciphertext = data[25:-32] - decryptor = Cipher( - algorithms.AES(self._encryption_key), modes.CBC(iv) - ).decryptor() - plaintext_padded = decryptor.update(ciphertext) - try: - plaintext_padded += decryptor.finalize() - except ValueError: - raise InvalidToken - unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder() - - unpadded = unpadder.update(plaintext_padded) - try: - unpadded += unpadder.finalize() - except ValueError: - raise InvalidToken - return unpadded - - -class MultiFernet: - def __init__(self, fernets: typing.Iterable[Fernet]): - fernets = list(fernets) - if not fernets: - raise ValueError( - "MultiFernet requires at least one Fernet instance" - ) - self._fernets = fernets - - def encrypt(self, msg: bytes) -> bytes: - return self.encrypt_at_time(msg, int(time.time())) - - def encrypt_at_time(self, msg: bytes, current_time: int) -> bytes: - return self._fernets[0].encrypt_at_time(msg, current_time) - - def rotate(self, msg: typing.Union[bytes, str]) -> bytes: - timestamp, data = Fernet._get_unverified_token_data(msg) - for f in self._fernets: - try: - p = f._decrypt_data(data, timestamp, None) - break - except InvalidToken: - pass - else: - raise InvalidToken - - iv = os.urandom(16) - return self._fernets[0]._encrypt_from_parts(p, timestamp, iv) - - def decrypt( - self, msg: typing.Union[bytes, str], ttl: typing.Optional[int] = None - ) -> bytes: - for f in self._fernets: - try: - return f.decrypt(msg, ttl) - except InvalidToken: - pass - raise InvalidToken - - def decrypt_at_time( - self, msg: typing.Union[bytes, str], ttl: int, current_time: int - ) -> bytes: - for f in self._fernets: - try: - return f.decrypt_at_time(msg, ttl, current_time) - except InvalidToken: - pass - raise InvalidToken diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py deleted file mode 100644 index 007694bc5..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -""" -Hazardous Materials - -This is a "Hazardous Materials" module. You should ONLY use it if you're -100% absolutely sure that you know what you're doing because this module -is full of land mines, dragons, and dinosaurs with laser guns. -""" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py deleted file mode 100644 index 927ffc4c5..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/_oid.py +++ /dev/null @@ -1,293 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.bindings._rust import ( - ObjectIdentifier as ObjectIdentifier, -) -from cryptography.hazmat.primitives import hashes - - -class ExtensionOID: - SUBJECT_DIRECTORY_ATTRIBUTES = ObjectIdentifier("2.5.29.9") - SUBJECT_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.14") - KEY_USAGE = ObjectIdentifier("2.5.29.15") - SUBJECT_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.17") - ISSUER_ALTERNATIVE_NAME = ObjectIdentifier("2.5.29.18") - BASIC_CONSTRAINTS = ObjectIdentifier("2.5.29.19") - NAME_CONSTRAINTS = ObjectIdentifier("2.5.29.30") - CRL_DISTRIBUTION_POINTS = ObjectIdentifier("2.5.29.31") - CERTIFICATE_POLICIES = ObjectIdentifier("2.5.29.32") - POLICY_MAPPINGS = ObjectIdentifier("2.5.29.33") - AUTHORITY_KEY_IDENTIFIER = ObjectIdentifier("2.5.29.35") - POLICY_CONSTRAINTS = ObjectIdentifier("2.5.29.36") - EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37") - FRESHEST_CRL = ObjectIdentifier("2.5.29.46") - INHIBIT_ANY_POLICY = ObjectIdentifier("2.5.29.54") - ISSUING_DISTRIBUTION_POINT = ObjectIdentifier("2.5.29.28") - AUTHORITY_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.1") - SUBJECT_INFORMATION_ACCESS = ObjectIdentifier("1.3.6.1.5.5.7.1.11") - OCSP_NO_CHECK = ObjectIdentifier("1.3.6.1.5.5.7.48.1.5") - TLS_FEATURE = ObjectIdentifier("1.3.6.1.5.5.7.1.24") - CRL_NUMBER = ObjectIdentifier("2.5.29.20") - DELTA_CRL_INDICATOR = ObjectIdentifier("2.5.29.27") - PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier( - "1.3.6.1.4.1.11129.2.4.2" - ) - PRECERT_POISON = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.3") - SIGNED_CERTIFICATE_TIMESTAMPS = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.5") - - -class OCSPExtensionOID: - NONCE = ObjectIdentifier("1.3.6.1.5.5.7.48.1.2") - - -class CRLEntryExtensionOID: - CERTIFICATE_ISSUER = ObjectIdentifier("2.5.29.29") - CRL_REASON = ObjectIdentifier("2.5.29.21") - INVALIDITY_DATE = ObjectIdentifier("2.5.29.24") - - -class NameOID: - COMMON_NAME = ObjectIdentifier("2.5.4.3") - COUNTRY_NAME = ObjectIdentifier("2.5.4.6") - LOCALITY_NAME = ObjectIdentifier("2.5.4.7") - STATE_OR_PROVINCE_NAME = ObjectIdentifier("2.5.4.8") - STREET_ADDRESS = ObjectIdentifier("2.5.4.9") - ORGANIZATION_NAME = ObjectIdentifier("2.5.4.10") - ORGANIZATIONAL_UNIT_NAME = ObjectIdentifier("2.5.4.11") - SERIAL_NUMBER = ObjectIdentifier("2.5.4.5") - SURNAME = ObjectIdentifier("2.5.4.4") - GIVEN_NAME = ObjectIdentifier("2.5.4.42") - TITLE = ObjectIdentifier("2.5.4.12") - GENERATION_QUALIFIER = ObjectIdentifier("2.5.4.44") - X500_UNIQUE_IDENTIFIER = ObjectIdentifier("2.5.4.45") - DN_QUALIFIER = ObjectIdentifier("2.5.4.46") - PSEUDONYM = ObjectIdentifier("2.5.4.65") - USER_ID = ObjectIdentifier("0.9.2342.19200300.100.1.1") - DOMAIN_COMPONENT = ObjectIdentifier("0.9.2342.19200300.100.1.25") - EMAIL_ADDRESS = ObjectIdentifier("1.2.840.113549.1.9.1") - JURISDICTION_COUNTRY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.3") - JURISDICTION_LOCALITY_NAME = ObjectIdentifier("1.3.6.1.4.1.311.60.2.1.1") - JURISDICTION_STATE_OR_PROVINCE_NAME = ObjectIdentifier( - "1.3.6.1.4.1.311.60.2.1.2" - ) - BUSINESS_CATEGORY = ObjectIdentifier("2.5.4.15") - POSTAL_ADDRESS = ObjectIdentifier("2.5.4.16") - POSTAL_CODE = ObjectIdentifier("2.5.4.17") - INN = ObjectIdentifier("1.2.643.3.131.1.1") - OGRN = ObjectIdentifier("1.2.643.100.1") - SNILS = ObjectIdentifier("1.2.643.100.3") - UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") - - -class SignatureAlgorithmOID: - RSA_WITH_MD5 = ObjectIdentifier("1.2.840.113549.1.1.4") - RSA_WITH_SHA1 = ObjectIdentifier("1.2.840.113549.1.1.5") - # This is an alternate OID for RSA with SHA1 that is occasionally seen - _RSA_WITH_SHA1 = ObjectIdentifier("1.3.14.3.2.29") - RSA_WITH_SHA224 = ObjectIdentifier("1.2.840.113549.1.1.14") - RSA_WITH_SHA256 = ObjectIdentifier("1.2.840.113549.1.1.11") - RSA_WITH_SHA384 = ObjectIdentifier("1.2.840.113549.1.1.12") - RSA_WITH_SHA512 = ObjectIdentifier("1.2.840.113549.1.1.13") - RSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.13") - RSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.14") - RSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.15") - RSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.16") - RSASSA_PSS = ObjectIdentifier("1.2.840.113549.1.1.10") - ECDSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10045.4.1") - ECDSA_WITH_SHA224 = ObjectIdentifier("1.2.840.10045.4.3.1") - ECDSA_WITH_SHA256 = ObjectIdentifier("1.2.840.10045.4.3.2") - ECDSA_WITH_SHA384 = ObjectIdentifier("1.2.840.10045.4.3.3") - ECDSA_WITH_SHA512 = ObjectIdentifier("1.2.840.10045.4.3.4") - ECDSA_WITH_SHA3_224 = ObjectIdentifier("2.16.840.1.101.3.4.3.9") - ECDSA_WITH_SHA3_256 = ObjectIdentifier("2.16.840.1.101.3.4.3.10") - ECDSA_WITH_SHA3_384 = ObjectIdentifier("2.16.840.1.101.3.4.3.11") - ECDSA_WITH_SHA3_512 = ObjectIdentifier("2.16.840.1.101.3.4.3.12") - DSA_WITH_SHA1 = ObjectIdentifier("1.2.840.10040.4.3") - DSA_WITH_SHA224 = ObjectIdentifier("2.16.840.1.101.3.4.3.1") - DSA_WITH_SHA256 = ObjectIdentifier("2.16.840.1.101.3.4.3.2") - DSA_WITH_SHA384 = ObjectIdentifier("2.16.840.1.101.3.4.3.3") - DSA_WITH_SHA512 = ObjectIdentifier("2.16.840.1.101.3.4.3.4") - ED25519 = ObjectIdentifier("1.3.101.112") - ED448 = ObjectIdentifier("1.3.101.113") - GOSTR3411_94_WITH_3410_2001 = ObjectIdentifier("1.2.643.2.2.3") - GOSTR3410_2012_WITH_3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") - GOSTR3410_2012_WITH_3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") - - -_SIG_OIDS_TO_HASH: typing.Dict[ - ObjectIdentifier, typing.Optional[hashes.HashAlgorithm] -] = { - SignatureAlgorithmOID.RSA_WITH_MD5: hashes.MD5(), - SignatureAlgorithmOID.RSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID._RSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.RSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.RSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.RSA_WITH_SHA384: hashes.SHA384(), - SignatureAlgorithmOID.RSA_WITH_SHA512: hashes.SHA512(), - SignatureAlgorithmOID.RSA_WITH_SHA3_224: hashes.SHA3_224(), - SignatureAlgorithmOID.RSA_WITH_SHA3_256: hashes.SHA3_256(), - SignatureAlgorithmOID.RSA_WITH_SHA3_384: hashes.SHA3_384(), - SignatureAlgorithmOID.RSA_WITH_SHA3_512: hashes.SHA3_512(), - SignatureAlgorithmOID.ECDSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.ECDSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.ECDSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.ECDSA_WITH_SHA384: hashes.SHA384(), - SignatureAlgorithmOID.ECDSA_WITH_SHA512: hashes.SHA512(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_224: hashes.SHA3_224(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_256: hashes.SHA3_256(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_384: hashes.SHA3_384(), - SignatureAlgorithmOID.ECDSA_WITH_SHA3_512: hashes.SHA3_512(), - SignatureAlgorithmOID.DSA_WITH_SHA1: hashes.SHA1(), - SignatureAlgorithmOID.DSA_WITH_SHA224: hashes.SHA224(), - SignatureAlgorithmOID.DSA_WITH_SHA256: hashes.SHA256(), - SignatureAlgorithmOID.ED25519: None, - SignatureAlgorithmOID.ED448: None, - SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: None, - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: None, - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: None, -} - - -class ExtendedKeyUsageOID: - SERVER_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.1") - CLIENT_AUTH = ObjectIdentifier("1.3.6.1.5.5.7.3.2") - CODE_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.3") - EMAIL_PROTECTION = ObjectIdentifier("1.3.6.1.5.5.7.3.4") - TIME_STAMPING = ObjectIdentifier("1.3.6.1.5.5.7.3.8") - OCSP_SIGNING = ObjectIdentifier("1.3.6.1.5.5.7.3.9") - ANY_EXTENDED_KEY_USAGE = ObjectIdentifier("2.5.29.37.0") - SMARTCARD_LOGON = ObjectIdentifier("1.3.6.1.4.1.311.20.2.2") - KERBEROS_PKINIT_KDC = ObjectIdentifier("1.3.6.1.5.2.3.5") - IPSEC_IKE = ObjectIdentifier("1.3.6.1.5.5.7.3.17") - CERTIFICATE_TRANSPARENCY = ObjectIdentifier("1.3.6.1.4.1.11129.2.4.4") - - -class AuthorityInformationAccessOID: - CA_ISSUERS = ObjectIdentifier("1.3.6.1.5.5.7.48.2") - OCSP = ObjectIdentifier("1.3.6.1.5.5.7.48.1") - - -class SubjectInformationAccessOID: - CA_REPOSITORY = ObjectIdentifier("1.3.6.1.5.5.7.48.5") - - -class CertificatePoliciesOID: - CPS_QUALIFIER = ObjectIdentifier("1.3.6.1.5.5.7.2.1") - CPS_USER_NOTICE = ObjectIdentifier("1.3.6.1.5.5.7.2.2") - ANY_POLICY = ObjectIdentifier("2.5.29.32.0") - - -class AttributeOID: - CHALLENGE_PASSWORD = ObjectIdentifier("1.2.840.113549.1.9.7") - UNSTRUCTURED_NAME = ObjectIdentifier("1.2.840.113549.1.9.2") - - -_OID_NAMES = { - NameOID.COMMON_NAME: "commonName", - NameOID.COUNTRY_NAME: "countryName", - NameOID.LOCALITY_NAME: "localityName", - NameOID.STATE_OR_PROVINCE_NAME: "stateOrProvinceName", - NameOID.STREET_ADDRESS: "streetAddress", - NameOID.ORGANIZATION_NAME: "organizationName", - NameOID.ORGANIZATIONAL_UNIT_NAME: "organizationalUnitName", - NameOID.SERIAL_NUMBER: "serialNumber", - NameOID.SURNAME: "surname", - NameOID.GIVEN_NAME: "givenName", - NameOID.TITLE: "title", - NameOID.GENERATION_QUALIFIER: "generationQualifier", - NameOID.X500_UNIQUE_IDENTIFIER: "x500UniqueIdentifier", - NameOID.DN_QUALIFIER: "dnQualifier", - NameOID.PSEUDONYM: "pseudonym", - NameOID.USER_ID: "userID", - NameOID.DOMAIN_COMPONENT: "domainComponent", - NameOID.EMAIL_ADDRESS: "emailAddress", - NameOID.JURISDICTION_COUNTRY_NAME: "jurisdictionCountryName", - NameOID.JURISDICTION_LOCALITY_NAME: "jurisdictionLocalityName", - NameOID.JURISDICTION_STATE_OR_PROVINCE_NAME: ( - "jurisdictionStateOrProvinceName" - ), - NameOID.BUSINESS_CATEGORY: "businessCategory", - NameOID.POSTAL_ADDRESS: "postalAddress", - NameOID.POSTAL_CODE: "postalCode", - NameOID.INN: "INN", - NameOID.OGRN: "OGRN", - NameOID.SNILS: "SNILS", - NameOID.UNSTRUCTURED_NAME: "unstructuredName", - SignatureAlgorithmOID.RSA_WITH_MD5: "md5WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA1: "sha1WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA224: "sha224WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA256: "sha256WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA384: "sha384WithRSAEncryption", - SignatureAlgorithmOID.RSA_WITH_SHA512: "sha512WithRSAEncryption", - SignatureAlgorithmOID.RSASSA_PSS: "RSASSA-PSS", - SignatureAlgorithmOID.ECDSA_WITH_SHA1: "ecdsa-with-SHA1", - SignatureAlgorithmOID.ECDSA_WITH_SHA224: "ecdsa-with-SHA224", - SignatureAlgorithmOID.ECDSA_WITH_SHA256: "ecdsa-with-SHA256", - SignatureAlgorithmOID.ECDSA_WITH_SHA384: "ecdsa-with-SHA384", - SignatureAlgorithmOID.ECDSA_WITH_SHA512: "ecdsa-with-SHA512", - SignatureAlgorithmOID.DSA_WITH_SHA1: "dsa-with-sha1", - SignatureAlgorithmOID.DSA_WITH_SHA224: "dsa-with-sha224", - SignatureAlgorithmOID.DSA_WITH_SHA256: "dsa-with-sha256", - SignatureAlgorithmOID.ED25519: "ed25519", - SignatureAlgorithmOID.ED448: "ed448", - SignatureAlgorithmOID.GOSTR3411_94_WITH_3410_2001: ( - "GOST R 34.11-94 with GOST R 34.10-2001" - ), - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_256: ( - "GOST R 34.10-2012 with GOST R 34.11-2012 (256 bit)" - ), - SignatureAlgorithmOID.GOSTR3410_2012_WITH_3411_2012_512: ( - "GOST R 34.10-2012 with GOST R 34.11-2012 (512 bit)" - ), - ExtendedKeyUsageOID.SERVER_AUTH: "serverAuth", - ExtendedKeyUsageOID.CLIENT_AUTH: "clientAuth", - ExtendedKeyUsageOID.CODE_SIGNING: "codeSigning", - ExtendedKeyUsageOID.EMAIL_PROTECTION: "emailProtection", - ExtendedKeyUsageOID.TIME_STAMPING: "timeStamping", - ExtendedKeyUsageOID.OCSP_SIGNING: "OCSPSigning", - ExtendedKeyUsageOID.SMARTCARD_LOGON: "msSmartcardLogin", - ExtendedKeyUsageOID.KERBEROS_PKINIT_KDC: "pkInitKDC", - ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES: "subjectDirectoryAttributes", - ExtensionOID.SUBJECT_KEY_IDENTIFIER: "subjectKeyIdentifier", - ExtensionOID.KEY_USAGE: "keyUsage", - ExtensionOID.SUBJECT_ALTERNATIVE_NAME: "subjectAltName", - ExtensionOID.ISSUER_ALTERNATIVE_NAME: "issuerAltName", - ExtensionOID.BASIC_CONSTRAINTS: "basicConstraints", - ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS: ( - "signedCertificateTimestampList" - ), - ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS: ( - "signedCertificateTimestampList" - ), - ExtensionOID.PRECERT_POISON: "ctPoison", - CRLEntryExtensionOID.CRL_REASON: "cRLReason", - CRLEntryExtensionOID.INVALIDITY_DATE: "invalidityDate", - CRLEntryExtensionOID.CERTIFICATE_ISSUER: "certificateIssuer", - ExtensionOID.NAME_CONSTRAINTS: "nameConstraints", - ExtensionOID.CRL_DISTRIBUTION_POINTS: "cRLDistributionPoints", - ExtensionOID.CERTIFICATE_POLICIES: "certificatePolicies", - ExtensionOID.POLICY_MAPPINGS: "policyMappings", - ExtensionOID.AUTHORITY_KEY_IDENTIFIER: "authorityKeyIdentifier", - ExtensionOID.POLICY_CONSTRAINTS: "policyConstraints", - ExtensionOID.EXTENDED_KEY_USAGE: "extendedKeyUsage", - ExtensionOID.FRESHEST_CRL: "freshestCRL", - ExtensionOID.INHIBIT_ANY_POLICY: "inhibitAnyPolicy", - ExtensionOID.ISSUING_DISTRIBUTION_POINT: ("issuingDistributionPoint"), - ExtensionOID.AUTHORITY_INFORMATION_ACCESS: "authorityInfoAccess", - ExtensionOID.SUBJECT_INFORMATION_ACCESS: "subjectInfoAccess", - ExtensionOID.OCSP_NO_CHECK: "OCSPNoCheck", - ExtensionOID.CRL_NUMBER: "cRLNumber", - ExtensionOID.DELTA_CRL_INDICATOR: "deltaCRLIndicator", - ExtensionOID.TLS_FEATURE: "TLSFeature", - AuthorityInformationAccessOID.OCSP: "OCSP", - AuthorityInformationAccessOID.CA_ISSUERS: "caIssuers", - SubjectInformationAccessOID.CA_REPOSITORY: "caRepository", - CertificatePoliciesOID.CPS_QUALIFIER: "id-qt-cps", - CertificatePoliciesOID.CPS_USER_NOTICE: "id-qt-unotice", - OCSPExtensionOID.NONCE: "OCSPNonce", - AttributeOID.CHALLENGE_PASSWORD: "challengePassword", -} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py deleted file mode 100644 index 3926f85f1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. -from typing import Any - - -def default_backend() -> Any: - from cryptography.hazmat.backends.openssl.backend import backend - - return backend diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py deleted file mode 100644 index 42c4539df..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography.hazmat.backends.openssl.backend import backend - -__all__ = ["backend"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py deleted file mode 100644 index 5b0fd2217..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/aead.py +++ /dev/null @@ -1,306 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import InvalidTag - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - from cryptography.hazmat.primitives.ciphers.aead import ( - AESCCM, - AESGCM, - AESOCB3, - AESSIV, - ChaCha20Poly1305, - ) - - _AEAD_TYPES = typing.Union[ - AESCCM, AESGCM, AESOCB3, AESSIV, ChaCha20Poly1305 - ] - -_ENCRYPT = 1 -_DECRYPT = 0 - - -def _aead_cipher_name(cipher: "_AEAD_TYPES") -> bytes: - from cryptography.hazmat.primitives.ciphers.aead import ( - AESCCM, - AESGCM, - AESOCB3, - AESSIV, - ChaCha20Poly1305, - ) - - if isinstance(cipher, ChaCha20Poly1305): - return b"chacha20-poly1305" - elif isinstance(cipher, AESCCM): - return f"aes-{len(cipher._key) * 8}-ccm".encode("ascii") - elif isinstance(cipher, AESOCB3): - return f"aes-{len(cipher._key) * 8}-ocb".encode("ascii") - elif isinstance(cipher, AESSIV): - return f"aes-{len(cipher._key) * 8 // 2}-siv".encode("ascii") - else: - assert isinstance(cipher, AESGCM) - return f"aes-{len(cipher._key) * 8}-gcm".encode("ascii") - - -def _evp_cipher(cipher_name: bytes, backend: "Backend"): - if cipher_name.endswith(b"-siv"): - evp_cipher = backend._lib.EVP_CIPHER_fetch( - backend._ffi.NULL, - cipher_name, - backend._ffi.NULL, - ) - backend.openssl_assert(evp_cipher != backend._ffi.NULL) - evp_cipher = backend._ffi.gc(evp_cipher, backend._lib.EVP_CIPHER_free) - else: - evp_cipher = backend._lib.EVP_get_cipherbyname(cipher_name) - backend.openssl_assert(evp_cipher != backend._ffi.NULL) - - return evp_cipher - - -def _aead_create_ctx( - backend: "Backend", - cipher: "_AEAD_TYPES", - key: bytes, -): - ctx = backend._lib.EVP_CIPHER_CTX_new() - backend.openssl_assert(ctx != backend._ffi.NULL) - ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free) - cipher_name = _aead_cipher_name(cipher) - evp_cipher = _evp_cipher(cipher_name, backend) - key_ptr = backend._ffi.from_buffer(key) - res = backend._lib.EVP_CipherInit_ex( - ctx, - evp_cipher, - backend._ffi.NULL, - key_ptr, - backend._ffi.NULL, - 0, - ) - backend.openssl_assert(res != 0) - return ctx - - -def _aead_setup( - backend: "Backend", - cipher_name: bytes, - key: bytes, - nonce: bytes, - tag: typing.Optional[bytes], - tag_len: int, - operation: int, -): - evp_cipher = _evp_cipher(cipher_name, backend) - ctx = backend._lib.EVP_CIPHER_CTX_new() - ctx = backend._ffi.gc(ctx, backend._lib.EVP_CIPHER_CTX_free) - res = backend._lib.EVP_CipherInit_ex( - ctx, - evp_cipher, - backend._ffi.NULL, - backend._ffi.NULL, - backend._ffi.NULL, - int(operation == _ENCRYPT), - ) - backend.openssl_assert(res != 0) - # CCM requires the IVLEN to be set before calling SET_TAG on decrypt - res = backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, - backend._lib.EVP_CTRL_AEAD_SET_IVLEN, - len(nonce), - backend._ffi.NULL, - ) - backend.openssl_assert(res != 0) - if operation == _DECRYPT: - assert tag is not None - _set_tag(backend, ctx, tag) - elif cipher_name.endswith(b"-ccm"): - res = backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, tag_len, backend._ffi.NULL - ) - backend.openssl_assert(res != 0) - - nonce_ptr = backend._ffi.from_buffer(nonce) - key_ptr = backend._ffi.from_buffer(key) - res = backend._lib.EVP_CipherInit_ex( - ctx, - backend._ffi.NULL, - backend._ffi.NULL, - key_ptr, - nonce_ptr, - int(operation == _ENCRYPT), - ) - backend.openssl_assert(res != 0) - return ctx - - -def _set_tag(backend, ctx, tag: bytes) -> None: - res = backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag - ) - backend.openssl_assert(res != 0) - - -def _set_nonce_operation(backend, ctx, nonce: bytes, operation: int) -> None: - nonce_ptr = backend._ffi.from_buffer(nonce) - res = backend._lib.EVP_CipherInit_ex( - ctx, - backend._ffi.NULL, - backend._ffi.NULL, - backend._ffi.NULL, - nonce_ptr, - int(operation == _ENCRYPT), - ) - backend.openssl_assert(res != 0) - - -def _set_length(backend: "Backend", ctx, data_len: int) -> None: - intptr = backend._ffi.new("int *") - res = backend._lib.EVP_CipherUpdate( - ctx, backend._ffi.NULL, intptr, backend._ffi.NULL, data_len - ) - backend.openssl_assert(res != 0) - - -def _process_aad(backend: "Backend", ctx, associated_data: bytes) -> None: - outlen = backend._ffi.new("int *") - res = backend._lib.EVP_CipherUpdate( - ctx, backend._ffi.NULL, outlen, associated_data, len(associated_data) - ) - backend.openssl_assert(res != 0) - - -def _process_data(backend: "Backend", ctx, data: bytes) -> bytes: - outlen = backend._ffi.new("int *") - buf = backend._ffi.new("unsigned char[]", len(data)) - res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) - if res == 0: - # AES SIV can error here if the data is invalid on decrypt - backend._consume_errors() - raise InvalidTag - return backend._ffi.buffer(buf, outlen[0])[:] - - -def _encrypt( - backend: "Backend", - cipher: "_AEAD_TYPES", - nonce: bytes, - data: bytes, - associated_data: typing.List[bytes], - tag_length: int, - ctx: typing.Any = None, -) -> bytes: - from cryptography.hazmat.primitives.ciphers.aead import AESCCM, AESSIV - - if ctx is None: - cipher_name = _aead_cipher_name(cipher) - ctx = _aead_setup( - backend, - cipher_name, - cipher._key, - nonce, - None, - tag_length, - _ENCRYPT, - ) - else: - _set_nonce_operation(backend, ctx, nonce, _ENCRYPT) - - # CCM requires us to pass the length of the data before processing anything - # However calling this with any other AEAD results in an error - if isinstance(cipher, AESCCM): - _set_length(backend, ctx, len(data)) - - for ad in associated_data: - _process_aad(backend, ctx, ad) - processed_data = _process_data(backend, ctx, data) - outlen = backend._ffi.new("int *") - # All AEADs we support besides OCB are streaming so they return nothing - # in finalization. OCB can return up to (16 byte block - 1) bytes so - # we need a buffer here too. - buf = backend._ffi.new("unsigned char[]", 16) - res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen) - backend.openssl_assert(res != 0) - processed_data += backend._ffi.buffer(buf, outlen[0])[:] - tag_buf = backend._ffi.new("unsigned char[]", tag_length) - res = backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, backend._lib.EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf - ) - backend.openssl_assert(res != 0) - tag = backend._ffi.buffer(tag_buf)[:] - - if isinstance(cipher, AESSIV): - # RFC 5297 defines the output as IV || C, where the tag we generate is - # the "IV" and C is the ciphertext. This is the opposite of our - # other AEADs, which are Ciphertext || Tag - backend.openssl_assert(len(tag) == 16) - return tag + processed_data - else: - return processed_data + tag - - -def _decrypt( - backend: "Backend", - cipher: "_AEAD_TYPES", - nonce: bytes, - data: bytes, - associated_data: typing.List[bytes], - tag_length: int, - ctx: typing.Any = None, -) -> bytes: - from cryptography.hazmat.primitives.ciphers.aead import AESCCM, AESSIV - - if len(data) < tag_length: - raise InvalidTag - - if isinstance(cipher, AESSIV): - # RFC 5297 defines the output as IV || C, where the tag we generate is - # the "IV" and C is the ciphertext. This is the opposite of our - # other AEADs, which are Ciphertext || Tag - tag = data[:tag_length] - data = data[tag_length:] - else: - tag = data[-tag_length:] - data = data[:-tag_length] - if ctx is None: - cipher_name = _aead_cipher_name(cipher) - ctx = _aead_setup( - backend, cipher_name, cipher._key, nonce, tag, tag_length, _DECRYPT - ) - else: - _set_nonce_operation(backend, ctx, nonce, _DECRYPT) - _set_tag(backend, ctx, tag) - - # CCM requires us to pass the length of the data before processing anything - # However calling this with any other AEAD results in an error - if isinstance(cipher, AESCCM): - _set_length(backend, ctx, len(data)) - - for ad in associated_data: - _process_aad(backend, ctx, ad) - # CCM has a different error path if the tag doesn't match. Errors are - # raised in Update and Final is irrelevant. - if isinstance(cipher, AESCCM): - outlen = backend._ffi.new("int *") - buf = backend._ffi.new("unsigned char[]", len(data)) - res = backend._lib.EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) - if res != 1: - backend._consume_errors() - raise InvalidTag - - processed_data = backend._ffi.buffer(buf, outlen[0])[:] - else: - processed_data = _process_data(backend, ctx, data) - outlen = backend._ffi.new("int *") - # OCB can return up to 15 bytes (16 byte block - 1) in finalization - buf = backend._ffi.new("unsigned char[]", 16) - res = backend._lib.EVP_CipherFinal_ex(ctx, buf, outlen) - processed_data += backend._ffi.buffer(buf, outlen[0])[:] - if res == 0: - backend._consume_errors() - raise InvalidTag - - return processed_data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py deleted file mode 100644 index 48f4265b0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py +++ /dev/null @@ -1,2514 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import collections -import contextlib -import itertools -import typing -import warnings -from contextlib import contextmanager - -from cryptography import utils, x509 -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.backends.openssl import aead -from cryptography.hazmat.backends.openssl.ciphers import _CipherContext -from cryptography.hazmat.backends.openssl.cmac import _CMACContext -from cryptography.hazmat.backends.openssl.dh import ( - _dh_params_dup, - _DHParameters, - _DHPrivateKey, - _DHPublicKey, -) -from cryptography.hazmat.backends.openssl.dsa import ( - _DSAParameters, - _DSAPrivateKey, - _DSAPublicKey, -) -from cryptography.hazmat.backends.openssl.ec import ( - _EllipticCurvePrivateKey, - _EllipticCurvePublicKey, -) -from cryptography.hazmat.backends.openssl.ed448 import ( - _ED448_KEY_SIZE, - _Ed448PrivateKey, - _Ed448PublicKey, -) -from cryptography.hazmat.backends.openssl.ed25519 import ( - _Ed25519PrivateKey, - _Ed25519PublicKey, -) -from cryptography.hazmat.backends.openssl.hashes import _HashContext -from cryptography.hazmat.backends.openssl.hmac import _HMACContext -from cryptography.hazmat.backends.openssl.poly1305 import ( - _POLY1305_KEY_SIZE, - _Poly1305Context, -) -from cryptography.hazmat.backends.openssl.rsa import ( - _RSAPrivateKey, - _RSAPublicKey, -) -from cryptography.hazmat.backends.openssl.x448 import ( - _X448PrivateKey, - _X448PublicKey, -) -from cryptography.hazmat.backends.openssl.x25519 import ( - _X25519PrivateKey, - _X25519PublicKey, -) -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.bindings.openssl import binding -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding -from cryptography.hazmat.primitives.asymmetric import ( - dh, - dsa, - ec, - ed448, - ed25519, - rsa, - x448, - x25519, -) -from cryptography.hazmat.primitives.asymmetric.padding import ( - MGF1, - OAEP, - PSS, - PKCS1v15, -) -from cryptography.hazmat.primitives.asymmetric.types import ( - CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, - PRIVATE_KEY_TYPES, - PUBLIC_KEY_TYPES, -) -from cryptography.hazmat.primitives.ciphers import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers.algorithms import ( - AES, - AES128, - AES256, - ARC4, - SM4, - Camellia, - ChaCha20, - TripleDES, - _BlowfishInternal, - _CAST5Internal, - _IDEAInternal, - _SEEDInternal, -) -from cryptography.hazmat.primitives.ciphers.modes import ( - CBC, - CFB, - CFB8, - CTR, - ECB, - GCM, - OFB, - XTS, - Mode, -) -from cryptography.hazmat.primitives.kdf import scrypt -from cryptography.hazmat.primitives.serialization import ssh -from cryptography.hazmat.primitives.serialization.pkcs12 import ( - _ALLOWED_PKCS12_TYPES, - _PKCS12_CAS_TYPES, - PBES, - PKCS12Certificate, - PKCS12KeyAndCertificates, -) - -_MemoryBIO = collections.namedtuple("_MemoryBIO", ["bio", "char_ptr"]) - - -# Not actually supported, just used as a marker for some serialization tests. -class _RC2: - pass - - -class Backend: - """ - OpenSSL API binding interfaces. - """ - - name = "openssl" - - # FIPS has opinions about acceptable algorithms and key sizes, but the - # disallowed algorithms are still present in OpenSSL. They just error if - # you try to use them. To avoid that we allowlist the algorithms in - # FIPS 140-3. This isn't ideal, but FIPS 140-3 is trash so here we are. - _fips_aead = { - b"aes-128-ccm", - b"aes-192-ccm", - b"aes-256-ccm", - b"aes-128-gcm", - b"aes-192-gcm", - b"aes-256-gcm", - } - # TripleDES encryption is disallowed/deprecated throughout 2023 in - # FIPS 140-3. To keep it simple we denylist any use of TripleDES (TDEA). - _fips_ciphers = (AES,) - # Sometimes SHA1 is still permissible. That logic is contained - # within the various *_supported methods. - _fips_hashes = ( - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - hashes.SHA512_224, - hashes.SHA512_256, - hashes.SHA3_224, - hashes.SHA3_256, - hashes.SHA3_384, - hashes.SHA3_512, - hashes.SHAKE128, - hashes.SHAKE256, - ) - _fips_ecdh_curves = ( - ec.SECP224R1, - ec.SECP256R1, - ec.SECP384R1, - ec.SECP521R1, - ) - _fips_rsa_min_key_size = 2048 - _fips_rsa_min_public_exponent = 65537 - _fips_dsa_min_modulus = 1 << 2048 - _fips_dh_min_key_size = 2048 - _fips_dh_min_modulus = 1 << _fips_dh_min_key_size - - def __init__(self): - self._binding = binding.Binding() - self._ffi = self._binding.ffi - self._lib = self._binding.lib - self._fips_enabled = self._is_fips_enabled() - - self._cipher_registry = {} - self._register_default_ciphers() - if self._fips_enabled and self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: - warnings.warn( - "OpenSSL FIPS mode is enabled. Can't enable DRBG fork safety.", - UserWarning, - ) - else: - self.activate_osrandom_engine() - self._dh_types = [self._lib.EVP_PKEY_DH] - if self._lib.Cryptography_HAS_EVP_PKEY_DHX: - self._dh_types.append(self._lib.EVP_PKEY_DHX) - - def __repr__(self) -> str: - return "".format( - self.openssl_version_text(), - self._fips_enabled, - self._binding._legacy_provider_loaded, - ) - - def openssl_assert( - self, - ok: bool, - errors: typing.Optional[typing.List[binding._OpenSSLError]] = None, - ) -> None: - return binding._openssl_assert(self._lib, ok, errors=errors) - - def _is_fips_enabled(self) -> bool: - if self._lib.Cryptography_HAS_300_FIPS: - mode = self._lib.EVP_default_properties_is_fips_enabled( - self._ffi.NULL - ) - else: - mode = self._lib.FIPS_mode() - - if mode == 0: - # OpenSSL without FIPS pushes an error on the error stack - self._lib.ERR_clear_error() - return bool(mode) - - def _enable_fips(self) -> None: - # This function enables FIPS mode for OpenSSL 3.0.0 on installs that - # have the FIPS provider installed properly. - self._binding._enable_fips() - assert self._is_fips_enabled() - self._fips_enabled = self._is_fips_enabled() - - def activate_builtin_random(self) -> None: - if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: - # Obtain a new structural reference. - e = self._lib.ENGINE_get_default_RAND() - if e != self._ffi.NULL: - self._lib.ENGINE_unregister_RAND(e) - # Reset the RNG to use the built-in. - res = self._lib.RAND_set_rand_method(self._ffi.NULL) - self.openssl_assert(res == 1) - # decrement the structural reference from get_default_RAND - res = self._lib.ENGINE_finish(e) - self.openssl_assert(res == 1) - - @contextlib.contextmanager - def _get_osurandom_engine(self): - # Fetches an engine by id and returns it. This creates a structural - # reference. - e = self._lib.ENGINE_by_id(self._lib.Cryptography_osrandom_engine_id) - self.openssl_assert(e != self._ffi.NULL) - # Initialize the engine for use. This adds a functional reference. - res = self._lib.ENGINE_init(e) - self.openssl_assert(res == 1) - - try: - yield e - finally: - # Decrement the structural ref incremented by ENGINE_by_id. - res = self._lib.ENGINE_free(e) - self.openssl_assert(res == 1) - # Decrement the functional ref incremented by ENGINE_init. - res = self._lib.ENGINE_finish(e) - self.openssl_assert(res == 1) - - def activate_osrandom_engine(self) -> None: - if self._lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: - # Unregister and free the current engine. - self.activate_builtin_random() - with self._get_osurandom_engine() as e: - # Set the engine as the default RAND provider. - res = self._lib.ENGINE_set_default_RAND(e) - self.openssl_assert(res == 1) - # Reset the RNG to use the engine - res = self._lib.RAND_set_rand_method(self._ffi.NULL) - self.openssl_assert(res == 1) - - def osrandom_engine_implementation(self) -> str: - buf = self._ffi.new("char[]", 64) - with self._get_osurandom_engine() as e: - res = self._lib.ENGINE_ctrl_cmd( - e, b"get_implementation", len(buf), buf, self._ffi.NULL, 0 - ) - self.openssl_assert(res > 0) - return self._ffi.string(buf).decode("ascii") - - def openssl_version_text(self) -> str: - """ - Friendly string name of the loaded OpenSSL library. This is not - necessarily the same version as it was compiled against. - - Example: OpenSSL 1.1.1d 10 Sep 2019 - """ - return self._ffi.string( - self._lib.OpenSSL_version(self._lib.OPENSSL_VERSION) - ).decode("ascii") - - def openssl_version_number(self) -> int: - return self._lib.OpenSSL_version_num() - - def create_hmac_ctx( - self, key: bytes, algorithm: hashes.HashAlgorithm - ) -> _HMACContext: - return _HMACContext(self, key, algorithm) - - def _evp_md_from_algorithm(self, algorithm: hashes.HashAlgorithm): - if algorithm.name == "blake2b" or algorithm.name == "blake2s": - alg = "{}{}".format( - algorithm.name, algorithm.digest_size * 8 - ).encode("ascii") - else: - alg = algorithm.name.encode("ascii") - - evp_md = self._lib.EVP_get_digestbyname(alg) - return evp_md - - def _evp_md_non_null_from_algorithm(self, algorithm: hashes.HashAlgorithm): - evp_md = self._evp_md_from_algorithm(algorithm) - self.openssl_assert(evp_md != self._ffi.NULL) - return evp_md - - def hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if self._fips_enabled and not isinstance(algorithm, self._fips_hashes): - return False - - evp_md = self._evp_md_from_algorithm(algorithm) - return evp_md != self._ffi.NULL - - def signature_hash_supported( - self, algorithm: hashes.HashAlgorithm - ) -> bool: - # Dedicated check for hashing algorithm use in message digest for - # signatures, e.g. RSA PKCS#1 v1.5 SHA1 (sha1WithRSAEncryption). - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return False - return self.hash_supported(algorithm) - - def scrypt_supported(self) -> bool: - if self._fips_enabled: - return False - else: - return self._lib.Cryptography_HAS_SCRYPT == 1 - - def hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - # FIPS mode still allows SHA1 for HMAC - if self._fips_enabled and isinstance(algorithm, hashes.SHA1): - return True - - return self.hash_supported(algorithm) - - def create_hash_ctx( - self, algorithm: hashes.HashAlgorithm - ) -> hashes.HashContext: - return _HashContext(self, algorithm) - - def cipher_supported(self, cipher: CipherAlgorithm, mode: Mode) -> bool: - if self._fips_enabled: - # FIPS mode requires AES. TripleDES is disallowed/deprecated in - # FIPS 140-3. - if not isinstance(cipher, self._fips_ciphers): - return False - - try: - adapter = self._cipher_registry[type(cipher), type(mode)] - except KeyError: - return False - evp_cipher = adapter(self, cipher, mode) - return self._ffi.NULL != evp_cipher - - def register_cipher_adapter(self, cipher_cls, mode_cls, adapter): - if (cipher_cls, mode_cls) in self._cipher_registry: - raise ValueError( - "Duplicate registration for: {} {}.".format( - cipher_cls, mode_cls - ) - ) - self._cipher_registry[cipher_cls, mode_cls] = adapter - - def _register_default_ciphers(self) -> None: - for cipher_cls in [AES, AES128, AES256]: - for mode_cls in [CBC, CTR, ECB, OFB, CFB, CFB8, GCM]: - self.register_cipher_adapter( - cipher_cls, - mode_cls, - GetCipherByName( - "{cipher.name}-{cipher.key_size}-{mode.name}" - ), - ) - for mode_cls in [CBC, CTR, ECB, OFB, CFB]: - self.register_cipher_adapter( - Camellia, - mode_cls, - GetCipherByName("{cipher.name}-{cipher.key_size}-{mode.name}"), - ) - for mode_cls in [CBC, CFB, CFB8, OFB]: - self.register_cipher_adapter( - TripleDES, mode_cls, GetCipherByName("des-ede3-{mode.name}") - ) - self.register_cipher_adapter( - TripleDES, ECB, GetCipherByName("des-ede3") - ) - self.register_cipher_adapter( - ChaCha20, type(None), GetCipherByName("chacha20") - ) - self.register_cipher_adapter(AES, XTS, _get_xts_cipher) - for mode_cls in [ECB, CBC, OFB, CFB, CTR]: - self.register_cipher_adapter( - SM4, mode_cls, GetCipherByName("sm4-{mode.name}") - ) - # Don't register legacy ciphers if they're unavailable. Hypothetically - # this wouldn't be necessary because we test availability by seeing if - # we get an EVP_CIPHER * in the _CipherContext __init__, but OpenSSL 3 - # will return a valid pointer even though the cipher is unavailable. - if ( - self._binding._legacy_provider_loaded - or not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER - ): - for mode_cls in [CBC, CFB, OFB, ECB]: - self.register_cipher_adapter( - _BlowfishInternal, - mode_cls, - GetCipherByName("bf-{mode.name}"), - ) - for mode_cls in [CBC, CFB, OFB, ECB]: - self.register_cipher_adapter( - _SEEDInternal, - mode_cls, - GetCipherByName("seed-{mode.name}"), - ) - for cipher_cls, mode_cls in itertools.product( - [_CAST5Internal, _IDEAInternal], - [CBC, OFB, CFB, ECB], - ): - self.register_cipher_adapter( - cipher_cls, - mode_cls, - GetCipherByName("{cipher.name}-{mode.name}"), - ) - self.register_cipher_adapter( - ARC4, type(None), GetCipherByName("rc4") - ) - # We don't actually support RC2, this is just used by some tests. - self.register_cipher_adapter( - _RC2, type(None), GetCipherByName("rc2") - ) - - def create_symmetric_encryption_ctx( - self, cipher: CipherAlgorithm, mode: Mode - ) -> _CipherContext: - return _CipherContext(self, cipher, mode, _CipherContext._ENCRYPT) - - def create_symmetric_decryption_ctx( - self, cipher: CipherAlgorithm, mode: Mode - ) -> _CipherContext: - return _CipherContext(self, cipher, mode, _CipherContext._DECRYPT) - - def pbkdf2_hmac_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - return self.hmac_supported(algorithm) - - def derive_pbkdf2_hmac( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: bytes, - iterations: int, - key_material: bytes, - ) -> bytes: - buf = self._ffi.new("unsigned char[]", length) - evp_md = self._evp_md_non_null_from_algorithm(algorithm) - key_material_ptr = self._ffi.from_buffer(key_material) - res = self._lib.PKCS5_PBKDF2_HMAC( - key_material_ptr, - len(key_material), - salt, - len(salt), - iterations, - evp_md, - length, - buf, - ) - self.openssl_assert(res == 1) - return self._ffi.buffer(buf)[:] - - def _consume_errors(self) -> typing.List[binding._OpenSSLError]: - return binding._consume_errors(self._lib) - - def _consume_errors_with_text( - self, - ) -> typing.List[binding._OpenSSLErrorWithText]: - return binding._consume_errors_with_text(self._lib) - - def _bn_to_int(self, bn) -> int: - assert bn != self._ffi.NULL - self.openssl_assert(not self._lib.BN_is_negative(bn)) - - bn_num_bytes = self._lib.BN_num_bytes(bn) - bin_ptr = self._ffi.new("unsigned char[]", bn_num_bytes) - bin_len = self._lib.BN_bn2bin(bn, bin_ptr) - # A zero length means the BN has value 0 - self.openssl_assert(bin_len >= 0) - val = int.from_bytes(self._ffi.buffer(bin_ptr)[:bin_len], "big") - return val - - def _int_to_bn(self, num: int, bn=None): - """ - Converts a python integer to a BIGNUM. The returned BIGNUM will not - be garbage collected (to support adding them to structs that take - ownership of the object). Be sure to register it for GC if it will - be discarded after use. - """ - assert bn is None or bn != self._ffi.NULL - - if bn is None: - bn = self._ffi.NULL - - binary = num.to_bytes(int(num.bit_length() / 8.0 + 1), "big") - bn_ptr = self._lib.BN_bin2bn(binary, len(binary), bn) - self.openssl_assert(bn_ptr != self._ffi.NULL) - return bn_ptr - - def generate_rsa_private_key( - self, public_exponent: int, key_size: int - ) -> rsa.RSAPrivateKey: - rsa._verify_rsa_parameters(public_exponent, key_size) - - rsa_cdata = self._lib.RSA_new() - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - - bn = self._int_to_bn(public_exponent) - bn = self._ffi.gc(bn, self._lib.BN_free) - - res = self._lib.RSA_generate_key_ex( - rsa_cdata, key_size, bn, self._ffi.NULL - ) - self.openssl_assert(res == 1) - evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) - - # We can skip RSA key validation here since we just generated the key - return _RSAPrivateKey( - self, rsa_cdata, evp_pkey, unsafe_skip_rsa_key_validation=True - ) - - def generate_rsa_parameters_supported( - self, public_exponent: int, key_size: int - ) -> bool: - return ( - public_exponent >= 3 - and public_exponent & 1 != 0 - and key_size >= 512 - ) - - def load_rsa_private_numbers( - self, - numbers: rsa.RSAPrivateNumbers, - unsafe_skip_rsa_key_validation: bool, - ) -> rsa.RSAPrivateKey: - rsa._check_private_key_components( - numbers.p, - numbers.q, - numbers.d, - numbers.dmp1, - numbers.dmq1, - numbers.iqmp, - numbers.public_numbers.e, - numbers.public_numbers.n, - ) - rsa_cdata = self._lib.RSA_new() - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - p = self._int_to_bn(numbers.p) - q = self._int_to_bn(numbers.q) - d = self._int_to_bn(numbers.d) - dmp1 = self._int_to_bn(numbers.dmp1) - dmq1 = self._int_to_bn(numbers.dmq1) - iqmp = self._int_to_bn(numbers.iqmp) - e = self._int_to_bn(numbers.public_numbers.e) - n = self._int_to_bn(numbers.public_numbers.n) - res = self._lib.RSA_set0_factors(rsa_cdata, p, q) - self.openssl_assert(res == 1) - res = self._lib.RSA_set0_key(rsa_cdata, n, e, d) - self.openssl_assert(res == 1) - res = self._lib.RSA_set0_crt_params(rsa_cdata, dmp1, dmq1, iqmp) - self.openssl_assert(res == 1) - evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) - - return _RSAPrivateKey( - self, - rsa_cdata, - evp_pkey, - unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, - ) - - def load_rsa_public_numbers( - self, numbers: rsa.RSAPublicNumbers - ) -> rsa.RSAPublicKey: - rsa._check_public_key_components(numbers.e, numbers.n) - rsa_cdata = self._lib.RSA_new() - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - e = self._int_to_bn(numbers.e) - n = self._int_to_bn(numbers.n) - res = self._lib.RSA_set0_key(rsa_cdata, n, e, self._ffi.NULL) - self.openssl_assert(res == 1) - evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) - - return _RSAPublicKey(self, rsa_cdata, evp_pkey) - - def _create_evp_pkey_gc(self): - evp_pkey = self._lib.EVP_PKEY_new() - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - return evp_pkey - - def _rsa_cdata_to_evp_pkey(self, rsa_cdata): - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set1_RSA(evp_pkey, rsa_cdata) - self.openssl_assert(res == 1) - return evp_pkey - - def _bytes_to_bio(self, data: bytes): - """ - Return a _MemoryBIO namedtuple of (BIO, char*). - - The char* is the storage for the BIO and it must stay alive until the - BIO is finished with. - """ - data_ptr = self._ffi.from_buffer(data) - bio = self._lib.BIO_new_mem_buf(data_ptr, len(data)) - self.openssl_assert(bio != self._ffi.NULL) - - return _MemoryBIO(self._ffi.gc(bio, self._lib.BIO_free), data_ptr) - - def _create_mem_bio_gc(self): - """ - Creates an empty memory BIO. - """ - bio_method = self._lib.BIO_s_mem() - self.openssl_assert(bio_method != self._ffi.NULL) - bio = self._lib.BIO_new(bio_method) - self.openssl_assert(bio != self._ffi.NULL) - bio = self._ffi.gc(bio, self._lib.BIO_free) - return bio - - def _read_mem_bio(self, bio) -> bytes: - """ - Reads a memory BIO. This only works on memory BIOs. - """ - buf = self._ffi.new("char **") - buf_len = self._lib.BIO_get_mem_data(bio, buf) - self.openssl_assert(buf_len > 0) - self.openssl_assert(buf[0] != self._ffi.NULL) - bio_data = self._ffi.buffer(buf[0], buf_len)[:] - return bio_data - - def _evp_pkey_to_private_key( - self, evp_pkey, unsafe_skip_rsa_key_validation: bool - ) -> PRIVATE_KEY_TYPES: - """ - Return the appropriate type of PrivateKey given an evp_pkey cdata - pointer. - """ - - key_type = self._lib.EVP_PKEY_id(evp_pkey) - - if key_type == self._lib.EVP_PKEY_RSA: - rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - return _RSAPrivateKey( - self, - rsa_cdata, - evp_pkey, - unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, - ) - elif ( - key_type == self._lib.EVP_PKEY_RSA_PSS - and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL - and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL - and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E - ): - # At the moment the way we handle RSA PSS keys is to strip the - # PSS constraints from them and treat them as normal RSA keys - # Unfortunately the RSA * itself tracks this data so we need to - # extract, serialize, and reload it without the constraints. - rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - bio = self._create_mem_bio_gc() - res = self._lib.i2d_RSAPrivateKey_bio(bio, rsa_cdata) - self.openssl_assert(res == 1) - return self.load_der_private_key( - self._read_mem_bio(bio), - password=None, - unsafe_skip_rsa_key_validation=unsafe_skip_rsa_key_validation, - ) - elif key_type == self._lib.EVP_PKEY_DSA: - dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - return _DSAPrivateKey(self, dsa_cdata, evp_pkey) - elif key_type == self._lib.EVP_PKEY_EC: - ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) - self.openssl_assert(ec_cdata != self._ffi.NULL) - ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) - return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) - elif key_type in self._dh_types: - dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) - self.openssl_assert(dh_cdata != self._ffi.NULL) - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - return _DHPrivateKey(self, dh_cdata, evp_pkey) - elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None): - # EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL - return _Ed25519PrivateKey(self, evp_pkey) - elif key_type == getattr(self._lib, "EVP_PKEY_X448", None): - # EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL - return _X448PrivateKey(self, evp_pkey) - elif key_type == self._lib.EVP_PKEY_X25519: - return _X25519PrivateKey(self, evp_pkey) - elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None): - # EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL - return _Ed448PrivateKey(self, evp_pkey) - else: - raise UnsupportedAlgorithm("Unsupported key type.") - - def _evp_pkey_to_public_key(self, evp_pkey) -> PUBLIC_KEY_TYPES: - """ - Return the appropriate type of PublicKey given an evp_pkey cdata - pointer. - """ - - key_type = self._lib.EVP_PKEY_id(evp_pkey) - - if key_type == self._lib.EVP_PKEY_RSA: - rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - return _RSAPublicKey(self, rsa_cdata, evp_pkey) - elif ( - key_type == self._lib.EVP_PKEY_RSA_PSS - and not self._lib.CRYPTOGRAPHY_IS_LIBRESSL - and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL - and not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111E - ): - rsa_cdata = self._lib.EVP_PKEY_get1_RSA(evp_pkey) - self.openssl_assert(rsa_cdata != self._ffi.NULL) - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - bio = self._create_mem_bio_gc() - res = self._lib.i2d_RSAPublicKey_bio(bio, rsa_cdata) - self.openssl_assert(res == 1) - return self.load_der_public_key(self._read_mem_bio(bio)) - elif key_type == self._lib.EVP_PKEY_DSA: - dsa_cdata = self._lib.EVP_PKEY_get1_DSA(evp_pkey) - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - return _DSAPublicKey(self, dsa_cdata, evp_pkey) - elif key_type == self._lib.EVP_PKEY_EC: - ec_cdata = self._lib.EVP_PKEY_get1_EC_KEY(evp_pkey) - if ec_cdata == self._ffi.NULL: - errors = self._consume_errors_with_text() - raise ValueError("Unable to load EC key", errors) - ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) - return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) - elif key_type in self._dh_types: - dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) - self.openssl_assert(dh_cdata != self._ffi.NULL) - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - return _DHPublicKey(self, dh_cdata, evp_pkey) - elif key_type == getattr(self._lib, "EVP_PKEY_ED25519", None): - # EVP_PKEY_ED25519 is not present in CRYPTOGRAPHY_IS_LIBRESSL - return _Ed25519PublicKey(self, evp_pkey) - elif key_type == getattr(self._lib, "EVP_PKEY_X448", None): - # EVP_PKEY_X448 is not present in CRYPTOGRAPHY_IS_LIBRESSL - return _X448PublicKey(self, evp_pkey) - elif key_type == self._lib.EVP_PKEY_X25519: - return _X25519PublicKey(self, evp_pkey) - elif key_type == getattr(self._lib, "EVP_PKEY_ED448", None): - # EVP_PKEY_ED448 is not present in CRYPTOGRAPHY_IS_LIBRESSL - return _Ed448PublicKey(self, evp_pkey) - else: - raise UnsupportedAlgorithm("Unsupported key type.") - - def _oaep_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - return isinstance( - algorithm, - ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ) - - def rsa_padding_supported(self, padding: AsymmetricPadding) -> bool: - if isinstance(padding, PKCS1v15): - return True - elif isinstance(padding, PSS) and isinstance(padding._mgf, MGF1): - # SHA1 is permissible in MGF1 in FIPS even when SHA1 is blocked - # as signature algorithm. - if self._fips_enabled and isinstance( - padding._mgf._algorithm, hashes.SHA1 - ): - return True - else: - return self.hash_supported(padding._mgf._algorithm) - elif isinstance(padding, OAEP) and isinstance(padding._mgf, MGF1): - return self._oaep_hash_supported( - padding._mgf._algorithm - ) and self._oaep_hash_supported(padding._algorithm) - else: - return False - - def generate_dsa_parameters(self, key_size: int) -> dsa.DSAParameters: - if key_size not in (1024, 2048, 3072, 4096): - raise ValueError( - "Key size must be 1024, 2048, 3072, or 4096 bits." - ) - - ctx = self._lib.DSA_new() - self.openssl_assert(ctx != self._ffi.NULL) - ctx = self._ffi.gc(ctx, self._lib.DSA_free) - - res = self._lib.DSA_generate_parameters_ex( - ctx, - key_size, - self._ffi.NULL, - 0, - self._ffi.NULL, - self._ffi.NULL, - self._ffi.NULL, - ) - - self.openssl_assert(res == 1) - - return _DSAParameters(self, ctx) - - def generate_dsa_private_key( - self, parameters: dsa.DSAParameters - ) -> dsa.DSAPrivateKey: - ctx = self._lib.DSAparams_dup( - parameters._dsa_cdata # type: ignore[attr-defined] - ) - self.openssl_assert(ctx != self._ffi.NULL) - ctx = self._ffi.gc(ctx, self._lib.DSA_free) - self._lib.DSA_generate_key(ctx) - evp_pkey = self._dsa_cdata_to_evp_pkey(ctx) - - return _DSAPrivateKey(self, ctx, evp_pkey) - - def generate_dsa_private_key_and_parameters( - self, key_size: int - ) -> dsa.DSAPrivateKey: - parameters = self.generate_dsa_parameters(key_size) - return self.generate_dsa_private_key(parameters) - - def _dsa_cdata_set_values(self, dsa_cdata, p, q, g, pub_key, priv_key): - res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) - self.openssl_assert(res == 1) - res = self._lib.DSA_set0_key(dsa_cdata, pub_key, priv_key) - self.openssl_assert(res == 1) - - def load_dsa_private_numbers( - self, numbers: dsa.DSAPrivateNumbers - ) -> dsa.DSAPrivateKey: - dsa._check_dsa_private_numbers(numbers) - parameter_numbers = numbers.public_numbers.parameter_numbers - - dsa_cdata = self._lib.DSA_new() - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - - p = self._int_to_bn(parameter_numbers.p) - q = self._int_to_bn(parameter_numbers.q) - g = self._int_to_bn(parameter_numbers.g) - pub_key = self._int_to_bn(numbers.public_numbers.y) - priv_key = self._int_to_bn(numbers.x) - self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) - - evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) - - return _DSAPrivateKey(self, dsa_cdata, evp_pkey) - - def load_dsa_public_numbers( - self, numbers: dsa.DSAPublicNumbers - ) -> dsa.DSAPublicKey: - dsa._check_dsa_parameters(numbers.parameter_numbers) - dsa_cdata = self._lib.DSA_new() - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - - p = self._int_to_bn(numbers.parameter_numbers.p) - q = self._int_to_bn(numbers.parameter_numbers.q) - g = self._int_to_bn(numbers.parameter_numbers.g) - pub_key = self._int_to_bn(numbers.y) - priv_key = self._ffi.NULL - self._dsa_cdata_set_values(dsa_cdata, p, q, g, pub_key, priv_key) - - evp_pkey = self._dsa_cdata_to_evp_pkey(dsa_cdata) - - return _DSAPublicKey(self, dsa_cdata, evp_pkey) - - def load_dsa_parameter_numbers( - self, numbers: dsa.DSAParameterNumbers - ) -> dsa.DSAParameters: - dsa._check_dsa_parameters(numbers) - dsa_cdata = self._lib.DSA_new() - self.openssl_assert(dsa_cdata != self._ffi.NULL) - dsa_cdata = self._ffi.gc(dsa_cdata, self._lib.DSA_free) - - p = self._int_to_bn(numbers.p) - q = self._int_to_bn(numbers.q) - g = self._int_to_bn(numbers.g) - res = self._lib.DSA_set0_pqg(dsa_cdata, p, q, g) - self.openssl_assert(res == 1) - - return _DSAParameters(self, dsa_cdata) - - def _dsa_cdata_to_evp_pkey(self, dsa_cdata): - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set1_DSA(evp_pkey, dsa_cdata) - self.openssl_assert(res == 1) - return evp_pkey - - def dsa_supported(self) -> bool: - return not self._fips_enabled - - def dsa_hash_supported(self, algorithm: hashes.HashAlgorithm) -> bool: - if not self.dsa_supported(): - return False - return self.signature_hash_supported(algorithm) - - def cmac_algorithm_supported(self, algorithm) -> bool: - return self.cipher_supported( - algorithm, CBC(b"\x00" * algorithm.block_size) - ) - - def create_cmac_ctx(self, algorithm: BlockCipherAlgorithm) -> _CMACContext: - return _CMACContext(self, algorithm) - - def load_pem_private_key( - self, - data: bytes, - password: typing.Optional[bytes], - unsafe_skip_rsa_key_validation: bool, - ) -> PRIVATE_KEY_TYPES: - return self._load_key( - self._lib.PEM_read_bio_PrivateKey, - data, - password, - unsafe_skip_rsa_key_validation, - ) - - def load_pem_public_key(self, data: bytes) -> PUBLIC_KEY_TYPES: - mem_bio = self._bytes_to_bio(data) - # In OpenSSL 3.0.x the PEM_read_bio_PUBKEY function will invoke - # the default password callback if you pass an encrypted private - # key. This is very, very, very bad as the default callback can - # trigger an interactive console prompt, which will hang the - # Python process. We therefore provide our own callback to - # catch this and error out properly. - userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *") - evp_pkey = self._lib.PEM_read_bio_PUBKEY( - mem_bio.bio, - self._ffi.NULL, - self._ffi.addressof( - self._lib._original_lib, "Cryptography_pem_password_cb" - ), - userdata, - ) - if evp_pkey != self._ffi.NULL: - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - return self._evp_pkey_to_public_key(evp_pkey) - else: - # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still - # need to check to see if it is a pure PKCS1 RSA public key (not - # embedded in a subjectPublicKeyInfo) - self._consume_errors() - res = self._lib.BIO_reset(mem_bio.bio) - self.openssl_assert(res == 1) - rsa_cdata = self._lib.PEM_read_bio_RSAPublicKey( - mem_bio.bio, - self._ffi.NULL, - self._ffi.addressof( - self._lib._original_lib, "Cryptography_pem_password_cb" - ), - userdata, - ) - if rsa_cdata != self._ffi.NULL: - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) - return _RSAPublicKey(self, rsa_cdata, evp_pkey) - else: - self._handle_key_loading_error() - - def load_pem_parameters(self, data: bytes) -> dh.DHParameters: - mem_bio = self._bytes_to_bio(data) - # only DH is supported currently - dh_cdata = self._lib.PEM_read_bio_DHparams( - mem_bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL - ) - if dh_cdata != self._ffi.NULL: - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - return _DHParameters(self, dh_cdata) - else: - self._handle_key_loading_error() - - def load_der_private_key( - self, - data: bytes, - password: typing.Optional[bytes], - unsafe_skip_rsa_key_validation: bool, - ) -> PRIVATE_KEY_TYPES: - # OpenSSL has a function called d2i_AutoPrivateKey that in theory - # handles this automatically, however it doesn't handle encrypted - # private keys. Instead we try to load the key two different ways. - # First we'll try to load it as a traditional key. - bio_data = self._bytes_to_bio(data) - key = self._evp_pkey_from_der_traditional_key(bio_data, password) - if key: - return self._evp_pkey_to_private_key( - key, unsafe_skip_rsa_key_validation - ) - else: - # Finally we try to load it with the method that handles encrypted - # PKCS8 properly. - return self._load_key( - self._lib.d2i_PKCS8PrivateKey_bio, - data, - password, - unsafe_skip_rsa_key_validation, - ) - - def _evp_pkey_from_der_traditional_key(self, bio_data, password): - key = self._lib.d2i_PrivateKey_bio(bio_data.bio, self._ffi.NULL) - if key != self._ffi.NULL: - key = self._ffi.gc(key, self._lib.EVP_PKEY_free) - if password is not None: - raise TypeError( - "Password was given but private key is not encrypted." - ) - - return key - else: - self._consume_errors() - return None - - def load_der_public_key(self, data: bytes) -> PUBLIC_KEY_TYPES: - mem_bio = self._bytes_to_bio(data) - evp_pkey = self._lib.d2i_PUBKEY_bio(mem_bio.bio, self._ffi.NULL) - if evp_pkey != self._ffi.NULL: - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - return self._evp_pkey_to_public_key(evp_pkey) - else: - # It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still - # need to check to see if it is a pure PKCS1 RSA public key (not - # embedded in a subjectPublicKeyInfo) - self._consume_errors() - res = self._lib.BIO_reset(mem_bio.bio) - self.openssl_assert(res == 1) - rsa_cdata = self._lib.d2i_RSAPublicKey_bio( - mem_bio.bio, self._ffi.NULL - ) - if rsa_cdata != self._ffi.NULL: - rsa_cdata = self._ffi.gc(rsa_cdata, self._lib.RSA_free) - evp_pkey = self._rsa_cdata_to_evp_pkey(rsa_cdata) - return _RSAPublicKey(self, rsa_cdata, evp_pkey) - else: - self._handle_key_loading_error() - - def load_der_parameters(self, data: bytes) -> dh.DHParameters: - mem_bio = self._bytes_to_bio(data) - dh_cdata = self._lib.d2i_DHparams_bio(mem_bio.bio, self._ffi.NULL) - if dh_cdata != self._ffi.NULL: - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - return _DHParameters(self, dh_cdata) - elif self._lib.Cryptography_HAS_EVP_PKEY_DHX: - # We check to see if the is dhx. - self._consume_errors() - res = self._lib.BIO_reset(mem_bio.bio) - self.openssl_assert(res == 1) - dh_cdata = self._lib.d2i_DHxparams_bio(mem_bio.bio, self._ffi.NULL) - if dh_cdata != self._ffi.NULL: - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - return _DHParameters(self, dh_cdata) - - self._handle_key_loading_error() - - def _cert2ossl(self, cert: x509.Certificate) -> typing.Any: - data = cert.public_bytes(serialization.Encoding.DER) - mem_bio = self._bytes_to_bio(data) - x509 = self._lib.d2i_X509_bio(mem_bio.bio, self._ffi.NULL) - self.openssl_assert(x509 != self._ffi.NULL) - x509 = self._ffi.gc(x509, self._lib.X509_free) - return x509 - - def _ossl2cert(self, x509: typing.Any) -> x509.Certificate: - bio = self._create_mem_bio_gc() - res = self._lib.i2d_X509_bio(bio, x509) - self.openssl_assert(res == 1) - return rust_x509.load_der_x509_certificate(self._read_mem_bio(bio)) - - def _csr2ossl(self, csr: x509.CertificateSigningRequest) -> typing.Any: - data = csr.public_bytes(serialization.Encoding.DER) - mem_bio = self._bytes_to_bio(data) - x509_req = self._lib.d2i_X509_REQ_bio(mem_bio.bio, self._ffi.NULL) - self.openssl_assert(x509_req != self._ffi.NULL) - x509_req = self._ffi.gc(x509_req, self._lib.X509_REQ_free) - return x509_req - - def _crl2ossl(self, crl: x509.CertificateRevocationList) -> typing.Any: - data = crl.public_bytes(serialization.Encoding.DER) - mem_bio = self._bytes_to_bio(data) - x509_crl = self._lib.d2i_X509_CRL_bio(mem_bio.bio, self._ffi.NULL) - self.openssl_assert(x509_crl != self._ffi.NULL) - x509_crl = self._ffi.gc(x509_crl, self._lib.X509_CRL_free) - return x509_crl - - def _crl_is_signature_valid( - self, - crl: x509.CertificateRevocationList, - public_key: CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, - ) -> bool: - if not isinstance( - public_key, - ( - _DSAPublicKey, - _RSAPublicKey, - _EllipticCurvePublicKey, - ), - ): - raise TypeError( - "Expecting one of DSAPublicKey, RSAPublicKey," - " or EllipticCurvePublicKey." - ) - x509_crl = self._crl2ossl(crl) - res = self._lib.X509_CRL_verify(x509_crl, public_key._evp_pkey) - - if res != 1: - self._consume_errors() - return False - - return True - - def _csr_is_signature_valid( - self, csr: x509.CertificateSigningRequest - ) -> bool: - x509_req = self._csr2ossl(csr) - pkey = self._lib.X509_REQ_get_pubkey(x509_req) - self.openssl_assert(pkey != self._ffi.NULL) - pkey = self._ffi.gc(pkey, self._lib.EVP_PKEY_free) - res = self._lib.X509_REQ_verify(x509_req, pkey) - - if res != 1: - self._consume_errors() - return False - - return True - - def _check_keys_correspond(self, key1, key2): - if self._lib.EVP_PKEY_cmp(key1._evp_pkey, key2._evp_pkey) != 1: - raise ValueError("Keys do not correspond") - - def _load_key( - self, openssl_read_func, data, password, unsafe_skip_rsa_key_validation - ): - mem_bio = self._bytes_to_bio(data) - - userdata = self._ffi.new("CRYPTOGRAPHY_PASSWORD_DATA *") - if password is not None: - utils._check_byteslike("password", password) - password_ptr = self._ffi.from_buffer(password) - userdata.password = password_ptr - userdata.length = len(password) - - evp_pkey = openssl_read_func( - mem_bio.bio, - self._ffi.NULL, - self._ffi.addressof( - self._lib._original_lib, "Cryptography_pem_password_cb" - ), - userdata, - ) - - if evp_pkey == self._ffi.NULL: - if userdata.error != 0: - self._consume_errors() - if userdata.error == -1: - raise TypeError( - "Password was not given but private key is encrypted" - ) - else: - assert userdata.error == -2 - raise ValueError( - "Passwords longer than {} bytes are not supported " - "by this backend.".format(userdata.maxsize - 1) - ) - else: - self._handle_key_loading_error() - - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - - if password is not None and userdata.called == 0: - raise TypeError( - "Password was given but private key is not encrypted." - ) - - assert ( - password is not None and userdata.called == 1 - ) or password is None - - return self._evp_pkey_to_private_key( - evp_pkey, unsafe_skip_rsa_key_validation - ) - - def _handle_key_loading_error(self) -> typing.NoReturn: - errors = self._consume_errors() - - if not errors: - raise ValueError( - "Could not deserialize key data. The data may be in an " - "incorrect format or it may be encrypted with an unsupported " - "algorithm." - ) - - elif ( - errors[0]._lib_reason_match( - self._lib.ERR_LIB_EVP, self._lib.EVP_R_BAD_DECRYPT - ) - or errors[0]._lib_reason_match( - self._lib.ERR_LIB_PKCS12, - self._lib.PKCS12_R_PKCS12_CIPHERFINAL_ERROR, - ) - or ( - self._lib.Cryptography_HAS_PROVIDERS - and errors[0]._lib_reason_match( - self._lib.ERR_LIB_PROV, - self._lib.PROV_R_BAD_DECRYPT, - ) - ) - ): - raise ValueError("Bad decrypt. Incorrect password?") - - elif any( - error._lib_reason_match( - self._lib.ERR_LIB_EVP, - self._lib.EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM, - ) - for error in errors - ): - raise ValueError("Unsupported public key algorithm.") - - else: - errors_with_text = binding._errors_with_text(errors) - raise ValueError( - "Could not deserialize key data. The data may be in an " - "incorrect format, it may be encrypted with an unsupported " - "algorithm, or it may be an unsupported key type (e.g. EC " - "curves with explicit parameters).", - errors_with_text, - ) - - def elliptic_curve_supported(self, curve: ec.EllipticCurve) -> bool: - try: - curve_nid = self._elliptic_curve_to_nid(curve) - except UnsupportedAlgorithm: - curve_nid = self._lib.NID_undef - - group = self._lib.EC_GROUP_new_by_curve_name(curve_nid) - - if group == self._ffi.NULL: - self._consume_errors() - return False - else: - self.openssl_assert(curve_nid != self._lib.NID_undef) - self._lib.EC_GROUP_free(group) - return True - - def elliptic_curve_signature_algorithm_supported( - self, - signature_algorithm: ec.EllipticCurveSignatureAlgorithm, - curve: ec.EllipticCurve, - ) -> bool: - # We only support ECDSA right now. - if not isinstance(signature_algorithm, ec.ECDSA): - return False - - return self.elliptic_curve_supported(curve) - - def generate_elliptic_curve_private_key( - self, curve: ec.EllipticCurve - ) -> ec.EllipticCurvePrivateKey: - """ - Generate a new private key on the named curve. - """ - - if self.elliptic_curve_supported(curve): - ec_cdata = self._ec_key_new_by_curve(curve) - - res = self._lib.EC_KEY_generate_key(ec_cdata) - self.openssl_assert(res == 1) - - evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) - - return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) - else: - raise UnsupportedAlgorithm( - "Backend object does not support {}.".format(curve.name), - _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, - ) - - def load_elliptic_curve_private_numbers( - self, numbers: ec.EllipticCurvePrivateNumbers - ) -> ec.EllipticCurvePrivateKey: - public = numbers.public_numbers - - ec_cdata = self._ec_key_new_by_curve(public.curve) - - private_value = self._ffi.gc( - self._int_to_bn(numbers.private_value), self._lib.BN_clear_free - ) - res = self._lib.EC_KEY_set_private_key(ec_cdata, private_value) - if res != 1: - self._consume_errors() - raise ValueError("Invalid EC key.") - - self._ec_key_set_public_key_affine_coordinates( - ec_cdata, public.x, public.y - ) - - evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) - - return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) - - def load_elliptic_curve_public_numbers( - self, numbers: ec.EllipticCurvePublicNumbers - ) -> ec.EllipticCurvePublicKey: - ec_cdata = self._ec_key_new_by_curve(numbers.curve) - self._ec_key_set_public_key_affine_coordinates( - ec_cdata, numbers.x, numbers.y - ) - evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) - - return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) - - def load_elliptic_curve_public_bytes( - self, curve: ec.EllipticCurve, point_bytes: bytes - ) -> ec.EllipticCurvePublicKey: - ec_cdata = self._ec_key_new_by_curve(curve) - group = self._lib.EC_KEY_get0_group(ec_cdata) - self.openssl_assert(group != self._ffi.NULL) - point = self._lib.EC_POINT_new(group) - self.openssl_assert(point != self._ffi.NULL) - point = self._ffi.gc(point, self._lib.EC_POINT_free) - with self._tmp_bn_ctx() as bn_ctx: - res = self._lib.EC_POINT_oct2point( - group, point, point_bytes, len(point_bytes), bn_ctx - ) - if res != 1: - self._consume_errors() - raise ValueError("Invalid public bytes for the given curve") - - res = self._lib.EC_KEY_set_public_key(ec_cdata, point) - self.openssl_assert(res == 1) - evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) - return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) - - def derive_elliptic_curve_private_key( - self, private_value: int, curve: ec.EllipticCurve - ) -> ec.EllipticCurvePrivateKey: - ec_cdata = self._ec_key_new_by_curve(curve) - - get_func, group = self._ec_key_determine_group_get_func(ec_cdata) - - point = self._lib.EC_POINT_new(group) - self.openssl_assert(point != self._ffi.NULL) - point = self._ffi.gc(point, self._lib.EC_POINT_free) - - value = self._int_to_bn(private_value) - value = self._ffi.gc(value, self._lib.BN_clear_free) - - with self._tmp_bn_ctx() as bn_ctx: - res = self._lib.EC_POINT_mul( - group, point, value, self._ffi.NULL, self._ffi.NULL, bn_ctx - ) - self.openssl_assert(res == 1) - - bn_x = self._lib.BN_CTX_get(bn_ctx) - bn_y = self._lib.BN_CTX_get(bn_ctx) - - res = get_func(group, point, bn_x, bn_y, bn_ctx) - if res != 1: - self._consume_errors() - raise ValueError("Unable to derive key from private_value") - - res = self._lib.EC_KEY_set_public_key(ec_cdata, point) - self.openssl_assert(res == 1) - private = self._int_to_bn(private_value) - private = self._ffi.gc(private, self._lib.BN_clear_free) - res = self._lib.EC_KEY_set_private_key(ec_cdata, private) - self.openssl_assert(res == 1) - - evp_pkey = self._ec_cdata_to_evp_pkey(ec_cdata) - - return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) - - def _ec_key_new_by_curve(self, curve: ec.EllipticCurve): - curve_nid = self._elliptic_curve_to_nid(curve) - return self._ec_key_new_by_curve_nid(curve_nid) - - def _ec_key_new_by_curve_nid(self, curve_nid: int): - ec_cdata = self._lib.EC_KEY_new_by_curve_name(curve_nid) - self.openssl_assert(ec_cdata != self._ffi.NULL) - return self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) - - def elliptic_curve_exchange_algorithm_supported( - self, algorithm: ec.ECDH, curve: ec.EllipticCurve - ) -> bool: - if self._fips_enabled and not isinstance( - curve, self._fips_ecdh_curves - ): - return False - - return self.elliptic_curve_supported(curve) and isinstance( - algorithm, ec.ECDH - ) - - def _ec_cdata_to_evp_pkey(self, ec_cdata): - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set1_EC_KEY(evp_pkey, ec_cdata) - self.openssl_assert(res == 1) - return evp_pkey - - def _elliptic_curve_to_nid(self, curve: ec.EllipticCurve) -> int: - """ - Get the NID for a curve name. - """ - - curve_aliases = {"secp192r1": "prime192v1", "secp256r1": "prime256v1"} - - curve_name = curve_aliases.get(curve.name, curve.name) - - curve_nid = self._lib.OBJ_sn2nid(curve_name.encode()) - if curve_nid == self._lib.NID_undef: - raise UnsupportedAlgorithm( - "{} is not a supported elliptic curve".format(curve.name), - _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, - ) - return curve_nid - - @contextmanager - def _tmp_bn_ctx(self): - bn_ctx = self._lib.BN_CTX_new() - self.openssl_assert(bn_ctx != self._ffi.NULL) - bn_ctx = self._ffi.gc(bn_ctx, self._lib.BN_CTX_free) - self._lib.BN_CTX_start(bn_ctx) - try: - yield bn_ctx - finally: - self._lib.BN_CTX_end(bn_ctx) - - def _ec_key_determine_group_get_func(self, ctx): - """ - Given an EC_KEY determine the group and what function is required to - get point coordinates. - """ - self.openssl_assert(ctx != self._ffi.NULL) - - nid_two_field = self._lib.OBJ_sn2nid(b"characteristic-two-field") - self.openssl_assert(nid_two_field != self._lib.NID_undef) - - group = self._lib.EC_KEY_get0_group(ctx) - self.openssl_assert(group != self._ffi.NULL) - - method = self._lib.EC_GROUP_method_of(group) - self.openssl_assert(method != self._ffi.NULL) - - nid = self._lib.EC_METHOD_get_field_type(method) - self.openssl_assert(nid != self._lib.NID_undef) - - if nid == nid_two_field and self._lib.Cryptography_HAS_EC2M: - get_func = self._lib.EC_POINT_get_affine_coordinates_GF2m - else: - get_func = self._lib.EC_POINT_get_affine_coordinates_GFp - - assert get_func - - return get_func, group - - def _ec_key_set_public_key_affine_coordinates(self, ctx, x: int, y: int): - """ - Sets the public key point in the EC_KEY context to the affine x and y - values. - """ - - if x < 0 or y < 0: - raise ValueError( - "Invalid EC key. Both x and y must be non-negative." - ) - - x = self._ffi.gc(self._int_to_bn(x), self._lib.BN_free) - y = self._ffi.gc(self._int_to_bn(y), self._lib.BN_free) - res = self._lib.EC_KEY_set_public_key_affine_coordinates(ctx, x, y) - if res != 1: - self._consume_errors() - raise ValueError("Invalid EC key.") - - def _private_key_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - key, - evp_pkey, - cdata, - ) -> bytes: - # validate argument types - if not isinstance(encoding, serialization.Encoding): - raise TypeError("encoding must be an item from the Encoding enum") - if not isinstance(format, serialization.PrivateFormat): - raise TypeError( - "format must be an item from the PrivateFormat enum" - ) - if not isinstance( - encryption_algorithm, serialization.KeySerializationEncryption - ): - raise TypeError( - "Encryption algorithm must be a KeySerializationEncryption " - "instance" - ) - - # validate password - if isinstance(encryption_algorithm, serialization.NoEncryption): - password = b"" - elif isinstance( - encryption_algorithm, serialization.BestAvailableEncryption - ): - password = encryption_algorithm.password - if len(password) > 1023: - raise ValueError( - "Passwords longer than 1023 bytes are not supported by " - "this backend" - ) - elif ( - isinstance( - encryption_algorithm, serialization._KeySerializationEncryption - ) - and encryption_algorithm._format - is format - is serialization.PrivateFormat.OpenSSH - ): - password = encryption_algorithm.password - else: - raise ValueError("Unsupported encryption type") - - # PKCS8 + PEM/DER - if format is serialization.PrivateFormat.PKCS8: - if encoding is serialization.Encoding.PEM: - write_bio = self._lib.PEM_write_bio_PKCS8PrivateKey - elif encoding is serialization.Encoding.DER: - write_bio = self._lib.i2d_PKCS8PrivateKey_bio - else: - raise ValueError("Unsupported encoding for PKCS8") - return self._private_key_bytes_via_bio( - write_bio, evp_pkey, password - ) - - # TraditionalOpenSSL + PEM/DER - if format is serialization.PrivateFormat.TraditionalOpenSSL: - if self._fips_enabled and not isinstance( - encryption_algorithm, serialization.NoEncryption - ): - raise ValueError( - "Encrypted traditional OpenSSL format is not " - "supported in FIPS mode." - ) - key_type = self._lib.EVP_PKEY_id(evp_pkey) - - if encoding is serialization.Encoding.PEM: - if key_type == self._lib.EVP_PKEY_RSA: - write_bio = self._lib.PEM_write_bio_RSAPrivateKey - elif key_type == self._lib.EVP_PKEY_DSA: - write_bio = self._lib.PEM_write_bio_DSAPrivateKey - elif key_type == self._lib.EVP_PKEY_EC: - write_bio = self._lib.PEM_write_bio_ECPrivateKey - else: - raise ValueError( - "Unsupported key type for TraditionalOpenSSL" - ) - return self._private_key_bytes_via_bio( - write_bio, cdata, password - ) - - if encoding is serialization.Encoding.DER: - if password: - raise ValueError( - "Encryption is not supported for DER encoded " - "traditional OpenSSL keys" - ) - if key_type == self._lib.EVP_PKEY_RSA: - write_bio = self._lib.i2d_RSAPrivateKey_bio - elif key_type == self._lib.EVP_PKEY_EC: - write_bio = self._lib.i2d_ECPrivateKey_bio - elif key_type == self._lib.EVP_PKEY_DSA: - write_bio = self._lib.i2d_DSAPrivateKey_bio - else: - raise ValueError( - "Unsupported key type for TraditionalOpenSSL" - ) - return self._bio_func_output(write_bio, cdata) - - raise ValueError("Unsupported encoding for TraditionalOpenSSL") - - # OpenSSH + PEM - if format is serialization.PrivateFormat.OpenSSH: - if encoding is serialization.Encoding.PEM: - return ssh._serialize_ssh_private_key( - key, password, encryption_algorithm - ) - - raise ValueError( - "OpenSSH private key format can only be used" - " with PEM encoding" - ) - - # Anything that key-specific code was supposed to handle earlier, - # like Raw. - raise ValueError("format is invalid with this key") - - def _private_key_bytes_via_bio(self, write_bio, evp_pkey, password): - if not password: - evp_cipher = self._ffi.NULL - else: - # This is a curated value that we will update over time. - evp_cipher = self._lib.EVP_get_cipherbyname(b"aes-256-cbc") - - return self._bio_func_output( - write_bio, - evp_pkey, - evp_cipher, - password, - len(password), - self._ffi.NULL, - self._ffi.NULL, - ) - - def _bio_func_output(self, write_bio, *args): - bio = self._create_mem_bio_gc() - res = write_bio(bio, *args) - self.openssl_assert(res == 1) - return self._read_mem_bio(bio) - - def _public_key_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - key, - evp_pkey, - cdata, - ) -> bytes: - if not isinstance(encoding, serialization.Encoding): - raise TypeError("encoding must be an item from the Encoding enum") - if not isinstance(format, serialization.PublicFormat): - raise TypeError( - "format must be an item from the PublicFormat enum" - ) - - # SubjectPublicKeyInfo + PEM/DER - if format is serialization.PublicFormat.SubjectPublicKeyInfo: - if encoding is serialization.Encoding.PEM: - write_bio = self._lib.PEM_write_bio_PUBKEY - elif encoding is serialization.Encoding.DER: - write_bio = self._lib.i2d_PUBKEY_bio - else: - raise ValueError( - "SubjectPublicKeyInfo works only with PEM or DER encoding" - ) - return self._bio_func_output(write_bio, evp_pkey) - - # PKCS1 + PEM/DER - if format is serialization.PublicFormat.PKCS1: - # Only RSA is supported here. - key_type = self._lib.EVP_PKEY_id(evp_pkey) - if key_type != self._lib.EVP_PKEY_RSA: - raise ValueError("PKCS1 format is supported only for RSA keys") - - if encoding is serialization.Encoding.PEM: - write_bio = self._lib.PEM_write_bio_RSAPublicKey - elif encoding is serialization.Encoding.DER: - write_bio = self._lib.i2d_RSAPublicKey_bio - else: - raise ValueError("PKCS1 works only with PEM or DER encoding") - return self._bio_func_output(write_bio, cdata) - - # OpenSSH + OpenSSH - if format is serialization.PublicFormat.OpenSSH: - if encoding is serialization.Encoding.OpenSSH: - return ssh.serialize_ssh_public_key(key) - - raise ValueError( - "OpenSSH format must be used with OpenSSH encoding" - ) - - # Anything that key-specific code was supposed to handle earlier, - # like Raw, CompressedPoint, UncompressedPoint - raise ValueError("format is invalid with this key") - - def dh_supported(self) -> bool: - return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL - - def generate_dh_parameters( - self, generator: int, key_size: int - ) -> dh.DHParameters: - if key_size < dh._MIN_MODULUS_SIZE: - raise ValueError( - "DH key_size must be at least {} bits".format( - dh._MIN_MODULUS_SIZE - ) - ) - - if generator not in (2, 5): - raise ValueError("DH generator must be 2 or 5") - - dh_param_cdata = self._lib.DH_new() - self.openssl_assert(dh_param_cdata != self._ffi.NULL) - dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free) - - res = self._lib.DH_generate_parameters_ex( - dh_param_cdata, key_size, generator, self._ffi.NULL - ) - if res != 1: - errors = self._consume_errors_with_text() - raise ValueError("Unable to generate DH parameters", errors) - - return _DHParameters(self, dh_param_cdata) - - def _dh_cdata_to_evp_pkey(self, dh_cdata): - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set1_DH(evp_pkey, dh_cdata) - self.openssl_assert(res == 1) - return evp_pkey - - def generate_dh_private_key( - self, parameters: dh.DHParameters - ) -> dh.DHPrivateKey: - dh_key_cdata = _dh_params_dup( - parameters._dh_cdata, self # type: ignore[attr-defined] - ) - - res = self._lib.DH_generate_key(dh_key_cdata) - self.openssl_assert(res == 1) - - evp_pkey = self._dh_cdata_to_evp_pkey(dh_key_cdata) - - return _DHPrivateKey(self, dh_key_cdata, evp_pkey) - - def generate_dh_private_key_and_parameters( - self, generator: int, key_size: int - ) -> dh.DHPrivateKey: - return self.generate_dh_private_key( - self.generate_dh_parameters(generator, key_size) - ) - - def load_dh_private_numbers( - self, numbers: dh.DHPrivateNumbers - ) -> dh.DHPrivateKey: - parameter_numbers = numbers.public_numbers.parameter_numbers - - dh_cdata = self._lib.DH_new() - self.openssl_assert(dh_cdata != self._ffi.NULL) - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - - p = self._int_to_bn(parameter_numbers.p) - g = self._int_to_bn(parameter_numbers.g) - - if parameter_numbers.q is not None: - q = self._int_to_bn(parameter_numbers.q) - else: - q = self._ffi.NULL - - pub_key = self._int_to_bn(numbers.public_numbers.y) - priv_key = self._int_to_bn(numbers.x) - - res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) - self.openssl_assert(res == 1) - - res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key) - self.openssl_assert(res == 1) - - codes = self._ffi.new("int[]", 1) - res = self._lib.DH_check(dh_cdata, codes) - self.openssl_assert(res == 1) - - # DH_check will return DH_NOT_SUITABLE_GENERATOR if p % 24 does not - # equal 11 when the generator is 2 (a quadratic nonresidue). - # We want to ignore that error because p % 24 == 23 is also fine. - # Specifically, g is then a quadratic residue. Within the context of - # Diffie-Hellman this means it can only generate half the possible - # values. That sounds bad, but quadratic nonresidues leak a bit of - # the key to the attacker in exchange for having the full key space - # available. See: https://crypto.stackexchange.com/questions/12961 - if codes[0] != 0 and not ( - parameter_numbers.g == 2 - and codes[0] ^ self._lib.DH_NOT_SUITABLE_GENERATOR == 0 - ): - raise ValueError("DH private numbers did not pass safety checks.") - - evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) - - return _DHPrivateKey(self, dh_cdata, evp_pkey) - - def load_dh_public_numbers( - self, numbers: dh.DHPublicNumbers - ) -> dh.DHPublicKey: - dh_cdata = self._lib.DH_new() - self.openssl_assert(dh_cdata != self._ffi.NULL) - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - - parameter_numbers = numbers.parameter_numbers - - p = self._int_to_bn(parameter_numbers.p) - g = self._int_to_bn(parameter_numbers.g) - - if parameter_numbers.q is not None: - q = self._int_to_bn(parameter_numbers.q) - else: - q = self._ffi.NULL - - pub_key = self._int_to_bn(numbers.y) - - res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) - self.openssl_assert(res == 1) - - res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL) - self.openssl_assert(res == 1) - - evp_pkey = self._dh_cdata_to_evp_pkey(dh_cdata) - - return _DHPublicKey(self, dh_cdata, evp_pkey) - - def load_dh_parameter_numbers( - self, numbers: dh.DHParameterNumbers - ) -> dh.DHParameters: - dh_cdata = self._lib.DH_new() - self.openssl_assert(dh_cdata != self._ffi.NULL) - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - - p = self._int_to_bn(numbers.p) - g = self._int_to_bn(numbers.g) - - if numbers.q is not None: - q = self._int_to_bn(numbers.q) - else: - q = self._ffi.NULL - - res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) - self.openssl_assert(res == 1) - - return _DHParameters(self, dh_cdata) - - def dh_parameters_supported( - self, p: int, g: int, q: typing.Optional[int] = None - ) -> bool: - dh_cdata = self._lib.DH_new() - self.openssl_assert(dh_cdata != self._ffi.NULL) - dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) - - p = self._int_to_bn(p) - g = self._int_to_bn(g) - - if q is not None: - q = self._int_to_bn(q) - else: - q = self._ffi.NULL - - res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) - self.openssl_assert(res == 1) - - codes = self._ffi.new("int[]", 1) - res = self._lib.DH_check(dh_cdata, codes) - self.openssl_assert(res == 1) - - return codes[0] == 0 - - def dh_x942_serialization_supported(self) -> bool: - return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 - - def x25519_load_public_bytes(self, data: bytes) -> x25519.X25519PublicKey: - # If/when LibreSSL adds support for EVP_PKEY_new_raw_public_key we - # can switch to it (Cryptography_HAS_RAW_KEY) - if len(data) != 32: - raise ValueError("An X25519 public key is 32 bytes long") - - evp_pkey = self._create_evp_pkey_gc() - res = self._lib.EVP_PKEY_set_type(evp_pkey, self._lib.NID_X25519) - self.openssl_assert(res == 1) - res = self._lib.EVP_PKEY_set1_tls_encodedpoint( - evp_pkey, data, len(data) - ) - self.openssl_assert(res == 1) - return _X25519PublicKey(self, evp_pkey) - - def x25519_load_private_bytes( - self, data: bytes - ) -> x25519.X25519PrivateKey: - # If/when LibreSSL adds support for EVP_PKEY_new_raw_private_key we - # can switch to it (Cryptography_HAS_RAW_KEY) drop the - # zeroed_bytearray garbage. - # OpenSSL only has facilities for loading PKCS8 formatted private - # keys using the algorithm identifiers specified in - # https://tools.ietf.org/html/draft-ietf-curdle-pkix-09. - # This is the standard PKCS8 prefix for a 32 byte X25519 key. - # The form is: - # 0:d=0 hl=2 l= 46 cons: SEQUENCE - # 2:d=1 hl=2 l= 1 prim: INTEGER :00 - # 5:d=1 hl=2 l= 5 cons: SEQUENCE - # 7:d=2 hl=2 l= 3 prim: OBJECT :1.3.101.110 - # 12:d=1 hl=2 l= 34 prim: OCTET STRING (the key) - # Of course there's a bit more complexity. In reality OCTET STRING - # contains an OCTET STRING of length 32! So the last two bytes here - # are \x04\x20, which is an OCTET STRING of length 32. - if len(data) != 32: - raise ValueError("An X25519 private key is 32 bytes long") - - pkcs8_prefix = b'0.\x02\x01\x000\x05\x06\x03+en\x04"\x04 ' - with self._zeroed_bytearray(48) as ba: - ba[0:16] = pkcs8_prefix - ba[16:] = data - bio = self._bytes_to_bio(ba) - evp_pkey = self._lib.d2i_PrivateKey_bio(bio.bio, self._ffi.NULL) - - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - self.openssl_assert( - self._lib.EVP_PKEY_id(evp_pkey) == self._lib.EVP_PKEY_X25519 - ) - return _X25519PrivateKey(self, evp_pkey) - - def _evp_pkey_keygen_gc(self, nid): - evp_pkey_ctx = self._lib.EVP_PKEY_CTX_new_id(nid, self._ffi.NULL) - self.openssl_assert(evp_pkey_ctx != self._ffi.NULL) - evp_pkey_ctx = self._ffi.gc(evp_pkey_ctx, self._lib.EVP_PKEY_CTX_free) - res = self._lib.EVP_PKEY_keygen_init(evp_pkey_ctx) - self.openssl_assert(res == 1) - evp_ppkey = self._ffi.new("EVP_PKEY **") - res = self._lib.EVP_PKEY_keygen(evp_pkey_ctx, evp_ppkey) - self.openssl_assert(res == 1) - self.openssl_assert(evp_ppkey[0] != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_ppkey[0], self._lib.EVP_PKEY_free) - return evp_pkey - - def x25519_generate_key(self) -> x25519.X25519PrivateKey: - evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X25519) - return _X25519PrivateKey(self, evp_pkey) - - def x25519_supported(self) -> bool: - if self._fips_enabled: - return False - return not self._lib.CRYPTOGRAPHY_IS_LIBRESSL - - def x448_load_public_bytes(self, data: bytes) -> x448.X448PublicKey: - if len(data) != 56: - raise ValueError("An X448 public key is 56 bytes long") - - evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( - self._lib.NID_X448, self._ffi.NULL, data, len(data) - ) - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - return _X448PublicKey(self, evp_pkey) - - def x448_load_private_bytes(self, data: bytes) -> x448.X448PrivateKey: - if len(data) != 56: - raise ValueError("An X448 private key is 56 bytes long") - - data_ptr = self._ffi.from_buffer(data) - evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( - self._lib.NID_X448, self._ffi.NULL, data_ptr, len(data) - ) - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - return _X448PrivateKey(self, evp_pkey) - - def x448_generate_key(self) -> x448.X448PrivateKey: - evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_X448) - return _X448PrivateKey(self, evp_pkey) - - def x448_supported(self) -> bool: - if self._fips_enabled: - return False - return ( - not self._lib.CRYPTOGRAPHY_IS_LIBRESSL - and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL - ) - - def ed25519_supported(self) -> bool: - if self._fips_enabled: - return False - return self._lib.CRYPTOGRAPHY_HAS_WORKING_ED25519 - - def ed25519_load_public_bytes( - self, data: bytes - ) -> ed25519.Ed25519PublicKey: - utils._check_bytes("data", data) - - if len(data) != ed25519._ED25519_KEY_SIZE: - raise ValueError("An Ed25519 public key is 32 bytes long") - - evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( - self._lib.NID_ED25519, self._ffi.NULL, data, len(data) - ) - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - - return _Ed25519PublicKey(self, evp_pkey) - - def ed25519_load_private_bytes( - self, data: bytes - ) -> ed25519.Ed25519PrivateKey: - if len(data) != ed25519._ED25519_KEY_SIZE: - raise ValueError("An Ed25519 private key is 32 bytes long") - - utils._check_byteslike("data", data) - data_ptr = self._ffi.from_buffer(data) - evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( - self._lib.NID_ED25519, self._ffi.NULL, data_ptr, len(data) - ) - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - - return _Ed25519PrivateKey(self, evp_pkey) - - def ed25519_generate_key(self) -> ed25519.Ed25519PrivateKey: - evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED25519) - return _Ed25519PrivateKey(self, evp_pkey) - - def ed448_supported(self) -> bool: - if self._fips_enabled: - return False - return ( - not self._lib.CRYPTOGRAPHY_OPENSSL_LESS_THAN_111B - and not self._lib.CRYPTOGRAPHY_IS_BORINGSSL - ) - - def ed448_load_public_bytes(self, data: bytes) -> ed448.Ed448PublicKey: - utils._check_bytes("data", data) - if len(data) != _ED448_KEY_SIZE: - raise ValueError("An Ed448 public key is 57 bytes long") - - evp_pkey = self._lib.EVP_PKEY_new_raw_public_key( - self._lib.NID_ED448, self._ffi.NULL, data, len(data) - ) - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - - return _Ed448PublicKey(self, evp_pkey) - - def ed448_load_private_bytes(self, data: bytes) -> ed448.Ed448PrivateKey: - utils._check_byteslike("data", data) - if len(data) != _ED448_KEY_SIZE: - raise ValueError("An Ed448 private key is 57 bytes long") - - data_ptr = self._ffi.from_buffer(data) - evp_pkey = self._lib.EVP_PKEY_new_raw_private_key( - self._lib.NID_ED448, self._ffi.NULL, data_ptr, len(data) - ) - self.openssl_assert(evp_pkey != self._ffi.NULL) - evp_pkey = self._ffi.gc(evp_pkey, self._lib.EVP_PKEY_free) - - return _Ed448PrivateKey(self, evp_pkey) - - def ed448_generate_key(self) -> ed448.Ed448PrivateKey: - evp_pkey = self._evp_pkey_keygen_gc(self._lib.NID_ED448) - return _Ed448PrivateKey(self, evp_pkey) - - def derive_scrypt( - self, - key_material: bytes, - salt: bytes, - length: int, - n: int, - r: int, - p: int, - ) -> bytes: - buf = self._ffi.new("unsigned char[]", length) - key_material_ptr = self._ffi.from_buffer(key_material) - res = self._lib.EVP_PBE_scrypt( - key_material_ptr, - len(key_material), - salt, - len(salt), - n, - r, - p, - scrypt._MEM_LIMIT, - buf, - length, - ) - if res != 1: - errors = self._consume_errors_with_text() - # memory required formula explained here: - # https://blog.filippo.io/the-scrypt-parameters/ - min_memory = 128 * n * r // (1024**2) - raise MemoryError( - "Not enough memory to derive key. These parameters require" - " {} MB of memory.".format(min_memory), - errors, - ) - return self._ffi.buffer(buf)[:] - - def aead_cipher_supported(self, cipher) -> bool: - cipher_name = aead._aead_cipher_name(cipher) - if self._fips_enabled and cipher_name not in self._fips_aead: - return False - # SIV isn't loaded through get_cipherbyname but instead a new fetch API - # only available in 3.0+. But if we know we're on 3.0+ then we know - # it's supported. - if cipher_name.endswith(b"-siv"): - return self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER == 1 - else: - return ( - self._lib.EVP_get_cipherbyname(cipher_name) != self._ffi.NULL - ) - - @contextlib.contextmanager - def _zeroed_bytearray(self, length: int) -> typing.Iterator[bytearray]: - """ - This method creates a bytearray, which we copy data into (hopefully - also from a mutable buffer that can be dynamically erased!), and then - zero when we're done. - """ - ba = bytearray(length) - try: - yield ba - finally: - self._zero_data(ba, length) - - def _zero_data(self, data, length: int) -> None: - # We clear things this way because at the moment we're not - # sure of a better way that can guarantee it overwrites the - # memory of a bytearray and doesn't just replace the underlying char *. - for i in range(length): - data[i] = 0 - - @contextlib.contextmanager - def _zeroed_null_terminated_buf(self, data): - """ - This method takes bytes, which can be a bytestring or a mutable - buffer like a bytearray, and yields a null-terminated version of that - data. This is required because PKCS12_parse doesn't take a length with - its password char * and ffi.from_buffer doesn't provide null - termination. So, to support zeroing the data via bytearray we - need to build this ridiculous construct that copies the memory, but - zeroes it after use. - """ - if data is None: - yield self._ffi.NULL - else: - data_len = len(data) - buf = self._ffi.new("char[]", data_len + 1) - self._ffi.memmove(buf, data, data_len) - try: - yield buf - finally: - # Cast to a uint8_t * so we can assign by integer - self._zero_data(self._ffi.cast("uint8_t *", buf), data_len) - - def load_key_and_certificates_from_pkcs12( - self, data: bytes, password: typing.Optional[bytes] - ) -> typing.Tuple[ - typing.Optional[PRIVATE_KEY_TYPES], - typing.Optional[x509.Certificate], - typing.List[x509.Certificate], - ]: - pkcs12 = self.load_pkcs12(data, password) - return ( - pkcs12.key, - pkcs12.cert.certificate if pkcs12.cert else None, - [cert.certificate for cert in pkcs12.additional_certs], - ) - - def load_pkcs12( - self, data: bytes, password: typing.Optional[bytes] - ) -> PKCS12KeyAndCertificates: - if password is not None: - utils._check_byteslike("password", password) - - bio = self._bytes_to_bio(data) - p12 = self._lib.d2i_PKCS12_bio(bio.bio, self._ffi.NULL) - if p12 == self._ffi.NULL: - self._consume_errors() - raise ValueError("Could not deserialize PKCS12 data") - - p12 = self._ffi.gc(p12, self._lib.PKCS12_free) - evp_pkey_ptr = self._ffi.new("EVP_PKEY **") - x509_ptr = self._ffi.new("X509 **") - sk_x509_ptr = self._ffi.new("Cryptography_STACK_OF_X509 **") - with self._zeroed_null_terminated_buf(password) as password_buf: - res = self._lib.PKCS12_parse( - p12, password_buf, evp_pkey_ptr, x509_ptr, sk_x509_ptr - ) - if res == 0: - self._consume_errors() - raise ValueError("Invalid password or PKCS12 data") - - cert = None - key = None - additional_certificates = [] - - if evp_pkey_ptr[0] != self._ffi.NULL: - evp_pkey = self._ffi.gc(evp_pkey_ptr[0], self._lib.EVP_PKEY_free) - # We don't support turning off RSA key validation when loading - # PKCS12 keys - key = self._evp_pkey_to_private_key( - evp_pkey, unsafe_skip_rsa_key_validation=False - ) - - if x509_ptr[0] != self._ffi.NULL: - x509 = self._ffi.gc(x509_ptr[0], self._lib.X509_free) - cert_obj = self._ossl2cert(x509) - name = None - maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL) - if maybe_name != self._ffi.NULL: - name = self._ffi.string(maybe_name) - cert = PKCS12Certificate(cert_obj, name) - - if sk_x509_ptr[0] != self._ffi.NULL: - sk_x509 = self._ffi.gc(sk_x509_ptr[0], self._lib.sk_X509_free) - num = self._lib.sk_X509_num(sk_x509_ptr[0]) - - # In OpenSSL < 3.0.0 PKCS12 parsing reverses the order of the - # certificates. - indices: typing.Iterable[int] - if ( - self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER - or self._lib.CRYPTOGRAPHY_IS_BORINGSSL - ): - indices = range(num) - else: - indices = reversed(range(num)) - - for i in indices: - x509 = self._lib.sk_X509_value(sk_x509, i) - self.openssl_assert(x509 != self._ffi.NULL) - x509 = self._ffi.gc(x509, self._lib.X509_free) - addl_cert = self._ossl2cert(x509) - addl_name = None - maybe_name = self._lib.X509_alias_get0(x509, self._ffi.NULL) - if maybe_name != self._ffi.NULL: - addl_name = self._ffi.string(maybe_name) - additional_certificates.append( - PKCS12Certificate(addl_cert, addl_name) - ) - - return PKCS12KeyAndCertificates(key, cert, additional_certificates) - - def serialize_key_and_certificates_to_pkcs12( - self, - name: typing.Optional[bytes], - key: typing.Optional[_ALLOWED_PKCS12_TYPES], - cert: typing.Optional[x509.Certificate], - cas: typing.Optional[typing.List[_PKCS12_CAS_TYPES]], - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - password = None - if name is not None: - utils._check_bytes("name", name) - - if isinstance(encryption_algorithm, serialization.NoEncryption): - nid_cert = -1 - nid_key = -1 - pkcs12_iter = 0 - mac_iter = 0 - mac_alg = self._ffi.NULL - elif isinstance( - encryption_algorithm, serialization.BestAvailableEncryption - ): - # PKCS12 encryption is hopeless trash and can never be fixed. - # OpenSSL 3 supports PBESv2, but Libre and Boring do not, so - # we use PBESv1 with 3DES on the older paths. - if self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: - nid_cert = self._lib.NID_aes_256_cbc - nid_key = self._lib.NID_aes_256_cbc - else: - nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC - nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC - # At least we can set this higher than OpenSSL's default - pkcs12_iter = 20000 - # mac_iter chosen for compatibility reasons, see: - # https://www.openssl.org/docs/man1.1.1/man3/PKCS12_create.html - # Did we mention how lousy PKCS12 encryption is? - mac_iter = 1 - # MAC algorithm can only be set on OpenSSL 3.0.0+ - mac_alg = self._ffi.NULL - password = encryption_algorithm.password - elif ( - isinstance( - encryption_algorithm, serialization._KeySerializationEncryption - ) - and encryption_algorithm._format - is serialization.PrivateFormat.PKCS12 - ): - # Default to OpenSSL's defaults. Behavior will vary based on the - # version of OpenSSL cryptography is compiled against. - nid_cert = 0 - nid_key = 0 - # Use the default iters we use in best available - pkcs12_iter = 20000 - # See the Best Available comment for why this is 1 - mac_iter = 1 - password = encryption_algorithm.password - keycertalg = encryption_algorithm._key_cert_algorithm - if keycertalg is PBES.PBESv1SHA1And3KeyTripleDESCBC: - nid_cert = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC - nid_key = self._lib.NID_pbe_WithSHA1And3_Key_TripleDES_CBC - elif keycertalg is PBES.PBESv2SHA256AndAES256CBC: - if not self._lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: - raise UnsupportedAlgorithm( - "PBESv2 is not supported by this version of OpenSSL" - ) - nid_cert = self._lib.NID_aes_256_cbc - nid_key = self._lib.NID_aes_256_cbc - else: - assert keycertalg is None - # We use OpenSSL's defaults - - if encryption_algorithm._hmac_hash is not None: - if not self._lib.Cryptography_HAS_PKCS12_SET_MAC: - raise UnsupportedAlgorithm( - "Setting MAC algorithm is not supported by this " - "version of OpenSSL." - ) - mac_alg = self._evp_md_non_null_from_algorithm( - encryption_algorithm._hmac_hash - ) - self.openssl_assert(mac_alg != self._ffi.NULL) - else: - mac_alg = self._ffi.NULL - - if encryption_algorithm._kdf_rounds is not None: - pkcs12_iter = encryption_algorithm._kdf_rounds - - else: - raise ValueError("Unsupported key encryption type") - - if cas is None or len(cas) == 0: - sk_x509 = self._ffi.NULL - else: - sk_x509 = self._lib.sk_X509_new_null() - sk_x509 = self._ffi.gc(sk_x509, self._lib.sk_X509_free) - - # This list is to keep the x509 values alive until end of function - ossl_cas = [] - for ca in cas: - if isinstance(ca, PKCS12Certificate): - ca_alias = ca.friendly_name - ossl_ca = self._cert2ossl(ca.certificate) - with self._zeroed_null_terminated_buf( - ca_alias - ) as ca_name_buf: - res = self._lib.X509_alias_set1( - ossl_ca, ca_name_buf, -1 - ) - self.openssl_assert(res == 1) - else: - ossl_ca = self._cert2ossl(ca) - ossl_cas.append(ossl_ca) - res = self._lib.sk_X509_push(sk_x509, ossl_ca) - backend.openssl_assert(res >= 1) - - with self._zeroed_null_terminated_buf(password) as password_buf: - with self._zeroed_null_terminated_buf(name) as name_buf: - ossl_cert = self._cert2ossl(cert) if cert else self._ffi.NULL - if key is not None: - evp_pkey = key._evp_pkey # type: ignore[union-attr] - else: - evp_pkey = self._ffi.NULL - - p12 = self._lib.PKCS12_create( - password_buf, - name_buf, - evp_pkey, - ossl_cert, - sk_x509, - nid_key, - nid_cert, - pkcs12_iter, - mac_iter, - 0, - ) - - if ( - self._lib.Cryptography_HAS_PKCS12_SET_MAC - and mac_alg != self._ffi.NULL - ): - self._lib.PKCS12_set_mac( - p12, - password_buf, - -1, - self._ffi.NULL, - 0, - mac_iter, - mac_alg, - ) - - self.openssl_assert(p12 != self._ffi.NULL) - p12 = self._ffi.gc(p12, self._lib.PKCS12_free) - - bio = self._create_mem_bio_gc() - res = self._lib.i2d_PKCS12_bio(bio, p12) - self.openssl_assert(res > 0) - return self._read_mem_bio(bio) - - def poly1305_supported(self) -> bool: - if self._fips_enabled: - return False - return self._lib.Cryptography_HAS_POLY1305 == 1 - - def create_poly1305_ctx(self, key: bytes) -> _Poly1305Context: - utils._check_byteslike("key", key) - if len(key) != _POLY1305_KEY_SIZE: - raise ValueError("A poly1305 key is 32 bytes long") - - return _Poly1305Context(self, key) - - def pkcs7_supported(self) -> bool: - return not self._lib.CRYPTOGRAPHY_IS_BORINGSSL - - def load_pem_pkcs7_certificates( - self, data: bytes - ) -> typing.List[x509.Certificate]: - utils._check_bytes("data", data) - bio = self._bytes_to_bio(data) - p7 = self._lib.PEM_read_bio_PKCS7( - bio.bio, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL - ) - if p7 == self._ffi.NULL: - self._consume_errors() - raise ValueError("Unable to parse PKCS7 data") - - p7 = self._ffi.gc(p7, self._lib.PKCS7_free) - return self._load_pkcs7_certificates(p7) - - def load_der_pkcs7_certificates( - self, data: bytes - ) -> typing.List[x509.Certificate]: - utils._check_bytes("data", data) - bio = self._bytes_to_bio(data) - p7 = self._lib.d2i_PKCS7_bio(bio.bio, self._ffi.NULL) - if p7 == self._ffi.NULL: - self._consume_errors() - raise ValueError("Unable to parse PKCS7 data") - - p7 = self._ffi.gc(p7, self._lib.PKCS7_free) - return self._load_pkcs7_certificates(p7) - - def _load_pkcs7_certificates(self, p7): - nid = self._lib.OBJ_obj2nid(p7.type) - self.openssl_assert(nid != self._lib.NID_undef) - if nid != self._lib.NID_pkcs7_signed: - raise UnsupportedAlgorithm( - "Only basic signed structures are currently supported. NID" - " for this data was {}".format(nid), - _Reasons.UNSUPPORTED_SERIALIZATION, - ) - - sk_x509 = p7.d.sign.cert - num = self._lib.sk_X509_num(sk_x509) - certs = [] - for i in range(num): - x509 = self._lib.sk_X509_value(sk_x509, i) - self.openssl_assert(x509 != self._ffi.NULL) - res = self._lib.X509_up_ref(x509) - self.openssl_assert(res == 1) - x509 = self._ffi.gc(x509, self._lib.X509_free) - cert = self._ossl2cert(x509) - certs.append(cert) - - return certs - - -class GetCipherByName: - def __init__(self, fmt: str): - self._fmt = fmt - - def __call__(self, backend: Backend, cipher: CipherAlgorithm, mode: Mode): - cipher_name = self._fmt.format(cipher=cipher, mode=mode).lower() - evp_cipher = backend._lib.EVP_get_cipherbyname( - cipher_name.encode("ascii") - ) - - # try EVP_CIPHER_fetch if present - if ( - evp_cipher == backend._ffi.NULL - and backend._lib.Cryptography_HAS_300_EVP_CIPHER - ): - evp_cipher = backend._lib.EVP_CIPHER_fetch( - backend._ffi.NULL, - cipher_name.encode("ascii"), - backend._ffi.NULL, - ) - - backend._consume_errors() - return evp_cipher - - -def _get_xts_cipher(backend: Backend, cipher: AES, mode): - cipher_name = "aes-{}-xts".format(cipher.key_size // 2) - return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii")) - - -backend = Backend() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py deleted file mode 100644 index fd2b6612f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ciphers.py +++ /dev/null @@ -1,281 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import ciphers -from cryptography.hazmat.primitives.ciphers import algorithms, modes - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -class _CipherContext: - _ENCRYPT = 1 - _DECRYPT = 0 - _MAX_CHUNK_SIZE = 2**30 - 1 - - def __init__( - self, backend: "Backend", cipher, mode, operation: int - ) -> None: - self._backend = backend - self._cipher = cipher - self._mode = mode - self._operation = operation - self._tag: typing.Optional[bytes] = None - - if isinstance(self._cipher, ciphers.BlockCipherAlgorithm): - self._block_size_bytes = self._cipher.block_size // 8 - else: - self._block_size_bytes = 1 - - ctx = self._backend._lib.EVP_CIPHER_CTX_new() - ctx = self._backend._ffi.gc( - ctx, self._backend._lib.EVP_CIPHER_CTX_free - ) - - registry = self._backend._cipher_registry - try: - adapter = registry[type(cipher), type(mode)] - except KeyError: - raise UnsupportedAlgorithm( - "cipher {} in {} mode is not supported " - "by this backend.".format( - cipher.name, mode.name if mode else mode - ), - _Reasons.UNSUPPORTED_CIPHER, - ) - - evp_cipher = adapter(self._backend, cipher, mode) - if evp_cipher == self._backend._ffi.NULL: - msg = "cipher {0.name} ".format(cipher) - if mode is not None: - msg += "in {0.name} mode ".format(mode) - msg += ( - "is not supported by this backend (Your version of OpenSSL " - "may be too old. Current version: {}.)" - ).format(self._backend.openssl_version_text()) - raise UnsupportedAlgorithm(msg, _Reasons.UNSUPPORTED_CIPHER) - - if isinstance(mode, modes.ModeWithInitializationVector): - iv_nonce = self._backend._ffi.from_buffer( - mode.initialization_vector - ) - elif isinstance(mode, modes.ModeWithTweak): - iv_nonce = self._backend._ffi.from_buffer(mode.tweak) - elif isinstance(mode, modes.ModeWithNonce): - iv_nonce = self._backend._ffi.from_buffer(mode.nonce) - elif isinstance(cipher, algorithms.ChaCha20): - iv_nonce = self._backend._ffi.from_buffer(cipher.nonce) - else: - iv_nonce = self._backend._ffi.NULL - # begin init with cipher and operation type - res = self._backend._lib.EVP_CipherInit_ex( - ctx, - evp_cipher, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - operation, - ) - self._backend.openssl_assert(res != 0) - # set the key length to handle variable key ciphers - res = self._backend._lib.EVP_CIPHER_CTX_set_key_length( - ctx, len(cipher.key) - ) - self._backend.openssl_assert(res != 0) - if isinstance(mode, modes.GCM): - res = self._backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, - self._backend._lib.EVP_CTRL_AEAD_SET_IVLEN, - len(iv_nonce), - self._backend._ffi.NULL, - ) - self._backend.openssl_assert(res != 0) - if mode.tag is not None: - res = self._backend._lib.EVP_CIPHER_CTX_ctrl( - ctx, - self._backend._lib.EVP_CTRL_AEAD_SET_TAG, - len(mode.tag), - mode.tag, - ) - self._backend.openssl_assert(res != 0) - self._tag = mode.tag - - # pass key/iv - res = self._backend._lib.EVP_CipherInit_ex( - ctx, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.from_buffer(cipher.key), - iv_nonce, - operation, - ) - - # Check for XTS mode duplicate keys error - errors = self._backend._consume_errors() - lib = self._backend._lib - if res == 0 and ( - ( - lib.CRYPTOGRAPHY_OPENSSL_111D_OR_GREATER - and errors[0]._lib_reason_match( - lib.ERR_LIB_EVP, lib.EVP_R_XTS_DUPLICATED_KEYS - ) - ) - or ( - lib.Cryptography_HAS_PROVIDERS - and errors[0]._lib_reason_match( - lib.ERR_LIB_PROV, lib.PROV_R_XTS_DUPLICATED_KEYS - ) - ) - ): - raise ValueError("In XTS mode duplicated keys are not allowed") - - self._backend.openssl_assert(res != 0, errors=errors) - - # We purposely disable padding here as it's handled higher up in the - # API. - self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0) - self._ctx = ctx - - def update(self, data: bytes) -> bytes: - buf = bytearray(len(data) + self._block_size_bytes - 1) - n = self.update_into(data, buf) - return bytes(buf[:n]) - - def update_into(self, data: bytes, buf: bytes) -> int: - total_data_len = len(data) - if len(buf) < (total_data_len + self._block_size_bytes - 1): - raise ValueError( - "buffer must be at least {} bytes for this " - "payload".format(len(data) + self._block_size_bytes - 1) - ) - - data_processed = 0 - total_out = 0 - outlen = self._backend._ffi.new("int *") - baseoutbuf = self._backend._ffi.from_buffer(buf) - baseinbuf = self._backend._ffi.from_buffer(data) - - while data_processed != total_data_len: - outbuf = baseoutbuf + total_out - inbuf = baseinbuf + data_processed - inlen = min(self._MAX_CHUNK_SIZE, total_data_len - data_processed) - - res = self._backend._lib.EVP_CipherUpdate( - self._ctx, outbuf, outlen, inbuf, inlen - ) - if res == 0 and isinstance(self._mode, modes.XTS): - self._backend._consume_errors() - raise ValueError( - "In XTS mode you must supply at least a full block in the " - "first update call. For AES this is 16 bytes." - ) - else: - self._backend.openssl_assert(res != 0) - data_processed += inlen - total_out += outlen[0] - - return total_out - - def finalize(self) -> bytes: - if ( - self._operation == self._DECRYPT - and isinstance(self._mode, modes.ModeWithAuthenticationTag) - and self.tag is None - ): - raise ValueError( - "Authentication tag must be provided when decrypting." - ) - - buf = self._backend._ffi.new("unsigned char[]", self._block_size_bytes) - outlen = self._backend._ffi.new("int *") - res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen) - if res == 0: - errors = self._backend._consume_errors() - - if not errors and isinstance(self._mode, modes.GCM): - raise InvalidTag - - lib = self._backend._lib - self._backend.openssl_assert( - errors[0]._lib_reason_match( - lib.ERR_LIB_EVP, - lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH, - ) - or ( - lib.Cryptography_HAS_PROVIDERS - and errors[0]._lib_reason_match( - lib.ERR_LIB_PROV, - lib.PROV_R_WRONG_FINAL_BLOCK_LENGTH, - ) - ) - or ( - lib.CRYPTOGRAPHY_IS_BORINGSSL - and errors[0].reason - == lib.CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH - ), - errors=errors, - ) - raise ValueError( - "The length of the provided data is not a multiple of " - "the block length." - ) - - if ( - isinstance(self._mode, modes.GCM) - and self._operation == self._ENCRYPT - ): - tag_buf = self._backend._ffi.new( - "unsigned char[]", self._block_size_bytes - ) - res = self._backend._lib.EVP_CIPHER_CTX_ctrl( - self._ctx, - self._backend._lib.EVP_CTRL_AEAD_GET_TAG, - self._block_size_bytes, - tag_buf, - ) - self._backend.openssl_assert(res != 0) - self._tag = self._backend._ffi.buffer(tag_buf)[:] - - res = self._backend._lib.EVP_CIPHER_CTX_reset(self._ctx) - self._backend.openssl_assert(res == 1) - return self._backend._ffi.buffer(buf)[: outlen[0]] - - def finalize_with_tag(self, tag: bytes) -> bytes: - tag_len = len(tag) - if tag_len < self._mode._min_tag_length: - raise ValueError( - "Authentication tag must be {} bytes or longer.".format( - self._mode._min_tag_length - ) - ) - elif tag_len > self._block_size_bytes: - raise ValueError( - "Authentication tag cannot be more than {} bytes.".format( - self._block_size_bytes - ) - ) - res = self._backend._lib.EVP_CIPHER_CTX_ctrl( - self._ctx, self._backend._lib.EVP_CTRL_AEAD_SET_TAG, len(tag), tag - ) - self._backend.openssl_assert(res != 0) - self._tag = tag - return self.finalize() - - def authenticate_additional_data(self, data: bytes) -> None: - outlen = self._backend._ffi.new("int *") - res = self._backend._lib.EVP_CipherUpdate( - self._ctx, - self._backend._ffi.NULL, - outlen, - self._backend._ffi.from_buffer(data), - len(data), - ) - self._backend.openssl_assert(res != 0) - - @property - def tag(self) -> typing.Optional[bytes]: - return self._tag diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py deleted file mode 100644 index 6f7363294..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/cmac.py +++ /dev/null @@ -1,87 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import ( - InvalidSignature, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.primitives import constant_time -from cryptography.hazmat.primitives.ciphers.modes import CBC - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - from cryptography.hazmat.primitives import ciphers - - -class _CMACContext: - def __init__( - self, - backend: "Backend", - algorithm: "ciphers.BlockCipherAlgorithm", - ctx=None, - ) -> None: - if not backend.cmac_algorithm_supported(algorithm): - raise UnsupportedAlgorithm( - "This backend does not support CMAC.", - _Reasons.UNSUPPORTED_CIPHER, - ) - - self._backend = backend - self._key = algorithm.key - self._algorithm = algorithm - self._output_length = algorithm.block_size // 8 - - if ctx is None: - registry = self._backend._cipher_registry - adapter = registry[type(algorithm), CBC] - - evp_cipher = adapter(self._backend, algorithm, CBC) - - ctx = self._backend._lib.CMAC_CTX_new() - - self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free) - - key_ptr = self._backend._ffi.from_buffer(self._key) - res = self._backend._lib.CMAC_Init( - ctx, - key_ptr, - len(self._key), - evp_cipher, - self._backend._ffi.NULL, - ) - self._backend.openssl_assert(res == 1) - - self._ctx = ctx - - def update(self, data: bytes) -> None: - res = self._backend._lib.CMAC_Update(self._ctx, data, len(data)) - self._backend.openssl_assert(res == 1) - - def finalize(self) -> bytes: - buf = self._backend._ffi.new("unsigned char[]", self._output_length) - length = self._backend._ffi.new("size_t *", self._output_length) - res = self._backend._lib.CMAC_Final(self._ctx, buf, length) - self._backend.openssl_assert(res == 1) - - self._ctx = None - - return self._backend._ffi.buffer(buf)[:] - - def copy(self) -> "_CMACContext": - copied_ctx = self._backend._lib.CMAC_CTX_new() - copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.CMAC_CTX_free - ) - res = self._backend._lib.CMAC_CTX_copy(copied_ctx, self._ctx) - self._backend.openssl_assert(res == 1) - return _CMACContext(self._backend, self._algorithm, ctx=copied_ctx) - - def verify(self, signature: bytes) -> None: - digest = self.finalize() - if not constant_time.bytes_eq(digest, signature): - raise InvalidSignature("Signature did not match digest.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py deleted file mode 100644 index df91d6d8a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/decode_asn1.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography import x509 - -# CRLReason ::= ENUMERATED { -# unspecified (0), -# keyCompromise (1), -# cACompromise (2), -# affiliationChanged (3), -# superseded (4), -# cessationOfOperation (5), -# certificateHold (6), -# -- value 7 is not used -# removeFromCRL (8), -# privilegeWithdrawn (9), -# aACompromise (10) } -_CRL_ENTRY_REASON_ENUM_TO_CODE = { - x509.ReasonFlags.unspecified: 0, - x509.ReasonFlags.key_compromise: 1, - x509.ReasonFlags.ca_compromise: 2, - x509.ReasonFlags.affiliation_changed: 3, - x509.ReasonFlags.superseded: 4, - x509.ReasonFlags.cessation_of_operation: 5, - x509.ReasonFlags.certificate_hold: 6, - x509.ReasonFlags.remove_from_crl: 8, - x509.ReasonFlags.privilege_withdrawn: 9, - x509.ReasonFlags.aa_compromise: 10, -} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py deleted file mode 100644 index c429c0239..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dh.py +++ /dev/null @@ -1,317 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import dh - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -def _dh_params_dup(dh_cdata, backend: "Backend"): - lib = backend._lib - ffi = backend._ffi - - param_cdata = lib.DHparams_dup(dh_cdata) - backend.openssl_assert(param_cdata != ffi.NULL) - param_cdata = ffi.gc(param_cdata, lib.DH_free) - if lib.CRYPTOGRAPHY_IS_LIBRESSL: - # In libressl DHparams_dup don't copy q - q = ffi.new("BIGNUM **") - lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL) - q_dup = lib.BN_dup(q[0]) - res = lib.DH_set0_pqg(param_cdata, ffi.NULL, q_dup, ffi.NULL) - backend.openssl_assert(res == 1) - - return param_cdata - - -def _dh_cdata_to_parameters(dh_cdata, backend: "Backend") -> "_DHParameters": - param_cdata = _dh_params_dup(dh_cdata, backend) - return _DHParameters(backend, param_cdata) - - -class _DHParameters(dh.DHParameters): - def __init__(self, backend: "Backend", dh_cdata): - self._backend = backend - self._dh_cdata = dh_cdata - - def parameter_numbers(self) -> dh.DHParameterNumbers: - p = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - q_val: typing.Optional[int] - if q[0] == self._backend._ffi.NULL: - q_val = None - else: - q_val = self._backend._bn_to_int(q[0]) - return dh.DHParameterNumbers( - p=self._backend._bn_to_int(p[0]), - g=self._backend._bn_to_int(g[0]), - q=q_val, - ) - - def generate_private_key(self) -> dh.DHPrivateKey: - return self._backend.generate_dh_private_key(self) - - def parameter_bytes( - self, - encoding: serialization.Encoding, - format: serialization.ParameterFormat, - ) -> bytes: - if encoding is serialization.Encoding.OpenSSH: - raise TypeError("OpenSSH encoding is not supported") - - if format is not serialization.ParameterFormat.PKCS3: - raise ValueError("Only PKCS3 serialization is supported") - - q = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_pqg( - self._dh_cdata, self._backend._ffi.NULL, q, self._backend._ffi.NULL - ) - if ( - q[0] != self._backend._ffi.NULL - and not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX - ): - raise UnsupportedAlgorithm( - "DH X9.42 serialization is not supported", - _Reasons.UNSUPPORTED_SERIALIZATION, - ) - - if encoding is serialization.Encoding.PEM: - if q[0] != self._backend._ffi.NULL: - write_bio = self._backend._lib.PEM_write_bio_DHxparams - else: - write_bio = self._backend._lib.PEM_write_bio_DHparams - elif encoding is serialization.Encoding.DER: - if q[0] != self._backend._ffi.NULL: - write_bio = self._backend._lib.i2d_DHxparams_bio - else: - write_bio = self._backend._lib.i2d_DHparams_bio - else: - raise TypeError("encoding must be an item from the Encoding enum") - - bio = self._backend._create_mem_bio_gc() - res = write_bio(bio, self._dh_cdata) - self._backend.openssl_assert(res == 1) - return self._backend._read_mem_bio(bio) - - -def _get_dh_num_bits(backend, dh_cdata) -> int: - p = backend._ffi.new("BIGNUM **") - backend._lib.DH_get0_pqg(dh_cdata, p, backend._ffi.NULL, backend._ffi.NULL) - backend.openssl_assert(p[0] != backend._ffi.NULL) - return backend._lib.BN_num_bits(p[0]) - - -class _DHPrivateKey(dh.DHPrivateKey): - def __init__(self, backend: "Backend", dh_cdata, evp_pkey): - self._backend = backend - self._dh_cdata = dh_cdata - self._evp_pkey = evp_pkey - self._key_size_bytes = self._backend._lib.DH_size(dh_cdata) - - @property - def key_size(self) -> int: - return _get_dh_num_bits(self._backend, self._dh_cdata) - - def private_numbers(self) -> dh.DHPrivateNumbers: - p = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - if q[0] == self._backend._ffi.NULL: - q_val = None - else: - q_val = self._backend._bn_to_int(q[0]) - pub_key = self._backend._ffi.new("BIGNUM **") - priv_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, priv_key) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL) - return dh.DHPrivateNumbers( - public_numbers=dh.DHPublicNumbers( - parameter_numbers=dh.DHParameterNumbers( - p=self._backend._bn_to_int(p[0]), - g=self._backend._bn_to_int(g[0]), - q=q_val, - ), - y=self._backend._bn_to_int(pub_key[0]), - ), - x=self._backend._bn_to_int(priv_key[0]), - ) - - def exchange(self, peer_public_key: dh.DHPublicKey) -> bytes: - if not isinstance(peer_public_key, _DHPublicKey): - raise TypeError("peer_public_key must be a DHPublicKey") - - ctx = self._backend._lib.EVP_PKEY_CTX_new( - self._evp_pkey, self._backend._ffi.NULL - ) - self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - ctx = self._backend._ffi.gc(ctx, self._backend._lib.EVP_PKEY_CTX_free) - res = self._backend._lib.EVP_PKEY_derive_init(ctx) - self._backend.openssl_assert(res == 1) - res = self._backend._lib.EVP_PKEY_derive_set_peer( - ctx, peer_public_key._evp_pkey - ) - # Invalid kex errors here in OpenSSL 3.0 because checks were moved - # to EVP_PKEY_derive_set_peer - self._exchange_assert(res == 1) - keylen = self._backend._ffi.new("size_t *") - res = self._backend._lib.EVP_PKEY_derive( - ctx, self._backend._ffi.NULL, keylen - ) - # Invalid kex errors here in OpenSSL < 3 - self._exchange_assert(res == 1) - self._backend.openssl_assert(keylen[0] > 0) - buf = self._backend._ffi.new("unsigned char[]", keylen[0]) - res = self._backend._lib.EVP_PKEY_derive(ctx, buf, keylen) - self._backend.openssl_assert(res == 1) - - key = self._backend._ffi.buffer(buf, keylen[0])[:] - pad = self._key_size_bytes - len(key) - - if pad > 0: - key = (b"\x00" * pad) + key - - return key - - def _exchange_assert(self, ok: bool) -> None: - if not ok: - errors_with_text = self._backend._consume_errors_with_text() - raise ValueError( - "Error computing shared key.", - errors_with_text, - ) - - def public_key(self) -> dh.DHPublicKey: - dh_cdata = _dh_params_dup(self._dh_cdata, self._backend) - pub_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_key( - self._dh_cdata, pub_key, self._backend._ffi.NULL - ) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - pub_key_dup = self._backend._lib.BN_dup(pub_key[0]) - self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL) - - res = self._backend._lib.DH_set0_key( - dh_cdata, pub_key_dup, self._backend._ffi.NULL - ) - self._backend.openssl_assert(res == 1) - evp_pkey = self._backend._dh_cdata_to_evp_pkey(dh_cdata) - return _DHPublicKey(self._backend, dh_cdata, evp_pkey) - - def parameters(self) -> dh.DHParameters: - return _dh_cdata_to_parameters(self._dh_cdata, self._backend) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - if format is not serialization.PrivateFormat.PKCS8: - raise ValueError( - "DH private keys support only PKCS8 serialization" - ) - if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX: - q = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_pqg( - self._dh_cdata, - self._backend._ffi.NULL, - q, - self._backend._ffi.NULL, - ) - if q[0] != self._backend._ffi.NULL: - raise UnsupportedAlgorithm( - "DH X9.42 serialization is not supported", - _Reasons.UNSUPPORTED_SERIALIZATION, - ) - - return self._backend._private_key_bytes( - encoding, - format, - encryption_algorithm, - self, - self._evp_pkey, - self._dh_cdata, - ) - - -class _DHPublicKey(dh.DHPublicKey): - def __init__(self, backend: "Backend", dh_cdata, evp_pkey): - self._backend = backend - self._dh_cdata = dh_cdata - self._evp_pkey = evp_pkey - self._key_size_bits = _get_dh_num_bits(self._backend, self._dh_cdata) - - @property - def key_size(self) -> int: - return self._key_size_bits - - def public_numbers(self) -> dh.DHPublicNumbers: - p = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - if q[0] == self._backend._ffi.NULL: - q_val = None - else: - q_val = self._backend._bn_to_int(q[0]) - pub_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_key( - self._dh_cdata, pub_key, self._backend._ffi.NULL - ) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - return dh.DHPublicNumbers( - parameter_numbers=dh.DHParameterNumbers( - p=self._backend._bn_to_int(p[0]), - g=self._backend._bn_to_int(g[0]), - q=q_val, - ), - y=self._backend._bn_to_int(pub_key[0]), - ) - - def parameters(self) -> dh.DHParameters: - return _dh_cdata_to_parameters(self._dh_cdata, self._backend) - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - if format is not serialization.PublicFormat.SubjectPublicKeyInfo: - raise ValueError( - "DH public keys support only " - "SubjectPublicKeyInfo serialization" - ) - - if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX: - q = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DH_get0_pqg( - self._dh_cdata, - self._backend._ffi.NULL, - q, - self._backend._ffi.NULL, - ) - if q[0] != self._backend._ffi.NULL: - raise UnsupportedAlgorithm( - "DH X9.42 serialization is not supported", - _Reasons.UNSUPPORTED_SERIALIZATION, - ) - - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py deleted file mode 100644 index 15bd84a7b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/dsa.py +++ /dev/null @@ -1,236 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.backends.openssl.utils import ( - _calculate_digest_and_algorithm, -) -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import dsa -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -def _dsa_sig_sign( - backend: "Backend", private_key: "_DSAPrivateKey", data: bytes -) -> bytes: - sig_buf_len = backend._lib.DSA_size(private_key._dsa_cdata) - sig_buf = backend._ffi.new("unsigned char[]", sig_buf_len) - buflen = backend._ffi.new("unsigned int *") - - # The first parameter passed to DSA_sign is unused by OpenSSL but - # must be an integer. - res = backend._lib.DSA_sign( - 0, data, len(data), sig_buf, buflen, private_key._dsa_cdata - ) - backend.openssl_assert(res == 1) - backend.openssl_assert(buflen[0]) - - return backend._ffi.buffer(sig_buf)[: buflen[0]] - - -def _dsa_sig_verify( - backend: "Backend", - public_key: "_DSAPublicKey", - signature: bytes, - data: bytes, -) -> None: - # The first parameter passed to DSA_verify is unused by OpenSSL but - # must be an integer. - res = backend._lib.DSA_verify( - 0, data, len(data), signature, len(signature), public_key._dsa_cdata - ) - - if res != 1: - backend._consume_errors() - raise InvalidSignature - - -class _DSAParameters(dsa.DSAParameters): - def __init__(self, backend: "Backend", dsa_cdata): - self._backend = backend - self._dsa_cdata = dsa_cdata - - def parameter_numbers(self) -> dsa.DSAParameterNumbers: - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - return dsa.DSAParameterNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - g=self._backend._bn_to_int(g[0]), - ) - - def generate_private_key(self) -> dsa.DSAPrivateKey: - return self._backend.generate_dsa_private_key(self) - - -class _DSAPrivateKey(dsa.DSAPrivateKey): - _key_size: int - - def __init__(self, backend: "Backend", dsa_cdata, evp_pkey): - self._backend = backend - self._dsa_cdata = dsa_cdata - self._evp_pkey = evp_pkey - - p = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg( - dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL - ) - self._backend.openssl_assert(p[0] != backend._ffi.NULL) - self._key_size = self._backend._lib.BN_num_bits(p[0]) - - @property - def key_size(self) -> int: - return self._key_size - - def private_numbers(self) -> dsa.DSAPrivateNumbers: - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - pub_key = self._backend._ffi.new("BIGNUM **") - priv_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - self._backend._lib.DSA_get0_key(self._dsa_cdata, pub_key, priv_key) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL) - return dsa.DSAPrivateNumbers( - public_numbers=dsa.DSAPublicNumbers( - parameter_numbers=dsa.DSAParameterNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - g=self._backend._bn_to_int(g[0]), - ), - y=self._backend._bn_to_int(pub_key[0]), - ), - x=self._backend._bn_to_int(priv_key[0]), - ) - - def public_key(self) -> dsa.DSAPublicKey: - dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) - self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL) - dsa_cdata = self._backend._ffi.gc( - dsa_cdata, self._backend._lib.DSA_free - ) - pub_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_key( - self._dsa_cdata, pub_key, self._backend._ffi.NULL - ) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - pub_key_dup = self._backend._lib.BN_dup(pub_key[0]) - res = self._backend._lib.DSA_set0_key( - dsa_cdata, pub_key_dup, self._backend._ffi.NULL - ) - self._backend.openssl_assert(res == 1) - evp_pkey = self._backend._dsa_cdata_to_evp_pkey(dsa_cdata) - return _DSAPublicKey(self._backend, dsa_cdata, evp_pkey) - - def parameters(self) -> dsa.DSAParameters: - dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) - self._backend.openssl_assert(dsa_cdata != self._backend._ffi.NULL) - dsa_cdata = self._backend._ffi.gc( - dsa_cdata, self._backend._lib.DSA_free - ) - return _DSAParameters(self._backend, dsa_cdata) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - return self._backend._private_key_bytes( - encoding, - format, - encryption_algorithm, - self, - self._evp_pkey, - self._dsa_cdata, - ) - - def sign( - self, - data: bytes, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> bytes: - data, _ = _calculate_digest_and_algorithm(data, algorithm) - return _dsa_sig_sign(self._backend, self, data) - - -class _DSAPublicKey(dsa.DSAPublicKey): - _key_size: int - - def __init__(self, backend: "Backend", dsa_cdata, evp_pkey): - self._backend = backend - self._dsa_cdata = dsa_cdata - self._evp_pkey = evp_pkey - p = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg( - dsa_cdata, p, self._backend._ffi.NULL, self._backend._ffi.NULL - ) - self._backend.openssl_assert(p[0] != backend._ffi.NULL) - self._key_size = self._backend._lib.BN_num_bits(p[0]) - - @property - def key_size(self) -> int: - return self._key_size - - def public_numbers(self) -> dsa.DSAPublicNumbers: - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - g = self._backend._ffi.new("BIGNUM **") - pub_key = self._backend._ffi.new("BIGNUM **") - self._backend._lib.DSA_get0_pqg(self._dsa_cdata, p, q, g) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) - self._backend._lib.DSA_get0_key( - self._dsa_cdata, pub_key, self._backend._ffi.NULL - ) - self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL) - return dsa.DSAPublicNumbers( - parameter_numbers=dsa.DSAParameterNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - g=self._backend._bn_to_int(g[0]), - ), - y=self._backend._bn_to_int(pub_key[0]), - ) - - def parameters(self) -> dsa.DSAParameters: - dsa_cdata = self._backend._lib.DSAparams_dup(self._dsa_cdata) - dsa_cdata = self._backend._ffi.gc( - dsa_cdata, self._backend._lib.DSA_free - ) - return _DSAParameters(self._backend, dsa_cdata) - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def verify( - self, - signature: bytes, - data: bytes, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> None: - data, _ = _calculate_digest_and_algorithm(data, algorithm) - return _dsa_sig_verify(self._backend, self, signature, data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py deleted file mode 100644 index 9bc6dd384..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ec.py +++ /dev/null @@ -1,315 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import ( - InvalidSignature, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.backends.openssl.utils import ( - _calculate_digest_and_algorithm, - _evp_pkey_derive, -) -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ec - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -def _check_signature_algorithm( - signature_algorithm: ec.EllipticCurveSignatureAlgorithm, -) -> None: - if not isinstance(signature_algorithm, ec.ECDSA): - raise UnsupportedAlgorithm( - "Unsupported elliptic curve signature algorithm.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - -def _ec_key_curve_sn(backend: "Backend", ec_key) -> str: - group = backend._lib.EC_KEY_get0_group(ec_key) - backend.openssl_assert(group != backend._ffi.NULL) - - nid = backend._lib.EC_GROUP_get_curve_name(group) - # The following check is to find EC keys with unnamed curves and raise - # an error for now. - if nid == backend._lib.NID_undef: - raise ValueError( - "ECDSA keys with explicit parameters are unsupported at this time" - ) - - # This is like the above check, but it also catches the case where you - # explicitly encoded a curve with the same parameters as a named curve. - # Don't do that. - if ( - not backend._lib.CRYPTOGRAPHY_IS_LIBRESSL - and backend._lib.EC_GROUP_get_asn1_flag(group) == 0 - ): - raise ValueError( - "ECDSA keys with explicit parameters are unsupported at this time" - ) - - curve_name = backend._lib.OBJ_nid2sn(nid) - backend.openssl_assert(curve_name != backend._ffi.NULL) - - sn = backend._ffi.string(curve_name).decode("ascii") - return sn - - -def _mark_asn1_named_ec_curve(backend: "Backend", ec_cdata): - """ - Set the named curve flag on the EC_KEY. This causes OpenSSL to - serialize EC keys along with their curve OID which makes - deserialization easier. - """ - - backend._lib.EC_KEY_set_asn1_flag( - ec_cdata, backend._lib.OPENSSL_EC_NAMED_CURVE - ) - - -def _check_key_infinity(backend: "Backend", ec_cdata) -> None: - point = backend._lib.EC_KEY_get0_public_key(ec_cdata) - backend.openssl_assert(point != backend._ffi.NULL) - group = backend._lib.EC_KEY_get0_group(ec_cdata) - backend.openssl_assert(group != backend._ffi.NULL) - if backend._lib.EC_POINT_is_at_infinity(group, point): - raise ValueError( - "Cannot load an EC public key where the point is at infinity" - ) - - -def _sn_to_elliptic_curve(backend: "Backend", sn: str) -> ec.EllipticCurve: - try: - return ec._CURVE_TYPES[sn]() - except KeyError: - raise UnsupportedAlgorithm( - "{} is not a supported elliptic curve".format(sn), - _Reasons.UNSUPPORTED_ELLIPTIC_CURVE, - ) - - -def _ecdsa_sig_sign( - backend: "Backend", private_key: "_EllipticCurvePrivateKey", data: bytes -) -> bytes: - max_size = backend._lib.ECDSA_size(private_key._ec_key) - backend.openssl_assert(max_size > 0) - - sigbuf = backend._ffi.new("unsigned char[]", max_size) - siglen_ptr = backend._ffi.new("unsigned int[]", 1) - res = backend._lib.ECDSA_sign( - 0, data, len(data), sigbuf, siglen_ptr, private_key._ec_key - ) - backend.openssl_assert(res == 1) - return backend._ffi.buffer(sigbuf)[: siglen_ptr[0]] - - -def _ecdsa_sig_verify( - backend: "Backend", - public_key: "_EllipticCurvePublicKey", - signature: bytes, - data: bytes, -) -> None: - res = backend._lib.ECDSA_verify( - 0, data, len(data), signature, len(signature), public_key._ec_key - ) - if res != 1: - backend._consume_errors() - raise InvalidSignature - - -class _EllipticCurvePrivateKey(ec.EllipticCurvePrivateKey): - def __init__(self, backend: "Backend", ec_key_cdata, evp_pkey): - self._backend = backend - self._ec_key = ec_key_cdata - self._evp_pkey = evp_pkey - - sn = _ec_key_curve_sn(backend, ec_key_cdata) - self._curve = _sn_to_elliptic_curve(backend, sn) - _mark_asn1_named_ec_curve(backend, ec_key_cdata) - _check_key_infinity(backend, ec_key_cdata) - - @property - def curve(self) -> ec.EllipticCurve: - return self._curve - - @property - def key_size(self) -> int: - return self.curve.key_size - - def exchange( - self, algorithm: ec.ECDH, peer_public_key: ec.EllipticCurvePublicKey - ) -> bytes: - if not ( - self._backend.elliptic_curve_exchange_algorithm_supported( - algorithm, self.curve - ) - ): - raise UnsupportedAlgorithm( - "This backend does not support the ECDH algorithm.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - if peer_public_key.curve.name != self.curve.name: - raise ValueError( - "peer_public_key and self are not on the same curve" - ) - - return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) - - def public_key(self) -> ec.EllipticCurvePublicKey: - group = self._backend._lib.EC_KEY_get0_group(self._ec_key) - self._backend.openssl_assert(group != self._backend._ffi.NULL) - - curve_nid = self._backend._lib.EC_GROUP_get_curve_name(group) - public_ec_key = self._backend._ec_key_new_by_curve_nid(curve_nid) - - point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) - self._backend.openssl_assert(point != self._backend._ffi.NULL) - - res = self._backend._lib.EC_KEY_set_public_key(public_ec_key, point) - self._backend.openssl_assert(res == 1) - - evp_pkey = self._backend._ec_cdata_to_evp_pkey(public_ec_key) - - return _EllipticCurvePublicKey(self._backend, public_ec_key, evp_pkey) - - def private_numbers(self) -> ec.EllipticCurvePrivateNumbers: - bn = self._backend._lib.EC_KEY_get0_private_key(self._ec_key) - private_value = self._backend._bn_to_int(bn) - return ec.EllipticCurvePrivateNumbers( - private_value=private_value, - public_numbers=self.public_key().public_numbers(), - ) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - return self._backend._private_key_bytes( - encoding, - format, - encryption_algorithm, - self, - self._evp_pkey, - self._ec_key, - ) - - def sign( - self, - data: bytes, - signature_algorithm: ec.EllipticCurveSignatureAlgorithm, - ) -> bytes: - _check_signature_algorithm(signature_algorithm) - data, _ = _calculate_digest_and_algorithm( - data, - signature_algorithm.algorithm, - ) - return _ecdsa_sig_sign(self._backend, self, data) - - -class _EllipticCurvePublicKey(ec.EllipticCurvePublicKey): - def __init__(self, backend: "Backend", ec_key_cdata, evp_pkey): - self._backend = backend - self._ec_key = ec_key_cdata - self._evp_pkey = evp_pkey - - sn = _ec_key_curve_sn(backend, ec_key_cdata) - self._curve = _sn_to_elliptic_curve(backend, sn) - _mark_asn1_named_ec_curve(backend, ec_key_cdata) - _check_key_infinity(backend, ec_key_cdata) - - @property - def curve(self) -> ec.EllipticCurve: - return self._curve - - @property - def key_size(self) -> int: - return self.curve.key_size - - def public_numbers(self) -> ec.EllipticCurvePublicNumbers: - get_func, group = self._backend._ec_key_determine_group_get_func( - self._ec_key - ) - point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) - self._backend.openssl_assert(point != self._backend._ffi.NULL) - - with self._backend._tmp_bn_ctx() as bn_ctx: - bn_x = self._backend._lib.BN_CTX_get(bn_ctx) - bn_y = self._backend._lib.BN_CTX_get(bn_ctx) - - res = get_func(group, point, bn_x, bn_y, bn_ctx) - self._backend.openssl_assert(res == 1) - - x = self._backend._bn_to_int(bn_x) - y = self._backend._bn_to_int(bn_y) - - return ec.EllipticCurvePublicNumbers(x=x, y=y, curve=self._curve) - - def _encode_point(self, format: serialization.PublicFormat) -> bytes: - if format is serialization.PublicFormat.CompressedPoint: - conversion = self._backend._lib.POINT_CONVERSION_COMPRESSED - else: - assert format is serialization.PublicFormat.UncompressedPoint - conversion = self._backend._lib.POINT_CONVERSION_UNCOMPRESSED - - group = self._backend._lib.EC_KEY_get0_group(self._ec_key) - self._backend.openssl_assert(group != self._backend._ffi.NULL) - point = self._backend._lib.EC_KEY_get0_public_key(self._ec_key) - self._backend.openssl_assert(point != self._backend._ffi.NULL) - with self._backend._tmp_bn_ctx() as bn_ctx: - buflen = self._backend._lib.EC_POINT_point2oct( - group, point, conversion, self._backend._ffi.NULL, 0, bn_ctx - ) - self._backend.openssl_assert(buflen > 0) - buf = self._backend._ffi.new("char[]", buflen) - res = self._backend._lib.EC_POINT_point2oct( - group, point, conversion, buf, buflen, bn_ctx - ) - self._backend.openssl_assert(buflen == res) - - return self._backend._ffi.buffer(buf)[:] - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - if ( - encoding is serialization.Encoding.X962 - or format is serialization.PublicFormat.CompressedPoint - or format is serialization.PublicFormat.UncompressedPoint - ): - if encoding is not serialization.Encoding.X962 or format not in ( - serialization.PublicFormat.CompressedPoint, - serialization.PublicFormat.UncompressedPoint, - ): - raise ValueError( - "X962 encoding must be used with CompressedPoint or " - "UncompressedPoint format" - ) - - return self._encode_point(format) - else: - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def verify( - self, - signature: bytes, - data: bytes, - signature_algorithm: ec.EllipticCurveSignatureAlgorithm, - ) -> None: - _check_signature_algorithm(signature_algorithm) - data, _ = _calculate_digest_and_algorithm( - data, - signature_algorithm.algorithm, - ) - _ecdsa_sig_verify(self._backend, self, signature, data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py deleted file mode 100644 index 6f393e5b6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed25519.py +++ /dev/null @@ -1,155 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import exceptions -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric.ed25519 import ( - _ED25519_KEY_SIZE, - _ED25519_SIG_SIZE, - Ed25519PrivateKey, - Ed25519PublicKey, -) - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -class _Ed25519PublicKey(Ed25519PublicKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PublicFormat.Raw - ): - if ( - encoding is not serialization.Encoding.Raw - or format is not serialization.PublicFormat.Raw - ): - raise ValueError( - "When using Raw both encoding and format must be Raw" - ) - - return self._raw_public_bytes() - - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def _raw_public_bytes(self) -> bytes: - buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_public_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) - return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] - - def verify(self, signature: bytes, data: bytes) -> None: - evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() - self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) - evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.EVP_MD_CTX_free - ) - res = self._backend._lib.EVP_DigestVerifyInit( - evp_md_ctx, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._evp_pkey, - ) - self._backend.openssl_assert(res == 1) - res = self._backend._lib.EVP_DigestVerify( - evp_md_ctx, signature, len(signature), data, len(data) - ) - if res != 1: - self._backend._consume_errors() - raise exceptions.InvalidSignature - - -class _Ed25519PrivateKey(Ed25519PrivateKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_key(self) -> Ed25519PublicKey: - buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_public_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) - public_bytes = self._backend._ffi.buffer(buf)[:] - return self._backend.ed25519_load_public_bytes(public_bytes) - - def sign(self, data: bytes) -> bytes: - evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() - self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) - evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.EVP_MD_CTX_free - ) - res = self._backend._lib.EVP_DigestSignInit( - evp_md_ctx, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._evp_pkey, - ) - self._backend.openssl_assert(res == 1) - buf = self._backend._ffi.new("unsigned char[]", _ED25519_SIG_SIZE) - buflen = self._backend._ffi.new("size_t *", len(buf)) - res = self._backend._lib.EVP_DigestSign( - evp_md_ctx, buf, buflen, data, len(data) - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED25519_SIG_SIZE) - return self._backend._ffi.buffer(buf, buflen[0])[:] - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PrivateFormat.Raw - ): - if ( - format is not serialization.PrivateFormat.Raw - or encoding is not serialization.Encoding.Raw - or not isinstance( - encryption_algorithm, serialization.NoEncryption - ) - ): - raise ValueError( - "When using Raw both encoding and format must be Raw " - "and encryption_algorithm must be NoEncryption()" - ) - - return self._raw_private_bytes() - - return self._backend._private_key_bytes( - encoding, format, encryption_algorithm, self, self._evp_pkey, None - ) - - def _raw_private_bytes(self) -> bytes: - buf = self._backend._ffi.new("unsigned char []", _ED25519_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _ED25519_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_private_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED25519_KEY_SIZE) - return self._backend._ffi.buffer(buf, _ED25519_KEY_SIZE)[:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py deleted file mode 100644 index 0d27ea638..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/ed448.py +++ /dev/null @@ -1,156 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import exceptions -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric.ed448 import ( - Ed448PrivateKey, - Ed448PublicKey, -) - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - -_ED448_KEY_SIZE = 57 -_ED448_SIG_SIZE = 114 - - -class _Ed448PublicKey(Ed448PublicKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PublicFormat.Raw - ): - if ( - encoding is not serialization.Encoding.Raw - or format is not serialization.PublicFormat.Raw - ): - raise ValueError( - "When using Raw both encoding and format must be Raw" - ) - - return self._raw_public_bytes() - - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def _raw_public_bytes(self) -> bytes: - buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_public_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) - return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] - - def verify(self, signature: bytes, data: bytes) -> None: - evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() - self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) - evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.EVP_MD_CTX_free - ) - res = self._backend._lib.EVP_DigestVerifyInit( - evp_md_ctx, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._evp_pkey, - ) - self._backend.openssl_assert(res == 1) - res = self._backend._lib.EVP_DigestVerify( - evp_md_ctx, signature, len(signature), data, len(data) - ) - if res != 1: - self._backend._consume_errors() - raise exceptions.InvalidSignature - - -class _Ed448PrivateKey(Ed448PrivateKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_key(self) -> Ed448PublicKey: - buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_public_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) - public_bytes = self._backend._ffi.buffer(buf)[:] - return self._backend.ed448_load_public_bytes(public_bytes) - - def sign(self, data: bytes) -> bytes: - evp_md_ctx = self._backend._lib.EVP_MD_CTX_new() - self._backend.openssl_assert(evp_md_ctx != self._backend._ffi.NULL) - evp_md_ctx = self._backend._ffi.gc( - evp_md_ctx, self._backend._lib.EVP_MD_CTX_free - ) - res = self._backend._lib.EVP_DigestSignInit( - evp_md_ctx, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._evp_pkey, - ) - self._backend.openssl_assert(res == 1) - buf = self._backend._ffi.new("unsigned char[]", _ED448_SIG_SIZE) - buflen = self._backend._ffi.new("size_t *", len(buf)) - res = self._backend._lib.EVP_DigestSign( - evp_md_ctx, buf, buflen, data, len(data) - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED448_SIG_SIZE) - return self._backend._ffi.buffer(buf, buflen[0])[:] - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PrivateFormat.Raw - ): - if ( - format is not serialization.PrivateFormat.Raw - or encoding is not serialization.Encoding.Raw - or not isinstance( - encryption_algorithm, serialization.NoEncryption - ) - ): - raise ValueError( - "When using Raw both encoding and format must be Raw " - "and encryption_algorithm must be NoEncryption()" - ) - - return self._raw_private_bytes() - - return self._backend._private_key_bytes( - encoding, format, encryption_algorithm, self, self._evp_pkey, None - ) - - def _raw_private_bytes(self) -> bytes: - buf = self._backend._ffi.new("unsigned char []", _ED448_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _ED448_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_private_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _ED448_KEY_SIZE) - return self._backend._ffi.buffer(buf, _ED448_KEY_SIZE)[:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py deleted file mode 100644 index 52d4646a7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hashes.py +++ /dev/null @@ -1,86 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import hashes - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -class _HashContext(hashes.HashContext): - def __init__( - self, backend: "Backend", algorithm: hashes.HashAlgorithm, ctx=None - ) -> None: - self._algorithm = algorithm - - self._backend = backend - - if ctx is None: - ctx = self._backend._lib.EVP_MD_CTX_new() - ctx = self._backend._ffi.gc( - ctx, self._backend._lib.EVP_MD_CTX_free - ) - evp_md = self._backend._evp_md_from_algorithm(algorithm) - if evp_md == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( - "{} is not a supported hash on this backend.".format( - algorithm.name - ), - _Reasons.UNSUPPORTED_HASH, - ) - res = self._backend._lib.EVP_DigestInit_ex( - ctx, evp_md, self._backend._ffi.NULL - ) - self._backend.openssl_assert(res != 0) - - self._ctx = ctx - - @property - def algorithm(self) -> hashes.HashAlgorithm: - return self._algorithm - - def copy(self) -> "_HashContext": - copied_ctx = self._backend._lib.EVP_MD_CTX_new() - copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.EVP_MD_CTX_free - ) - res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) - self._backend.openssl_assert(res != 0) - return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) - - def update(self, data: bytes) -> None: - data_ptr = self._backend._ffi.from_buffer(data) - res = self._backend._lib.EVP_DigestUpdate( - self._ctx, data_ptr, len(data) - ) - self._backend.openssl_assert(res != 0) - - def finalize(self) -> bytes: - if isinstance(self.algorithm, hashes.ExtendableOutputFunction): - # extendable output functions use a different finalize - return self._finalize_xof() - else: - buf = self._backend._ffi.new( - "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE - ) - outlen = self._backend._ffi.new("unsigned int *") - res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) - self._backend.openssl_assert(res != 0) - self._backend.openssl_assert( - outlen[0] == self.algorithm.digest_size - ) - return self._backend._ffi.buffer(buf)[: outlen[0]] - - def _finalize_xof(self) -> bytes: - buf = self._backend._ffi.new( - "unsigned char[]", self.algorithm.digest_size - ) - res = self._backend._lib.EVP_DigestFinalXOF( - self._ctx, buf, self.algorithm.digest_size - ) - self._backend.openssl_assert(res != 0) - return self._backend._ffi.buffer(buf)[: self.algorithm.digest_size] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py deleted file mode 100644 index ba3dfb53f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/hmac.py +++ /dev/null @@ -1,84 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import ( - InvalidSignature, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.primitives import constant_time, hashes - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -class _HMACContext(hashes.HashContext): - def __init__( - self, - backend: "Backend", - key: bytes, - algorithm: hashes.HashAlgorithm, - ctx=None, - ): - self._algorithm = algorithm - self._backend = backend - - if ctx is None: - ctx = self._backend._lib.HMAC_CTX_new() - self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - ctx = self._backend._ffi.gc(ctx, self._backend._lib.HMAC_CTX_free) - evp_md = self._backend._evp_md_from_algorithm(algorithm) - if evp_md == self._backend._ffi.NULL: - raise UnsupportedAlgorithm( - "{} is not a supported hash on this backend".format( - algorithm.name - ), - _Reasons.UNSUPPORTED_HASH, - ) - key_ptr = self._backend._ffi.from_buffer(key) - res = self._backend._lib.HMAC_Init_ex( - ctx, key_ptr, len(key), evp_md, self._backend._ffi.NULL - ) - self._backend.openssl_assert(res != 0) - - self._ctx = ctx - self._key = key - - @property - def algorithm(self) -> hashes.HashAlgorithm: - return self._algorithm - - def copy(self) -> "_HMACContext": - copied_ctx = self._backend._lib.HMAC_CTX_new() - self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL) - copied_ctx = self._backend._ffi.gc( - copied_ctx, self._backend._lib.HMAC_CTX_free - ) - res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx) - self._backend.openssl_assert(res != 0) - return _HMACContext( - self._backend, self._key, self.algorithm, ctx=copied_ctx - ) - - def update(self, data: bytes) -> None: - data_ptr = self._backend._ffi.from_buffer(data) - res = self._backend._lib.HMAC_Update(self._ctx, data_ptr, len(data)) - self._backend.openssl_assert(res != 0) - - def finalize(self) -> bytes: - buf = self._backend._ffi.new( - "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE - ) - outlen = self._backend._ffi.new("unsigned int *") - res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen) - self._backend.openssl_assert(res != 0) - self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size) - return self._backend._ffi.buffer(buf)[: outlen[0]] - - def verify(self, signature: bytes) -> None: - digest = self.finalize() - if not constant_time.bytes_eq(digest, signature): - raise InvalidSignature("Signature did not match digest.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py deleted file mode 100644 index d0d44f6fd..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/poly1305.py +++ /dev/null @@ -1,67 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.exceptions import InvalidSignature -from cryptography.hazmat.primitives import constant_time - -_POLY1305_TAG_SIZE = 16 -_POLY1305_KEY_SIZE = 32 - - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -class _Poly1305Context: - def __init__(self, backend: "Backend", key: bytes) -> None: - self._backend = backend - - key_ptr = self._backend._ffi.from_buffer(key) - # This function copies the key into OpenSSL-owned memory so we don't - # need to retain it ourselves - evp_pkey = self._backend._lib.EVP_PKEY_new_raw_private_key( - self._backend._lib.NID_poly1305, - self._backend._ffi.NULL, - key_ptr, - len(key), - ) - self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) - self._evp_pkey = self._backend._ffi.gc( - evp_pkey, self._backend._lib.EVP_PKEY_free - ) - ctx = self._backend._lib.EVP_MD_CTX_new() - self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - self._ctx = self._backend._ffi.gc( - ctx, self._backend._lib.EVP_MD_CTX_free - ) - res = self._backend._lib.EVP_DigestSignInit( - self._ctx, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - self._evp_pkey, - ) - self._backend.openssl_assert(res == 1) - - def update(self, data: bytes) -> None: - data_ptr = self._backend._ffi.from_buffer(data) - res = self._backend._lib.EVP_DigestSignUpdate( - self._ctx, data_ptr, len(data) - ) - self._backend.openssl_assert(res != 0) - - def finalize(self) -> bytes: - buf = self._backend._ffi.new("unsigned char[]", _POLY1305_TAG_SIZE) - outlen = self._backend._ffi.new("size_t *", _POLY1305_TAG_SIZE) - res = self._backend._lib.EVP_DigestSignFinal(self._ctx, buf, outlen) - self._backend.openssl_assert(res != 0) - self._backend.openssl_assert(outlen[0] == _POLY1305_TAG_SIZE) - return self._backend._ffi.buffer(buf)[: outlen[0]] - - def verify(self, tag: bytes) -> None: - mac = self.finalize() - if not constant_time.bytes_eq(mac, tag): - raise InvalidSignature("Value did not match computed tag.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py deleted file mode 100644 index e18bab3ff..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/rsa.py +++ /dev/null @@ -1,588 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import threading -import typing - -from cryptography.exceptions import ( - InvalidSignature, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.backends.openssl.utils import ( - _calculate_digest_and_algorithm, -) -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils -from cryptography.hazmat.primitives.asymmetric.padding import ( - MGF1, - OAEP, - PSS, - AsymmetricPadding, - PKCS1v15, - _Auto, - _DigestLength, - _MaxLength, - calculate_max_pss_salt_length, -) -from cryptography.hazmat.primitives.asymmetric.rsa import ( - RSAPrivateKey, - RSAPrivateNumbers, - RSAPublicKey, - RSAPublicNumbers, -) - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -def _get_rsa_pss_salt_length( - backend: "Backend", - pss: PSS, - key: typing.Union[RSAPrivateKey, RSAPublicKey], - hash_algorithm: hashes.HashAlgorithm, -) -> int: - salt = pss._salt_length - - if isinstance(salt, _MaxLength): - return calculate_max_pss_salt_length(key, hash_algorithm) - elif isinstance(salt, _DigestLength): - return hash_algorithm.digest_size - elif isinstance(salt, _Auto): - if isinstance(key, RSAPrivateKey): - raise ValueError( - "PSS salt length can only be set to AUTO when verifying" - ) - return backend._lib.RSA_PSS_SALTLEN_AUTO - else: - return salt - - -def _enc_dec_rsa( - backend: "Backend", - key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"], - data: bytes, - padding: AsymmetricPadding, -) -> bytes: - if not isinstance(padding, AsymmetricPadding): - raise TypeError("Padding must be an instance of AsymmetricPadding.") - - if isinstance(padding, PKCS1v15): - padding_enum = backend._lib.RSA_PKCS1_PADDING - elif isinstance(padding, OAEP): - padding_enum = backend._lib.RSA_PKCS1_OAEP_PADDING - - if not isinstance(padding._mgf, MGF1): - raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend.", - _Reasons.UNSUPPORTED_MGF, - ) - - if not backend.rsa_padding_supported(padding): - raise UnsupportedAlgorithm( - "This combination of padding and hash algorithm is not " - "supported by this backend.", - _Reasons.UNSUPPORTED_PADDING, - ) - - else: - raise UnsupportedAlgorithm( - "{} is not supported by this backend.".format(padding.name), - _Reasons.UNSUPPORTED_PADDING, - ) - - return _enc_dec_rsa_pkey_ctx(backend, key, data, padding_enum, padding) - - -def _enc_dec_rsa_pkey_ctx( - backend: "Backend", - key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"], - data: bytes, - padding_enum: int, - padding: AsymmetricPadding, -) -> bytes: - init: typing.Callable[[typing.Any], int] - crypt: typing.Callable[[typing.Any, typing.Any, int, bytes, int], int] - if isinstance(key, _RSAPublicKey): - init = backend._lib.EVP_PKEY_encrypt_init - crypt = backend._lib.EVP_PKEY_encrypt - else: - init = backend._lib.EVP_PKEY_decrypt_init - crypt = backend._lib.EVP_PKEY_decrypt - - pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL) - backend.openssl_assert(pkey_ctx != backend._ffi.NULL) - pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) - res = init(pkey_ctx) - backend.openssl_assert(res == 1) - res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) - backend.openssl_assert(res > 0) - buf_size = backend._lib.EVP_PKEY_size(key._evp_pkey) - backend.openssl_assert(buf_size > 0) - if isinstance(padding, OAEP): - mgf1_md = backend._evp_md_non_null_from_algorithm( - padding._mgf._algorithm - ) - res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md) - backend.openssl_assert(res > 0) - oaep_md = backend._evp_md_non_null_from_algorithm(padding._algorithm) - res = backend._lib.EVP_PKEY_CTX_set_rsa_oaep_md(pkey_ctx, oaep_md) - backend.openssl_assert(res > 0) - - if ( - isinstance(padding, OAEP) - and padding._label is not None - and len(padding._label) > 0 - ): - # set0_rsa_oaep_label takes ownership of the char * so we need to - # copy it into some new memory - labelptr = backend._lib.OPENSSL_malloc(len(padding._label)) - backend.openssl_assert(labelptr != backend._ffi.NULL) - backend._ffi.memmove(labelptr, padding._label, len(padding._label)) - res = backend._lib.EVP_PKEY_CTX_set0_rsa_oaep_label( - pkey_ctx, labelptr, len(padding._label) - ) - backend.openssl_assert(res == 1) - - outlen = backend._ffi.new("size_t *", buf_size) - buf = backend._ffi.new("unsigned char[]", buf_size) - # Everything from this line onwards is written with the goal of being as - # constant-time as is practical given the constraints of Python and our - # API. See Bleichenbacher's '98 attack on RSA, and its many many variants. - # As such, you should not attempt to change this (particularly to "clean it - # up") without understanding why it was written this way (see - # Chesterton's Fence), and without measuring to verify you have not - # introduced observable time differences. - res = crypt(pkey_ctx, buf, outlen, data, len(data)) - resbuf = backend._ffi.buffer(buf)[: outlen[0]] - backend._lib.ERR_clear_error() - if res <= 0: - raise ValueError("Encryption/decryption failed.") - return resbuf - - -def _rsa_sig_determine_padding( - backend: "Backend", - key: typing.Union["_RSAPrivateKey", "_RSAPublicKey"], - padding: AsymmetricPadding, - algorithm: typing.Optional[hashes.HashAlgorithm], -) -> int: - if not isinstance(padding, AsymmetricPadding): - raise TypeError("Expected provider of AsymmetricPadding.") - - pkey_size = backend._lib.EVP_PKEY_size(key._evp_pkey) - backend.openssl_assert(pkey_size > 0) - - if isinstance(padding, PKCS1v15): - # Hash algorithm is ignored for PKCS1v15-padding, may be None. - padding_enum = backend._lib.RSA_PKCS1_PADDING - elif isinstance(padding, PSS): - if not isinstance(padding._mgf, MGF1): - raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend.", - _Reasons.UNSUPPORTED_MGF, - ) - - # PSS padding requires a hash algorithm - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - # Size of key in bytes - 2 is the maximum - # PSS signature length (salt length is checked later) - if pkey_size - algorithm.digest_size - 2 < 0: - raise ValueError( - "Digest too large for key size. Use a larger " - "key or different digest." - ) - - padding_enum = backend._lib.RSA_PKCS1_PSS_PADDING - else: - raise UnsupportedAlgorithm( - "{} is not supported by this backend.".format(padding.name), - _Reasons.UNSUPPORTED_PADDING, - ) - - return padding_enum - - -# Hash algorithm can be absent (None) to initialize the context without setting -# any message digest algorithm. This is currently only valid for the PKCS1v15 -# padding type, where it means that the signature data is encoded/decoded -# as provided, without being wrapped in a DigestInfo structure. -def _rsa_sig_setup( - backend: "Backend", - padding: AsymmetricPadding, - algorithm: typing.Optional[hashes.HashAlgorithm], - key: typing.Union["_RSAPublicKey", "_RSAPrivateKey"], - init_func: typing.Callable[[typing.Any], int], -): - padding_enum = _rsa_sig_determine_padding(backend, key, padding, algorithm) - pkey_ctx = backend._lib.EVP_PKEY_CTX_new(key._evp_pkey, backend._ffi.NULL) - backend.openssl_assert(pkey_ctx != backend._ffi.NULL) - pkey_ctx = backend._ffi.gc(pkey_ctx, backend._lib.EVP_PKEY_CTX_free) - res = init_func(pkey_ctx) - if res != 1: - errors = backend._consume_errors() - raise ValueError("Unable to sign/verify with this key", errors) - - if algorithm is not None: - evp_md = backend._evp_md_non_null_from_algorithm(algorithm) - res = backend._lib.EVP_PKEY_CTX_set_signature_md(pkey_ctx, evp_md) - if res <= 0: - backend._consume_errors() - raise UnsupportedAlgorithm( - "{} is not supported by this backend for RSA signing.".format( - algorithm.name - ), - _Reasons.UNSUPPORTED_HASH, - ) - res = backend._lib.EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, padding_enum) - if res <= 0: - backend._consume_errors() - raise UnsupportedAlgorithm( - "{} is not supported for the RSA signature operation.".format( - padding.name - ), - _Reasons.UNSUPPORTED_PADDING, - ) - if isinstance(padding, PSS): - assert isinstance(algorithm, hashes.HashAlgorithm) - res = backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( - pkey_ctx, - _get_rsa_pss_salt_length(backend, padding, key, algorithm), - ) - backend.openssl_assert(res > 0) - - mgf1_md = backend._evp_md_non_null_from_algorithm( - padding._mgf._algorithm - ) - res = backend._lib.EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, mgf1_md) - backend.openssl_assert(res > 0) - - return pkey_ctx - - -def _rsa_sig_sign( - backend: "Backend", - padding: AsymmetricPadding, - algorithm: hashes.HashAlgorithm, - private_key: "_RSAPrivateKey", - data: bytes, -) -> bytes: - pkey_ctx = _rsa_sig_setup( - backend, - padding, - algorithm, - private_key, - backend._lib.EVP_PKEY_sign_init, - ) - buflen = backend._ffi.new("size_t *") - res = backend._lib.EVP_PKEY_sign( - pkey_ctx, backend._ffi.NULL, buflen, data, len(data) - ) - backend.openssl_assert(res == 1) - buf = backend._ffi.new("unsigned char[]", buflen[0]) - res = backend._lib.EVP_PKEY_sign(pkey_ctx, buf, buflen, data, len(data)) - if res != 1: - errors = backend._consume_errors_with_text() - raise ValueError( - "Digest or salt length too long for key size. Use a larger key " - "or shorter salt length if you are specifying a PSS salt", - errors, - ) - - return backend._ffi.buffer(buf)[:] - - -def _rsa_sig_verify( - backend: "Backend", - padding: AsymmetricPadding, - algorithm: hashes.HashAlgorithm, - public_key: "_RSAPublicKey", - signature: bytes, - data: bytes, -) -> None: - pkey_ctx = _rsa_sig_setup( - backend, - padding, - algorithm, - public_key, - backend._lib.EVP_PKEY_verify_init, - ) - res = backend._lib.EVP_PKEY_verify( - pkey_ctx, signature, len(signature), data, len(data) - ) - # The previous call can return negative numbers in the event of an - # error. This is not a signature failure but we need to fail if it - # occurs. - backend.openssl_assert(res >= 0) - if res == 0: - backend._consume_errors() - raise InvalidSignature - - -def _rsa_sig_recover( - backend: "Backend", - padding: AsymmetricPadding, - algorithm: typing.Optional[hashes.HashAlgorithm], - public_key: "_RSAPublicKey", - signature: bytes, -) -> bytes: - pkey_ctx = _rsa_sig_setup( - backend, - padding, - algorithm, - public_key, - backend._lib.EVP_PKEY_verify_recover_init, - ) - - # Attempt to keep the rest of the code in this function as constant/time - # as possible. See the comment in _enc_dec_rsa_pkey_ctx. Note that the - # buflen parameter is used even though its value may be undefined in the - # error case. Due to the tolerant nature of Python slicing this does not - # trigger any exceptions. - maxlen = backend._lib.EVP_PKEY_size(public_key._evp_pkey) - backend.openssl_assert(maxlen > 0) - buf = backend._ffi.new("unsigned char[]", maxlen) - buflen = backend._ffi.new("size_t *", maxlen) - res = backend._lib.EVP_PKEY_verify_recover( - pkey_ctx, buf, buflen, signature, len(signature) - ) - resbuf = backend._ffi.buffer(buf)[: buflen[0]] - backend._lib.ERR_clear_error() - # Assume that all parameter errors are handled during the setup phase and - # any error here is due to invalid signature. - if res != 1: - raise InvalidSignature - return resbuf - - -class _RSAPrivateKey(RSAPrivateKey): - _evp_pkey: object - _rsa_cdata: object - _key_size: int - - def __init__( - self, - backend: "Backend", - rsa_cdata, - evp_pkey, - *, - unsafe_skip_rsa_key_validation: bool, - ): - res: int - # RSA_check_key is slower in OpenSSL 3.0.0 due to improved - # primality checking. In normal use this is unlikely to be a problem - # since users don't load new keys constantly, but for TESTING we've - # added an init arg that allows skipping the checks. You should not - # use this in production code unless you understand the consequences. - if not unsafe_skip_rsa_key_validation: - res = backend._lib.RSA_check_key(rsa_cdata) - if res != 1: - errors = backend._consume_errors_with_text() - raise ValueError("Invalid private key", errors) - # 2 is prime and passes an RSA key check, so we also check - # if p and q are odd just to be safe. - p = backend._ffi.new("BIGNUM **") - q = backend._ffi.new("BIGNUM **") - backend._lib.RSA_get0_factors(rsa_cdata, p, q) - backend.openssl_assert(p[0] != backend._ffi.NULL) - backend.openssl_assert(q[0] != backend._ffi.NULL) - p_odd = backend._lib.BN_is_odd(p[0]) - q_odd = backend._lib.BN_is_odd(q[0]) - if p_odd != 1 or q_odd != 1: - errors = backend._consume_errors_with_text() - raise ValueError("Invalid private key", errors) - - self._backend = backend - self._rsa_cdata = rsa_cdata - self._evp_pkey = evp_pkey - # Used for lazy blinding - self._blinded = False - self._blinding_lock = threading.Lock() - - n = self._backend._ffi.new("BIGNUM **") - self._backend._lib.RSA_get0_key( - self._rsa_cdata, - n, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - ) - self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) - self._key_size = self._backend._lib.BN_num_bits(n[0]) - - def _enable_blinding(self) -> None: - # If you call blind on an already blinded RSA key OpenSSL will turn - # it off and back on, which is a performance hit we want to avoid. - if not self._blinded: - with self._blinding_lock: - self._non_threadsafe_enable_blinding() - - def _non_threadsafe_enable_blinding(self) -> None: - # This is only a separate function to allow for testing to cover both - # branches. It should never be invoked except through _enable_blinding. - # Check if it's not True again in case another thread raced past the - # first non-locked check. - if not self._blinded: - res = self._backend._lib.RSA_blinding_on( - self._rsa_cdata, self._backend._ffi.NULL - ) - self._backend.openssl_assert(res == 1) - self._blinded = True - - @property - def key_size(self) -> int: - return self._key_size - - def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: - self._enable_blinding() - key_size_bytes = (self.key_size + 7) // 8 - if key_size_bytes != len(ciphertext): - raise ValueError("Ciphertext length must be equal to key size.") - - return _enc_dec_rsa(self._backend, self, ciphertext, padding) - - def public_key(self) -> RSAPublicKey: - ctx = self._backend._lib.RSAPublicKey_dup(self._rsa_cdata) - self._backend.openssl_assert(ctx != self._backend._ffi.NULL) - ctx = self._backend._ffi.gc(ctx, self._backend._lib.RSA_free) - evp_pkey = self._backend._rsa_cdata_to_evp_pkey(ctx) - return _RSAPublicKey(self._backend, ctx, evp_pkey) - - def private_numbers(self) -> RSAPrivateNumbers: - n = self._backend._ffi.new("BIGNUM **") - e = self._backend._ffi.new("BIGNUM **") - d = self._backend._ffi.new("BIGNUM **") - p = self._backend._ffi.new("BIGNUM **") - q = self._backend._ffi.new("BIGNUM **") - dmp1 = self._backend._ffi.new("BIGNUM **") - dmq1 = self._backend._ffi.new("BIGNUM **") - iqmp = self._backend._ffi.new("BIGNUM **") - self._backend._lib.RSA_get0_key(self._rsa_cdata, n, e, d) - self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(e[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(d[0] != self._backend._ffi.NULL) - self._backend._lib.RSA_get0_factors(self._rsa_cdata, p, q) - self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(q[0] != self._backend._ffi.NULL) - self._backend._lib.RSA_get0_crt_params( - self._rsa_cdata, dmp1, dmq1, iqmp - ) - self._backend.openssl_assert(dmp1[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(dmq1[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(iqmp[0] != self._backend._ffi.NULL) - return RSAPrivateNumbers( - p=self._backend._bn_to_int(p[0]), - q=self._backend._bn_to_int(q[0]), - d=self._backend._bn_to_int(d[0]), - dmp1=self._backend._bn_to_int(dmp1[0]), - dmq1=self._backend._bn_to_int(dmq1[0]), - iqmp=self._backend._bn_to_int(iqmp[0]), - public_numbers=RSAPublicNumbers( - e=self._backend._bn_to_int(e[0]), - n=self._backend._bn_to_int(n[0]), - ), - ) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - return self._backend._private_key_bytes( - encoding, - format, - encryption_algorithm, - self, - self._evp_pkey, - self._rsa_cdata, - ) - - def sign( - self, - data: bytes, - padding: AsymmetricPadding, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> bytes: - self._enable_blinding() - data, algorithm = _calculate_digest_and_algorithm(data, algorithm) - return _rsa_sig_sign(self._backend, padding, algorithm, self, data) - - -class _RSAPublicKey(RSAPublicKey): - _evp_pkey: object - _rsa_cdata: object - _key_size: int - - def __init__(self, backend: "Backend", rsa_cdata, evp_pkey): - self._backend = backend - self._rsa_cdata = rsa_cdata - self._evp_pkey = evp_pkey - - n = self._backend._ffi.new("BIGNUM **") - self._backend._lib.RSA_get0_key( - self._rsa_cdata, - n, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - ) - self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) - self._key_size = self._backend._lib.BN_num_bits(n[0]) - - @property - def key_size(self) -> int: - return self._key_size - - def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: - return _enc_dec_rsa(self._backend, self, plaintext, padding) - - def public_numbers(self) -> RSAPublicNumbers: - n = self._backend._ffi.new("BIGNUM **") - e = self._backend._ffi.new("BIGNUM **") - self._backend._lib.RSA_get0_key( - self._rsa_cdata, n, e, self._backend._ffi.NULL - ) - self._backend.openssl_assert(n[0] != self._backend._ffi.NULL) - self._backend.openssl_assert(e[0] != self._backend._ffi.NULL) - return RSAPublicNumbers( - e=self._backend._bn_to_int(e[0]), - n=self._backend._bn_to_int(n[0]), - ) - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, self._rsa_cdata - ) - - def verify( - self, - signature: bytes, - data: bytes, - padding: AsymmetricPadding, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> None: - data, algorithm = _calculate_digest_and_algorithm(data, algorithm) - _rsa_sig_verify( - self._backend, padding, algorithm, self, signature, data - ) - - def recover_data_from_signature( - self, - signature: bytes, - padding: AsymmetricPadding, - algorithm: typing.Optional[hashes.HashAlgorithm], - ) -> bytes: - if isinstance(algorithm, asym_utils.Prehashed): - raise TypeError( - "Prehashed is only supported in the sign and verify methods. " - "It cannot be used with recover_data_from_signature." - ) - return _rsa_sig_recover( - self._backend, padding, algorithm, self, signature - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py deleted file mode 100644 index 3a70a5818..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/utils.py +++ /dev/null @@ -1,52 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric.utils import Prehashed - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -def _evp_pkey_derive(backend: "Backend", evp_pkey, peer_public_key) -> bytes: - ctx = backend._lib.EVP_PKEY_CTX_new(evp_pkey, backend._ffi.NULL) - backend.openssl_assert(ctx != backend._ffi.NULL) - ctx = backend._ffi.gc(ctx, backend._lib.EVP_PKEY_CTX_free) - res = backend._lib.EVP_PKEY_derive_init(ctx) - backend.openssl_assert(res == 1) - res = backend._lib.EVP_PKEY_derive_set_peer(ctx, peer_public_key._evp_pkey) - backend.openssl_assert(res == 1) - keylen = backend._ffi.new("size_t *") - res = backend._lib.EVP_PKEY_derive(ctx, backend._ffi.NULL, keylen) - backend.openssl_assert(res == 1) - backend.openssl_assert(keylen[0] > 0) - buf = backend._ffi.new("unsigned char[]", keylen[0]) - res = backend._lib.EVP_PKEY_derive(ctx, buf, keylen) - if res != 1: - errors_with_text = backend._consume_errors_with_text() - raise ValueError("Error computing shared key.", errors_with_text) - - return backend._ffi.buffer(buf, keylen[0])[:] - - -def _calculate_digest_and_algorithm( - data: bytes, - algorithm: typing.Union[Prehashed, hashes.HashAlgorithm], -) -> typing.Tuple[bytes, hashes.HashAlgorithm]: - if not isinstance(algorithm, Prehashed): - hash_ctx = hashes.Hash(algorithm) - hash_ctx.update(data) - data = hash_ctx.finalize() - else: - algorithm = algorithm._algorithm - - if len(data) != algorithm.digest_size: - raise ValueError( - "The provided data must be the same length as the hash " - "algorithm's digest size." - ) - - return (data, algorithm) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py deleted file mode 100644 index e3b41eced..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x25519.py +++ /dev/null @@ -1,132 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric.x25519 import ( - X25519PrivateKey, - X25519PublicKey, -) - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - - -_X25519_KEY_SIZE = 32 - - -class _X25519PublicKey(X25519PublicKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PublicFormat.Raw - ): - if ( - encoding is not serialization.Encoding.Raw - or format is not serialization.PublicFormat.Raw - ): - raise ValueError( - "When using Raw both encoding and format must be Raw" - ) - - return self._raw_public_bytes() - - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def _raw_public_bytes(self) -> bytes: - ucharpp = self._backend._ffi.new("unsigned char **") - res = self._backend._lib.EVP_PKEY_get1_tls_encodedpoint( - self._evp_pkey, ucharpp - ) - self._backend.openssl_assert(res == 32) - self._backend.openssl_assert(ucharpp[0] != self._backend._ffi.NULL) - data = self._backend._ffi.gc( - ucharpp[0], self._backend._lib.OPENSSL_free - ) - return self._backend._ffi.buffer(data, res)[:] - - -class _X25519PrivateKey(X25519PrivateKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_key(self) -> X25519PublicKey: - bio = self._backend._create_mem_bio_gc() - res = self._backend._lib.i2d_PUBKEY_bio(bio, self._evp_pkey) - self._backend.openssl_assert(res == 1) - evp_pkey = self._backend._lib.d2i_PUBKEY_bio( - bio, self._backend._ffi.NULL - ) - self._backend.openssl_assert(evp_pkey != self._backend._ffi.NULL) - evp_pkey = self._backend._ffi.gc( - evp_pkey, self._backend._lib.EVP_PKEY_free - ) - return _X25519PublicKey(self._backend, evp_pkey) - - def exchange(self, peer_public_key: X25519PublicKey) -> bytes: - if not isinstance(peer_public_key, X25519PublicKey): - raise TypeError("peer_public_key must be X25519PublicKey.") - - return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PrivateFormat.Raw - ): - if ( - format is not serialization.PrivateFormat.Raw - or encoding is not serialization.Encoding.Raw - or not isinstance( - encryption_algorithm, serialization.NoEncryption - ) - ): - raise ValueError( - "When using Raw both encoding and format must be Raw " - "and encryption_algorithm must be NoEncryption()" - ) - - return self._raw_private_bytes() - - return self._backend._private_key_bytes( - encoding, format, encryption_algorithm, self, self._evp_pkey, None - ) - - def _raw_private_bytes(self) -> bytes: - # If/when LibreSSL adds support for EVP_PKEY_get_raw_private_key we - # can switch to it (Cryptography_HAS_RAW_KEY) - # The trick we use here is serializing to a PKCS8 key and just - # using the last 32 bytes, which is the key itself. - bio = self._backend._create_mem_bio_gc() - res = self._backend._lib.i2d_PKCS8PrivateKey_bio( - bio, - self._evp_pkey, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - 0, - self._backend._ffi.NULL, - self._backend._ffi.NULL, - ) - self._backend.openssl_assert(res == 1) - pkcs8 = self._backend._read_mem_bio(bio) - self._backend.openssl_assert(len(pkcs8) == 48) - return pkcs8[-_X25519_KEY_SIZE:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py deleted file mode 100644 index d738188c7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/backends/openssl/x448.py +++ /dev/null @@ -1,117 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.backends.openssl.utils import _evp_pkey_derive -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric.x448 import ( - X448PrivateKey, - X448PublicKey, -) - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.backend import Backend - -_X448_KEY_SIZE = 56 - - -class _X448PublicKey(X448PublicKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PublicFormat, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PublicFormat.Raw - ): - if ( - encoding is not serialization.Encoding.Raw - or format is not serialization.PublicFormat.Raw - ): - raise ValueError( - "When using Raw both encoding and format must be Raw" - ) - - return self._raw_public_bytes() - - return self._backend._public_key_bytes( - encoding, format, self, self._evp_pkey, None - ) - - def _raw_public_bytes(self) -> bytes: - buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_public_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) - return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:] - - -class _X448PrivateKey(X448PrivateKey): - def __init__(self, backend: "Backend", evp_pkey): - self._backend = backend - self._evp_pkey = evp_pkey - - def public_key(self) -> X448PublicKey: - buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_public_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) - public_bytes = self._backend._ffi.buffer(buf)[:] - return self._backend.x448_load_public_bytes(public_bytes) - - def exchange(self, peer_public_key: X448PublicKey) -> bytes: - if not isinstance(peer_public_key, X448PublicKey): - raise TypeError("peer_public_key must be X448PublicKey.") - - return _evp_pkey_derive(self._backend, self._evp_pkey, peer_public_key) - - def private_bytes( - self, - encoding: serialization.Encoding, - format: serialization.PrivateFormat, - encryption_algorithm: serialization.KeySerializationEncryption, - ) -> bytes: - if ( - encoding is serialization.Encoding.Raw - or format is serialization.PrivateFormat.Raw - ): - if ( - format is not serialization.PrivateFormat.Raw - or encoding is not serialization.Encoding.Raw - or not isinstance( - encryption_algorithm, serialization.NoEncryption - ) - ): - raise ValueError( - "When using Raw both encoding and format must be Raw " - "and encryption_algorithm must be NoEncryption()" - ) - - return self._raw_private_bytes() - - return self._backend._private_key_bytes( - encoding, format, encryption_algorithm, self, self._evp_pkey, None - ) - - def _raw_private_bytes(self) -> bytes: - buf = self._backend._ffi.new("unsigned char []", _X448_KEY_SIZE) - buflen = self._backend._ffi.new("size_t *", _X448_KEY_SIZE) - res = self._backend._lib.EVP_PKEY_get_raw_private_key( - self._evp_pkey, buf, buflen - ) - self._backend.openssl_assert(res == 1) - self._backend.openssl_assert(buflen[0] == _X448_KEY_SIZE) - return self._backend._ffi.buffer(buf, _X448_KEY_SIZE)[:] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py deleted file mode 100644 index b50933623..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyd b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_openssl.pyd deleted file mode 100644 index d76347c3887a0b69c982c8e4dd18078c6da3166c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3964416 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%0AaU4?dK@lyDHnmGevv4ych;of(BQj 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

    9$rCkj}Q+PYxhbx%4o}nEGp{DuY4*!|aR}gKk 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�HoZ{oM+{t(7KLnBHR${OWft`fY&0D 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ℑ1V1RKNlkvq6PZlQ`O#o&J6F;E9eqldAD@6( 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 Y*PoD4w!36%VmP4L-wWt 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

    7d>ZG)Wb6VfuxfUK<#VgpZN4@AgNT1^RU=4q+- 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

    6Z8KQUd=hBN0%u5|@-dQ4dqx@)s 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

    odWN$*-{UdB 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�^9J&1 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

    OTBq)qWf+7&_SWS3xH@4uQo%U zPam5LIuy^4Z-C>^!xR4~*0{oAQL3hahL`DYp_ySzf@lFK#TuY5DPoPIS}}?(+1P|+ zV;UtJ=R8iy#tJ`!R%{Po$d*3DwfXoGrx}>ADS9D*rBVBcP^`!_b$?j4f4K(1@&Fc* z?#2^jy{&Yhw1#kaYt(P$`i+wr{L1h~076SCG84-HOnb}f#A{gz1LoET#j#j?3LRKB zbXObX_ctQ17jUOa;Ym4vlM2Sq~x<`xrB0QmK1aW;Us3`yUDttQxBc}5(m`jfvyrs{bN z%x;gmF=$Wb8q%U^q(`Z(ui_5W_$erL>U^FekI}eQqbKPx8iIf-{2S)6Y7iefnoI|jg^BPFefz|2`t+psV0!j# z>S)+{23KE>N0*Y#_3}Vzqz!y3zeBdTn31fJqi<)iBf(-_^nzB3SJliuiZrW&pwnSQ zFNxpH3{jR7_}PW@LW9CPy&RgEP5cZMTXCekNQb3Iu5C^Q&+H&C29Yu3>u$oXH44ob+P_#i8?>8;FKDrK|WPmlZ zw;M0Gi7jcdkWRJs`&g3@_D1C~fKD$xqikWRQte`RjD9Wd$KX(J9SQ1Diw_v$FW)AN z`jrq%XnAe+pD?yBAoV~B=z$EjNN%)^>4ALU4HfR-Tg--rAPgUgCxC3%f&>BSJyHnB zo=4G<;F^HcK~~l6Q751vvKr81%<7V4Wi_DmqqC0@ufk|dK93>m*PufWG#J&&eY8&a z5a>rfDx&Uq0}TmHJ;|$Hva~!jTnNT*Q2L>cNv0nVcW^dLkdJ};U)yll;vVe+EC*hk zl~}pz{jaDj2TpUPc4K8ZfF&%%5>(+I2Yq_*d3xWg0&?OsdtTj_GPF7f12|QPk!FYM zG?{ zg116agU&zs2%Ek4qCRalch{cG-L-B&-h}Xe_0j9>J6HVh*v9Z{xdn;t`z84+845>= zZ_6KM9fw|gt}@Y!B7Z2W*$DZ=;j=bE{&3p3|L^jL$uIs!@`q>76le{UKh%%??d170r1~U{&3D=NlT0&$R7@Yu@jJ+4kF5x zuMS22aMjWM@ha1%%dUh5>^GgN>CNmSAa(rt38V_WX{i=^bB#-HraJT{R_Qlyrm{b1 zNsqemwBKOIY1v`Y?}P6+{q77w4cL`X*0wr(z2kJ&p~&fOS#H}1%d#m_w&Kl!k!tbRPVAAYWwBlwx);^$NwKQ&MFpz~17O21Kiuk_>1 z)Yld?P5OjePL-+qm(-WwRyMS7uzzWDVIk<8h5VX_Bo0p-BePNxghB&1E!C+TfY7d^ zC3Yh+Y0{gPoHRm?U_O-74YQ78TPXdpw+eU~ARHS`rnPS*BI9;i08>{Dw-2*$_Dnd1 zGvRAR+`g}gXU^KEh>Rj-{b}X=9JG2)%sN;g_ufGcSRokh_>Z z-2OaQE&}sR$9;oZj&>4#JZftUkwL-OT}uJRp!5lNtRKB`cs%c=6g+Z^b;$Z4_l--z zBd$`P_t{cGoFh<`{e61@mO&qLv#^;X>R>a-W|~)HpJNsnr0k={0ivw}(c=<3NUvX` zC*#%ZN^>H*2g-M~f8`rhi|`?~Owa5SMA0Ni5^sO271LOhZyyKcI4+>p{L!4tjRSUf`g z<6PjDq7Y~)M#{R!!cc zTtRfgXK?mxP%S~B3{Dg|*!s{Doc=7-TG#@j*0L2F-a3iu2VLqsZ14!Qp*|%$CZK(K z$N29|HI^mfL2RbY)Zzt7RH$cwa{%5_FP3P2Y>AX%O9YB4_0@<14VA@oxT)-mHsFy> z6Qo3<2`|XBO)B)jGpQn>E_m96;y69*_72C6^Tu~20`_EnD@42k3(>Th(=3fXsDFX4{G8dxuuy0G#W zc0yw`c{`(%bx4RR<9`wf$RvK$@(dbx&rd91h(;AWLd&daX8>wh$^0({Nm6*M#Lzja ztDOLKI10(qUBK9BEKZl7+V3!T1Zw$5hb-RQ;TOcKJ^b=zXy{$rkMTTEUb%X<43mwM zS6+N4he?XO^3gk!n?>Dls+X;?=yxcYK`5K~7QsO2C04*7>fg8ajh(LD|F4|+KA4>@BE ziLDtaL-d*PMrKI)2TJA0Klo@xrrGk3Z@2CzWxWqkP|H?2zbs{S3;7E|RwO0ukR)S0 z{b|nUDI$;zw4x&dVShse!cPN5AcTns=l)M6t!S1mX`M8JR?v<{-9xS7^pmr)8cye~ zTPZ>ke#)qv0DNW9Q-mZ^>DqqSMANz1t??|}I|oqYAxgEmgSIuD2NL0)Upe{qsLY$na@>o3+{CmV zLTEPHdbLvp>r~Z>Z*BS;v@l~3vutQS-+ZZzcrT>1t(?!dzFp>XC}hff4whC{0T_@0 zpr9vgo`hM6s&`vRvz_GaJ_*C#t8S8=Tb>Ds?xi3QJcUPRYILaiz+#r*z9y(U+nGp+(aMF%OlQ}0ah{7nSHOOPY^zt^TcPXg^mwp zESQ&f3dp>?EgjCwTk84yQLgs)N8MC-zMhw%aih6$y6EG?hD0aF%!cPzE7GZ!!?x&R z@U-q^T#KX$qBQe2cMwlnNo4U%QmMjZGe>j8cI<)UGkHE&e>%t=FhkNOEg`m`AG;rh z$SyKOlJx2ST`udhv%@uW(4D&geVuAG+6oJoPV{RR@Pkni>FK_PE445#-a%2pJB=Jy zC{XV#X!Wrb_j+)@fqfTnc5Dg3}j$R8D4ib`m!+RSCFFtI{11kPwn%xpv4cgK-95u`^|qiz(otr zFH(P0_>Ba09dQ0a*d{Bxnt1u~q<-u9doB^YRJ!Z=l7u{{!B!bnYSp1XZ`$FDC8God z3O$8|iO6yW9=XpKDm0O*Kd$g2JC zBWo%h1e#gkheB=}&iZ)aHo@`$&H;fcmP23B#8Qw4TXoeje%-3;07>)}nPOaDP>%4u zDp%J95z%dy$Clmuv4OuwFhkQ6ur zMF61)U;%cxh)p@**og4S8)O5Hsv&KZ9Y1gaZR7+tyIIyNT&?Q;S`wA8DGGJq3poeJ z$&_-xmJZh|AILk_D?44-f4w5B-ZioSV2Ws%Ei+`j(#E$nlAgU@X#-2k;A#Rci5AM| zrMd+5scpSvDFixoUbT?)Hmp||!CtSlz6Z*lv;>IzSS(a2b^)74_G@eT$Yq1xuYK+Z zFwZ8=Jrm;=z)|zV=9RN}E=3GaV_BaYDudF3QTqgo852ugZhbY4I|v^6d>wl+CDR3qrau2a+?Av~>QyXdB;rvI4g==n{YX ze4iHYoC&O>M$yGc!&w-SmB+$}pInA3u{R7) zE{s&w9$-qY!Q5OiSIEY6@GzPwS$-AB#@QG&`RK8rojCF;L1Q%_(QwS}JH06$lg0uH zcEKj9vuM}?H}GN6kSC4=*Bh#bLsHf4Q5ncw77eoOFy%my9-UU5h$Gm>>W;mJyk5vh{nZJrL{GF@vZ|LHEzc#IQVqIZSh`PQkpF@{a0e5&&qKV8 zOwrOu?1NdY87xtXK5)nRdZDl-13Bj*Cb=`4oQHUlof#e`Cu{<0uY!#@6!F|{G9Lxd zmwD(*wio*H|MXDAjSp|?p@^T35sob%GePR1h);G(o^74lVZs%kHF{SY&brg>Q6l;Qje;>3&fWE3=2Q4{z&VhB z^HKy?@9@k? zGHAY+L&jT;gb)POS12b6l?M{hfUig%oG+B$UwD93trTT^1jQ0T0B55tRqOpS(Rzo5 zX^Pf+Bq%se-gU+#g{1e`QI^i_D18T#?o3V^v|Nu`tu!cW=q#!)fNi9@&rCE1C^`tt zsUroshrrRiVJCzLaw=nw^@7~qUI)2P(PwLQCB_c`Tn*a_Le7$p=XIk@a~Gh-2L#i7@8RMuahyexBl?LYAiJp++Q1oD#z*Kkb$ z31Tt1IOHfbjD@s>{R}V!_7o^-K}zW49`(Zg!a3xCbD$6veeIY1i;wXF{p7tQ%aORg z96lm?k=Qu61_(RarKKD$;C5xL*ps?eWbFk9Cu}D;*dLC@LC35ST9ZmG0MNRtc)2WQ z?3EWMX6&D*%Z$A*^di7FHtFfQ4nnN%Fsm$k?S8S;6nbTt;c{58_or(xM6Ejw#$<>}Ml1J)BDZ*F)f`KcLA z#8${*;pb~qA@;!Z)swpsPO11#PLF%Mg?nn+j1=s1;*O*F{c2JscY!Oco6YYl!o$|j z^tk+f7rye3BENmhSk?jjewTP}NbGm9@cR+m!K0b|k=KgYuRNn-M{v%wF_^0nA8dXd zocNh6p4r@mXJPYU8wT%S^A9=8X7ej6K(n~ynvF21isyiD2$Ekf-YCv8&AJomLVHn$0Ggr`p2!?HL;ahff`DA}FoW6ra;VJV#H9=Y z?k4tE(IiZS4gU_efp^aW?fx9+KCvJ8oy&$F0BJ|}G-Np&omb8BW4?0bT^BQOy02$?a5I%9K48r-yQ>Z=5rx|;Z2}~18s0}j2Pr*n@ zw;!J>uSXAzvZ=i=&I86{s{SKX#*w9Z^#K`_CFl|CXn7#jbu}3W${#TTgEj?FIXfo^ z{^0U@v=rp^T$vfQuZz6?7>GTzg_RfN{WLFH{ob9x!t535$HE+SMj968VwZ)f3`{ad z6x6IAe)-kT$ta_?7CkB<7Nu%~!1mf;-7k(l=HsgQ@ zHQZOfmnq<&XM9(Ah^wtIv>2K{5pID;dhiqK(z|wM_6z&Ehh-)|i5BGy_pQX`VVMWu zThQu7d(-;LK~4a%u4HAemh*R6;Wfdm!vqCW>{)D@BrrwGJV$@mJrm>r13#37P(Z!s zGTyBogFXy41Z?SlV>tGyn!b%LY$FTXD&>?CBU~qv1kT3JqBzCo;CS@|byv7$|L70j z_d&pEjaV%5PK`HwrcmUBIFLI+o1yN7d9EIhO$J=sVILj?P(m)sPO zRBv}=uGFf{H{ql^#4)!Cg4#O>GyB| z8>0}@sAzC)m%-q;1Qm?91ti!AEm4f(g5m;#YlK7*M1mno+tbQ869E;CdsI|hz__3- zDnv)eh+7yPZTE_;V_cAcDBn46)!jOsq#N-6dp@6e=7z3&>(;G0?^5qpwniB+8YN8n zI?~`%w2S4gnDY-vuT$rY;dp1X0Quy0q!wU;5oGPj@bEN|lJ(%!Ei~Ajbe+iX`mA!~ zfzOB`bB+Fi>Q&{i*M#cTEkAPq4?Ur325vc&iwuDVyP9#gVdZ(&54FQQZ)O0$hGHie z$(1U9#d&302gA9LkZ%KLgC+>hjzgZM#@PY@32`MS=Kx^zUk!(r2Us71;c8RS9M~=d zX_ya*CL_|63u%cPllQwG`4Iu!UPXR5rYMf(RaoA~-NGP6Jed1=mA#`!9$*Jh%L%vj zueSNFSZfmKp0N0-+m0mPALSHyDn`8v6nMJQ;_BrmIUKzfcNPXCrv+6P++}S}tqlH| z5?GOL27mvavfG3DVR;x*WbpeA5k}hz7%`^;qc*|sKWEF+wRqZi7~pJj0tK-S>16Qd zAU+Rk`SrKfVlSG2#byTo4qI#cM|X>}-NKfhYD5pfz^8`dB5TgIjv4MkE(4JuAq&jl zchMWdeY4#Ez5L4?5_WjJ?zwbW52i_zOg({5?^K{40E7dC*A8{`}N(?;* zEqX?Y2Sd+AxD)HVPThP82QUK;$l`qc8yG86q1vbC1#rP&xmrQb0n+oVYlWVX``Qb1 z3(SSGv3UMw`nO?FC=yk?2a!ot{K3(-#r^Lx1PSZpo6)Inp%Pd?au@rJB97@QZB-w4 z7ezb(MI45{Pz<^uGWl7Tr1!p)WIO?A;jKmsKl=^Q!UGsfKE{G4oUqm)m`7t<<`KsL zgn8hLw!s*UuD+H0xsIh>uKv1v*6dh0MC#H1dkUC=NIkj~e*ln}V6q&G-7Eyoo*ya* z+;_Nz!2MzM0lWDE_!f5aA1y`iwN>Ii=Noo|-h|B%f-NrSHS<6RZ#=V`U*PUB>Xdr~ zPvBCeEtt>8GT}G#bKQ6XvV(L`6_L{$ASv2>Bf|Im?}kKm8HsjaJ*B@@Y=U!*{I~8`FKoweYa>sscKL>>%N)y&+3#W) zE`DqLbZ=Xh?>aVHM(zHV1|2;yPtACg)1kG_$rFNM{spnN@@1LG+~S7*u>=x$P}GE8 z$c+FuMqu&07!XCC*GRV&8gN$x%0c)v;{$}Q7KUI0YY(@=LRN{JG;-Np&LLfe!S%pJ zt8P!63*c?Tm#GP)BIr)+kyZniFr;ikk1JVSJ}a@PExtg2rd}7EhQHh;bB<@lY4}7Y z{Hid=gRhS2Em7gMX4&NX9Fhm2wq`)mW~OYE-pnNAfllg;oVI@k`92r<#8hRry_WHP zi`E-CZQd5j_qk$!VVh!kb!Bc8s{6ahN7R7{E_VDQv0zFMf=qhn^+w*=D4gwKTr|K+ zU|kUp!iPv8fz*wkXYss&Z1Jfdx|uDxURHO2u8@#Ry4HB{r|HwcPo7hgankHu@D8E# zH0-ZK780>Z_SbaseZt#1aMk_;8NQ=_S*MIFT0_#;J(`3Kkk1z1*py%i!%BEhEH2Gd>=4Nw64NWA$O0-h0V%e%@F zGxR3Um{jwnn4D~4aTjj$-I^}|5h-uftr+C0U(H6&F#3mqOGmVU4S}|eK{hc}UcL0D zu=LV9mo+&fJHCMq50gK)YCzSUTCgf`q|!VHiWdF8kaP|Op%y~9MUA>#UV?yy8L_PL zAl?Ck4*FeyxHT>s zqL0GnArL)GEJwbh$5{~l2U~nYK&X63L`%M;?@L`PAn=v4|f6+p*WtTX|GY%7&$&+so$=5 zj?mBlBCr$-jBReAGvWIUf*8ZM1okm@XP|;uwe6jtv7i%Y23&Pe_fSm$+U*@(z^t-I zyI<7-|3Q4&fc-LyFMACs)>vP>O4NH}pcmFfIJKjX2)|R?boZ>U#P7}*4r6WDJIK$u zS*(E5Am1fFr{?$EELA5L^&eh$ygV(O5|&v_Mo)AsXoPTWWEH4a0=W}n0X&cpni=z9HF zeDm{j4#j}1klugc9ZpDZTy?*drzqo{WMwQe3rm8wzv*Jue_FcE?>BXpu5+}c>*P>> zi{|{XR`}(IxM+Sk<1}zZ1`PiqF)qHbhSqpSOJAja+lD)!&v>E1rRhw$@9*d1J_#7!8MX(Kr{qQjJ&xKYuAak zng)Ij-lHA-bXoJ~<7fAu1tq`!WZ~xym?H!~`=CH0_ACdSYT@TPw)oVMKy`(QZOtKjO1Lq(tA ziJHx_N}5+Pt|!Hs6O5+msGiiMcS%^)bE%xIl=@W0atc|j>q*`9Be2|2J*m$|bJ)rC zr1F+v3!18)RP)iC8i{52l7Bk&q^dul!lOfH{~y+q+D^*QdXg#se^*ax7wo?P9d|vc z4{vAq$1?uZc|EBcf7-^bCv~kBipL{JNo!WiV0_rDdQxxx$|cu1lXXAgM3ZRFJ92T= zlPWw>IA<3OM6|dqR8Q)PkJgF|E~1gs8^4qsePao3jTkys70yxVxSrG}Eu=(uQS21;q!#QX0#;mV zW;0!j&5lyF-^&eD3krLQIA>C{ULBU&6vHS92{Y$BW-$B5R>VK`qk%QC-+i`Kg@ z7>;G$6PvdLvJ{~={eo;mt#_Zm7N6<{Uvb9(=qMlSwN~dm@lNa`n5cjnk2Gx;UNXwZ zdgvDqURWh-FYaAwF$0$bfT=8HB!6 zo-|`B85~Zlhbyviy!;#P)G-6C$i|DYUn_yKGDZdz^_~1L!Hn2Gj6x6>+sEIVMH_%= zj-^RLbfuOsR2(+w)riHxaN@AqmKay+4v&&~z)1w3Br)2&NIvLC_2mG5uP6bZ@=uyp zbdpkRrLlTMjn!i}2_weQI?P1sS6sp`R_nLjuo0mdm%yx7!r#3I>ttbcuu~#m$Un9K zZDojFti+qW2kVGZqm!-BXG}h?tDIvX39=1nZ-m>U5f7SFZ4<}7jFu{#!-ak6&f*3O zj{=#9Hw`mOL8AtNw8Tu&Uk4~%RB8#+vSMwfkWQfO#L$=arRg|Impko|j^$M=o>X_w z=0uuaY8u#C)OV*gHyD{$gd5t_UsdWy2f!k)7%T5mcWJrMj$56?WV{q#%3V2!Yyj7* z?(1+pOu%v#IE-8`clH4SFyl`vw>=e0IF-icTc_6Us{Lt*7g#LjtxLF&l8F|r#f_Ny zxpIFf09BNKsxKEvL~8&Mt>@zvusYx%wiqUn9mDpA-;nnpRgt5h@2`MW+yfLaK-+S{ z6*j-gZo^EF*YE3v=3Ww_+6dsp0UQ`J#u8LP{65C2|F5~BM8gK7I@UB=8c=Ufryow| zl%c{aM`FFpocj@t;YVY9HW1P$=C}?32!QzVaT}L!NUjhcctmlb%UBXnJAUon>{=fb z3vP}>uINi)#eHzmO!}qcz>3dOQxybBA$6ToGn8v_U@$47bWCEA~N&)2Rk za_aq{_WuzNCY#9~v@C$V-4rCsoupSkspDkwl-a@u~3t9P;YW{}-_Q ze<%Tf#m_1DA1LdQJ`QC`y}uO&qEB4l_*a_LQcNY_-GudtgE4~9q8*jV(qqTnt{>q^|!$-oF@~Am2yc z4_(bE2@pt}BfOt6ehj#bmIMaZb0$Z2WAGie@&cF8VhD_Um3IJH<<4e2FF_$7f^dC!khj{MfYth%WG9aq~z&s15MIS4O$O4@?{pgF>APbgT_f zi3b5>;g_!UJ2Bkbji9VMxcMiPJxHo6Ux?Dt1 zzh2RMWAF8U!zV$pMHLYSQlF0&6weDIU`Mef{0bT?uGT^Rh=uz3Cbhfr$kBmd_&++v z3$qgL0cTNmFx-eh8BV^AqDH;;PlacG^@+tZBe1j}3~(qYBH>eGfBJPJc^nK+qCgLU zBaIbmhWLXkyV8=ASVZ--jsH&k5BZk=0X|Um%4deXr4esTjrJec@TzAG?#xS~BvdZ^HFnx6%{=`)Z2B18C zbR5Pa9HRLofvy|@EJq_;hJQm!H{dl;8FFV&m48P$%n&o)aTFUd5>xTFxUn7^3q~t2 zh4TKv^+C3xit|&2_>!vMQ0n}8j3L)QY#an*3@^o>B^3Mm&m#O;i$5_meP*j4ClC8^ z0&%~EG_;|@ISqQK>{o^h&rsmF@v^xXO)y%+f2pZcNWj(dQ>11-TKK`WxLOa$hbG42 zfdU!)GCnuBUh>8<>sxcIVRMp!9tW}xWXhlp%Uw}9F~E9cy?T$k5dF<6`}(b+OflO) zQVwF~AW5z~$`JBZNhdfEEdn%kbR1+b60+y zx17!v`t`DO3yq4pc#SCjr3qE%pLmYp848MAL++azx^55@d>c@c;czo) za?y0-sXzOKce{;0DdMS@Y$ZImZ9Mh8AIa0T;JKR-PrYoJz~EL~^ltwwSVttD`jyqv zatAJ2`5eNQp6a*%3M2gm7cmEbfres6VuO)7aWx&sQ$MY=K_I|ASqgp&cmy1Fo2O2> ziqmZykTQy=p8ur{5Bd*SFHMo98F>w&l?}Eh?>3^#q1KUnouX+w5isFhljQfSeYO04 zk)1}mL2&0*mI_-M3k9Q_RW6PDB9u#8QG6_lysT6I!&AwuSK6Y?{1UUWT_pHQIj}?8o=uU+A2V#vuI))O$W=uQ6vR()4Xm_$|H||a{Ej+U)Itlu6U(x5^RYOyUgd&W zGQMzUf#%)2TA+Op#*)N%H4Y?^IGb~*O8Mt#z5^d-htEMLA&8JTNjV$}Kn`!{$4}Io zqs4o%-<;ucDbCiLqa(q16Wj1BR2DiF%tLvwwbXhS=9@=xYt*j(5Hx@W;BaiZH8ENr-L|B+5s>?3qi0uYYTzo8ON zRBmy|M9}Uk6Fi_;$OHzj;u7XEEC~sj#Iwq`1KfE;d8oDdcD^A}6X%6zG?@hY5_R}| zrYeO>fS)nm@SX*{*Q&<*dSW8ATp$YlAz#*yGCls9O5B2Dif9!9&RSrmz!f!md!a_J zIe?`XkLkKxj08+MEHZQzS9@IS4eyDe6TPj9$a&Wg@BArAyjy9x5YY$r7t$Dr=pN0_ z|8^+gyQeB{0zFWM9|f}o7_C|WQ<|pX<*e9;w zLTM{XxJDk1`tH?O>d@ZR`iTP^?cbe?^|aVCG}bKq)2O%Hi&*L{6+@SL8)wr>NnXca z7|J?z<6dcC2kM50#om3qU?*l_=jsuH9k5C{!n^@X7it0xJGP_dciDvtNk8DuYgz+AO@C+obX;_ad!CSa(EbVg`hN3>JdaZt<~&cwD4gs_mA1)c@dE8~47OknT&f$evckTko=XrWVf>Ig zTXNc~rlN=_@)yNut**u5K@7`3aIGfp#A4GNCP-Em=~|-UYGx1xqs%1_0YXKfX1^{1 zgMbM%NC)6wwd#e3y8~QH^7(VsAKfapg%~X)$78G~p4I02!S&=N>hgY|J%2U#tl%S2 z0u$Go5?sM<#_`fiYRHF5z+Z4QSg)~VWH@DV=1-q;Rry{`RMn`XmZ~Efcx>T4*vv;T&~9V(c7!1 z1O#B4a@xd={1B61U^Nnom9^;z{8jrM_N+;w>`|0 zPt`MH2V8z-aUJgEjDPcsFq*~qsot<&{rXk^lBeU4^9n=b)2*&|V#9&jhCoaJyQF|N zsbhNNh;vRaMKP@juoIV`&B8SIEM|W+MwA^G40rFp3PMSbyNcBbK@;)UwY<$?|u#KC(S z#zgB3nV5Pg*V;-?#uCLjN~~cc))4Fnn8V{H1qx0V>T;+06Z;5YK7Er*Wao$C@8Wts zZaqeGICD?ZjQm<3@r{01y}asQar{gk`0rac>E<-S^Jt!ZRGS-GKjRl=u^#zj^x zfcLhTl>^M|l}p^0a=+)|2N=0hYd**%o})n3pu8Tw3~wxL1rbcw%V&A}I^=h1^(tri zw7;&M^@F~v*U!XH=l>V%WI77Pd*gplTmy2Ullc4Dy|+O8^hTts!)Mk4IJ5!|jhOSc zM=Sl%B2xqDUYzZ6DYvMXSGrxw@^lRHUVBxbzQ+L$5M8PmAOnuh(()Y=3^fZF!WN(U?RA47%r-12n)cm>zqI0~s~cRx3yAB`-wO*zDRSt6 zAG}Z0vy9@W7c_eCBlf=rlBL4|tc*|a*tF^|eF$K!Le`xHEHH%SYT5Q$PI*d5ZhHNt z>&mvy+LZcBH#TfTt*zbqODBLT%wgp#%P^}{qMZ6m2cXrdzx0@OgXpT!oMDg)Fn?rT zf9d`2W&NPycxl6yWgdRlT}}12?|^|=@*LBx4HE-vd@;Op)iK(>Fcn*###U3RJ6mN zFDjxIq4K6VnfqzQSDy^62ncOj4mO&AZOme$5>Inwj>zY!w$e4)QMzh_(OaLEhoOU< zbfp|w(IRoyIxd0&mLk#;xW;y>)vw&^9uPo6{Te4q%(z!Xz|wfzV#IUX5Zy17OqMv{ zdhmJKH6!|Z@0dJYi>KR0UmNiZxsXacb1wkh&E&ba6QVohMWNqaanY>eWfb}p(Hw;1 z12)`~;HS`h;z73f)VSqFG(&9+#ZrjoMAh6B@iW}I{N=4o^tpg~?M#^!yhP;joJ{n& zZC-CHk2xbQd2B`GgKj+WGs9j6V%Ggc#Efv#orm_&g15c+8E7g~@>9pp9E}~PiJy6H z5^zdsnZ-tGxcKLHx*`7p-SWG#)0*7%k**%!2dhA68jcks{ zi2)#`+^1IG#nrO-bMI|9E-O85i}2@(Qi@MDOYzS5^9!$a%AbQ(F9+1@n4@sp!U}(A ze@kcf8HVqIVehvLFg$*FVoMm%H}lCD}Gp?AT^1&e%-bOu3L)g~hD{Sl_m`nYEt_#>6&bcs&*X zR?C4rt0__u2Z3%8+e~+XP)D|zq4h$CyW^ti@D)(K*k(@XEiI#M@ApG&>8Z|pK`8W0 zTspVSB**X4pBHe{vuw5F%FOIDPnX){3Ac__@FYF^OzU!+JoWy*9ccg~pWfder{UQ8 z7U1}Nf~eE=cm$3EeHxCH0!I_+ex{{EL~asWa<~FA^49sdMX9Yga$U7w;vr!>o3N1U ztpj=PVLxG%w1lNAG8h&#?hYQ59|!x9=rB%ni5;I8gt&AG9n1qekzGB|;2>{FCsplr7|qR}Nloj)!RV7!5gRtx6B(jkypyF^+VanWG9k}W;e z!GLwbr}A+T$Y6qPa~>3D$0#%}882>o5S~#>>K*n%15(HP`GBe(ZNsGja53h*^z1>e z#~ipA{#7Y}ShQ~?mqojPjlWp&7h`391$VWLFPPAcL&-3|XJMZBi0%BI(gVduT%v0w zj=?;R1Fx85Jb|=bbP6WaCckGLB4PkT zgge;zJ%?URNa3@r{2pDE@kg~4-fZ%FHg>_EPJYkaYp6pIY^Wp0swP_j-jkq+lwf|w z;MFi>vAR>52M*(bB9tIs%oWgqLnZ3y6KIX|B=l1D7`wWOanLytJ3wkgvT*Z>D_RQJmDB$k(?&;*27vDB1V%!IQ%O0PQU!Umuvq{yLJcUoIAw zxC|G~63_P=X8CQ7wEVn-#YTs-rKjo&@=L(}`Scc%uTQ^}nS4D3IoOyw07Cp9rl44Q z@^$|6Zal(2%?N8}w{i|>IyGzP8q9_GH`K3J2wmzd4;d1m- zoqF&b>d+xMf<_q2;d<+vTUm&aNSU|h2s`4DIBz^9`)aE`qmUnBvCPRhp+Z!qD*{gH z!HXKei<*!RhUgb>J{Z~&4Wmy-@H5^|lE2VBbQ7Fg^9)Mo!`70A+Yl_NDO@7@B-+Gd zG+-VW3FiH&5=UXky5d@M;j2Ev?y6y?D8@e`<-0C_O47NiIWyoBz7>3DsQhz}3k2 zxn97M#ifV`YbKbSbO$}V>s0k`+hQPXavXHC6=#e62Q&x7f#Qkb(`>h;Ih!hWpR*^ATj)mr>kpmHM8mjn2p3K()c}hcD4-2%z1H<6+*(- zA_yXcS0nUyQaL__76xX%c#4TKu8b?yYcz9N_Y!F_ycdj_l(tOpd(wS8d!$~ol~nzJ_ceiNnG&{C*d3eYmhv^>cc+pKX6 zYGycQjTVpD8NEYE)D8j|JnX7JE`3PTv)G5T)coW5A$G0&=UhBsuE!pEQIY!YX&3S9 z@0<0V{&q{fFSh*k_%4sXx9}xMsz@_w(p8_e?^;T#&iuXi{m)hAGI#cqoOIa?g{YJ; zl3ka&1knU=@%!XJ2J^tQz9&(G0BrD(+^7nrK zpwP++T(l^;1jZW?B|jt41XQ#EIevQgcrsgjs>dRu?_jn=0ki7v&0$*p-kOIq*&i0D z@x?MpcwgrJ-iMxaL8uh!P9;~#arR2J?~#$N5}Er?v<;6SKNUemJj}bK%kXfCe|W?n z+_=1K%`y|Hc#CWi8Fx+Ki zcyM+w+yKVLv5b8MIt&Pi9p;R9i$!;Q7rfoW%=>>}zeu30H#{6SKdk4TeewZxBIUi& z=gKaWSc!)<{eav8_U;AMy!#@f+fZCnFzPcDha-Ruy}9mJ)1KgTdGiHa?WA?}L7iSc z^gyQL`p@LkOr+qkr&!LKG6Gs?*I4nZFlKymybg5wGtMBftoY*hx%OwF(~Im3*db!G zIS%uOG@8g1;BPuUG89C%-|%p&n*5ApB?lq)sxTFpnG!6?7%WLG#yqjPrwj=>4p)P& zu;&b-wZxXHp>fDo36B*84o_oPzIFeWNgD&5Zd?!fDO3xA8B~Q2NC#%cMS|fCSae5< zHTRRZ`M!EZ`jBqBqmLEEW4dt6>eUCK5kzvviroWQDAAml+=^EqHf&qI&f02%rQ+ZL z7X84k8{@iL_bCQ}ZZa-C1Rf5COdt%g7FtSB_1Z)BaM_rUfAKaa`Gpgb4@7a}6_hT* zn2LN(6gFe{6jVz^Di31JS*#;mJQ4SnLBLN%RgO963b)3M4vjxs@TXSq<>f&&agc24 zvoXPbXMzLn`3Xiw3(K553W>aa=uC|=LKB`&ADMiU(SU)vJ=U(AlKiy;TwE3`b2v6HHOIq@67x=Yo@u|ZfH0rSiC(i~HZ+8)e znTf(|W{lDVLoYgCG2bFFs(3#?VbtL?{N1Kv!Q3JN2i{g%gmtxwLQ4l;`t!a|efX%& z(Zs*nnLOM|z?f6;80c)oXM}=F9`4j4bT;0pSJ0xm*QF3#Go4xmr;Y<210$ouFa@C& zsH}DB=kDTt&8#kT?wc|LBKuqh&=&RnJsrxvO|1)k;{BZnuf!@**E+RV_I}HF`>`YA z6CLh)jXSE};K->Qn3ljs``g2^1R#!vsK5)Th|y0el0Fni^c+3-4Ex4+j^_tUkqq zugffm3EXhh#b8%W>g!s0|6;tq-n_p7JVzgK2By@eKE>P;h>nrbO4cA>8s!#FILmvFF z{?%eHfC5@J)1jdiXk+Sm@M!?(qE!xnAbXdquMz}M?raIjw<2LLFbQjO(g{&kmyKb1cr?(QjynuzjS~n zpSxS6uflC6N+X45CAd=NtF33{nmgrbV~?Ry?eVM>`%$|-a`a9iJRaE^-;`T9moMfy zEI1Rb3J>cu(G-5gEqZW97D1Nb5Hc`Vh&EKL=qty;=&cczKS)#7J;DD(nsI8(miEFs zL@pKKqP6g^VWLPJ0at6PtGf}Wu$u^&*kEFwd1X0SkHPO&{ExbdQST1@!WHpEXZ_R^dj!H9t)z+ zWK0hL|#Wi}7gVz0MVs|G6t7vIYo*Ql=iVv|WDqWz zfyCDsUVq`wA_dAI3^cD_!4{vI3G|EkQZK7)KlRqdl(VyUFNx02Tk5*NWs>PopN+=x z6>1eS$%A%!cOAbm>PzRmr9O8j<257y(;!+4#zX(Mb}^!qlzgG_nwJ0Rw4v?cHHH7_ zgasbF#`vLI*#C6+Up;s=uOMED(RBPz)h{|g_4uFuR^tJx>6$=wb#3jw<9J&d{-^qX z2vGlyiw5f9Fx7}s@?whsb>*)XP%>&C^E)>?k%6Xs7vcd|7UM?z2Gw*iI%@0GV$7iB(Gok4%cJ$v z2Fyn^gk8M$Xx)Q4tPGlQ22Zq)oxfz*!)V3heX%Bb&z7X+mR_^HqYyvDHq3#hMe(T~OOSR-F(>HfjQ zCDbcmnkCKU&Soyn$VjMFswPNTZf0UqQ0>YAMNfhPs&|3`^~7u9bc54lsOMwszEL|2 zUo}E&>+tA~@ZXyhSuRaixvXsrN=2Y$Dt0Lx-`}V@t~L*tYbKEvMqIyPC2j zi2UNYLL-;rqG@CYm|2>|p%%@#N;j6+jlcc?GT|{iW3Q6OKEOqD`j6RihN}4|oLi&S zO}wyNS2eOmq5WU~8whQGj8MIbU44SyuZwldC3efhY=NIU=Oo>7vfVOQ3SV+PIl0E> zrCht%<_*AgO8bPw(-vCxFFdVYxkZZ)`|zFFTy$W>$@g&GKTiGQK*0iDTdE*9SPu+| z4Cd)J_7uG^{%1w}gkU_?}bZl?ZM=q3(Ga-}g|&`gwV7Tw?>=$(!y zco!z@Mnh$wn>b6!T(q^&O<7j??u6OYTz_;99zn^tI4)9msd=zaZpAn`BKLdw2V%Zv zqprk5xTi}6lF}>6Fodc27drPXtz^tsbtV6`QoWu^M>CyZd9MlH9qP0wO|~G&(-w0BaA3QmCx&z-Rze2-xAzV z8(Z-v7h#TZ6Xv!c7k9jb+n*dX^-lWlZ&MiE1`lfrlSs9;^aX=sks)U_b)HRLnd~QC zBYdJc%kPGu>-{Fj?urjuyjlx7Pcr0L#%P3!4vqX+ok888u6mF98B2!&(UH<7se&_H zorpfH$lP=IGEm=}h>@@&bN83ux}&Z%c@HpOsG8t>TYHiz zz6Vg76Pjx5TdxML%ey`ODy3`qTlLoLPU``Deh3~lxjF870DE6y>H$nTIz>Hz1CX#mbufc^0E0ne znbZT=3kCBmR#kb!$My1!0M$>4t01K43tL0khQTqCsOjOqcL_BV12xBemf(|l_` zOo_Y0qwUoL=-mHe>eWoW6>Q{RF^7#vT-1brlZ|v1zc}<6mz!l%;}=JP`e=TE?kOHw zs0MB8)g1e`dq=v!N~Ud@3sgB1!Q;7fAO(rP-v_ZA^WmK=GIowu0&6mB_qJ1mL zPFA|l2iLnsRISlg$Pr#EWW_8%YK}-Imlbjc)VCm;htdT`WmzE?@L#k-;)n1;k%*CF z#ZX`jk+h!_;!)TK;$HiSKG@kdp`as-(lMbt-i+CBorbmFuM(+NlGWLv;7nWMc+8>T zj{{+}HX?Qn@q4URU4n6pk=I3iM?5HVJ`O)kG|?t;Y^zGt)N9)s7sM!-%EDk90y*W{ z5EvbuhtbDXSzddme#=B!Tz*wzB4A$v@iqp81Y*h{XU?`Xh(@O^4JB&p9bA?MV;wP8 z%@Nv~xsROseE`+=XioMuLL{F}w}|8h*mn{|X!#$0f*En8-Et{gxHbHxNyz8-Pb~5o z&6b|3XQgZneYm)c6mHK`6BpYRH}^7ah{^sWk3BCKm25n<_(m-vY!l*8_LRzgsL$^% z>B9xKG(LH6)HhLYRXRK$VBYmMMM?ZzM*gQmt_2AdFTv2<`;*27O+sJt>#eQivG!JS zNgaRJ_Ki9e_~$2{RwyO>G+@Cx>smTHYSGFv~{HC zbhi)?>zS4V?yfWRKNVgrsO^P|X8O-G0h`>I9P@#o_IU7q&GZ+t1^G`i3|!+fds`;| zX^v}4os4hSei-(T7Q9v6vy-4z2-!~GNXP%wTxFwG>ND`X0u6SH{}%_1|EW;+Ck0|= zexJNDat8cQ)BKp4sw%+tkH?Z=+Xs~}$oW^wyl6Ln?(BQ`FLHkTz!Rn-yUL+OH%gDk zQqbcD#4;a`g9}!x0q6s)w;sR8_&XR~gS15%&t;j>@vKJ%+?&93L_>a<(d&mpknGbU zGQgizJ{67?5zgS2`G^LDy~8Tl^SX3@s^4hDNHkvreBpjSK$VC4133Jax%}8h$WWrb z8}1fl2Jln9@r>3(3i#FJsq$n!;fN<25(S{LY)Jy3(HmbNeUvS-)8GUYR)w9;QctoY6kz@P0w~t*?;8zK9H{3$o#kzx`eJ% zh#|*Qn2efF2e+g_CmFvU{#-fZc%Cdktv09H&7vK9ljnZCtVBJvgUmbLDi$QE&7H1) zXKkg8c;U~QdD>{tGiV32b-3a-1Saw#uLi^u594pcnMGe7_zEFjKGx>Sg$}g+xl2zB zsut9nkZrEkahyY^a<8iqz@6YnS^@?G-r5`JhLon(hf z{a!)PS?QpTPHb2BjAQZuo|6K>@Eba2aIk-57<9&j5$kX(Z7A$tF5uY~7Y&}p?*bk@ z)g%Br;Zx_~LJ?hxX=0vm+oP=>bJowpuRvC7uFOQgMg1ov+ROf3>$e%%3%8kRueOPA z&d6SPau}QS~9(gSM9Dr|* z2~@U7KJtD5G?Ljm3$n@l%!6-nBdYClIX9N}s98)} z@k^&)lZ@Al^v9p30I$ABlJGkBMUB_=^vAv#=#Mv+d+;hSC0?K1a(KO}y9cl46~rsa zEFJwZy1)UdM}K^?%mY-@HG%5tIz#=j^)dnKJGf||UImki0QHZ52~f9QWdU_8TYPFK zkc$8{j_-j&C-fpYLYtmHzkEh!`r~;$1+V(mR{C!``r|`ad+^Hm|Muf0M6ont^$yw& zqB%!Q24MF^0KbR9e)_xyY^^ATmgZQ_e_5L2l2*=^-t=Uv?FY~uZ%X$sn=ehi*ZwB! z4vTTIU+2f=F+VO_-B%|0aYu{YBw8zX=PZ=FJt=Z=X{oN|1t&R)@)w?g{kd$?hqQ5_$tGv%_?H#nLW#}1GOzyuMLFEZX$Qzr)1;r)`FCh3GwfTc%P_U=VP z60+vPNJ7!!;{}l0I*=*96!8mVTX0Vyvgd;EDUU(Xm5I*lv(-`E=slKK;|*L~F#I?! zKv|5Ju3Nv+ zfFZV8XeZg~oJ*yHT6BO^Rx=DqY;3`kW(+m_8*XPoijHcH;};XvYLXl5aE!<@0jy5e zJIo~Ona~E4HC=UtHdCB?v5@5;Tr^q6-!No(VS|um8N`U*)~#R*;$JT}WGQ3OWqP!h zE8M|tg`io$m5=VtU(n|jb-7Fn5GkUP)>%94aN3yN9}YSTv%)=%3n zAbkV(C&sZ-qegt%y67dAYL zUEWZ=|N4yWmXaN``9F^HwJcs0b0rqDE~^~JXrid8&hz80y#cn-fLBGBE+uvtQqid1 z1ZeO?Bc4F`3)8KT{O&wa8bEvu6pd2@*r$Wwm zlf~YP{U7ynHqK79EDTesu_~)42p@V97tM#pU{w-6)r{INwJ^E>=NL5G?LcRstHyAOrQ8T@Ic+XzpET4{l$w)sv;|hrT z_wV3}y5cWdp2|Q!z3^EI?^OG#LNL0de2{M^{ptGYhrcmu*xpaY1g;W)(TG32;7eWK zY4FXYB?QVQ*LON#!j{)h$6O!)?2n5E;D<0_h<snx)9loS`aQ{i%(rSam(wc zdrxsew&VR2H3GGq#RJ1yZOX{Lxy|Jsyuy?;>$~}0fk=641R`raSp^(-fP(4-^y;nL zN>N)dAPi=mT7Y5}DTE`J-4TwW%emZBi?Is;b11PQeCK>*2}e0B5?&mXlNcf6&~b~^ zzvj-qN&dY|G!X{V$`?cy#`&h|k6=@k_7U0Yro~!vcCEN7$zfG^k|Q}?@xCQ$<@v${ zQ^)(3Fy0rzp&{s%y7;>X=NkC7}PQS$&88s}C=>DMss$adzPd=n`Wf4O)b9 zEhY_w0`66WJMq$WYUX1?8L~anzp*4(agjq7c*bL~|B=-iqoG!iMSy|tS2PJ(wCWnC zAj**JJeqIdbCHT7@j~=KFBBwd9%@!F*gZS9T5EF94y^o@s4qH1pf9@=q89H7U`x`f z-W(@NBkb*=0wjh|X+$MI78>cjBa=cUfhE*BxPB`Jkw4eblz7C^-Lq!Lc(m@(pXBI}o}-KL2M7ie zELBL59gfWJ#6rO5kvj!)JHjERS@bTj?BK-Jch56Q-y1sarUzzIV{)5i8efuz0p*73 zo}9%5y+RIvYsR6V*t}F?q!^yPpD41zO>kDI8oGf)2L*>qTc9vJrctDWy;JBL?s#!E zXOayktuNH^MGl-Sdpndbt?!NEqDFJ_&xNLNDqIzR1Yqii=&sI>mas?}ztF^+s+wCI;`3c$8`lm-O_@(=T-3k{(`8(Rj(a5O|q> zOA=mgeMs;!&BjZq3op;1xO`Tyo8%cQb@}$237TuqNdq*c)edNk#}K%M^QP#mKNmFP z&JfTXfr|#sr_UPDOn62>bNMn0G_SG6r>+HzI{^*A&UgC>U} zqHZ956j8V60bA68IbtMB*bm0WPWT@f^c_c1;8n9fehw~L?1{<~jMnf8_4er$e!7;Y zKPozrh`?&LB1uV}j`r^Hf_ws%rIGqej5s`YcZ$P>Y=F+enL%&trg;T_!gp?sp&E(T zm48O{`;9&2Xok`2$D_uWD-$)iUTmcBjEAr-3|9TtHMg3wJ=Z^M9PEY#m=XM0f@@25PP5a~*-BS*R0;KsT9N zzaD2VSu)JDKEXFS5f51ptXCy`AR6VntQC7STX4glvMPpPz)ftd{s{I`8QJW)Xz0I6 z{O&4t1~Twwb0wlVPd_IV-kT=Q{qd9f8HFzq%m6zpKgC~dre-etLqrKL;b^T~3>;*v z_)(A5x2=x|lVsSO?p?@Paz@ETZh!8te92{~JPI~)I0hmbF2{)8gS=?>Mv4B@Rz;SA zEK~3kum4|zP}Lr{R?48J5zkeI*Qm;$xd7Gr?KSe|sBc0V`tZteHb+UT9@-gN9yMGH zc@zorleMKo;sv_jEAj}(b8DwYxjvp-LSX_pZ|X}~FdTFBSr1pmvrP@L{ceoNlon{K z?T6S-yA}iRfYxI>!>rlh z5igg6+2H(}T5T%(m7Un<-@L_t(KCkgi6d8GQDzhdBQpUg-^*9X>b78YTeS}?i$1Ux zRa+`Pup`8<5^4gMxI=EmoFbTMTXqB7MyuuM9 zjJs5ET?^6^{|`W7Eg!Dr7vmxI^k|neDMfsh_x_-o{XYS4Ar$gBS5R|3aU-SD-`xHv z)o`W*;I#Oksqd6G0r0&T63NT|?iBv#sqgfw<}=nZH_9@nuJ82GB3I%;y7irYyV^mB ztIke~daI;E2DhnoAcrCMT~@d~4%JzGr{2>utM7FGcOL$fQGKW8)1BqTeoOS_CZR0N zpmO`c*0Qp=z%Z$Ni2*H?K>hv5ry_JzoB^&=fV;BsAlL@%KX-6MsH19yN|Jj5xAOZ57qocADMK$yt6wvl%E}7Gv!yK zUy`y3czUSyG*}ME|3@|F5UdV;={48C!1kIFQm$d<81lFC{;S3e`{fyG8X^!_)%<%^@cl15Ljtl@F*|5BygzJKRy@l0dug_#~vIAhI?5xva4P#!>Jc|rJ6emE3}(_d(N&SWR;G><+*+EB6SZ8 znp`C*j*Zok;@ELq0dZX%`w2VVQFGsmFfnie9&v`=`=Xe&<-fDrQC34WD_I0|DXjT6 z!ZBdg*5>2i;5QV2q>wKNtxh-+32EA1Z79E_$-$pnZ z#`*KLh9SbZYC2+)DcTFvD69u9>IL|}P*L-8E?`)SR|Hoc=f}7q@O1{yk@A{>*m};v zP5h5M;bQa)o)8CAl>QBBMO1ESe*=96j1qji8@7*Q08Yvq18hD4DSDr1qZWy`oN~&5Iw|?b#yb=IT?O|b&5vdpfg6=(9HrsNv zR>9zUcA}bcQjHf|w>rMUM2oE)?8m>fvk{&RNL_4DgL&|;SS3t|5fdw}y=c)AE-Q-6 zw5Ws#>?I*bRTR!VfKVNV0hf2z!(Hfdv#6dXQrr2J<{H&;k%NN|jvRCD(rv zQ<-=!4x^CZEsC&gE-J+kIW>287dN;K7LJwqv2XDIK&O|y+*}F7s-7c3gL^6;FBo0U zhDU@9@5lC4Gs`1kMj#n_K0XD@>;W{|^r-7w7upJx1lXy?Q?!sxWB(Y#a8(hq(1Vr- z5>-Q@z6wpNx&x2CMt*6%Pu*~`O|^zz=MYa0y;74k^h&}I39IQf?4{S75un%BnY_hC zuTP;O1zE1_0qp!bpo%bRi6{dkuT z(!9#D*l;x~&QG%obtNu`3@HIS-%_5;t3L92GWjj;&$EnZra;(aot?Ke!5PiDXsGN@j>ARo zubW|jNSuL|65Z0zZh4z6MQAw{NiEpZoP!KUy>nT@mcHt;pit+9ypTH2u9N#l(QYiN z%))yNb!r$JYhS7Wqvq)Vyd@9Y&}R$2P&E$Idz>`rlSW5Xiv)AfC!Y<5Eq|w}&|v!T z=rcBbesiLWKCwNoXAd@YwtxQ1=^(~P74CK0cl!Qa_BU7C_MKB4e?!3dr&`pj*SfYb zn`+$21?-`idbdsVQMG_V$W!nYzKxd%(PZH#oNUCOLUj;>E$W@)I^6q+j|%c*vy406 z4;JvPz(s@alDnx413EuzKjwz}EQp=V7N6=dtdp&iW!#y~@54f;wC~gm&CI?t{!>E7 zw(n$=KmE{18$Gr@)+7^c8+g3=bDFdJy`25Xp#al~*!Foi`|6uCXK!GCHfMj3{~~7x zOAn6)TheS@3S>o~bm@YKlC=fCz6O1OugCB^yWw{>>WnU3*FK-A(*+BFBo;&+9-W=(I zi72}iXWrnGvecvEpzp@t5dG3kPZKpuw6(r;cBiOXgT_&J9hE70wM zi-ztKFkCdx!5tHINV zbmynA^&pi4nl!0*UHPY#dbj2t>Fu$HEk=9TLd|F;^$tpyIQ3!6a`ub|g8irFuQCRl zE1)hPyndtn!%mz_$sg_fytukFr1vtG#)CQ4@d9pr2P;&fcKBE*3PY3pyp!FUQVM&O z`+0TRaGRn!%Fly7u@_G!o$M7U;!|c)D%8z!^XT$WJ0uJg(p3W8~>tJk3L% z_;fwrZQi-r@dD$I61*LQi^kiRw*znF!xtkn2BN+k8HHNZzrhxtswy&k7_&@*6o56j z0G8+|UZ8kHX8zuy0a%~&|?$^3CmeV+Tj}q-JXXP;f%zrAl z)Y`r-4w~^{0Sp{8Fs1XDF72SfKf*I=gOQsHgmKR+#6|6jd##dz@F3#vAJu}rxYyH` zsk`Ji9Olig$cO6WhvS`tyhj|%yT!a^aHEf#K!~|>koUhYaByRIWUYh@c761b*({SJ*!A*BuG;S8L zrKcJXyaFU=<06(T%xr3q+)C?m3VPQT4QObG2qK>DjYe zhdO{si@%`oCYg52ae&K5$^2#D4FZ?5ZGG*Y@`zG&SLXZdMAgTlX9rqEW`0)}6-So~t42EbiI0DxUC zbNB^ja=98%O#tN1eu>s_0!2fyzxigqYu0jo@D^-{OTNdEL=ik&H3RXIJL4W` z?sC*kHlGo8kU;8<=1My7wmVBoj{;x9Su!XbI?A6Y28VM;O+GSCP$qspm$+(GxAJVJ zNQDb!ZP`vsYPQ3vHf*PHfWYV>Tr_hTRRb9DSeg%R5$Jt7--6x%woqIhHA7lzuCrQp zWs6U}2iyxM!kUEuiz19DV_@r`}nEvGWq|c(9iwxqE7V`mbd{WMr zlIfaWd{SVb6Q8sraz>$seh-6^wj_;4)y&O`Pr3;H9xFa+!pH1A=W)5X;*E~0F18A)Q*T_P7_>?gR16r zM9e@|F&~SIaR~vZc!wxo@IdJINqlUQBf`!(f+K>TCq<&R6`j+R0{rSt9%mSeL$OOr zqIw?p+euVgz7*hwE;?(U&Pt)g>HGlZaI%F zMR=xpj!@K3H(3+~=L($-T?(+r+*7ST7#BJlengHWDDPoNdo3agZX}Swwy9bNbs2a~ zc+n=A6~`Dh4;p>D{yE672Hdaa4$?%JhQ9#??XpzC>%y-aw>b%NVL<=dYVr6a>T37~W=V z&^rCOt(fyxWG!a3FjFelJ9accAUQ+$5g1Uuei}#C$p1Q zJ+R>gHd<|ct$oA_&=~KfBQ#q?wQ@yhR#)Q9{EsTuep1GW<-h3Xf6NQPalyJ?Y%ZE?`^?mpCcb!@5BGA zClxpQaD5}D4)5fP5OHX|6GrfbD{?@Oh#qkIn>qmGpQrs&N+A$oZHWrrfJDehym%9|9^pVoN5QC1- zN8DRv28e3LH$!um(r-882z=sT*ymz%a0Fhs6!tk>U;ef*vZS7UR!eWiJ0PreWH_`l z{6TxpDcuh$AUX2KOH=DPVwRBlm$X#$X+fQ8R-_(69a>=Uw!l+*^zUiFxGeR z|A=ejN(1z@{B3ECU@@|g0h{)ONuXBqN~lesWj|VImdrQTB6A%hSB^CcYlw%7%U0lw zG570VK&xGqE6Vrcg|)mnvhZ$sW-L~0p$s8Bb}beS|D3TFD6A&VAt>9LPxM`@3)p9ksM(_5=dLGzJ^;I$gO7}Z48LCPM{9q zbT^5JWj)X?Z6ji@#dFlK+mCsDJEeceidV&xt!kSWWty5WO>s<%9(#h%egJWl!4Ef9}0b03-Jj^lAIA1*SI^px$A zku7R)z%3)i2OLZG0fR4XRy@wwJw#>>#6`=@k7pQ}dH!^fnU~D6Wae_V_|){hja@^S zc?!EmTJbo(yhWX@Pc}eawHT8I!X@Bd zw)0?ZiOJbEftAJDn6ZeX(dQcYRnyztOhG%4a@UZKZOUCw$4C6oW%BO9@N|D-UNEv9 zYFn2b7YuJlmz8du@&&deH|;8Lz7Q7;=bY&PEn^$YbxX)@`FR>ziqJA`ssOzTIgeU7 zT+Nog>XtoayA$Ds+nwulc+Y6&afs}A`D!gY;85pIFh$5s7f67V{4goLk(I9Z**|Hs z(~-Oh6{%P1g(C1?iKp>I9;fiHd*sd5eQk=+{!W%pbjU&MCy;p?gJDVHw!EG@L8wbr z7ugXBPM|YBFi{}vgeTBG8#Uz`+Y1w3qhUM|;be+1uDL~EJTa?$M~M3}Oz2cNdMfdb z=1YWXmNXGDO!S#2|3>Kjk_(Mnsp2xYNhboRWiqCy@9_DqB1uCTAsn+IC*cS$T3|?*= zMMv;YcCWM8ryVbwIlPV0LkJEI>N;@ZkCIYw)p0$biNRO}Itz!sJ8ssHV2HgLk-P?yF~;W$y%SBe(ed;e;+>`AFlKY2ufFu8y&3WpyJi{Ez#$UB+`r%lO@B$;L&uR7-GaR6k-LksuxvSBR%#Arim> z`#1JLy8Qhnq03lS`EE3oH{kz5P0Wxf!dt}<(0q~5V~vc4+qdiSZ`iFHaD&*biII^o z#>&4DrguWB?3&J0V9{`nFJcECg=zq=C|0>e|s>HuA z8W|~$K;{SL`fCDmSy&+VEG**_{c6U^C0c?DlUIsx@#}D#Ajw3;iZ)xE+VYWq%J*R+h zSdi=~-7?Z{`3ZY}5p)Z5%OQ446I#TsuE7{x%n1~67p>QSLUG8#++;QL*ab?wj_{9( zRCr8vd6HnQ*1gq8Pf_2k6gyR~3Kco~0R-hZ^s0h;y6Fe|0og4&+FX0}qts5^4}`ds zuKmFE=gNMdlX%cyp4rS&KvQ-WZ60!`gLu$ik0RgG{!LF@-+&$E=~_Jfr^JJnZZC?{ zLAYqe>Afp}1_s!idATt33o0!2=sC9d)RdjfK7cADAe=J5rU+k?(!V)iugv_L?>{cQ z7VpVC9<_a_s_+J4f#VXrFPS@)z)5)-J%~ z-*la4HW81CDae}wDIP4FQKKX9b0-!*_jEL^$-2Az(bnn~xGeF{{y@6^0| zkbrR<6sw6Ir%GL~PMs>8#rB+ZS+GYPoPL#tDtDTM$#hhnWm@CB53l~is&s+u) zC<1Zpp{y3cd&>DuK3ph3LLgmraFlvrJ9)Ye?0B(> z!Hj`#rxRvPS*7D?Uv3eI+=h$Zc#gZofM;b-0bDIE8gRqd(o=2p3wT;_A$VltiEThz z);lj*-}Ty4{Hi)P6Z=a*l|C%|7cUe3n^C=+1v}elNz;CZ6Yjy^a$WP%r$@&M(`e#%hd_nKz z^`||^+3svgefRro5&yzMLgg9OcmEE#^H|yG^~S zPc;F5NCLj?C_;UdQ%#~bYF02lUaA7w^{T}1L2U^!@{wn1i4ZAS^^#WZeLeOQ@{>%~ z?r2?6Y^qj$yRB5Ms#?k~3^jpZzV6FgJFQyvy4(gP3Wt^sCZy{oscO|xICvMt#oe0AbTJ`n{R0dLJXq)V zu+q#{+h_q4Xy4xAx<9DiL&7;02k#N5kqK}#Pp95*TF8Mhemn~u3Fc93QEs1 z{i_vK+h_A$gZl6c$dl9ZAkdmvf@AV+t#BweL8s?Am^4hpX?@XYuTBElgX{rB)4ZGf zf#NaS=p*d;NQfmkLM^Pr5pNbkisZbpdoB#AuUGWh*!%b=FsUTPHS+As!LV-;S_jut zKJ{OsKVpO5cohIe6%ba0|h^zHv^Vxmc0$;MrH(6{?R z0&aIwZyl3*$NKbO>p0kY8K;aGG}(kM@rD7DpTZ!GZucR7LUs(Ql&B|v{1w&_m~GcB z(+*E59kPO@L-6AZ^HJRgUeHzizXtsH2Dm;8t9VMFP!MTXez;k^vo-0Ap*oEJtVI*| z2>d^K>QOVn4+Se;dY;@rL`Fgb~>@kb#I84;b&RV5?L=38S-D>iw4Rj1P{9nJZ^ ztB~0WTr`-ozzpjpfB$CW$vi>RcIz9Ivn$i7rs#^y4M?yvF-n)ch;6q9q0lYJXNiZ=0~Ry!$F-Fu+Xprl9fP22wIJfW6F`&MFJVTYZAi*~2L&0w}f zC7Pf@K5&x4K?J&sll*+5<|I)*Z*h`|HYZsk{p#ScTD5-zB~%DXU}-?zjwh|nk(9E= z=BUp%&gSTT_6P!sXiIy0`=eU*>Q*F%3d`U$4%AaG<<4tCnsw-fR9(KmQ1xQ`RBy~x zFl3h2s_VPR(#~;CfzNy{#jjGKz0#*b(5_)g^{xf3$R@ z?2kH$Z@BN+EfC*uSJxEVuH#T}uW2-TjVH%FZ!_Z?X8bNl zITaU;lnt;ph+0#HR1#?Ae?v;6M%U+T@u}MZ-41tEX~j4ECCde#6!vh}`s1?;)SBCb zTLple1|TP+dLu_NzIpx$njgI- z&stFqkMLjAUDB*4H3Pk*Fm=Y+jcw}0xPcVM)OpI4K#$ARc}cl#>SVzMszqbq{hXL) zJ*n@3x%MrdU;ag)Iu877qJe50{H#PR-Jl<{(m1&Pu^gnF(9NtTb@-X$GLD0V|DV*8 z+7IECZhL6*=h-=vo)iyLPimL-LP$Tavq)?K3^WpRk)>N!+buKMQUu+kJMv{fe7R#S z;v3JFzG{y@vO%3{XI`KP=WKVj-1lHP?h|ivXpB};q|O#NbqnDl+&%fgx-{xZRc=_9 zZoP2Y<5dZ=+QH%VJ2R;#HE%1MLNke9YuO6K*rO0%==N8AFsx(#4!0jHVoyK5mic(v z%(bcYq+SB-+nCjo`TO&GJN(_VZ=JR7&*$$8*9t(c#6|P>-A)6L=(oD}6lqxmHm~`6 zSGM@n^S@o_&@V4o&xbnEFLw+GwA$*Ieb>s< zo2Oq6UM&dO1s9Ew_$g#Uz|Qe&q$PlhM%4fuue9^Ea1fFnowc zQjDXr>ygn3DVo7p*lVCJn0jQ>rN@B`RJj7rK^KVO-3_>zw1Hh(agn14$geI%pc_Pi ztNz#pmxvw!(L0+F;nV}NB?=0K!GP4lVB`cbO>ozrd!2+EhELF2_~z^6h0Q7s6v-Ap zWgW7dJp_DWvdHBV3>ksR;$irjT*A^e$P^bO4nVj%-aI;Bm4hrUC_52#Ann|&GlSs~ zQgUp0|JOSyH}=X;g26j*(HIRr&Cfd(z$CjRI<*$O< z7%uh>A1fm}eC65{HBU9+8>#ol*xNY3XmNw|*c>2(|9!Sg>S89Z-7J-xg&9pU(;T^tAiEX+%V`c3+F|+!LFzGkDa7-QJ&ShH(N)V zdV1Bm^Rw;Fw_@u-Y8j5-HTCqKK~G>)^wHPX+UPO*Xq~W?Mf+Crin5)?*a^Y! zsmB=q2`u=`J(KX$eURX%llZ$Y*Z;P8^+kUEK~`VEYuA1~#+AJVI}-4rU4o%n7yy)7 z@|`?wnL``F6zbuF^Vq7OTBpjz;pD)eslx!u4%8t45mPm?>I~YYQUCK>LC0gbXl8f{ zj3t7bkB*m?FGCh?2C>Dbeg>KaH<;y6%rGdL?V+$pEM|xG7?-Tmd?vm93*tZK2ww1> z%;WDCtVzbp9OA^0-mQWc!3yTIC3ki$+bz5I0Nd_e(In;b`s_r9ul77mK06rZUx)@+ z$v~-%!QA2o_@8)~d#&2|J*8F1TD2IB*wi#%gd&OtI0jhZQ?FV~o1rzUpYiYz(p_3vq4#a$>>}bparjpr;reddWV0z?S1qf3~0?{{&7A!QtL5tyhgbF^~Rfl z5i;7gt^d>YKGo-!R4Y`#m^4m|)q-OSZR%-Yb&2K;a+AWmdKBbrNiy)XD_&Pxyssq0$ zRMkj6c5K_$M}T~yG2CV{=uN7zPR9@w%8eR`*(1Y6EX&EY(m;ITYXRboxM(02z!D@p z;+f;5B{tmR5j(NPr{4ZnLsY-PQxJt*$;~WkyyyM8@DtR}sgs*sBBta0Y8;)by^&)N zYZ+~c!LZILj;590KIz9~yiUa&IQ&9vf5w{4H(64KvkF$)wHbKr9pLXSms;(i8n55T z1EwCs5Sz6u;e6ySy_Es@TtxNWFD#&#sw6`pZV@Kl9ARHk1T)NQmesZ(8G$X!c2EFn z_H++q|3CKL1iXssj2jP2Bv#|xsAyVg8#HPnRue@Ht~W~JL~bA|TPUn}k^<4xdF@r1^Ppia}qSegzg(CmWwY$M6XE&XuX?2fMd9p9mnzDob$S0Zh zLEspQ(_kBlgXWenQJ&ou>e5wg^LmtbW>^kyu(y2f5wShfYX=&zFPEeL6subEq|I>V zRF9zTX1&l}`M*SZ73PZRH^&~@=uqkhLS@-m-_Ra9h{N?z>UJFOK*`#Vg|rehn#5

    LTox5dsWlcYeE<6VXxs7cd>)w)nQih>o}LG#1e@!6~40Kz9GXlPV;l z<1~Ufoc+Lj2y3gdQ*7vP<`q{8*<@jldHbm#N8A3#he9uLmQ)lg2&e~9x{YgUrPR@X z69;q$0;g;^X~ZR@_M2wWZ~||kg-U4PO1xLOHI9OfS#qAKHCB!wt+zqNL=maDbWiDy zSZI2QrbR5YecqE^Il7(|Cs}APk9KOI!71rWveGV7R@#y82<>JeQB?gfPVU43_TlVf zPL?Mp4|k|~E}#6ut^{wj??b?NA}u;k(sN4?1Vc+?ZW-}Nh{eX4dQJTPj%XdAJI zKf3;=9l@?7y8aZ~ce@OD@R>k+xU$yy{J{C+#a&V>XWP!04tddl>ahc>8A)`Ku+FkJ zT+5cxM!Cn9(OJK7wv5&RC$*rf=_o_Gx^@leD%ij~6|pfu9#jHcV`}YXZIDhlD*-OX zR_xm<;&w&su$1EAfjbatgWg53akN%%938rlrYA;;SQ@<88hxJCAsQnSUCZP&Jic)G?h*bDf=`A^NS<23~xtNyeWkhoa-O62$IdIDUlA zR>v=ZN*qV794GE>4<(t%ezfv;!3LLuPfk)nNzaS|pNOC3T6hl-YK?FZAg_VF<2R&2 z;_`-E@N-B2_H<|?_8pkI@hDJ!#*xud@Q-)DnP8yq1I2K%F)dmD#FbZ{zP1NnBRq$8#an;5QYET_M1ACa->$J^ z442mairoQc0C!Ov^$FJC9DO43Aom?6^XrL@E~zVVt)6#3mKc%sIYweFUb|2~|1eiQA`}nP&-q_7m3yGg?bqMj+5)dL6p?`I(km#Y<)~Sr$ z8GMpy9}6}}-`GtdpS<%kZSTJO-`?4~v#>EAVqV$Vj=g)K5>nz8>dxNX^j;jj%6g(m zj+n(;qO1WZ53a}TG@knYcR|88?~_2npB$`6_@|1e?)$?o#Zy1IElZXcyNjp3y2G`; zzi!lmG!{=i0Z(Jr_rIM2I-YvkFp{ZQ-@S{c{wPD}TNWpM!pd+wU~w{gtLkhI@ze*> zm6Rf$`uFQ}JoP{3q==`UdQq?Asb_-Kl+sBrp87mE3?!b~rsp7(Ry_5B*Mz~QAyGPD z&@r)i>K0fESk(DPiNk$eL(u}`si&ydPH|uRfKM1t9Z*jWcAvb!Cyb{)b)eMt;Ln}f z9_5o?+JAf#>SjOt4@|uA)O#PLt$Z3y8@2J#>NP%kjHlk`ea+5k##6udUT?}TvLEk7 zdB#(peL2MiPbcbqJoT;rp~UdYlf-!Nt@hcV;yKv)uOhVFcxp4Wd(?g)E5pgahkDmK z*Gx_Qol_^XaacbD=Qpm-ir&0>d)L|*MNl>l%c#p{ChL!0g$LLf3bO#da(?V$cR>ms ztp%D#{8P}Jk3><_^MXgG%`v6pO2?{iV&5iw=S+gi-W~{@H064bX11K4(73+H z9QH-s(Prbd$7N;I`2wN-$C;t*b?tj$%23+%2J`bW>fQ*1h98%U2M;MWg9dM*!dYrri?gzNtfkYX|Q9B|$4fWac$`j@t? z+;<(|A{BdgD=Ug=lPaZ62-%kYd=xqrHV)6;=qp?J!euSEEm&VR3UA4T#b*yMCXieD z*Z&+)^22RHAY|+D``Im8JG)|S0n8}T7|R?sUK+z0KNnOk6_x=YUE79i(~T$ zc0!%sudUCO`Dcj&4>&ygijN4;u7T}1Ce0M^a1^jqIlV}VZ}8~vh8d$W>i0%3TQJ`f z5VhFHe%~dLiO%v&Pwz{A&OdTHR+>lkA%UaK7(gXnqVePL{3Lf30H`)1V68=KfNU6% z0etnLep=Hxy24Pk3U7Pj>u%E&q`Pt78%iJd0D^*6xm+E`nGf#4JI za6D|lIS?}JjSR}!jhUa>k4YxuB-f1>NedBBbGV3r%b-4#)wTgH3z%!+>>toGXq5jT zQNrONJdsG%_tlf<+$VpPcRXI7zK2UCp7@voGyKOYfUeWt@85C4JDFY_d8x~P__nuv z4BO-%P9>{}?rNahU0T!VepgWNil7_g|J;6NP!eW0+8b62W;ZI#wi2^jfmvHHd*gpy z%nslTmzaIl#q3l(jbZkGI7&?l+^I0T-;pUX`^2B(G28Wfh1pTWEOt;?r#qOP(E)U7 zBp&S|@sk6HS%}!ib=CAY08XE$II9eREq64mn~Mu{iO$*jv5hD9ArAw98+MaB@eB_s`(KvcJH_XA|R?xvmwN1dPa4UnM*`IM0t;t$C*JwLH@|oEiKM7?Wd(Z4HA8iS|*3L}}UI7Z(aOOY- znG#$+f3gcN{+D#%)!zfJ zPT8&kPblE^;vs~WoJ*nL^@p3&g4f3pf!EU2YY;(8`g-b+wBYsS zvL6etHD{y)uYAD8kzX70)ivSuF<=)5ucL2D3tp?96?lEGAVYpU{sb3Z*MF5Zygp`8 z!Yc$XhbWt#>1*B69}BOW$E5?Wp_-;BzivAy2CqE@UVBg&EBfj;CoOo*en#Mx9fQ}; zj(6cT>dUm@wTeXvuixdT1+Te}|5$jPnv)K^hHIK4ylM`N!D|rmOILc}_0K=21+NKD z3%n{7C6T_~DskcU>o3xV*K8IgyoMZ<7Q7}u_G967_mFhpHCodY;Z-s?2Cu`BUn(}3 zrmuU#X~FC8RRXVp3NnP(1IM}W`t-B3;WgoN4X*VhPv=N_kU@_>qQnN zye@+4Ar1Yw@Sz_IuXpcChrZ_jTEU3$nm#B7uR7$Hu9S!rr61pGP77XBpAdLu$KdtR zQ7*iW{3LC7&1X@<>jXG7(tuap;vWmIHAklducev?2`}@27`&R0U%GPNm_70MpVETY ziOU6El?pPXuP2LLcy+a>4X>#mYk2Je2S^(58vfvqh1bo~(t+1H&4YwjuI$`V@@qcw zOIPk5gID7XX~ApHWdg5E1sTHY#v&J9uY8m?yiQ~Vq_1tiNDE&3K9Cw->g4-8aXJ3e zgtt7DJ?u5*P#V}ZaCr?{fk(Sag|7V?Ph!rbyYxf&eRA38 zapBBJ73r)Th3FB=gIjN{$GdZNE(hQ^O;YsdAFsOoIV8S6f5DSje{#C*&js=QDeAF5 zAMNk$5B%SaAJ;hUJAG^npd=n|93V!IH@vKJ+p2COfZzy!08Q7iv!D0T^uY&kMhE@25y~|tP55Id}>EZ*Sq&`!(A4x zjMy7<6)n&+odVv&VtrpeB_99Mcdl=;?|12k$@stZinu;UMtb|4g#Z110(b|vxbW_) zyERVH>_vDIgZC#mD~-tL9^NO!_nrGZVziq8?`H=J?{&MLpy54!{}{Y`SWl@mLz%~* zx96LoJr5oaAe*87tIft0CN4AThMVDrD#_$RY#sJlWP#J}eFZD^&2aUFGIwlFezjU& z#R&urHMnZOG?e*jQmJq=8-N3HHK)?D%v*mqs{VXm<(GxfQ-04gg}OqSH>Ie84Ha)k zB{CnU3;GGItTYE8pySa=|vCW zCN!plYiRa%TiRqhJ-%mvyxq+kFy@_mOhR< z>8GHN9HD;?eQd`;4?-VT!Z)0PK8i4Ohd%biHR$6jY(Qa5tfCJ?AOiY0wpw8IED}W@ z1NWii=$<~FTi7jqa6MrO!0B0#&C?6Aq0Fy@t9A>ybWO*N`B3n`zHwmHX%`1|8-7x`hx3O?)nHeeA{(Zpgl$@SYtXdma&vbjq7B!B$a;m zzHhI1n&BNu>t$~JgxKV}6@jH*e@RVWuY@w^fnIt;UzwXgUp8EAq0Dnqyh^mk)7Q|X zQZ9WB=F~qXf6m%VpnJ_e4t+i9BXmQVpFN-0$8a+n68STTm4Lo3h^Mb}yn0E~*9?ST zjLNlla3KvJUqyII9#+unGHqLOMrA#D25PnQyLJbrzUgWeN({ga8%K$c0DA+_M~4#M z6S0~7Vm5nU)~QJGL+LsyKwyWcxeogeCDJT1uwuszNDeK++`{s>g_?Df9=MeM|>Na9(fdujHwn z)8G4l_t0O8_4WAv;tJVvk*+1}7=3w!Z)qJ-zWd-O!F!$<5oy*97dIlT^D(0h8VTG~5$eHr#Y=&v{O_1?9h*Jg|K zy5qUzS4l?k^!jR2DVJVXKTdRTcA@P5EJ45l5VP9;7X-lmPf-OM68*i3m4IH&c=>ul zvi`;%J0(Eh`-TGaGfj|tzz-ZpkS|00tcSF82=Z=`oY^6C&sF93Y1)XX^8BNP97$hk zs`9;;B~atnouEdo%Ik3u2@L!ncpiytY8kz|sPZ}8$t^gp0Z5;MU{Un- zS(eh@#VM*_Ln03+u##PxKWtgv?fl{EBKGXZ&M!AVCIl3lUoPJc!scx6IQX7P(+Ts- zj-SP-`bAu0{;({Qi8FtAMIeIt!}hZUR@dVUJw<-6?M7I2KYs|`rpO~c_z-l`lfH!f zSq6OfhCU8{7W9!7Bz;_+;#H!}Lm!wwj7}=$(nskdarAL{e;M-~yF2u;S;id7%t=uN z8xr|*A}dLE{&4+v_^D_O-vZ}toY3L?;Y_sb2%X>H8tB;%eR73P@c^ZeVnXMUBRGQI z%pVpk5t@n3A7U=JN0Cx3c1f0+L0&gD=3 zE#1kV9_OcHKrg+aujf{Qz81|Qef=WEt3-P|eSP^%VgoLHy|XxuzV_}b&^=**LtmE( zba9S+iYnNU(AU3MNxJlP?&q4mj%`XoU!TdQJS-=G!Cw+#n7zD@hrTvTf4uY4QHS@I zzD{~LfxZs>JVswF@Fhb67gjm+b-kQyxwHAD(ATS_yG~!ZkL;Yj*8O#t=<6%cOK<4w z{3k(QW&`Q#$rP^=?eX+AH>s3MUo){S!ec)@)`htIaOO6M4DJ6A=)(Vzq6#*o{2#0& zUHa5rGbJ}vAmeZBo)0)4IdG)7;3_|5@= zO-Pjd`Zb&*a7?5lzv@oeb^2Pdc<1yr`KDc>uX&)C-q2U(3ecB5gY#XmEzOI27Q}p#H91-IGSee*I?f;;EZ4fI-m%c7-*Yq{*`V{n4jFuhx z$`)aoy&Yl1E`8+)w!QTAmqU9?Uo-xZKwrQABt~CtX9#^=IKrW?XXRYVwCHQ!lXjiH z#y_xg`s)9eU81jJKrg+auX~q)zM8KgeeHN6`PF3ldNrw(OJA$+jiay65wZ*JJs`uO zuQLU@nE$1yf(@zpA1g_hzCLy6Yd>&ElKr(A!pfnqheViW--155^!2Fp$4g&F9^6~{ z`sMuz^wk%KYdH2-gDLd2dxb+^=fXdcru zxW4!r=%qLGRrWaOtKcfq*X=1@O`tE%|B_0%^mX1narAXFLYzTgPeF|7`JbGE8Okh9 zQH9VK*B2+Sl3kkrUH(@0^S=YxvmZVGlOskv>qpCRZiJfuU9HGFasDTpE|dZIBCa|A zL)f-w{wEOO{I5(X@%nOy5?_OFqR08)jfy-{%>R0%k7dAjZ|LLTM?oK1SCT%iPVs6o zeT+^j<7y0W&JlpzUjzD>_m!7E9ubIuK3+df=;Pes4t*?x>!Bz5IKR0Y z`e3+At;7K}<+o%&j9`BpqJxI#7X_?9KF%-0)(~W)-vLZqkcI7;Sf4=PO3v_n9H17+ zA8&@aeFU%6nBmcQ54V1<*3CQs)`Wxr)lPDEX{BnffbHRsHdM~`ik$XPX#j1YJ+y_5 zdVU{Usb+gcZhIwc&t!Y6|Azpt|A*~e_ClKN{Z#Ng@c%75XX5i5DdAbI>1B?lm!9!Q zOjWr!1GS6>t{t5H@@LMNsuhipK3Km|(3J1gpBjFYqo~?1gy_U#E<8vsy}08GQ8kFkaHs{-V^lG=o2; zZwkJ0*oGf|L;yRJFL)X2^!P-y^$m`~!O=zb`gb7tU?uJ+CvUB`cY}g7t!uKp+nKj8 zpt8H|%<~qjcrSc3ac@7QBqct-Kcr&5uEf(=T;MmHO0t0dtlwbSJNX()m;R z`*(VO@eOzNu)nzX_&B{a0UyV*?;d{@R9kTi;r12?kfQw|9!xVj|-gw z8Xq@FEPoO{jv5hE$0JA-K4x?z;NxS=FL#cQ?SJ0|d{l(| zc?{|3o=>_S>kqJd_~g!i$K&PS(~|M>Kw}Shx%eM0UOqZ82`{7YG{z^JodOy!-^lKh zB)r^S5s#O5^LvMv*0+=Kax8YQ05AI^QF!@4&hYf`$>@&-FYbC0W|Qg!ovnUjp3ofDWLIine0VK!o%KXJRZ)$ zj;-FvEAwB;cz7610T0|qqwp|M*ujH`7e7jeKVF~TBR*z++$<)fdsd5%tELtKm%yyU zw{@vaM1`y`>*^!Nk=plfLzlrHAs)P4wuE1OX=RZX6rm;EVqg68EPRO?H`ZsxTPm!G z-N09Z+l{6@5j-Xe=yNKdGRcl9px_mW2yL;?gj*O0ZBeH#@B}ABgN4c>a_Yjq_qhnY zX_!XnQda?Ozt@Y<=}v*a%BZtjWV1vPLQBiz5o%$JR5u9ikn=P|0sYcdKv%vc2<^Z^ zBf;yOp79o115TepqHt=##nHYyp=6(oXr(xH^obmGm?9oW^P6noZCWF@JI_mlP6q&>%qOZA1?S`f_&Wm zMm$Q6txZNrU-cAM0j0k53$5T4SWZ@mJqkGnR zCG1ugZ$b2FhkY|MDedG0QkvT$?Z!!VjAL%SBoRFu?LWYY4D_TvW9E>%T=d*CRHNrn zSK4LaX$(C#It6697S4XmPRTK6m&T*#4s71*20b2W7c;l5*91M>QB>oIx=+y*(9?=U zq32F`ShT1+8*xQ(qOR}z#FP`iW`17jVo_|IBzHTOf_Fh z)^sviI^&}R=K9;KL~0CsZ%$3bUaNgCT(sRXSIysD?0s-l5_6q_r!nlk=oHY*_2q{t zu{ZCOchZ*o?L`};7I_9!s0LFiTzImk;U=fEX+9_{&SF$=N{=a_hyAb?iOR4j?U^q zUv3lJjm#lstBPJW6(83rTNK zV!xb>l96Z%C^-^|Ldo}X5~oMRO~p)e=l+Ljf7uB>677ddJKly^U-m zDRx93Wa84XCCc%D)&+Cb(el=3FfwfWH+&=6$oTvvX2;ajQBx9e7_oo2TyPjsQ>u-e zQnf<3w8@le^=&Q=PsTxNsLNDtS-cWqzWNA7I<65Pe?b~!&Z@qKw~O1N8ka6~JXNjygoCg{77FJheC z>$?;9I9cCiNOlb4{>jPuZck3mx-%?D_bN;fBg|4T@C>p4)2K^Uw94r0dkmtM3KW`^2B}*-h((Y zxEa&d?)BZmP_Ok}sj=Lj)r>=FL%^N(wEyzb1Z6kjd7>_cqxrv2Vrdv z%yV(Hs6gW==4sEy(-@Ba;uO%5^@)F_#L@U;;&If1EhgP?WlH1m&zl5AamwyXGzB!g zhD4#^9(Y6I;J-Jf*be-a(jxyU{dUNWKaPL)VvoA#pKmu32}$;1M6zSJ$v7{We_p(} z2mblXUtQend1w;ZM5acA-i}$`R=y{mv z5&r3~xFMW<0{Q{O4ML(2_mSYqz6b7tIQ*T3NvwlEN2tZ@#mUX7`A5$GtdYqxPHi|N z%QnzIf}l@*rDG$n zk<*JY2gqp0L=xw7>QftJ(YXf4T)g$t+s{QIm;0|#z_wgQlf1PQq)!O?T z`zD=vA%|7P&r(dpiJ>@gI=?EzX!;x-l#0ho#Wnn5LLk4ObcH%qbca1>fNr@!3Y6e5 z*pa#TaiPG))tT!!P_f!e&&5;$HP{0%5n?L^8Fe^V8LRcuOowx5 zNdQz60AzzIP(L79o+W2Ix9??)i{LQscAVYL2`{FPYNcIqfiruH_0Q#l>({DoWu@T9 zE601g5>peemy&sfM@Dv{m@C;F?I`|+L*@IbW5t$sJ1d7Sk5D(Bp+)q)% zhVm?OK0x5}jJGNl+9HEing&pF2mI$XaRbitgw$l-Fis7)h?ynMfc<3rJbN`3o!B=& z`v&mG3!noj1xipi7@n6$%jKjj_AgE@(W-H$(=NS8_rlp@u0%Xn&BLVUQCa4&)phd8 zqAynO&QD4YHA6W3ndgr0E1xXlNzks)B-9Muyef3F9^ia^s-<&=q2~w1bSKftk6rgmpp!D zS|^*}8L$kbcIGE{kP1ke<+)0~aI8FXJ0>f5mD1lsiri<=$3drXa!`bUMb5u#X z=z_!IbW!Fb*yXo#gLkOwgIn+n#+0xWarR4U-tkYzqIwKI6q%7k(Fs^o&m&P5)ot>` zqc#R$TFV*faE>Z|KC0~D{jTrU9u@@yN}kH#JawME93J*EP6|*?hA@uT3BylNd>_3$ z-`Fch69YuApJ#J0JU=s|=Dz^@uy=c~2@p#gg2 zqlI&@SZbzyg=vaR(y|v z(3*@7MuI3$LZYD6PxW&Z`~xv)Y56AorBtw&u^-m@SbwLu{k;#58n7>=e)j_o+w1b<`g^MC?+El)LHI6sGSI(s68h)zk92S}>x%JD`$}ogXi^^M~0Crc;a`oWyyx{&s+DPKBSsMkup_ethk5ao6_due?vl*@ojORvp zD^kH*yqQ7P?WLCz7v9tG$OCVo0A;{^W}l zw=kR$Y`)n(?o2>T?BM9_)EuyH^6b8okO`9{PbNVymDHR-jF0xoI5Ti{qf$ILckyH# zwMMj0RZ8c6Jc%itLj7< zMFrz3exrg`;6^h%Z~!3j+qElcm%db}o@;vN_cgA9-XlOoOyNMC?2cLPfG*xo4G6-6 zGrHIDBsQS&`r$y+Dw2aF!vhz*i1NxF@N{dGbStiX*az6qkiofP>;py9)p=O2@IxQr z%P_OTNACNY#%b#b)9YZt_A!EWd?l0}(0xQ8=_Z`kg#t#?G(M7k9ue2iB~F1c(p+{f zJE^q&$sG0u0tWra9w(965TzDOWSoEbJZagGYt<@KIq|3qHBg0xw@Rs=I2@SJ10vHMsnrpuY^$g|ZUV zjV_e=RUc5&Dyzc%c*$3vEibYkYQVelJz0Fl)mN5_s#%adC;~K2p6O!jseJ_+bu$zs z^6?~gjN*OzVKuzZwWd0@vRaOH%}{jf@C z-!t&+=Xi#Lpp2%C=oa5F8sEkhi+6Ml452KH4}Os=xXlQDgeOsc!U^+v@JG21ml;vI zS~$n}R?4hr)*JXi<*Tk&7yvyEV@;&T$M{I-@lHG)8SZ~v@ird7nWqZW4Ku@|y3D~~ ztQ}_I^R#^W;3(JOJ~&U`3|KoVtv35!%-||Rr{i><>~l~D-`=t}q?`1z-fW-HHSl@d zlRwT>O&!gSaB#B@f|o=Z#Y+$ALih}_YO?SZeB`VIt(A1LGB_^Ogt&eG;%&P2k7Vf9 zbv~I!Q{<>Xq|yoeQdxq%FY;u(*|o-Kx>be^Zy7!f7v3|&9JbjsPT!0}9y6cq&qu~- zI}pkT3)5U3J)zW?i_0D6>UaBBtZwVafebQ3d&yVWuxq4%?liCoX4NDnxG8vvE3hjj z`T?1`w&E*al+b=40yEOOJUVr%OzAdCHkiZK)a?T~Uq8@3c=}gebVlwaQyhG&3i8ul z0wBjUlbXkP#v`f#ilT?{m@7|wbe|LOuMbBy?3GgE=7@RG8#Qr5x+qe^jWB5q>HF`} z9@1G?rWn%Om|eyV$upk&2)&(+XY3gH%k+#Vyyy1{@cULc;{v3Ks(xm;zd!A<4NiY- z)25{uTc5YNh4m>$bR_1!aoCC* zPs`57W3Jr1|CgCFO3%&k;OoLSs6(7V{hFw{Fv6^~$93h7w8!;gU5asy!~9o|%Y&bL z(;1JZKQBK~V7!xQhLgw-dA%UNyT3_$P+wn>Vot)=TJA+A}XFl znn6c>@g|(55i!PXYM&5*C0c@S0dv*IU3>P8&aW{F=jJa#Srbc_(+%I;{07`Gvju4& zG*=cDjpeiRX~y98Z%9A~1-P8AE{7NestSBew}jU%$j5aRtlomqg#01a*bA*mQ?VYB z3oF+QSYxZ3N5b?3pO3`yWI3j9BMY$JOVG%A@7h49Uj;373ml8FL68@2HfHsusW9vt z<4^nWLLUi_J`Vnj)AIU5WsY+{D&fSuemKHfvYIv-&GbMu;~NqicB5yf{T62t3kwFg z$5gjmiIxrJChsxHs9s-QlM($RrYo#t<~Lo)t~)chsea$mhUtAWt|}J{<=~;&r+s15 zrs>D9#c8+iq-Jvk>*vH4Q=95`6i?j)RZck-%cU>6;^-S(AP#3Qc|-h3782!{?-GK8 zTl5)Zkr)4*BoQdDXMWrO$Z0Qoc($P3{^BuGRFk3sQr;iT(ENOM)$ldtN0?%le37Zk za|yA?e)#r;myQUa3ce&187Pbxjd!3eP@~Cj$U4-xMN#7=fzWIz&9@?W%%+Hb!#6vB ztE6-DZKj~ZDqk{icn|u_MT(yP9Emjngf~Bbkh&bg0gUIyZ&t;d z>83R|znS$Rll2%m&aUuP`7OwAo}Ir433#ReL_EZrozGAYytEhzr+|LyD3G$OUk7kP z0gA#}nw?*S+GN(pn>>9}#hU`TnJx93)<^{GsYbvHfQHmR@?s+IAZ`5CX9Q)#8PS_TUN8r5HJTcDAz92rR{dp^QhvKH z5GBq?a)}Xqmoy8xGhWG^!lwLcR&O=s*D}R486VrO>zNm_*cNz(eChDQ5j;uSnxEgo zXAEyaDi`COi&}NjB$(#)`X<3DJSNeA^h)=EE4Ba?`z4@Kk|O$|;d8KiQ=}K zmQuWf+p3U7Zp&MqEbkS+vC0^~wW1Gp<0Fc7cq4K|_-!lhh}br!4!=ne;kT1-O?XN8 zjW5Oc?XT>95B$c`i?6KFTE&*K;C4z8nPTHezqVL zg=>*Ho(uAJ@G#L3q zdCiY`;yRziT!)K&$@kVsAA0~U=ckG^pAq#t;j?#9nef>cyjTH(#viT*Zv|oG)=P9% zwLNPVagXsUe#$}_+|{~c_5&iO=A$cn&qpIsANeT4K7fy^k;=t%b)f{0b4?8L53EGc zl9jFmo1jCo2#?f{3rE>TKT|B!h1EOo%T^?cg+|KBpvcO`dLvor?Z*;WNc^p1=y4rG z#Z}Wc#z+)0CoLvWMx(XH9ewL3nlLjEf*{gZQMkw=Gdx$w-TvXP-r`4?p{Wqw-~}5) zs3^3J#=U?oGS5>Sl$cHt7*AVhI-O6`38mq}V+)XDAT|T0({yr623{o5HRC;U%N(Z2 zZZBXC1t6L5k856bmhu(g-*#vZm%A zEtVHc!SVw2nPPpZdr;$~>gMtqpri-`INB<|&>D$#p#7B9H5OJE#2zg#%)Mr=BY=N5 z5qo4Nlui_PY5=_5h88Qr{fAIm^E!Z(AcLX^6hcwF0!Rq~x8b6A69BjZPnF7eCKJy@ z_lwG~-ZBU8Fq#%2x8fNV60KqVM{HGqIWYKZBlsK8VgR(LbH6Yw1ohfC71mb!mk%hW@iQV10cZ5Tl2sKCU61A|kJJmw z6ru71xiHIqfMGTQN`?<1KhL+*rO? znI{(&hkiE{bWj2Td;%6F+N(KhP$NxhU@9BHdQnmfT)l~Jq0;&^*2tLR(8WVJ^Fu=j zGEjyVk&(wfSxrhIK!38|Ts)&dF8@vRn=CY!O&X3cF)`4{32 z56N`K6HWOS<8}(}X<)JNrK&J(EXwvO+!7LV^u1r-H|Trzj0UPtQmq($^VRqZCqOL~ zj)oCd$e_AH2ttSLK|lSjJj$d`aKrF*td;V0orx49o4l^dDfk1{Xnqf>(gP6rmP+`L z{DI5;DH|G4T}=Lr11je#!wRI=f(AmD7Kg4UjTAs;4573%f);q42qb9|#3ap1>wgp; ztd3Pwp@>m1U#c<+p&gJ9JQYB24goFXp~8cR-!|kEr#9b%5#1r_?EDP8Pj>RLo}9hK z7?vtQCQ{_oTq>Os=~P^lHg@?a14w5^kRmBy zb($-YY8!VWq?I>UnYkN{;LVb&IUD`VK=YNBZEwQh&Bj%74=A$-WMd&nbXAX&n9HuM)B8EhdgPJ8GS;X9+cnGb_HO)BZy2x{F z>T7878eT>p0w^7U6>Y(dsK4mn;GTxaZO?V|r5RFkmu2VRO)P^NO^v9b_qGS7)?F^u zB~lwm7UFRT2ABuZJ&07f1kI5eTabbh^YHv2JTJoYqoW=0(*|VS@Dc<(PsznZ0cg&M z4x)cOoV`s2VJt<`&OoAXyAZ|^&aBIDf-;_5L=yy0&Qt*;hapkcN{c)};d7BiYvsrX z^bBAmi&}5eGjo9CDQ6FU>F*KCh0Ntl0cg22-b3AjD{)$!LxF8d1P8+Kua}87pM^Qg zHbdT`N74v{Dx1KG+M6cyZ{sfkhbhLV5v5VZZ0Um8vaPImTl*AREHGPO6H(0|G#x?L z(h!W70F4)IDFcm4)2JA7<}1=&n%{!HLlMkZ+Geh@UcmjJW-(nx=OemohdJ}z3@C1+ zKvl>J+VCRpSrPABkqU!DY`|O6Uj(w{IzH+P9?6SG=Vd+_VkrKtFY~Bt#8|C1p|@OF`yDnBtyqiwr^v9xMQN4T0H} z2f{jtX0%KGIU`FzmN3ON`R9y;DSjPYp5VYdympW^@|ui1ZSa#$}=OD_u$UDE7ZJ(8jSi7 zaNT4M{?-T%Cy^0p0qb+bsMueRfCh6>ce7o_(us(E&)F?{pJIvY)(GK`L!t=(rMoqq&qfx- z_k@Mah!MF%-J-p%4fNLoP}TN2?SQWq>V{OU%dZisS}WoklAC*5Ain*!6;$-CivEVO z2!2BLmG060z+4L19YNlv_Q`~qni)vt)A8#}24X2_0r0-w-)H zQpst);>u}%^L3{7~fPZ2exHdEDj!j?iI^M`b$Vm#p@JWdagkB+Sg3xCJ!=m$9p~B5b z6cwJ{qN#8<*{HX}e*G?HP^MvBhMGH^`NCI>MCGBR;p|)SQ5PKEn}MuA7?0K05I&Py zVx%iqN;cO57DgcqLrIs4Ssyb-O6=|8$EWL;vU)U)3qM=O0ld(&q~#LQa)JG{c0#Fw zLrBYhj<-l?c>%B)FRS4@QLcUw)qa-ON&vP;HN7X8Ejd1Xz2*cW7Lkp78mi>}+kg~` z>K3FB)psGG%*7>jD=r~=^~wRL($8+--o`W6v`0Q`V}1rwP9c3roArW$Ba&yUg@d`W zXkcREKz`y3hXRwc+z3+o9k(*YY>}04wpc{a!klO2G<7)1AU_I=U??&J!Yq;X8NP+8 z9^Yc6Pkc+Yo>?k0CZlk%nn4vV)XtyzdgfeS1093SqR=Zj(@Kn-OA3sfDRAE)Om4$& zXc^<`FbWicjRJ*mm_o$AF zbM_lSMksNXwg{>HE2A%%XO-N5m;ME*OBm(hHNuGVJ9p*`7%7;=99{}$^EJ%oYnUz6 zFk7r)wp7Dxm4?|uJ!4<2Jokl5^=x&Op2e!^3CXNQ`T>2c`xD`C!0SaBh~nK)o`)fD z_EMtLDoF(bS$txZ*Q_tEg^0{t{ss6GYUR&VE116uTrlAxa{0%}*G6?I*KMBbPJN9D zm(0nc%Mm6>Vanb>n2N_-dYA)jBVT%*Q$C*8b5t2v6-{c?ur8{Z2g{^Z()nuCIg>yg zV?iD6u0A#!eVZYP;p}Noi3A#jL`k6M?$8pb30XAuMlWCnhHn#@7{lqM}moAneapzXq+YQ2!MxSF%YT6fOoNAq-c zG)wbyg&KITx-&O_ynar;#H0d~J+*XC`N48Ue9q|-uJhD&7OxA`HQmB_n1~dS(o|cs z-8NXaqxMk;>{@NMM)1IqGuUZiXWn$Pl(%K85nRtKC?dc8 zkEq8wHG(U}^cziwJZ%O5soFKMG`L)if)!IXZvjk5NexRRg=S&CEJ7Mh3~lIE^^0Fu ziG#7;g-G-3)!?lzYOJ&t8_`My*P2M7TQ4BnXdDTf=R zyr9CGRzy)QRp3<-0RU4Gs;tl=g3aJM59PB^t*?>`NOCkR9>gs8ZkS#Uyd%?7vVaCBe)IJtBh0FkmG4rRHIhfT(w9Q6W2|v&=Xap zMW{FfsmWBeKnBK8s!JJ4P@#_5hz_B74sG7TV!aC`05wKWW4|250KLrK5YV{mWnJ8Mtq6DDzJzA zMa?z~#FU39oa`Krfq9vlu}MLCKWvF8eh<*%+eqt_Aw1!(XcmZ(IPj&*B+YCBB0P=N+z{ zuHZd2V_7o$yzGX6q}8BIi4MExvIV+T;>?~k!lQu>g>n>{-JQcaau|KN5dh?k;S1&L-EccWCCvwBEdRy`=#Q3cPRcRZtxPf!Nt;GrS+W&+GMx6=AwqN zIA5Z((6tQYi;tWGfNW55951lXD9=8lSPPTI`S&0RSPHzdo$G_)&7<=Vt*~B29Y1hJ z2nzyBDdHm!U}g=Lz8>BX#@mdquwG$L*digNdO+arRc5`;dM>3dtECXkLm7xY__P(9 z`q-1v>4`OVs5P?K-_V6UEee~Y+cxx(loUaFdIsKsQ1TX>7y;bp8c)M7ZHZ(6HI{oYC|yE{x@M`G-1o5SA)UvYQc{$Mwq5EQnUf z9*A>2GI709FYnA+$8z-Yeu#^J)n+~k2FD+A2e;DkwT^zU&!!ktv0=Uwrubsv9zQtp z(E6gvAlObV#nhb|u;A$W^pt6T1ZxGXZyBn9f18we-vj+xAM4ZQ6xxKp4v8*?!3u}6X?-2qb=3yAew&9DDc(b#p0 z5^MqE)D35Z_dL;TTwQ<@1xyPrFxG?m=%&u&y=X3o)`-+44h^+_1XYCS5Ym$gbd^~@ zI0z+ff|H?!qosI2Syn`)=PEE#yOJLhC}s9QT(!s#FT;7{2iiF;*X28hk{ogjjVKPC zO@?kk{~(0Of2Q?sz&>Do7M%?;00s!oZPBx^Kw11r`ZAg?;z(ZF zmq{eUxFXJX;#)WxZ;2^292YXpSdCuAEGP+Be%Q@&eG7Lm3)5IXQu~2l^%GoGGqbU) zuhI0jOgNh4G2JLtctVeo0>OL*0x)X>S|teSZ57{wfIx**62t-`JQwE1{cWrV_iZeP z`$hV`L*FYDD1UV#ifC4CWe?HqEI_7o1*L$U-30Cdi7a6ikQ)(6^{QooisU0n-Nr5g z`->!sdk?*E(R#{BVjsTr?rx< z;BOZGH!Y!o^*7964t%ON=W(eHPA80r5VZRo{Y_W2lP(Vkw~ZVtysCsb|9IWtxh5=~ z{3ZGt9@bV~MhCGH0F2d^t|&qtzvuVPEI4FtlSkR--zO66WF$&1ExkdDp(~I@SIBA2 z%#iaJ)&BpPALpVXnT!!SxL7J+^U9+;XL`lVSQ^i%<UdJAHW5H_WjM;%7vK&k)J z?VeefEUaLSu&ez%Sup1th#a{ThYyP4gG_>>fYcQM1Y$BthqWP}BtmV~g4A6G*O93> zRRp?2mcbcv1A>UKltZfyf#JRt`LO5nF!?(O;Wq`a7>B@OoW?c6>k9a08{d>QLN11D z>c{>C#OCP5a3!%~3xwApmSdVvbAJb4FHq4`kOG7`-3@B5zf2`Yg!jbcDnOf+D_D9x zgE$dcki!elqG6jhmah0g77f3Hs`!-=ydTXd@PbA%O1H1aksf#AicXHkwjW@g4G3U- zezswuVL-M-k}O$>`yd z_}vcwg(zY`GL|oIiCuDmR10%N}+YH0@wi~CA!ac8n$W?%5 zOz-X>s$x?;x)~55L!;{!DB7iBA_T0(;(P=An#8dOQoRmmCcTCImw7b8SS2a& z8U_G1;HJ4LzY&AOBdJzzOhAll5DyvS+RUM&@SjxS8wCfWBJ^C?qTEJC*a;u-tS@zy%tLz+&1jUG=gWMBXXjR$qTTE52?h566V~GJ0d*DU;K#~I%z%* zJ!{W48=uQHhqW6`ooK$I@!BJ@GV0nZL;bNsBYQ`Qte?Em{ub?k>8ugDpB3?s*F>0e z%@&>OYJCkmpn;bZC!3;6G3V-roHYo+%L2jz4Z;Eq!bH-BG80hdl%G!&5saYp+JN;; z16nh!XUKd3>%KWisQCn!s9bQEbHi=S&iON?S~QAIRR#eN1uGtFoZUwTE{6b3!_jM{ z+c)$vnj%v0eWU3K4kMKPuj2?E>>KVHcqR%-z%Io8JRFLON!2Atl-PfNh8FwxAd6!E zit9z}dt>)D=;$WD{UMH{F+-S9iGZI#Z&!E0sSj3rix$(JBGV%TzpV)rJ|7EkitVXk z{2n6sV7+W=|2?PLcmYBLq8zFezutW{5kMzqy~JizOC*1OehX4e#?_uOFt0@M4Tn9_ zq|BG$G+%V|9$2Cc#d*)y$s(;t(E$}f3>gg-PxA#HLEuXYYs)1*3YB|mhBuF`!K+HF zBMNmaHq}5#mP1I6^q~%CM@*w9 zh!IrG93~}*G0}nxk_ZcuL|QNs-m?Jj=jeOpWBp(Zme9t)uvHJ3QB`48VsWVqO9m5t zTrQ~R3Q%Q%tQ=Ga)XD+j10_g!#VJ89phnQrh;aX(nU(?!jgXDV1Kr4Wt7>P(;4)kk zZy~WfM+h{2k2Ql!Gf_|$2)JfoTDCbDI~ab;+-H#?O9}E=fQ9rVTqIbcJ^X$>eUT+hz1gaM zS}gP$#g$EX=>*~!Mubd^=(90`WMFL(wi%7LL*H{fV5!(}sWw+_qg#*mvWY@%NTc+` zy6XXS?^T!C2)KI&;l30Q zau1Jr`&7zEL_eqnaNc0K_Q$q2t~hiq_gTqQs?z$9R>2bVh|OdXA(kF5QtDMwM;=mV zZe@n&ixFw$^dq`T@JNIgM+aUz4K8^VEi$}$zIgS}Z6(CuryL=BuSoh_EwXh=txQLL z2C6WTPvu)-ZKp+AC0X!Mp$P5F@wjhRc0Hc{7I)|#amtNX6LIU^cy+ES>}_pf^|0~k+W3uEQOH(30Hpy_OA{fZ zT67>LOHP+%q-y5j910^?ftm=#Hgti|BNB-uz9mG6R4~I=#DN^)yZ?YkLUEhXbUj{~ zV$`nxA14A4?mZYKC(wmYj9^k^aKGcs=Ljxy*rz;BefW@E87z*(!_gP-!KfR8ZzOC3 z=?}?+vGB-|Diq~VoRQ!DoWb=zR*aJcBSO<+cHAh1z|UO+yVmTEO;=b?S;vk+qL#>J zt1Orv(fuS#S?o9~J~sVUk?E`W8006M$+e5ukx|*OCzN-~;ro#(kGH}MZ9B`G~4+6h>Q-ig|9mP1ae4N?@>B4AL6 zBBcQ-4B)!+;X2P{1xSl@YBKG~4ZjIBlW63JKA zC0BANMSw*yE>(=6_{JC`2z*5NwrA%x-^M)@!=!2*1Fr;Z(tZ!jl(CqlgXah$6-h%? zh3!f_`6gyFg0D7TJ|63US6*B(Y<2xF)o!73Fw}LqD)?&}Jm<;;d^mj*5T2x~mGyVt zO@i;^ktlpGxlE(?Qe<%{<`f|SkM-yM9u3(iZvW3zFV=e6|1;WKaOeAf5EBp!Dt9Ii zDHdbe*dbso5xK3%H#y}->T3CJk=w>nF#94*fo#6bdm zZ$uV}{G3{5I7H6*jo!i8$=8qY0jB%|4x*vJKqLe^<PfoX~UWGhU zZc%d_I9PrX84LoI`g`NR;k>Q|5O!7UuKRDe{6znaT7L3uQEOxUDV;dnN-XL*wG$_f zCMk;5F5(g4{vb#~5Io?za%Q0wAxPQu>$-9RgBg+2zka{UVVmo*n4oE0Yw5oicX=E= z+`vX3M)bs8BAiitE1W&w62hrRq6nw`LQOaiAd7@^^+jI!ru=_&cRHd?O=j))CX*_Z z1P}u)o%7)C3?#giVt>c`5O&=JSM+Ls2d!OBzDW7r{T(#5sVsbK2YbdiS;f?#r}N-g z6^+j6oScXRh3I2F2E5~Qh+6otTaY&Es5(cArLn(5h39Z8REr87W0UtZ-+8YZ(CbFv zJLm?H_D=9O6(yE)^ zXo8ZnB~GKHPJocJ9hPNVT;Y0LisFw4n3XyMGYH2_(-0s3Lb#3e+(AB~r^e(m>7Wy(h&+U-D zsI6e^7ZFGV$rykj83Sh|e1;iftCUIF3P!~k9jP?UML`i5!;vU~@#OhhU|fYP3XF;i zJOV?>3qR^jkQa~3IzTVw#U<;4{oL+W6xUdf9h)RRhH7eH4_`f;7ZY$Xh!+7|;A7RJkEE2EP z(;qz^*>qV~BW1M2bf%zJRzkhzlY2~94)s9#u5-V+uP*Qphn$5edo^?|0~O5q&R1z3Pl=>3BM$7J|CX{Q$ZIj=5wEpiJZ=nMP(>mHBYYH= z(q?!b@9bCo@h@RtSdcHFBF2cyzD&jxbt9&*FD%UBcuU&w1#56;_`=gAnb*vua9#v8 zn4x*MsmvCY*(#Y&FX7$t@CqcM1^m*XwV+k5SIPB1@oVG+g?I{rtO$1`71NqW3NZ?T ztO%I}lKFHAlIE}HG>=tSVQvDj<_(uZ!L5L2zzVlX60`~3oNxx70a&cSC=B}9 zurJJ7jlyt|+|A3CyLowXw}7=6zI)|$-$QDGvMfuAEc3BQ;X@gc+}k0U3)-0UA}CrR z95GZN1IVR`YXQUvYCJ>EPYLtVv;7*>!{l+hVJwaAU@NXgr#CSg5rE-CKqMl`JT>?QGI;&5F<@FoShzsO>kA4<3WEX! zUr<3ND3I|56)1yw=pO1;U=AuUdr@G9!YkM}E4)bB4K5NG25(b$x2d~kb=NF+WjHJ` zX!6n*9xutf8c7yRm**1^i9QCA=-3~aQ&s{3l1>+drPBrTq_+hNq?lz{^pcr;Ziz_LoRXY=O}W@Fk32%n2k@9b+G^LIs8KC_>nLjuhUvdSMWCr|0hLA z*UG6AjF7HuvHx&yzl`m{IVgvyp#O?233KfB^QYiTX^l^};2KT6gePD^KEVRu!~3WO zz(a79SM)KyWGyJc*eN?$9)Gy(P*=Rn!bASyGEZNw5Bhl({fGfb;V~`v?jCD^Ser8j zwN6lCZQrXztbH=w5o>?=trlws;t9pt=MaMY&X|!8uo;DQ`50uFgR{5Y z-4DFXjK*~(LhC8N_sXwOCcZD=7&0DTW{7*=aqUW~{JT8kf}{4XU%Y#^*F@`K{o)@d zcnj`){UXBki~s-pG9Ehff9d?P|H8rGPCe@XzkGh#j8DNNvGdEGsTZ!g6^Swyvd3z! zdJ$P%y_i3a88H&q_CglSHjlkfOY>nvum(w-z0d~vWJjw+_4C@b7i`KgU$BEoPOwU> zkFb;o&*PoFuq6H^+6#OM`eHnur(-X$INp*rpdzHjF!#0?zInM!3g=Zxjq?grW|7LQ zmdvO9yjvbNk%Sgh;MZDEE!P3L{sVr|bxJ$PWmbecY5~{_q6G>ZYp7N-pKd_X9B#&M zC@dAtV-;4|LXI_WwiFVf2z#LbcL6I{gar12$;20A1+W)d)j(J)?1hNj&8wEXc}wJO zL7u#Due|Pi$S-%xs-(!W78WUdXpSWJHb}CdmdVcT1@srg!$fA8AbjRJl7V9{5N+CC z;3ez@BFeEB4n+%MFO=Y-*VCDBCq;MlV`Rgpb1@K1W)MKF?FBxB3eJ*oDr3#JKn5SP z7i5Hm3uL@L(KrC50KpejkO>N8V8aTWf=dVzU$9o)Mbuq`x@+ZN!t>_f4!X0XbVY>S zduJqAr64FR6@;awf_c(J!2+o}S;HgA1(=4 zeOaR~*9V(<6+K+kI<>vxna}LID^cs6O4}cDnaHKCT1PI`pQ+{2P&}cw?|(Kkrp(!? zzHdj!U%bBVoxVQ-(kuS`m@veZvtG6lqrx=Fdw$JlBNC3HN$32U#YZQ+1i_bhehu47 znkUI>eJ{_iA#ta2VUh)Ux#S^ylDOm{tkjz&k6k>!hO>Fn8fWo-Ntend?VYV>aQ07Y zA1AlKf3MvA2(5)b95*!ed4m-V?IXa-MuCP{k)(Q4hvV9U&w{Jm>~X7Z~*fwX2VOEKU_k;Z%`Sdn>dvq!AVO{ums3d04w$3QmlEM_IFL zj#p54^i#+O`go+ffcG4B6AKj5UHFKW;2xl#MEH*_ASU8Z%b6jfE_?k{kuQHoqU@f5 z5I*wlomY`X`LdA09ClAaJbE+gcf283(YCW?!GR06JfLb~05J`I1!9nWn??lY5o)J^ z{qm8tYMkPTJi~by?lA&|ZySw+QGdnLc+Wi>2a#voXR?K7guwaDGeTrPL=~6R<($}R zuFC_H3`YhdWU!0y`8A*thmM&q^O4YEefFjD-QxBx6tk3D6L$;MojN2(d;jpRA9Z?N zYb1Y^ZbfM<@3Az__>jeN?-6fc0XJ}iZ{WTPJHFH+Im%-wL##{q4L5u~gwAvvv+l#9 zb}rBE#%eldxm-aY%oAeT7Yb}E!uez_N!5vXgA%6 zcljU>-7G8KZic6F8DGUrLqEZLu3m{Ur2TF2(~|&XB?FyjM|h)f%xRw5Fm& zWbFGPpsL9X3W9v#Xb=>nKJP)w;__|-n?npup87(2H>6c5-}gYI4M^kY3V0CF7zfJ} zJ zerqujz+8R6Ms0?`Qo;);Rf)X>KptUbT3aivFLhW3Sr4)LCeN(_5wqK9q?!t=hBdPV z1uT4_gbbO-CHV&A@+sR>pD9uN$FpQw@C?A*$V4B#F_9lD;psK}iUtwG1rEaO%ISX3 zsWoC@VbOP;ACFLG@;Z|dFDRrNS3R@H`a3cQ=ftA8>)) z2&9T7D8gL{9BCy^yXSZ5*D)rXA5w7`r~L6F~)P1VmoG^`iTw4XBf?M*cT0b_A@vhd>`coM9oi? z)=NBU9}*dFQzdPKN-D;&Civn&CrKnvaTsub!60WxbS0J6^1dfY@Yhi|m1eJotMR$V z0yMk(w42+1))bjO2-jKDZa{sy8CA&uRMNh0VlJ!JeTYtE>vDWyB-((_SVID}pG}ez zynuuJ=sNF-8*)?;QouZdI)H3qN4BBxb02=ZZz9u1ChK%X=1cJSaX2hi1&_}tqZ&rZeb1MYpP;1D*nf~G z2p&HWPsIOMu2pyri>i-j{9E$82D^$kD1yx9dTXA@?$~7ZKLGvffJMq*`wk3_z#105k5{3f-=^Ze;U7IcqauhuEwYMBl0NSsU{+KuEHi z7HKmAn~*?o6+rF}0h|3Cw|Hrg6au&5B@suGBC=B|!QObLFqeHONBZ)~Jr?%lKDOT4 z!TiG5dMoQQd<#`QKG+g+k9Aflx3l%u8$_b#{5e8^p|Wu#t0LgCvt` z;SsMvXE0P*f+fyjRgeT=g7;*A&&FF~LoC3B7~sEP4#kw14s!g<05{7uz$trZ_4<+8 zFZ^5`|3aEjORz7~28B**VU7ie^&=z@7>0X7Fhk!r==%<|z~`*adt6Ml+{eD9)W&jv zu(%Hjv3@G|z?^yKbKtY0vI=K@;Xx9R6?`gw=G@6h)O!&=Fryq5xK zuH>Ozf?*EEQ@Vmkv7b2!JPOK`E);aX#G6MDUI9PT4NOW@TB`DpU9|?~A!;hs#S%zH=IeAc z>yPZ#-F7!>*{yETy3&VTsU{R?(RGO^SU@E#h%-c>h(dX?zxU_d`<j@Mx9_x-vDy>{A1d(lt4aKEmm1`O)^yg|JXqsc~d?{<|E zs@G~1{xt5_UH0q&$PHz+!=J?cx>uiLgm)2+lRxf*DotbXMS?_lA90YBIK z7u{_BX@&AMO zG5&T4+WI4YA7jyC;~vN2IPP&xv*#Y~BS_ri_>&Y6+k>XvY;AU+{`|z3rf&@F&u+qb zuYCU5`aH_oW3j@LQ>R`3xDBHBwwqw5i=+|hlJa%RZTOk;Y#pCO(5VDotU!$^`B)Mo zezfOqrvN|S`zrz1LCEL|dn|&>r_VHs&$GlBCr?{q+$+yC)907Rrq5SB@hc>?@`Tzf zTVvD7_bp{{GnY@1Gu^fHxS_#S0_@Z)-+m9%XQ?~BX{#6Xl~QtbnF(~&i&QgJwL;yJ zg@*5M!*Te&;RKKGeFO>LTTiUUcggF|ee>a(CQXE<@qCiQ-5NXw0$tqn!`&a6Tb+>e zLRM7{N1DliPDU=_YLm**Zd0z< z8FxhM+CzL#qcJ(iJuROuZW<8VQODcqVPKt%WKvUg)5Aa8NUp;ZaFrm&5r zf!PfjO0Fi!zW6UUe0Flt47ny`Y*;WBlo(z~hX#!cN3hb=vH^P*VIU#vF^?FPcVl5Y zPJZ<*{6?$0C?daFw;DvN->Ks2ohfRnZ3J&EAsDU9RYa<7Jt0X8*`g&&uF%YHzF=7l z>*J^}U%$gTBA$tcg@RbDXmyOm$^;G;E4y&Qd_h@;Q75dn;Iiz5VOmmZuB2EF7tPrs z%%oS`hZ}165aSEgENE;?W9`@!MoZaNIX4+bpM)!SCcE%IX95}Lza6IN$=`L$8I z3XkcD^vLKmJ<()2sH`VapJA4$9xPsWxGod4-gK{$=)O(5z(%F9t&71|a)w$R79>`y zr-H;au8TTH8T|2}@!W}^@%=mLGNi&Ssv>qH*YW$i?AmsY7XN7}x~qXtPLRt3R0l*X z+O2zc0ua7aRw?r4>#ix)vA4AQ&{uJvSLr_#7MTSOl%wY953j1OKfD@Y?^P4^ce67c z(Ihh}jd`lg?Z4w0Wz@!l)WV4%b?#0^Z=d_S&cLPafdxkH-^Ov|e!{Vy+pyyY_fR-+W76Y+=~VW4zIEmsH1GJifL(^A`*XhaTlLdE zV!om2EeJ=$)Q@e9Y}ok@e2Y+d@W zpsN)=Q#b7+;}s>VKL|S^BwNG8w}A1ujVVU7OS_rk zMM{)_((G5wF zm`yWsp=gQMYJ}d)MC>_bO;cSFXj`e6C^?0z6>z^?x?m;gAcw`=XuH_iMUJhi{syde6{+!{7Qao1AXmMZSXfP-ao%zrNrC-+2 z+%e;sO#YZ_xLR#;c4q$1gTV(aKi1>hdiwvmVb;`~E4$ zeZGR@xKHx6p8MQGkhss+8m4idJ@`+KjpPdd8KK*e5 znu<5gn)Z=RUs|ggmBSP$w1Vf@s|B*oT<2abz%od4w9VlRf^rkwtFPHw?-xwCa#MKD!g(?g zm$`fd2bxL2gJe|l1;7Y1Q%f{xBPTYJ29~j7h^tAxTk&vAeJ#;{h$;&n@^HV0Rfytq z9#$n~!L%4xy{~-KC914Aa=+Zhv!K~VjQ4^@c%Q_Dv}jBGifMb1S)e*uSsYQ9<9F#% zg6<&2ha^fezo)>%CbS4?-OFhFiW(5sU15^#7@pYeiQbA8%-Mr~?Zfh0r;SV?csCyS z*V08(cvs(P4)lBry+{F537S@1U}CiF~b&J4Yot!1x&HcWj-do zH%ximE1~>-JcHmib1~*ranRF{xlFTiHC3rv(gFilh99v&us5BT)QL=%#F5+V-7a%Q zxHYx>gIw{8@ITx{h3a1vW_R~}fQ$V5&gdBX+04@mCtswrnHi7p++O~euW|L(H2xh9 zY~|&Hd-`5U$*&UshA+N~YSqo$QRd&g;Ac1U?);I0I-=t4L*GwMomrj%I1^sU+n&K- zyJMvPX=%7BclV(!q$wS%;OMQe=34ILgH|r)STciYec}5EdwlC7?D4ISIDG`EKrXr+ z<2)l5^;JwR)=v@SqM=itMeWO!=eVW$R6h+CIN?Ac0dF9Tn6q&$PFVQNdULRFBaI)? zcfNw!USVg7>wa{yasKr{~}KADsOGQAs;0rA0a#H!oUJx zan+Z^xY>X=R{s|oUv5NRw*T@rkKDFNim)vu!)UmMkpQLACD4Y*puRmVPdA<>d4O11 z(2Ys0E(k>d`l87{v)Mw~r6mp_c8P_t4ACpg)!au-I&UlTwAv@io7O&Or)N!U*(|Hm z;;CoNtYL1gA?*Q657pSz;?;bUz%A(&B-OfGF34{!0e1B6i2^un@t&}inb@&%5Iatq z>uh86N=&zSbC-B?T0Emk7jCblGW7M#OXr7$1Jg(rH#YGVgI@QZQ#E#(8<7Vvg?>4K zar)mNBsGvK=Rc=wn%um~$kCGsa;o!)J^@&4)a!vB0izzU2d~Br@Vh=lP5^5%vYIjZ z>=dly9}7)7yU9R9i{thmExhD6`92LHn;7v1#^gIylocW4AkhK&Gyb+57?D5a7B=O} zVj(-La{YNBB**1yHd`T0`7;(m9i6M_rH8P8EYyVvj~LR9;W(??7auNaAg1nZMmVo; zfpCtZtl~pZRh4zw9`cb`v6qjS)`$2+)4KH(G?Xm<GOvFwpBAq?@pSzJX(v-hoF0qwF*-?E;o$ zBzm#?*!%HV%7hAx)*0UCR@vHz+%4O6mLJ*b2s=rRwxkXdB_VAmQ+4II3PVCFsu21? zBoq?fuW-mE1q|9-#RpwxgI0gtmwybAx-Y-r6Cdjme^wy0s_KdEj1!AiP2CcczJMOO z(bWl$Q%s4DCoF?i-u8xvGDOE|HgsW|{I+szgQ}@cQz==M7bRbLlA~nqn})oT%FU-2 zC9h^pezg;CDGio7XyT-bM*4Fcjyv>6A6^pN;bbYGF$blU88hgBy1cH25z0n z7SLwu1Wfmo^Ce|~f&LZu*08=6>5LL0zS~bWMl#CJG8S>P(vu_R zuCe!STOqEsf}=HdLtU`(?r`>ln5a=m-?dX?h@*IPqc`ky{97$}mOt9@akZgPrI`qmSxQ94Dc zYLxyC1M(awZOn0Lc?dyVLlwHgP$lnC1x974f;&X3n0xhidK!>MxDxP$^ zjdBev}=5sh};E@@@m2Xvs^M4c!NQu_+jomG`-PiK?584KgI4Kxh z$_jxKNwAo^OLr(=IGr_v#pEck{=Xcz7oeg zif#(A>oy!0Gh{IY?IwteGm5vy0dM8&$vMQFpxmNcv}8Hpr7B^Xetehp#R}^=i47WE zF||=#+qz%DWSWU?`a0+F`Wn(YY*s0dXeWO!DqwZUYb;_oA(=TPw#@G5Vbl=QS-5DE8+qf$jICmB* zhFdBVm)2rQS7jl|yvI=-YF)26g3$;ItthJtFhdnaNngg*5m&WK4N$vGfGqtvpS$y} zn%2A3=n0EN803^=$Jk`DGkT_D>=!0H_FB&VL~6g)(yN?knQ9Ujij|H{V@*EI2zhEA zLj3W^7JaSqhvz>slP$7lrlvP%qMH#9N_ysa$&l*jI`a8yX2%PD@0!K^l%J#dGJpC^ zoHxhS?TCJpiS7%>?&Bo=#;3!vpC`lF_q0Y&hqIsVj2_gKn)Z{cD$SSd8aSR9V!uYW zN<~f+nUVWg)0SsnhS@!-(-Hkz??2^edPRb6Bf1YMi3oKd7`PUPmBO2&chAIWjt=jC za%LFataF++w#Wm9vsZmf8EqU33vMzNWkbKMH-@<6>bFu^Gj_#I{LNS}I2%0cjGo9u z6TU37wKT~`3zRN#u~yZP?{76KymWVjD^<2enNjNO-L4K}^6;i8t= zKNfz&O;`?=XH2?iM7im#=;Yh%dEX^(jOi^CVPfW1t>NpMn2W9&=d&%Y`EiJ5d@k%W9N<0EHQB!x_C4wN*-M#kqIMq4GG>`?Yp&)OH= zbMgUw$AzE(G^u$k(|B_xIAvRgS(wR39JKhUJv`*$Q4jZfSf5LEFl4U3@ImJNmQF8Mu;l2L z>VUaBE>MI-_=0^KnXVpB3BNu_ce64a%R2< zB)CvW(ARJY7?5eWNb=JZ_R&NQG?F>xREYH^))lIpg2cA+37B_+fPB|Xd@oZJ!!SaW z8^`x5WVc@r4xXrPNBkHiUU&%cT<^vVw@XQR3selhBl>ZWO(E=*21u%L+$^_n&`A;^ z%umadO~{=C5+bC+4(FGGs}7Tgx)?4Zyp0vXp~u3pNvIq2Zi{}c=Z1IMr%nLMX{|%H z6Bc%BjA18heqb^!{Xn`zT9r}-x0w)9dKZ4N0(FM)qu0ag3&GvEFc0B1h$_5>wKP$I zfNo;&rAzVS#iyjomv?8 zLQtP>R+zHF-<2l=kn6ap>w>8F!c5d#8%E1ex~36!mL;Q}RBNv1=+@{vV-F^BUB@nI zjlyHx>M!bQ3Zun{I_NCJsH-~{BGx1jxFsM=6}Ss^b6;;F%A+`T3cHyLVv#kzj^l)x zCm?gv2Ytq+`~Z#Uvr87NgzyG)e{l+11s--U{Q!E zjB*~Jvpo62uB|p`h`RM9?7G)-_-!@4znJ%2n%)P3a*H(fO_D1aQZ!IrD}+v&0bVTL zd`_^jwc-4+iA1pJoQ88}^A`Flzji}j5g+d{u}YKsG7+g-YVYp(77v;02ED`VaDGJy z-7a;rRC^P+;)CL58Wz!;Zim@vE;b?N!eMYF;p{NjE$mECws0mFb7Q%~vuWmyQB^=x zqT6IXySobWP31_bLH5(6kyh-vh^F=DRi7^9Js)UH4Oae{#(RQ+H|m&tg<`lwhI@!~ zl?;m>aZ>f1n5;wyG1(BbS&Q2l-DdaU1uj6h%Syc5-3X zE6?VB7)u+$20hN|hG(4(k4OR>*G@M5+D{3>+PT)xP7pV&R5jOjJbW9EX=tt%Iw@(z0OyKwP+3EBv4RRKjHwl8EH({|5Q9cG zX`v0z2~`Df%?J#*%flix#V_{oP7il`ScFG(4-Q|8T-C)F`q&8%UxI7gVMW75cxR7^ zr54pfzU}pi$8j~2FB437zYpKx;hcw424C-#bEI48!$*C(5f5+o@Y)!rywZ!=y6lJ^ zGMW*kIGPdlbgNDpQzI9xYD69i6#Z2XFmkw$nQKK4`c22!!%4~a zVG*s3zx0_XE$LV$zuX#%P0&rhUcjqp z&c(4X%@NZ%*M)m>u2@G`=3Jf^oC7Ob5 zlIAj~G6z#dkv^w$FjaIK2NRPJ4o%}=uB4cQU2ippg9QUw((Z+eee@00T zAzrPdHfsi4CPv}t)XkHc0{5?~dcoKhTaQ-LWfXHsF1Ix8EM|{ERW%p z{{__;f4@hbDl)d`>4NMl#K)tD<3W_f?rEqw)!=Lrb@;&eg@(f5=g zoKECgh@cgA*+|Om=c}O6i79HU#<#pAxZX|tmqERbbPy?ys;XzqL!X!a$)OcybQFCx zP64f8uyFu}euDP;-7e#BPE(}yc)BC{Pf0SNgc!t`y)pxh{sdVn;a-@67i;_y$Cb_( zCf4}Yt#C|1J7Eg0+6Zf+Y7%Xh{&_W1IMfnVn8MW!wGvqe|BNBac?vb;Fpf=2Mb#SY zw9q9+z! z>cbLAl4A41$ZVp zHI<1zgtFkBh`{e*{&*(>@EKj!HTpI>6*2g|-C^`mgx@pJWd8NCO!UouMbTTX+<}}2 zReTr?4B^~-Y6B|T9cUc14CDV~ySZ7YynIEjHZQ z1y!;A%VyLB(HX>)PE@eRFFh>#Yq#AoK_2ihZ1ui_t@IpOwNZ1o#+)hdE)^Te9-Fm^ z^C#plI{#d}5_zLjldZW^8!Uz_mqe$&ZmgKdo%(u(80zm0ABb~gbgIJ(m*-TxAdN_d zI_wKSqHhW>O7^r!^pyDVF11umL&+RBS`|@`RW4D~#g7;$9O^dOR%J{#rTcoFZt6Y1 zu9a@ef9AUKQKc>S;?ypTQ&tKn={Ywz)z=R(HQ1+doN}wivPQY}ZGyDr_Swf(76D(i zfV+yzEx?`ha=+4dxiequtvj$Hbh1?jX0TFi^~;;E;KCt*;%VG>2L7k{0$V!~dWcs@ z*@asQ_|bGTBDc{`JHSH*qkgfu$OFiwekJ@I>KA9ABW-z;wB<=Hg+xUN;Z=rqNwO3R zzvyts!ku}}8wiZkYjyf9r~}q*pEH9d#pO7{A@LNx-DHXwXLr@OqG3cIn~n`Y>_PzR zf44&@0FyhvO#>saO#Ot*wlvGYPgAFk=;rCY*s@u5E&k@!)g7s!R!54ZSVz_Z{BL&T zf>%^`WTTqzJ6;{Rl-ljRBPDqJJ{{>NnV8yiNUBW?SNO=}tC{<^wp1+Xn4EfcLM~&} zq|QzGAx^nDN^gda5+9`2ILo!hHO}a>(Vu#1bV^o@`A+3Se#%GN@XAYJvTLldy!|)i zCCuc3J>FKT*f@p}CxzO*Y3!opk+Cax)#GS0k`<<7kV|BuE+GlDwSQB2;&LdHGMn7hcKj}0c z$;nG91;#OOFeGRxM3VpW#S6pL0#&+SF0d*uxEEK96k+%%7WPS`_}6Mboi#s_eyg;V zt*rH1Taop{uy9s`bOAiR>Nn!MwW_c@o!U}eM2K6R>86$6r+~+`o)KF*M~ShPDKxg`o*F6t(Vcl_pr5rLC>%+#qp4 zkT^XNB;G*>znj@8HbY(E8O7C(vA{6Ws(j*m)m@Y|Sc_yfc}}Iw>Y4F8IM}VPKBfR34>h)eZ6Ugsvosqs1{hw>)X$fU$qc&jGF6fN$n>D*Pq9*FFF}B z@1rpbKO!cmFnV>Frp(k%+)EtqoR;&%^N?6a`22Kx;m>jHkct>9(`uis-%;Xim9K49 z$KMJU`LR@|EPch^Mzr)(q_4e2fL!)^@H86kr(Rn9PoZHUYDIlEoraSY8h&l*0ZGG! zx`9)SgFl1gIQS`(CKwj#?wn(C-M`^Dxh@sQyfI-hwXY=RDB4&&*kTayzDHt7e5-%q zrh&4EH*G(2JN3j)6&oZxwh}k`G*heHVqu6Xk;8&afh~o34`m8$!&mG*OclcFt|Qu6 z9Q~QNv$S|9j6%iGRcE2alg@LA&ze@*{`vhy}uLKDdHYby@MP=iW?o%$uHJv4m1EkWY6WK3-FCYS=76)4Kl zLV1LbVq=Sz{H1Si$)A4~+6H?Jq9gj@L?s0~G9NVZr$r*{b=EIAZ@NTDl=#|~==`%XL6NSc?&Jz=2WbVQMvv`12Nqr&mU@rfaVL(WAvu|hhU%H(366#QX-l)Zvl6GD( z?mQzeGixrR{F$Kq3Y&APQ2tC%-c25&n-MBlLHV=a-X@f1j5RC2Hm*l$Bb_wwq0+4+ zjz?gG8fm;vOItq=0Wv~?22P`QzDDl@HhuIHr6NaFRC!*C=czR|c9usC3FE;H(Fkdf z#oIMygwg3eVf1|wfRY9Er!1D=@=Uq$mT*0xGMG&aAL`%Dp`mwhXz275>5-e!(SD>h zj6RW=e3|lh`Ch=g=X&pzBDM!9&;72rQQUBfst3OO%>#|cUX5eMC3kagE)4c8ZUT3F z`tN_{`SiS5mU0e`kvhJP<3yAvAaiH`>Qa6H$J@VJ42f3}WcIH=ohqVC zTQ9CfXtGDaf8LyApksF6Z4L?@ID) z8F;cb$gZHd^B&?*T6ToKsMeO=;Iyr7W9(18Gs5}b39|j_0gpE*EMyh>@!O>pa2NZz zW!#t1)N0>j7hM>j{u9!bKPtGb%i^21O#kd68F#SW5%MWR!_mfKFp#b%K&v*>DQK{? znsqky>ECyZT{g{b*RG#3oNuJ;6Lz_@Ll5su} zjq~j&`zG+oB=&9>F=40*-U$TVOMfr3IDCKFRH?P_W}2RJuYuVz(RzY4rd+f{>P98o z;gV^gsALu$vCIWdKYkOt<_BRmri2O+;~T@3j-?5dZOsifGz)d%jl#v>Y184NgO%|* z%aF6lua~`!GR>WLY-{dgwXL~Dvs-f)&&jpFq&2tXpw`^-y4Kuz2MhhhfO74Dyy8hY zcluOpD%W1GP;stY`%C3rW?ojz&f+xJeu%u?7HXHNEH>}UrS5CWwI8Nf;c$+vD!fAM zT>C5J6}IQv50`huyswnE-@HfY^X6Rpt4wQ{Yk#%eo?QEDko1~i%=jvg9W6wBY{TBNv%fe zFP9OOaeelfShVa%w3*c`7X?ta@SV=sPTK5P^9cTp$Q5|Rn3t7FnqFOal2Cd-!GDyK7u1iV# zktIrxTQwvj{0X)8xsgF;oqP6#+W*Ii`VOBBseT*umY(}{j6TN&kC9)8v+=)C{?|J{ z7Ti^0h+&Gec~Y9B!7v*sY*PYaD+0Pd^ofhOO5Ek)30%zUJH{TW?T98h@SW(09_bi+ z1T=n7yYwO2Xc$RH^b2-~Ic)P_$Jm1)EHuK0Ids;_XKb=o+j62&(P}R)RTv?PDNp22 z`kivFZ+Hwx_1lFj%K&=G3H#}sYgsGqqY#L$9{HuYe1x03x zCve4YZC-N1oj|b$Qa|Zqa}IpUgZ&PC+Jj;ZB!0$&Jq~=icjMO` zl}fW1{~Yh{!v7NQ5An;m!Oe4n4Rz*s3mc*i_FykApzNzOvy!f+F(BMjm5W-{R0CZFwkxp3Z;xjV#Qh&>UAeU^C&!-~9)Kw)ZSV>e7VYgtzXW$_pZwL*X^RQtwun=77 z4W)sXdDw6oxW~hW)WE`VrMF!t;Kjfk<96t+2*Yb4R{DwUf1Xd%rN7ZMY%aF#isu znX4QANq*TR4!-s=+2c}EYlC}DK(M8V7WK%lj)Yls@sU`~+btspE3-7P$k7U7Pfi74 z)$uN@Iy&^Z;t`Zttz6x(7(ZEsN&+Z1x9C{R-_p5@S-*UY^~<6;tYKb~YiIqkg!Rkv zgIUAa!bQ~N7cN7pkz2TkhWx@s{A#&C?jM9{?QOVZQ z!(g2LMxnRqWFUwHVY(bmd+0xt`aq}U618}^1@;wT^awXFy)F2ZB~!g?IASZ+p!xZh zk1OgJ*B+;wPy**u%bjl}Rdhaej`Qs(`zG*7yMYBDmVYibMG?+7!M8@SDo_I;pjSUM z*_LInAo;rW(_{6Muc<}1wA6Um@zpXQq$vYytAUYfpuYmZ8_QXzGqbrG$qQL#wO1=p zn2rfK3%`UwVY>qn_MGLN1Cs)U^$vu}{V{s`)+9DzzhaT-)>DUY>=BkP?y3!O!?i@` zFci=j$~LBwTJ*1!It{TGnX!goKA4F^p*u-T+E`}POYF8PvOJs!Dp+GdX`2Zyt^KvE zpxl8EZuHGWzXXYj%Ws*k(>nm&sTj&GYG1xFBRjhE`{5J!gagmMt;IT1_q>FKgN_cb z7%P%+-fhJrZeXX&>lw+MB8rcW5kyu>(GlH-fQ%@8I}$a$JdWfs7xN#PS9U}{La;`Z z{sCe(qVyfuB*cqw>qNwLRYzDqR&pa&25rQI2rkw}Y(yaKck`hWxYhfbwCE2Ffg>LEj$~9Q#>wIQx70P;(RctXDd#6PTcbmeIO~ zub0C>2eU=t-oaJQ#BpqrB)aj4K1oba!~99Y~LSVS zVC&Iox+6xx6i{?5DS%8lMgIZC>tBB};O?4^BRT86-;3e0a_G?*&sG}R2{b$#Duae+ zqGETl&G2lb3>u#GmqG9hYI7-}D5y-sYSOaD9Z`0SJEkE3cf@bSMU=MI`b7sz0Gw>> z5z5KN9-(ZtNdAUUPB!)kq!h7 zD?R}y_O~+IexpCHAX@E+Zk6F^QbcNR;D}p?1wvXjvny0nJZjj9Zo@< zq}Toa2O_Q3H7s-2`~PtACw6^*zcR_!)mbnHMRp&0r82m%BRZ2=`tn3)bSd-mX-Nu( z6V&LMao4Dh==>gS%a0H$LB)E770IZ<&VU`u*Yg+ib9TtjnR-d{G^Y2eOryh8PKFbH zSKKHptg<`D9{Ui(=XV?$)4<9to(BH$zb$6-{V}F5x0tf=H6C{oXIwq)&fo|UH5(b> zA)s_Cie;k0iw}<s@!Wof7>SBe2JcD@?1(;Cs*&>^74>et%vE&&M-Tp%C`Ae#8Ngo!2qDE*^^gm9_BC{ozj=Dhvsu4f9zvOJ;KIVI{^4FO9ZSvXy%UG? zE=^p{pjyo+_stLxE4mIo8NnNDYHjG$Jy&Z4(2HlnG5#69mVI5y_+Na46pUY!pI&~K zjvZMa8cj5MS&UEUOUEJLl2?!#Xoo)9H0b+su;Is+;h^iw!G@oD(E4();T}M-Sl5>x z=0=~fM{3=TbuIrcEYV@LvT2zP(MpwVS&hvBYRFn6f_p4jSp z*s!(S!wy?3J?yZx+`|rAT~(-&!`4aQ^C(>R*K@s=iP%MikYbddG;O%m9^_xU&&^%1O(z;Z6`O8*Wi| z(C80cqOgiFbwuAay6L(!4nX~M)gr`p4o=gXR{NBnABr&A-w;vQ^%->z-1)$Z;V8m>?I~IUGX)#p-(EhyN z!wgYV{OE&!==k&oT3M0f=ThlvIsOHFsyf>ae}eJs>;0Y_cdmjpGv>tjblSgpeCqs> z#e4v?a!bc!ET#&drrD?Nf{G=~rOb8Lr%1XUWpw^$?~nh%y5HygO!n#*&dK9O8v=V$Ex9jzfGoovPp z3mM*Z|Ku8Nc%pN#1;StIia!7J9|!oIF_9&dhEQJ@PhL^|L6g6-%4M0+BQ#ecO)m)^ z8SS{9hMjRxqGSHWB$|tw^cHG5| z$ieUsheTQ~v59Q=L9hdmvTZuMG1l%fpsGC2XO|mvw_3JNyK9o ztvOYd-2?7Da3uxxR9SbLtE#zjO;sK`WR*whR_|2}s<2&m(8$15U3Z{yWXS4j$K4vB zNw0G>$7=w!U2DoZ4xq566zCO6ac`@K)4Cdn3p+1FWrtF9vHG(+1Rd6r{`q^~l=lyk zG!sqgC7JE!ywz6;G)HDmTal+3(Sw{>vJUXBXfe=`Dqf zWgM&h;(@OIZo}Z8Nn632X)P&@@OgZ*phFIj;cuysZDqS2o&8s)+xFy}u#XZixwkg+su`aw^5?y>@ zZUJb<5=ZJ4p~oK9GV5vK8HIy*d)VztXsB&iytT}eP^wwW%wc1z`o6{qPC>mW$OiN* zPag+^i>!;ea#Dsgx}>N=-!i5twGvdC`&^?`)?!SMUVv#ac6z|H7*ga^YcZsu(@tC! z9qM8cx=Tn5rFGd`gvt<)({hO7EFUy`rs1o1SK=7~>q-)Baf%wXa5h}f{e5SLg#*)~ zr#r?>3}TDyNlW)!3QEOY=zJzvvz{Qj#-Wi|Ao(h=Ohu{TTtEzERl zRN3p%HnYD5Kics{7pFazv|?9+vDj(SuZm!WRUjTZ`{mHTBYncU-O&P}7x>o`BO=Uf z`6cmLlh1KKhX#I<4}W{^0k2_eQ;g8_?9$h{hp2)`bSf9`Fj3s5iK6rgy)Rf`AEz_g zh51a?{Xbag?6{_C4hph=uXe%Toc4@W_tLvC&gmG~U1gkedf}jx!-26BmN{4cqbzga zH{?t1#dia1-b;zv*-LkecY8BHA!&tJ*NJ=D6*3B5tl*e&F7eaW`zq5`H`7*Wl2%kW zm+(HVJ(1sOz8j~Nao>r534JT`-AV`f`A&06=|s1K{tvroUSZZDPu6;M>$|_;g^Nc= zO}O|DjuS4@7DM9Q1ZkiA)-O&KF7}i!Y}3g-{oSMqv)Pm$XE2&Pny0IP*HaZRxo{ax ze@&|p&@&+hjLv>ihEcozLE}m1_C*yPU54v_V`h7#0pXQ6G~7aXPoqxQ*Mom@GTyKN4Iu>%}@K`)q%KNFjG8_Rkbrdgw(at+flgDwqEbhX=0CtMASUn|n^EhAn> zF8XmHnG{rY6xm$?$XA2*b$n#c;~qpQ+%tSgDxrWB7h-iBRShJ-fm?f>_*@6TjQvGk6ynx!fyVfVZ$|ZIi$Aj+3L1Qe(4%!g| z<=szmVUfxg>_*+&*faWSmR_E+PQ+YBbq*T$_ST)B_?Z1;M6);VObSTz70s`D1knji z_6VZ;P-4IaM#4AHc$ckbY<^hGAau(J;eE208&Pg_u+cJnTRoCn(Vz>$;0lJ3E0BZL zF)OS?WJ#a{wqo+?5fYyykOmF|3cZcMp@H`c*y&rHRb=9vhNTJ|wUqJoiSeCP{!4vC zd`S6r>sYkH7=sE_wJ1>5g2bv?M9N%HwNtHByIR2{R<%g%4ivdOgv)pPXk|k5eK_Q$ zOo+T+k>sU3lvhzOc_|YTKPWKe>mRS5kme@Tp41w*kK4G$ojuULtMqxflklcbE zLe+?9!3AoE>c1pfaFG(I@<`>X{|H`5qgF5KZ}p`9;|i}|vXEOS+mP;0;Jm`?kj3Uk zi;}tX8W8v(%l6vI1l(fzD3#*gFg++p9VF&!ww)s8vm!j|-)$B@5Xa5ppZL6=-#@(B zVot|#8?D`O%vC#0CaB@gUKi7DG0gn4l$-3eA1vPX_sVBsMWOJ{@QPnFYt2xA-%zKZ zal;4|3b$of;vlDDCW@M2cn59FMA-?PuuvFR?F$xI{47K>utgzE)((rZJAhcVQhbzE&jN)WQp$W*m`dDw2Pq36 z4K^hex&VWXSsf|KWrz9AXWzju4^VE=N~eQoTRUcN3lbZ;El%Y{*#j-b#toM#gBB+bbtQh!t$a3N~Ve>Jba` z!;3AuVfA&8vVjXW4qpzG8$N|hf=$Dp1XvOcrv+^s?se%$Tme~)Dxn{Cp&FL_@JAH3 zY4yi&@>|qucT?Re7z7xm7K5(Ywf$Mu46J)gac zUv-mARyxM+2kCCiT_V4%ws8Doke)UuYDL4cvOhJ%>!-9ga0KQ=8 z()p2KQ^Da%C+6`5hY|&$1PDGXM9CMbQNtSV_=7``yID^7A?MryPeO-zV-mU5!oAj9 zp+!z~jqp;(J<#}E(~cjOx!yUUIUBlgAd!{MSu7`Sd^F5&nGjScB&d#VHQ)FdEryKH zwP;%U0m>9-y0R?J@CtYGJk{$_h8YHM1|v3T38p zYmEX4$*+qzAk`O0%~$`7t3mtCyu}hV#uTVN+9&ru%@*uaxwkVoD%0kY`nX-N73B#! zUpv>i*?m%F-q0tE+7`Fw3k|A5-iDYKIYwSR4gc=cx<%9qv(@}d3F<6Lm_n@9*SWS+ z7zsz7ZE1Q%gGR~`IvGgRnalnyzW}1#{1%ZaShHCPHVs#pd9+0{I^;E^@v)~e8t`SJ z_oI|8w&t!KBDysiZOvu>MhRoeTRnyHb`(#}i-fkQLwUvqvF2ZuOMZ)b_Iar9(T_~~ zv{zNh59?Dp!3t^|hs0JA9t`{tU90KKz6}bwjg{S10tWP0`WF(qhIyvyWSLu~{SIgU zfK06jgeGzj5q0x{aBMv=9h(*yWj{v+=s+>3T=r^!*64}0=;qF7ymV$Ab&IlMhmcbd z$MTjD=ksLVmM-Vqc8bfkW9;4})VPaW9=Eb*WTI<#0tJI@v#k%*ae8Koxp`ZNn1z~1X7{DKoXKGei)b>u;=UT>>rQUhxZ-}!H6dvm%0+ixaj#TSEy2dy zGqe9YzTtu}e9 zP2}2TO!{OlF<3wigN>tm5h=g>%{>JMM98>+#G@B|_ zdi0(;YCvivA8h9Xpe6^6I!Fy9PR%82jTF#`)!ujo&{`|V&F%&93T;A$D0QR4W}-@3 zt<&?CeKf3mMPQw?k^V(sye|0M1lHDDp@EM$8puSq(%^z5n6~JS(jy=_9io$I;JO-) zh`<)oF7)_>c*5JFYI8=E2;*!^f;+6uZYvAGnp|+ppnS+m#il&a+Nj;;py!aCj2k1F zZV)@(%Q}eetKzgVKhFM;N)csZRQ3)4y2^1^@kMbhD8y=(cU_oYEn?$GL+! zx$Iv8AQk9IN>@|-EQ`tVdUJvhb+u%GVB_k2 z%HVKdB?TKLd_b-{aY~oc6&+&_B&nmtR(Pm-vx zLp!5za(dp~nfx)cVOxNX;{arTE%|z+qN{`%{xY+u?KU_= za!J(2W5Y{#Q1g`***1aKXew$ehJ~yM#w?=R*2Co22lbsh!+aXXV4?u>-6*AK-lvom zIAOkRlu)Q^!smZ*YxMp(VYHBMKFNM|TQbZqWH&|!*H!!*qlIr^*i2z*G6R4_hg|kE zF1y81TN7b|a&0G4nu_5=uE4XopVGGHI>%kRYIAMhZ_Rbq=FXkXH$(OfN4iWD%e6J; zvyuXGZO5Z9S&uIqumurvL#WnoYflm=x7oX0=5E$6Vga4k-g9$%FZ1233x9%^tNuk{ zp>un2==%rMl)7gT@1(uc?X8!>4(m}TkD(gVMH5{jM!nZPEKm78*YL<4i0E^Jte#8u z9I1HvBJ3YxrB{KgPiTjDP|n%~AWr0@Os5lI0V-8W3!3OXb=*6WrFAYri1h_J*kagTbb@H!g|VFV43$MeVGQ+m6q*_YrskflC2T z%(Y({B--X;fV~*E7MCZB+O`LY&K*HwVKGRYy9?bX?%gimk}ABF73Z7O6U348DjqVD8g8!5SY7 zzLavHfVGI8!JxFdnG1GmGMGo#AM?TQ8++bN@MQf^c*Qowbj<%vF!&{c9v^I82|T0k z0d~Tg*W#_|gQ*WTkKlu6uX{^{XImEEV(}~t<6e_`_<_8%X2KkItI1*PeVLucd^|h> zszrdIa?8N(1g12q?7yoZZB=HDd@>mP4+TZT>L-5|o+w+tw`@^D&BZnUp&wG2Ky>yk z`Y?Nq-HKt$!%XzuO!OU1f8lIfGu>-AjnHk)Lu zq;0mC_ud*(c<lX1h+X}p<^xmD?1sv{Dj@jG#US7fQ3r=idG7O@qZE_@QB zm@TSeO?3sg`;XFmBmprqbIW>VCn0g>xg&99$~E2x4=(fG_LuWA)l0DgVbXW9ZPcC& zQf}4O*tVX~QRN@Cr_hza+;yvu_;-8SNsch+_658acRDGZxZmCt)PD>c2Hkk3*o&j{ zKA1Npx5zqX&LZh5E)HPqyZusZnQF;vC#7;b5;p|Zx8@dv#EU|6?OefRk~Q1|-&#v` z0XHr6LFxe1bSGNc5n<^SH)>14op(N0FKwN>b>7{2>=qRhn2_GEFLe_7N+;UPRmiTc%8PdRTm?+l4PajzXUf6b!h~(Okvi)3mLs#hgUU zQSO@q%inxLF-Z!TPY^RearPAi1O%ik;I87XepQnvtXee`XEh|IZ1N2JqB$&lrJuS6 zjeQMCkUdSCi}&4f0Q02+25xMgGF~#Muk8QtM}FPP?;AHApefSwzG|O&5A9F=j2O3m z@>jd}Ucai-JKjE)`yLi*H!C3~7HzXTAKd$gW)fk;lxav7&;PhIG_7hgN>wll+X19X z%!JT9`u*tw31zvcxu*E)zpVb?SlRxhv}3I)!L+Hj!@^CQYJ6Dp9v4NS#*sFa{4sZv z<%>7Lm8}r7C$DK~i*tltllf7PLeL#hq=l?&=qDSU)fawYHxc1@%Nro=WCDEy$12^- zM|NLgh3F zMElqzpcQ*ofF5FG>eItmxjJd{yh-V?4iLuaGL0_#e#%t@hz<<4Rg6MR+dAJVHd=2? zl3djLu63fS_&oO!zHVxPRyHeZ%MCKb5ys>%Y&cq$%uNjM@eH6{L2z=}U`>i3#pT{?)u z9-*ski7q*)ExI(pV|0hKM3>JkT?iRh-yp{^_6L*5LH0`R4OBPU#w+JRb`xH`Qu{P> zoV&%uKh5R=b>95(AbYps{SfFXneq^5bLY;!Dy9Aq0gT)CB1rYPeznvnFG@? zg>q$Q=QbXm!`e#YV}@LOialJ0+EsT$Tl9fL+M+Y(`nym{=T+6-t(lx9RtWK0OZ=*7 zSW7Fdn*LdyJy%UfDIaXIsX-?GrG4!PuhYr$}DESf|ZqE z^{OmrIi#!r7R%6?OJ)3J zGKBQX;J%;Ca=xzDTh>(aORfaZ^F`PFPw`sf>%gpF;|-y=Y*wYkyf*(!HS>Bkfibfa z^AZ+%Zfwj;ug<1gVLPxd=Da=Fmvu$VzBCqP_LcU)h2mvh0G00S1(L}otn50o%qDtHR1IR3>nXn3Y{5m}t++E&(*EwrbE>Rs%-F3Wusyu5r&%wX-p{ll1%qKC)At z0>kJgVHIQ5^Z|2<8LO(1t1wp8qMEU)8bo`>s@Cys0!K1K->i}%Dzr3s9ahi#yys{$ z6{=wdK_`0JlzWcc%|H@Yy!nqkNh_RC?wIPS5A-L$X872zK8XElm;Q9yi@c=|vO-(J zL5ECa{0qXed10MRtwu~gh6-)oKTD_tk+Y0r_Li>HmzKI3V($y3m*`L2WYDb#LK<7-4f zsg~>2;}tx}?t*KT{ssH+3X$!Te+}{NRQ@Gqqf?FjxO?)i5ke~jCk|0s4*!JyKl2@1 zM!fjD@rtGMqkhG5|L1J!u?5F1J&v;&Rt>)-NXv*D`jm9aQsIhUtTmSHL{Nr#LqhPj zY%Puq!aB?#EDhdfBBy^X6q;xa1_%l)F8{0)^a?p(=^?C(x0rOgaabGgrT6iJLR+mz z&o@DOAR#=^xPy8fGztX1+D<;MEh?}z=&}q}8~Qe`UawCYhevRzX<^8@_8<_u0!2W3 z)uB+MCJa0|^U{zNqygSqs-Gm}C_egUwvt+LQ%+xDEv3v}MieVxW%Qd-R5I2PLoS8M*~{?Z<&1qHCtUGG@s=v4cfK(VH~$Jwl!0D z%fqW>TKJoA>|ribdxm|T+RfIRY`Kzd{S4n_Et}PmPClB0&-6+uQrNq+50yGzO4x1D zaFLJE52cBr??M$-DD>kLzx*L7T;)ArsWc`z#YMW@Gs){;OJ7q;>Qm#j#8gEEgVIh! zHw-*ABgj5Y)W4zd9$rQw-p(3)OleKe=8gY7V7ae0M48WlfFCHCs~7!PRWFHh2U9i1 zR&{g8=0-y5|N7ZBdO`w>UDPb!o|sl{c*j1!y`4EDZfJhBmD#v882A+?9|(AlyS8ev zNgwx+v-PQpo`=B4p&Q@zIBIO^NUE})rW@qrP%Lq0TU!FR8#mdS`($rx?)@K*mVSg0 zO)iO+evIAeOmxvQD$=dmN7;Vs3TKrRnaZ9GLBgCw^7iF(l9n*YglCXjXjr`RH^55!42tS?cTXU^t3tyzehHk;Fqd>h_h+buCS~y3|{P$yiM!tr|=PRkNVp8VuH{r6e&jj(!?O zTXU<61i9Vcyj3@HO)va{aC^VOCa8`3nj?<-yynfRa|Dw?+@Do*#2v6S%2tD^o|;de zTl*s0z+~Rggptp>`C^0Si%@&fvv1}zHJ#6_BhXJeXa|i$&pD#cR#~?JEIfkW_-PiDYK>4vyTmV1kk_C|P+r*s#ZR z@R*tOHQG{^X-de(oG!N968$iYFk+!$TWVF-yCDPFR%_ajQT%tk&YBim!KT4Z0jct` zgHX$z9R#O&x8WOmSi#nsf}0$5Gelb&f=JI5$6zYlqE3eiUtukKjGk*&tMVJHSEtA< zEt`p{DIQ;uT$oUm%;`utQApR!1y&~1g)U6Xfn*#MI>;4}?Q+msoJ{m!=`0l+*`6HP zE>^Z14ej_|nYQMyalN_0O|B2HCdCjg?#i~fAN$Q>c^0M;O{z+SF5*-0kZ|CW?0OFV zwY=V3R9jqaf}`Oc6>fK^dP{`MxtWcVOH=f_u|2|*TEqM81ZS3Tzc)*`<Ft=}HH=A+>!CC$dc?I!SJl znvnQJavo>NY`ueoqGkEbh=RE7F0KoQSfA2~yK9H5_$;qF46P#)0x} z^Yfi2yDizMu$2go-_AssO;B2p{UN`d(a$CGc?l59N(yn@)-1h|;krqciS8mxVS1_C zgqlK5q3qM*K(QOFtMgmd0)~u>y<=w+c?WylV-M1%VRdJvADgU}Cj1gOt~e(V)7A;g z#l#7Qs^}n9s?59gBFaKji%HoLZRav~Y_hmpU=p#&79a4Ht};Vw>!t`B9ygh>&5*O^ zU{fl79+~_fYPRv)aBd|K`Nm#ujg4GeV`)V~cqSTWS6ev5^L{y##6bjWwxqOr<8p{j z!X1#Nty?prT26g>Eo&aImAZh48-rr*l4PNK_=xI`uUn(aIu&^!4lRoB56%##v|M`oIm07>*4Z; z1h45=VA)?i0i_0!x@iwxDYRmip;QIb^c|7O zx3Lna+rlSU?_za(=$j}8Jp!pZX8($*)phZ;PEs-P#ZSAL=svo>OF8C;$LYVABb=s| zL^m>>+X)}$_M2^P*SLq(b33P{({r^-DbMVK8NE3aZD}GYGiHLw5nEN*YQb|j{5Fm- zL(4K6PATX7YPG481L2McX?2o-j_4=8;htzpUu9rg#_mrLJVAmORUC@(m0FC6nZq>9 z0*rsjs#9xOZ*c4RW0{G>GSYg(Ba@NJbGsd)3bId#B);gVmD@B#fm_nc}%7 zUDhnCzRKVqR<(aSunl{|r-Q-wDA)4*-V7ROes5+>ONW=|cSbC&BJ6;DL0(!T6xS;Y zGL$-2Ok6MWyjz~=sd;y6$3gRmQVNp?p2CLZE4Cq^P>0du!QkI1$VpZXy%lAKE8kK& zTZp3{*+pToUnrxMPg__TDC=bwoRh53(t}eXE~-Aw+U!NA#(jgPJYrr~K?L5rMds#5rTzmfWXC3=pGgi9WT)fb9>sdq_#_xXWn*B7> zgeu_+g__msGD|VL_Dud75nZPrw$^Bzsg%w>)<%oUiSpejISOpVLNe7hS~Xxsi|~m-I;_eKt{05BFl-4pwfGgB!KroG$ z<46XjlLCuBx#`#YQdr4O5OcvCPCRF~=CA#iHc8eRJ) zppNK!unSv}d7O)EVTNKlK`QB|l~Dwf*& zoN^o3qG5u-^xBo(mgDt%5*Qf79+Fn`*L44HX7OTxYTz{Oe5!-F4756<_@Op4V(TWT zrcdNO=TJx9#odQqzP5Inim>#6O5Hv0@rnv@ltR1sTAa7^mWRb4V`Y{N|MRJ#)Ky^q zcGbz{R8d12V!GLMs(>D=p>|U1SPdnWQfci&GO%@GHlI`^Nf~zwEB*2AX-kUVR&3lK z44oo3POrX3bgGKnuPb6qh%pOI1zy9a#!lv1}6AR|Y zYCe&zS*R_{AS(^M!njw78y;_?F2WBpAeLepqY-=d5Q!M$ky{9<$dh)Vj`sU|w7uD@ zJH3|aN+aw7(ja1nEmjFC+;{{cx+`b2?K(mjl;EyQ$$w~G{yoQYVOi9iZhd%=v*QPINVqt1CD1~Q|T>$-;R=5j*_u1q6}26NNm&vj`tW?9Nc zAB+$os|{tBE8noA`yz?dHlA$fjsdK`Vdq$v_-l&Bb}j*^?v_+2xW>c%9v(H$Y>)bzba<%tMqUo9+@Nz9c;p)pqzvKo115qX9Xo== z+lxWsUAuzBU)IWaquZ+J9){H=Sp5dCaYi$V4zOp1v&Bhtq~z}+-eK>v)S_ee&B37N zQh(#r!@7KmMRE2&vBxNFRXs7)(@kM--t(p~&D(ZU*yr)+rm&@FWTssY);GT#;&%s^ zzFwwGqslqDOM8F6L=z$d00lu)jqHz?-jCp{+n1868KxVG)1Ul8JN?P8X-d?5|pvXf6_wq)IjkxZ|_Yg~7x*17I% zT<2}<_Hofp>N;=t!K=@d5|h8*8mOq5NH6%v#T|{)4q<*z~*d3kLBmCMR#IAYvFj8#dk) zT)9@Yp8src)kApP=?@uPNRd!J01_U#Js5n-+Tue!LI&CUl{-l3CKG3++sTS}*}u-9 zeb+VQ6fd&?`#vnda|l6~!p<)|!uiwk8y2yRk_b_?bR~nt;wI7LgpjFm?}nK&YwRbB zmd)!)rJL?XZSg_hV0ubK@%x!tgh(uQRX^dZl?cuzJkDCFWM5BWzj&RW3BOHKg{)TaB2|LpD_{CzjHjO|LAB?|Dp9k{d-4(`u7akleJdE z>l*a(pE^$IiywSeOCpaIyKe{vk2b9VcD?9sI-_4<_{oXwAbSI3DasHsl}_TzdAF60 zQ%2YWcTc^>ZiI@<^486S&UjKhY>4m>GoKMPfLFNJ;xbNR@?40R!n@9SnG}Cm8pL0j z4u9Wbu6D&>kxpB!7^bbx=!wasZnC?%avg|G{TW7A+=vT2QL{1EF@(DHtjQ0hT4;vg zz_PJl?8DVUwY1yP!;Wy%x$7Gw)RyPU6%BTj{^|xDs0{g92%;HzuDNRrP0?{<`bNNH zT2abK+)_#PC%-Bc`_ol^RYresRFzAYV_l9%MU4P!(o#fawzc|HW?N~+t7ls~8tNZH zOJST&bUfQC?=GMB|7GuO;Nz~U{Qpou8l}!esTSp7s!=DaNUFF7MV)9;CX$q_O06po zQLzL?i2G}x>=p?pOqQBBo63By&?7=HM(z>{VUxHT}%en{AqS~>k>)<99x@%n>AN! zOx;5MXQS4hU9G*Opb#jB6lJo}pH&z1oc#1nJ&wy&9)i^CsFe$wLv|q}-+IC9HHua& z)P*Y)tyri7z)75FXmu;fYOo+qJy1`rM{ox{mgpTBR&iGIjuc4Nn#@KgTZ4Q_x0UqD z8?~^`U($!)XAAZDIKEKd`R1^MfB$@o`2vn_7k9=nzxqd8_TT$Y@v{GUi(zqpAH`&& zic}F_%}P=Ge(M>n@r|Bd8r%_mV|J?~vb|43rWF`cRsQ^tz4N57@P2C@c|>IwntQ#& z6vHW4GPBm=kXPXmLX445{%r6Yfa7lo{(%s$@^$M(h3eLc3e&BVw55A|o0OMSFP&&U zX!@e(T%^8t-k23P-F>{uW;V4PNN@YqwJ7{Hp)d;N;%8w?VoS}w;Lw4s*046&PYn=G z>y7f@H%Q07QHk(R;mU}mY7#l0k!xnHPp4&OHfQKN_T|PFpU-y(Z%{5FoqaUNA6b3) zmT{rtV30W4;*Ji<4r{bXz2)knwRK#BVyl<3`?uE++4=r*uR4_KID|u&*ZpDyalkVZ zy1&+W@^oV8DwU1cZt#ZQfy)~Y@Bd|X@b#QmWDDGw#J25e*#g%U&1TF=nf7J-=i0a3 zjlGyh&A#pHIOLFh+t<%7p-!V`B;qqkNny9m|9Cmxrj}P%gm3}EPaaAVG z3XJA&VradZ#QB#eSo|ur_^&$HYxA1ynT@Ert!w14fTZd^^CyuA1FZq>V5i$F6y1tP zSXnYRJSG5EPT@!3d}gEGl!aRJi)Cbf+ZuURc5dm>QM}?wt-4H#0ZHa(K9>Ha>|YlT ze*njE@#IqjmcHQv!_q5p{57oOEoP3yV%9vQ-Kg6uU|?>$b-!Vi*|)d~p2wb`VD+Hk z*`o6?-0{p7*EIHNbZhSq8aEx?*AeY4Pqm6)=CYe)4{LRSqFEGgK&6P>$psku-ziSH zTk+gzy-%3lUEa`C&AW!Y2D8JKQK_V0C;9cV+dGVLhIqLY<95Vd;yd8gZXs;-uoxT9 z=PyJ^>w1isMDhqB$N>G73}d?mooX@+5l_8iJ*hCRtE;)&j$x!W^IwPJcXAkC0R?}?(z;Co3zi7P}0Aw|9 zqLIDidcOu1JLxWs=!kv={q1Bg5~k4RPqWSS6edXNtSeEN8{oJ{RKv!PC0$fS7g@$| zXP2NOBsa0~qcLg(*R?P(;p3MN!v~>&ZGL}r(Mi#%%LX3dhKnrK>H*8Z?Wi}bnQb?& z#if=lHxA&6N^1B;CGR6c-n}(I1mHn zES=OoG!`NCADzWM#B12r9n$|7n27?=n3c*l>c!X$MME(vCcp#qIn(2S4FJg6)H31I zC&sA^PpF;h@9&Tn`3$ME(fzZ@wf6u)>896lH~mqKucK97?V4v`od5N$18WbdTl?Ar z3Vw`M%obZ2ro^Gr+MS`@Slxtx%*Jc1a~qJWuwt%xm(i{+oXX)0Kjg+RI$JoE@d}J> z^3;@Ox}=#)o(=%K9RN6_sQ2>(2KzsLG+j|+`igPbeL7J~q;=%U>6;-5UYKfD=iT$eLc zng}sQonT>)xtr_~SmiW4;vq=d6P z+zd7f9>D&EC|^SV$n!;cg&f6;U|7*NTEq%oe`C$EogKpv=wWm0x8D zfi<(FwoLxe6PbpU_j-H#Sl2F7Mq5Oub;lpM9KUKI#aMChTQC`RJ<|&m2iB)mZh(UF zeh-`BR7EcNrtK$;iY( zK#vw{It~hYwqc~07_4ZSnv}TidIKbO@|zk3K}-iwX_L|eY`XAj7Dxdn0cQY8`YdQ% z&FBdV+wz-g!0&!SB<&Vp)Fp;~$I9`Pt*{&Ws5|5K3X#sc;i%vL;E_nu5L@nFJQbl# z@*{%vaMMkOV=;eNy%gu5lDKZmtQi3{$V@13kr8}Ej;Ijeql7EbgY|KPF%AHT_i>B$ zkzp5xsXnQemwqjkbiE*jk~v>HMAfadbP)Z68Y(Tz$kox%m>;1WZ48N3=|t8m5)p+4 zQX+~)A+c&BaYEkeQsnzGE>U$wzn!*g?*CnNnpk8?Z4{-Q;X{z6o?&c8A_(;g<67$H zk$mQtn+QU^!N`n>Ak>xo@{2&L72W*EM2+6bx-iL$MyzrxLBJL4{#J0(c{cn~c;HIw z%$$Wy%+nb4#}8cTTcd2`E!;PMGnq`y<(+&$hD4XXOu9Oba1yzHR1Q1C+^?^tzf25$ zR3HDwkLjUVoXA#o>Ai{Eopyr~S{R-UI@B!EKrozi>V3DQ>VtZvku%J~e(o?61P4kS zI;&x`y{fA!i z!AxS??(Do}Y1cWm%MzEBjE<>1ElbbimL?=hUYohu5fw#dIqDQAbgl-KZA};m7wl$f=XCP^E_ZBU(ndcGIc@YTIT9fmYgv{`CP~6r}<>w7T7{%SS8^3 zp!StLnlO^AGS_Vah{R{jzsXnwaY-%a|5BBg@5L`BR`XSbwaJdd>R8eg#sO}LT1`bE zkH0d_PvEj(veY7K6+=QJ#BdnpRNJhPQ-qMZmJ%ZO#KZi9>sJu(y`>1~}531UPy&pJIUnpxGo0 z&?cL*lp=wvMDBVx>-M3W)*Q>`c8BT5jFX7-r4o+*D%bMkO3S*zti}xBb`}?Z^h{nb zaa`dElUc+tY6DKx)GnS~xpV+Mg&T7;`3)vCAU5D`MUmHS2s?rejrWAawjr{iRke$&#BFT1+1_^ zaC*!J;YhgHS=Ax6CNyYSY5M(-Pdg;TUS*(4e~f$>&GucC%}*G!7>MYN4N{;M*E?x; z^c1Tmcvg9imuw6Ra8477g_o@M5ZnXu zf+dzk+yk*QfKtg|HTb2O9iXfL;l;M^4nnxAa4|*o9Xsw`*j0~d%uR+n67`CnR9;Hi65k4uU`vnC zI%7x${%=>a16z71^uyTFqx4ZVgVecn3c=me`rm5Xo_0!H0K=EXOwysx2Jcl6!tX^?&lnM8;1Zazy?nqR&u=5SZjG)d3Hh`r{q zM(o=vO@^cH)<;#;*}3#?1g(d0R|HaqKy_KEGrAknW(4l`Ww%#W9_)$HTbIr9f0Ydn zN?N$CG1V3A;x@tRVJ4}(c3BM#OXLjXSZUZ^BM)G9^uFjFozX)bgOAQWF}m~3#UoGR ziP02Kj2{0dTy-kiUg6)RVDnHDeln-D6xa~sVUrWa7!Qnp7-ZEUkBu^(j650j8;>n) zEy2^R7!9=EZazCaL3$=Pxp&-^BJHYd{hsWOU$kWF(P?t~ewC(&G~hAED|*^urm|-F z-8meq(S)kG!4y?JHuEc3V}=9s`vAVmtIeA1qN9gwKk?T%zMt6pvap}HsMGcnSsdR_ zeAQyuPh3Wj_7hz%pSPcwXMd(et4Y!KIM|)p3P#E9O#I~e$SMLlqg;8 zqaZJx5zH%S)M=Ptp%$!@;*$cS9S&U6KZKJI~&@md8E-y*P?gXCUpHyn-;9{ zXq4SRqm}yTj>OPSN5*vo5STkB+%!~YxQ3FJ!v#Rvfw}EweBpNDyr~F-KfToVF(wm| zxdHMi3gE7d7gxNFa8&oV%Hdr!iyN(he;urTxN#0FyV05_hX$t{ zIWMbLaPIcln8&D4N(`<8G3TKITxZGUz?CUYSYgClKI$zW^_B{FO$3bgj}9MzZFNdJY3WNagVaau;XHW-cefSr}3J z!l>sRNkFs2NbwO~&2JMLa20vIecpb8aO< zMpuy@&iKWwYWf7ejWC6~!l$x&+FjvQ`l#WH%i@a@xs|l5EB8~sXv)2a?BKod#%l?k zB{aqxpFt3vRTVI09EBgOw%a(sv)X>Ck9oBnmdO2DJqaws5xX80%V2AQkzzlK$oaBh zZz{2<3-QE6WZiYkQ-xwLUYf8`9WE3va$ihi^sEfjb1e=uWlzOqKPvQ|EUu}p6LscpR9C)Og-#6v1g?PJVpuN88TIW81!;)=F76m8;) zK5@mQs)~x|(O}B0!^eeDT`|Qsqv9KKr@C*-eXn-kr2AgtzLNW%>%Oh-JHmZq?t4g( zT;&f8LJHp0sMt%j1#6v~WEgv_M5g5k7yVG%BuDbiq1A zzqEU&C)S~coVWyh)}e-+=m|dSP(wS{eR66e!|Dwm@!pJD7aPv%O*EXbiWh6phiGS` z>8**1qQ>~t8me1j4KX@g6fG@SbE2YCV^16vjffU3F?mE_3zj-6ii%>Gk6+&8;g^!H zBbrpdplSHFx^K#TPK}GZ6^71M#n%L@D4PJRr%zXy0yo2k$ zw52Rc*3lV_Pnt8a7YEi^f;{T#(WEBLzMOUr-8RSVd*Q>o%Gs!EA4#lLQP&ZCs!4>3 zj)>IcyAlLCwQAQ(H1WO>i(1$D#O!5h3I^KWD-1} zFs2GEUD35{%1ib?P?Pd?CN`2FOeE3HBy|xh@%GZJ?y1SZm))vYAl}CdG>P1`3_#^M z%)cFMNMw^yg=orf(^V3gUY#OlG)#zkpr9^FR$#LQaz;aK()ZAiQ~BTGAeK(^d9{DO zbiQs(Rmp`?l9WQpX@>TtWJ@8xexdYUU@#GNOY)tO+zgtmR-u1v8f__ZDWq2t@U^oN z@NfWvKvb>DhelB>#lH|KmNjm@HO!Pdbs$=!+^H@x#*6OnaCtXJ@BuPlQnc9d8_CBy5q5Y2~nb1&B4u@yIsFdvzGVBtW*5*)h@ze+84sSCu7;@ z{~|1I+^{wF)J%N#kw_-!3;j8V{+vgDHo3MN?c7Q>wYy|)e9dN4b}A#T{t70| zmBvOo=p*XkF21dtbm%un$~F$^FfI*eGvzDMO9j>kz!JDT~DXH(QeuNPW#!Hi7^5pU@&k_-mBpGrh{RocN zU;_{!!vKVVWeig~$WiX{`hOQKiZd+J3B3vNt;nXmiNWtfWW@P*nBEI}A@1-=KjIz& zO+Nx6Ao1`#{RpYSfxJC)MZ|&<9f1tsCbn&HorCI0A z=&D$baGdT$Bb;(b-ywByRXx$BRoSmd8=61#BW~8y$55@F_z=#qg8?(mb#HAZA6h&A zOv+B-VhxGkqj>%;nwUv!+nFsMmJuBiP>DFb>SQNQuDsW%j%@RzUJYSp=}d!gwkXbczh zud_YVUAk7fmjLNYO_{sN&X2P|{0#oei)cPV%5R$j8_M5YP0}&>T4@fcwUT0DaxLh( z?U47D%7-AeHuLdh&*rO$3`?&-MB4}!g~+dQviExA5z@)i1Ms?v93QUK%*O^N!zdQi zSvnL85RS6s@C$+9ml2Fdmk~OM_U_GwZ?P)n{MpbxA@7sp!}OSQ;^Y4wnjs3mmA=3( znxeo{rScsQl{lutPS$`5(`Ba(`q**TVcFs-=p$tdCnVm*EumxQ-iNvJ2Ah060jqP> zjzZgUORURJpcgq5Ap0mCnai}&YK=QYZRIYzoku`V~QnC&;gvU7?{7Fl;{)WnzX!k5Rsy!5piToTgJ(uE_ zH1G2%{^RIpSKjDKhKKdq8hROyuc0?2wJ<_Q|Ge4O&{yL48v0p_VdK(Ekk-(aXU(pm z^VZMJ%xHZ0=lT?#49gUabk?Hy4C$}4Tz1&{sbP}JAFRBspQ4nOP&8GZa_0jN)MQ}& zwCgRQ$fiAs!Pkc~zgmU&5|6sM!uvXnR*UiPR!x>)vsHsZ9L>zd;n;SKAKSJuV|^X< z-%4y-*LaEO{~XR<6jX380FpxIMqJR!nCBrs*ALA7kR9(=RRQ4##X^#HkFsp=!+$@H zk2W;kK%mxA5LJy8Z_jAJ*Ym5!qHx&Th8kj@TdZu(({BGvttQ)=OkS;Jg@xnyni37M z@{+eUUu|s38=xwg7;E|l=ldUKZGF1dj`(bkTNgB1OX~|>QOozI2^8^H_qDVhuOkNc z+SF>?e8{zyqIPH(FwUhT%yy5k<_U*h(2>af#(>H1=h*7I#YmX+3XPVs{@Rqpurl*m zyX2y<1=74+!bkN&M{WUYj;_efs838q+G2<7cCtvdt(SC5))5UV);FHQXE@K_U@D5) z`mbx^-$1xn){MDBL07=F8+Ac(72z78)wmReX=G{oYxzJIV8<^~ZK$sF;OnUW9U_V? zjpy;LyCJ>i4%ucxoa=C0rxus)}`N>UMAMQ5Ar!H-ae-$q>ZBeB)+S!4M3+2RJ zoV5J$io?y_SP44b0_@ejmpW=ytF1P`dpo)8-nGU~p7=7L>{S`_IuOdWSs-T~zxw+R zxMAqD#|=Ywy>AJHRYQ%RB0N^wM|~e|##ox0tLL;kr&%0)-xTVzyQg9G3fQNf3{a8= zNsZ`=exjZ{#de^nyRN`A!~)H*M8c{hhYAn=r@!+7E99;|Su^l>hFq35v{uFbmT z&=|QtBrW*1=&R~X$BuiI_N!vqwaBFNANm-dEV?Nc)(i`4mPNK#6>1o0Sf6&ki2M20 zDUj&#oE28g-vEK)42MZ3e_!wq1iwJjI7Nu1^NoD0cLYJukx?|fVYel-I~=gg#u|!2 zgpS43!T6-=#jco|{O*KWY7#$!7E_aN)A&G5bgM_>s2hC(y+B>UTDy-JjY&p8uuyG* zp05b>yfeTa^v7_vv+;DJL&^Zp6O{kO;LoE+>l)X@IC`#@0wL%HjhAQzfhBjSbL#Y3 zs3&(w^MWo=Jp)YGl?zo;dTeN z*Ia>L-9r8&b{)Xk^&x=?%-}_bzv3nJ9DiN0d zxv#l&^#v;tV}9SluD7*T&#)fCokO>{ra_vb=-86*TOmDmUU#lOs+?+eX*eAeV%t5T zYplQfx}rxr27k=~;TQ3Xaq|V*nAKr*k@5{@L230tbjtv8mX3%HX#O!Bd9|%kt@Sr_ zU}CY2qym?&`a5HiVyM~~@xr>_50zG%U_w(SK&fPdDq7_ztJ}8{s#CGq==I>~!ff;% ztPD5j&wh^NK#tExrSi!};D;2z=rg#q_3nC0bq;pHmdHKn_l4V)EA&BOdoFyD0d z9-j#`*bU3jflOeD1~EG(;TGs-nvmSP4p zE z*k`R&xy@P$*x$=Zw33s{>PVWlAE!CJ%9AvW4i4c5)MzZ(df>1ef7=L6n=$#eO>D*~ z6hF(atHc$GTkQMUWaa!!%#u#U+&6;{Ypr#(kie~g61`if-eH^_CzGE@;kb%mVN_*A z#ifKqqm7m-x7|t@Dd8}TdsKQ;aY})%_(+i&0~N;6x3@8;GV?iWY0gT^slC`2ei0FR zMMA!9xtCb7FUZ3gHy+xrK2zR4IZeL=voHN;?q-aAsGlWWsZQlBwStTM$SsUqF$S0Qn!DQSkWo#h3+-Z&qM5KBZ|2N2CSw5jd#{ zt)7yW@KL4$HN76EK=RFyoj8Yo$s?f0(Q@PwE4dwDv53!C(OP9AwigM-EIVduH=WV8 zVa;oFHdJ5gJ*&lGrASZ7IhCA9JJxagpShWHBnPAme}WQ>p;ExVpo)GPXDBBA`6(ZR zk8&&W@6==y1@E_NXR=H8C5DC+`V|7~Vx(BRrF#=OJNGCSw=!1c@1CcHenQ_o_|mUs zJsKTTwrE0U+-PA4Jm~8b4nS6x!w{+G0mup?h8@Y`c1tp{2M5k?+~6^3SQ1Z06tuAH zy#^4oSye>i8LP0^Oh)KVRz9pSTVSpSXLj1UEzlLj(8M(( zB!7AHBSwDY70pWIK1-L;O7TYLN`%kSElU;X)*(Ncld*pd6iAt9VwG+!XD@qjx>fm% zs)@!8y>}aNAwamVkqn<2KmF}8yr;W|uZPQG(A18KU12SZs7|W3h9;ekB#V0qg!uWa z#Kdo(2imOo%b5&Ek~$!MYDb}XG`~?XMY=-q82f&q@-#S24aBb@x0XQKo>v?i1%lcJ zr!N2rfe^pVI0Y|hRAod?SVu)tAvhUxoN%K*j4#pP3MmC6btLhI8EwfM6)YI7i*jlt z6i=iMig&DhyaPDxm zl#xkuiY0SwWqmPGA)hGT?ax$mRSFjh zfHviZ=qU%JOc_vc+JnOksR04s7;SG=jA%1T68XzZG1?Wq-y^~^X#wLvwYZaHLbhst5@`34JLhHmXEGiH^{!t?lVBN)_}MwwO?nI+ zmZ5T+RrR=CD5>p{zp_;HS7dl?hPIbqgF*;TcwkP2+bLZ#p7S8h*63AL(oAKZ+0;1e zS^-XFv+JGs1V0I69oa`NFnA8KPB=~!4OzzxS;tePD~ubmjz12vns(`YWStlzbKZ2s z0#Nu*u4Wcn9mbs+IE;6K+R=#Mwfxq$1>ouZdaMRs%!&?= zYL2#8wWjb``L=c=G)z0EI#SU}dzrUY`ube(fZW{cCu2NHp5sAc8Sm8dyI10d7kA-I zbnE@gDneys_3rMw6z}AMD?8$K$~~tn5K-E!7+%?<6!s=S_f zG<_v9y+2zVU#otmb18G|QEzl%(TD9SLCB1|LIc}IzSyHQ$)`c@OO%)`T$}`&185}) zA!3Rqu;!!`AB{_(z~wsN_|L2a({_X~3XsH*;Q+?nBnDzvDNn(d9={1yx!^aFG(9Ho z>rYcnNXuMLQ$5Vb;C`m_WE;x)wzNgy^wgk(o5Nlrw@9JBFD>Ug_k(#ELTK>?l3j!i zDbKemnQt_iB()gdDl0KOd=ymR{b4@d=kqvq3fSz!$LP#>z5$Q38L#psNd@vCrN=dU z%8l-8!B^eW7>8B)3+WBc+paKe>8=t}H^mfA66*q;evWf;ni%wMdu3jekyr6NWMi{6 zs^ana{$ye!fneL1;-c}bmL$9dmUB7DH3BGJjNPM@1%o>>OnS;bD=fFyvY&x0%rsIn z7TSgQ>x>lGQRm(k8YWQE&=~xMy;M7=1Evv6ZlqnNE6XSHnxLWGrOlQBOw`rW8TG?S zh0Q|OzE0F+8AzM4s6E4{4J|ugH8;v_HfOYi1Ja1{f;4_jm!WYX$L`h)!SHactAg8S zuKFLurdMk4c3r7K1Ah)brU-U5w5GDfuE}x&W%TSryVyLTCUC4Newr?D`QqF5?Kk1( zLIoBUt-jVzrg?(v_6%aw`|f*OfWOb)V*9xms*~?aeE4VxU#$dHXZ?pJOb0s#>BY8~ z1dwhAX23qDP!rt_Z~=%#=_~J-V(o6DY3dfAiVdHAv!D6+6ENX(m+|U#m6L_8#WN;b zw-ljOazBbMal?>j6irkjebyMCnjUx} zq6_0n#9dN~h{iP`^kb`vZSYxd@L6y0Sx@Jzlm^@MB3iIn_g6R>DGer40F7eadET7L zEfiI*?*k*-re?8Nbe#A&C#}2f&9)^1-KwZjZRjjoeEB7l+CrERvbs_SLDQ%P3|i46 zCoIE-aUZ^zd=Jn=k_>o~Vfi8_JI2_Jh%ENM8H#9}3U|=(>rPF<-zki>`jA36uMAT$nr!8Yi9s0|8TUleSa0Rw@vS5W3&kNLA|%qJD|USeMEwH;Bd?J~ujM$AzjbI+o<9q&=h zvd-e+FCa!*B=_H|h7r(U0Y5C?MZk1|fI17fzdUIv=ASwjC&;glw$k?|rSts)C`8^^e@(t(?UTy zu82pafghb6_sDIUJxlHm{sGERe8&83ZZTAa$tb%@kji|@+^d7CVtkUEXzE1UzUc|i zkw5fo0aE9Uu8n?_D&*{NBd#`qjg%dqBXc;_tTQ9DQaCU66Cm0=W^LpsNgWWwq!<+Y zT`@S-czA?u9>wakq8rNGAMooUu02aTLvdg0uQv*=*&ZE*Ic*Uwch%rD(=TP<}fJu2_ z~XJi-{jQ(6X)3FPjiA7Kq`?F z%&<^uu$gwvoS~{_3-jEGT!%h(+y0O;_1Q~^eQwvUn-MeM$QBPj_DEx0mXKtZ&Bq~+ zH|WV?80|?L39~MOG+cv@G<-&Z+j8gO%jfR5_Qh|+>yooC?osX$PbNzT`J664GNoan z>If_3e4-M3(?LyC>sLutxBa>Nz^n`tVlPSVA#`BkON1Vt7&@JeNaaJwKPc^w4F5Mh z{XqCv+Zh}6M({69&w23gSHdH|eC%dIm}a2?eO6Z=Z({y4*x+a&9+mn82!f~FRv4>g zSVga;ZLQli59Om8YQXVEf?(zx9CHSaK1DFO+UB+)Et3QKYNnW-aXEevicd=nzA1{q z_dWL+Bqi-YfH;SW-w+k$1QiAQjoPmOPYn>$4v1KuQ7P$BWdKk6bfemDR-aJ<;pCqz zv|kam-^s4KPkl)YEe38n9_N`6!sWgPny}UAhe2@1qjO50D80x{LSGAK(aD=?B-nnm0qHI}3-snqB$i zsCMP8`@cVZ+kc2G#RJT#zQzRWJ~lKEoiLsvIvPyQ5-L)I`s1U`XODP7w2{`qJhXA^ zI*707OCS#aooM6lkN;Oex>&J9?nqFv%~!z=Jt+MMdPbG4%qI(mh+&|T0ZwdIAAbc4 z2;wjiLx`bLOipuB7q-h9uhd7uBv!W0IEI_Z)}O&SC8PT$ulf|$wWgn^4bJTTVep0} z9lC6?%Yc2|@d3V%2fk zZ=gi@8SmAE&rs1RxG?TX5w0_OAY9!Ltc;%ASyC__s1*{FYR_NL*r*4T5wxQELBjM| zefD6_i|p~9SMmF{#&&+sY&-*Mr%dN|MJMrg&zX8LXq{dRLT|lFy!<&Ial3PNmL1VC zWLej^u*`(ic4vmPY>DuWb6GZ+MTs0tZ z$3}q!PYLW_{1$AtRnAp#anXza)HwQ2;rQ*2pZz#+^hbJ3t{QVQG1E)PbN{P+|6h&y z7t3SY?;7|#$bNbL{q)U!kA1&<-hGjUT0toi#TvF}=nf`^^ul>3mKWJsX%XZkq{yo{ zv-|b%@}u^2&|$r@+;W@5L+tFN(TCib>uOTfT;=K^qXJ&05oJSk0K1~!>tPKxy3OU8 zQpl$)Sx@~9LtUvDb2;;>7Z{~jLteYX#oae4nxaT_Z? zar9su7fojf<(1xyu0~JUcLR_0awGX&sYLSp=0ww$#z_S?ZE7sxYTLdFS9{4G3*XRK zqH(gmKWXl!#wl~R*j`h`iaW8ol6-7FWX(~m&TBAVvFQB2F*3CHF0-l_rb7>L{)tv@ zZdjz%AKmQfhQ% zOb>8?l^*GOYY?!vb`S5^0?Cu=5O-(y;%dXL4(dJ584KUiXxta!>Y&0GYSJREYvAS) zl;BAD7@&VCrO7k`mtY!kvLq7x=w?gF+)a%RDnQ8;0n-^p8C}IhvXbhLEvB_8{w~*q zt1bG&P_pVZc(nXfHHKeO7DXUk5wps4SbJul<9rd3wa;2Z<3>EQU&Y# zs9W6_2!7QszsXxjuQm$^)GbMk_L|Fm6}e4ZOiVQKn4P3X(0<8xXrCHpd+n$C9OryXf_~noip?;8$={4%CGAmi z!h3PG=*y6KEAKz@|MVn3Z%^?ft1>z@;KniBIEO@DS_)ZatpIK|DVs(}S+b0blE)W7H9~4Y!7p%X zpsvTu(6sBPOCxoLM(V7`7^$u|z#m`a7UcJ;Abju|y6dX-uQ`#{(55xCX*;Tk&uzjv z4$EOuX&G4cZQ$Pz?_UjQta%q(;5FIm_VJ$S#dG(&JCWy1uXzdkM~K}T?H^wblX=*B zm*sqS-utgzyrw87^rr>i_{_BK|892w?!=I^<8_Np5QzfV2KN8j1f-`I=tcBP7xJDn zgUNyBf3ja(8Pkwh+0msJ2phG&B^usw7OymS@k;aS&?h+|8+|a@8GUdGPeZ4o^V9i1 zds#L*x-)7;u|^LC3XkTm6Rk6bjL*YyG;RKNK-0IUEoKtOW1eF%Leu&9X=wO1(2#vf zvtj0|2(tYYyB~(4$Sl^%8(=AUg0h^AZZ6NP|w_0a28>&!=5Hc_uoW=bHA%gY^2HzuyChiC3M`B#Y#uc&UK zerZjfzlr*8ZD@59^;MM)Q4vy$GA+TCzVa8pdkCnszEK;xfrp=-82X+ygFS;1f+bsV zWjR4pA>=8^Ekavy*=W6q&D3eBMDnf8iR3#o#>Z$=mPo#*JCS^Uk9JrBbM3HZ1aa)7 zrp%3v=&Wxn;kS&!%YlcHc3#E@T9s?KWOBV>3%J31%AY(2$ZwBPgr_p68{++L$KcK- zys>QWBO@ZQ_mNMay^DU5w_Vu$_mL3}=vJdJam}+xcD*HIg7+_8MlFUa|6-f23xJR4 zx9EqnKQpLE0W9@84w237DvVeAXDv?~p zO7H~Rr&nZc#lqSfO{IlxH)e6^)w&z8Y~uSJEmUi1NZ1c%5yE7&+2+wko%>UBAF;LD zrUCr%wyC;}e=55sA(Sw#WNtX3X91crznd@8yw=fbw$BT3qAm8wpWf_Tq)>m(-BWz09R$TxqSIJMu)Lko78StyWQ7fi7h8oxLnQyhH1J%#fQL8;2s7~fv zFH%3%KbAR!Q5ShrHN9%P`j{~{^sV6?8P>&S*2NSs-%*{@RxA7U%>GN^0zlB4?$m29G-0zmr*3Q?aV2r%(2@$9FqzKjmykroy**r zB!qe)5Q`e_(Ozo(PSFl%$WCQ_p^El=Lg7o1-sQa%6gP>dO-fFC2i%u-->~~y+_%+x zmUUmreXHCz>Aqg~P2oe5y%rybPBZeEj=5@S!*?9zn&qYS&Ag^Cpq!?35%$Z#;h!Z9+bMmCL3nUk9_rzl`zF2>9; zyWb=kFrTSGQd*xCTC~8ly^+DuvS-k~1*9VDgs<>__mZxY zA2t5rhfzJ-Nq8mQK* zQ+AqY-6$;jrX}3JczFX_{U|z+3WcO8bL@!?DLRlxG}s)6)Fy}2CciMvxj1n;Y(w8J zm*U$d5{0{5GWNjRJT|P891B?dvghG<77{p6e2jxbNc3Mk&(^SM9ACqh-4NEjRUY&0 zYRq#j#^>kp&*Qpja5?{sgV~Grg>c^faUw9aa&J`bzs+_!Z$uHJE1QZG@STaUr%yHgzF^C+Fvif+IKzi0r z-g?`*u!D(c2Aujv9DM8D_xCteJ_fnYjK^A{E%BqG!%FK9jB7ialI=Ej-PTfUAkk92 zTP|zOtN7o884oPVH0R&exY|Ect4WIuD7BQsVn8ie-PC*LdNOr@3i8#4s=C6#-!nfn z@!?MsTVuCPFsoJnx?c#LVVXknk&Y?4q)j$Tt0Q?RxdKI`Mtqrc~{k@`m^K zz3{3bup6^QKWGum^vNRM<%#PqXk6FE{$ee~uWP&lm^j2X*aLLfMGvDC-xdA1J;MB^ zzkZ&@Xe$6WMno1^F5}9A+?2V&){$Vl3bN-fuQ3>wOUP zC$HhVtW+5~6#HnuNY?*8yW?l6j{0AVlUCEUM-t7cOsPZ$DUCV@?W)1Ylt zvQ6U6pj(ZnM;>9iV*2FiF;Cd?;qSvApj3sr07r!agBgKe))@H=$@p0Bg#nOO4!afQ zPc&TJIKZF**wk;}-6Imm)c8SdcJTLyUUGJ}c-RL>RhK=aTx*^8)SuOY;?X3~MnkYC zP<*49pgExU7=0IF)X^Ai))$)lC6}WRF3=RLjp53!+Y*Z(YTRRf4(sH)`JfAA1Z<1z zp9_SsFazogPKFS7$A`}jW-_{oggQ{D?Ca>Gu6kFw% zG+olHUjbW+5&2Hwb3jv=dXdg3Z%~?4wpLQ4M=l1FsM3d&<9o)~eAd z1a+6Uv_+R^r1fC-s+I?%a+kN|m!FC~6KK@M-OTncQ!__piXC>i7l&hVsq%< zplA@iwf>hpznSW+|90z+pCsGsZ|m4`SE{T2o3(r2X0<)KSq2hq=pn4${i|)j4bqeb z5L(`YQI&;ecAeB?-uxwW&AQ_gDsx+_QS~^RuHP2A;9oRyMH@C|)e&{M_0UpTPfzW;*n4**HsYJ&~l|pn|36w`hxnW zi>oDlR4t)(E;XCI{l|6X?pbrZ?$_`>(#5ZwUE7{P8Pi9}KmE+vP%s{i0am;cVK-H_ z>MCCmVOdvH#)$1Om6m(ITC;|RyX9IEMIqw%FtJ1Y`&s+7yWgyrdyGd> z*(@!W%fH-)U-Vx6GUySZk!MPs!~@I(+mkGz+?n7QTzG>ePb9)xTjxJ^$x4u|zqvAQ z53t4ywun(nc-`_TFW17P$6l_7iJe`pKY_2ZR`+U)i%x*?W7q%BIL<`<>dS$N8obS7 z&ct!C!mSoV;*S#~D_igS%E9CVE8otEs&2H^Tx-Zf$91j2F42r=FO670yJJ*;w+bkV z7Fa(0F7~Gr3R%#_>nIUUHRHY+%jGY> zAUp7)1cFEjCe5)2CfSmul%dJY0#Hqz`RpPcyM4PZhhovJ9h4brAv=v;^cQz%B-ZcT zr>-C+P}Ua_74nHvpSMukRljsMp{%r1$!TYgw^K*p;;-Ge#C>C)GVQ*t?rU*h$$eS( zP2ywkWi>>(DftHXbEfdR!zniHm88SiIcMJ#wq~{4)VUK8#06}@s?%{Ib?l|v?C%Ct z@#9U79zG*MQtkZXP0qjHe&FOhZ|>y$R2o-MbigLmF9S)$(bP4=Y-^f%aEgg< zPcezZ0*!P#5+T1^Qy?aCTN;IS*c=lUlAwg3g+kAmLePT5WVA>5nnt?Kv1fZJp&1`l zs7w{&05xDq%=j2_eID1XR#YXCt30K$(~XQdUUWKCWYP%9U6r!Qu9s3@(o0cA>WYnm zRixhFqXw%eYG)5&_iO0+T82g$sVQZS7n%-LoYjMn4CcNogItK0jwZCKXse-0p6X>n z1g6d9=Py7!_G##=)GUwV!Iq^7$!MH|)m}t6IBTK)gVT#FH9fe9~)rofVqh@6B6RTB+n1uM~T%~}GpQ8AB` z9~oB4DEDz3EGZX9m>Hwix-~CC z$zB|^bTE!4RCKe>bSE~y&yJ24rydn&Xm1Uc&qb6@Uz;^92S)-2+Zg%-E;!hx158{a zc&GgW4Ire)0YEbl5Yk4p;X+;$L}zyXa7C|oo7G~r&N=%m-Aq0E2xHoQfaC1kv0DQ> z*XA)3)tEzxF+KJ_@t99nV=7-zp7{Qd$;)8s27i^BeO7IZp(%fG6_$a*aGdemL+pvQ zlo~QeU*(|rXOw?wZJzj;Y2NvgmZ%y=Qo}vVNKR4;jHL18#V&54n;5?Qx8FFF!=;u5 z{VxUbx*5rohBw)s5e-s#h09QGKC7p*@PXdW!h8EFU7CetZX?B!WYT?k_m#{C8EOhF zWIH@MPdg@g;xWlnk1hiR=ZBTBDac2inr?HBX%6?;M>77=sYP$R)fck0_{BXA;66vS zdOmTFm)JrkzKbiY%1SepyxNmo9$)C9>?c^t;zo0>9W&>e%{YMSRyj?62`-(pel@ws zzF>W(lsXNR*PgdyI|vB5qFlWquFKtl2L@%4C%Y40-fjZO}b8DzGx)~~_F{i|m z`U3@54n|NumOSYCWH!2NZ#Ft>sx$fk8@Kn)Kzyq+_?er+3?A1NO&@#`D{rQDV*d#9 zHa2lb)ej8Jg@A04VT4_?2J>MF&}aTmT5L7+5+WA_ct_8nbfWqr#u^ z%Kgx8Bt94+IuSs#H||`eYSBICiga^RWdGuK@54Ti?nXT>bk4cvNnaf$Mi!E-QPK3(|j$ z%bfdOqL!R*g!^74;^X^XTkZRR`(9%TZrap1j4LI)7F9hs?jFAsVn-q-}90jKW*rY+}K=KE|qGO`Y46MNeka% zDu4=C9~E9Eyc4PGK3o*m>Il}c6H;E?_mbuSM^=a7NHpv4gZj5++GHB72-97K)%6Kg zj&uc;RDb$bo8s?1HQ!U=8+}66Yw&3KscH3#?7jHaje+1-{qh@!8mr@@ZogUixbL;y+^hZm*Apo${$cNX372XW7sv1m zr^aYHD`pswr=q!7BIoR_OP9IpCYduI%)AYTtJ1eI;!E&HTN;nXk?P{HxP%_BR7^xt zu7(m<%~5Ki^EkvR6OoZ3QC(DCO;b^R5~~mre@x!At3hg-`jkv+eb&@8FsmkW7&v9_ z)uE|Z`{5wRvCOt8 z#_oHqh-3O;=7{+nv2F$6t8;&9)q^iL`$-dVakZ-3b?@@K66GEvR6bHHb!%n=rcKz zvJkVdjfPOOeA^~ka0hA8G%849Op8dVwC!6zgd-D0WCxDw--#m6{0`F7)`2sb^6H!P=u@;Pf>&WgyX zk+OqN1iMZ<(7+D9C3}OSu!E0ByA6Dd;{ZN!k?vEo@o6kHH5nzv5WYDhJ?4z|$-&a2 zO2d4K1`<;hI2=#g3}Y-(Ez0VNs7$taScf+1ntD){8D4bGH+(N>Gf9;4ricpp9^H`` zTE2*Wc_|JxZG{OXFHHQ1UuIQ#*d}T=HOy)Sb`q^k$)e9{afbSsd3A;~MRk}a7P}d6 zES+iu3*$DY<6FsI7`HhcH)AnUJbES77$`fi#h`rR-^v8Hx`P?(i2(}he(9L`VL)j%hWhi?) zt{buUJ1Vi|V)B@wY>8#E(W9XU0CEj7iY0vC#yN<^Gaqcep8y$OJTNcMYsiBEHJipT zMos$?gBR1C4qXeY6)E6A}NAjQwmM5q%mpmpI`Y zaNi`EfC5S@6u-l7l-sP|;^+BgRu(eg+2R>vt=ly!<%>2RL265p+p0V{rHgW3v~T@J zISo7JkL+6$QLvCG_Z9oLsmX2UyDb{uM46Z~7E{73X9)y9wz@CxKAQ~W{_ZO&E*js1 zLl=^UEV%#;=720V3hp!&we(T+MvZ2ao3eaG^*O+e%$(UkjLcX*cN#{@o^QrIq9jN!pTJmFDJ5BvhO!^#krUVuAF_vG(nOf8F|`;lH|OLLB}fe2>cv>5 z;*rVhm0$B>ELHiVr~Y>T^f!Q*zSixlY(OgI*Zd7~4jn`Vj6t;l8}h}HRqHDmsx4B? zxRb|Q>@}SkeN+`QvP)-J*q)xqJy(TbV3t8EZ(1W#p-;}1Gbk=V#YEIA|75T62F$6Y zWkSIjCi?(dra}TOBTbc-)md7jWr2DYCXA#_bkp?0gpss~xAKjUG0Q`bkR%nwwDt+6 zz(G0SAWmW2GL0%Zw%heP`W=25?n$ECB5QEuBslVBM@E9R!dbI1RS#LESS!83C^1Wz zNHBq_1e3oa#GsT2u+w;6GohNoxGIZAog}N}0}rEicolF|RXd!sj=Xx;f$PZemfBVh zts{?8;*(uR=8b(A?XXg0i=(!xB%i@x8B!kteq!s;{2U-(Xv}d~_1oHkSg;2Ani&I4WA~W)@?TW53EY252 zF?fhZZ5i>hiXRt#MI->S;dvsYd^g!qPOl}#yC$Wihr9;v97#)@IY8?IXDb^`` z_RW5Z578gStMyemROW+&X}C3GXlr+LOJ2Yn3CZ^_{@0&yFq03+n=Kysn)%4(fGOiLLl~0I0Gu|XJrwpP>GW%-jdi=Rk-~cBv6S86Nf1& z{k5cY6c<)g4WXZ^x-jlF3|pP5CmR2RT84TCP{uHnaM}t*PoSu1{O|eY1a_m+A%a)B zw522Y6oX$FRaC*!!H!#=Pb+aCh)p8{8aRt7j4LJzWNDJ`%6)p+ykPSkmIwzSn_=ys zv_xD_t+qtm!bj`i?~?sz`pPI%O`S`p)^v3){ds>jW+H6Ki{CZzH|*?2)p6$CCRm;U%v&hV++;WHCHcjc6O9&h$;%3^yk zNg+(cl$<7K{ZS7C3t{zC0Txcl8yVkF$Y~a%+&2iP)tXCSVYB+rIfnbPj2k}9FL6Rw zd@1+!nQv4rq-L9Y4!o?eakZ7etQdeHRtc#!vDJn-8UnWtvIby?Z={SV^! zzW_B zW6%IGq(17I{Dk;XCPXt){@(f{Y_q2F*od|FXjdj2)~?KuGiS(IRAEuhkh3Th)W))p zp;S6UPm3#9TokqhlccNs;{#IAw8x9eFS*zrzR6I&PRN8P;o%S7zj^bK8lYvj?q57` z#}Rz8J%I?bLD>yw#*qE%7fguq2^?4CtRL2pbruibLy#=XZ?rKa;5rNVVfn)r@MP}G zw9usTzK=d|kRy#?zvS`GL&8SmwZ^NjISFJM&=?;QP;1;UEV{4!8El?#nW~wQmpnpg zUOO(}?*CzS@W%9qe%&tUE-jWZ$J(I6LQ7(SU`x zxGk~GAD~A(Y`2Oy1fXzycB^Q8;}`L<@Vd^m1g=rAJvwOxytEZ9SB8p=srJ7B=oIH;&B;R6as2%J{YE_)}| z3PWP7NF>H55NQ(QkoZJT{4seE)T?>+PP_`P!=uA8#?Y_O%hL;ggmB=-FVn52iN9sq551)Ng1j0JU@U6eB1DhxH6>MANH1lDQ6@EXDcoz4 z?GY)=+eMmMefA4#Q`oflou*vsH|a`%e%52&7k&!-?Skvp8KHJ)p&n;htv>$ZS& zov2TK0B4m3cUAE8$Xt#Q`UVueraJ48j%0OX1}iCyh7J@;HkgdB4Rs$A?64mXG)%*M z%o!ugEarS#1#tP|%Dd{M6ang#2vN}k^xROU**+*($f5V)$IRW-sJv|&GjlgJejYcm z?e=W_ZGz8~F6?plL9n6IV%?0&bYkQt+lkqzZdIi!WT=&ibrA77z)$X42_gnmaz6Cn zYO9F)RS)MiMhUJr63{yE$YF`WGU2U>Z78=LmZ`lTb>(MkLHg;Wss-tqb zdF!U%J#W$tkK&1S)Z!Pcs$!EpZ?Q6I*O$1qf6Z=Cq8^PAA^tZ(ws16mhabN z%0szokc(}3U->T-R9tV+R{r$M$m`b^AqW3rn~fpA-$iwON($kaw3ZH;i7@&12EWLK z=3@%K&WQyEoy^0G;z_qpF6B7oh^eWQ3*8^n+Mskvxuok3{w2Zhgk+YyueEqzGAe&( zA}t>$6dRt!6^}XN)PWgYZH_J{Q-{Xs2Ic-TxW*stTza3*3lh1PE8bv%aWp$drvl1P zC-R6(UYFhf_5Q<#Zt6d&WB=DA2LA`y65{IR;Jt?=t}=_?C;uQZcsfP2esO>6tv_jf z@H_28$Gj#nq_?W!?;tU^SH5qe99fs}>rcnm8cO}?Tf?S5eQRj+$MJPqpOwYB zLd96woB(e6?j;7`P3l{7Aq6GZs99|qH43+}kCwOSJL_pOKx0;Z8*eEq@oi%r@oso@ zc@F^E%k?z~{mQl3=%WMF54`eQS2=CAUpyHs>Uj@L?4UETxgQzd|0s_0{cX8$MtJ)s zV{gBNjY|20r%+g4p4fPmcq>O77{W6Em*TqMO7OU-co?Fz>w3 z_Jc)dVA|M3Bq1~q=!rUa$7h&nwyb3wog;Qc-((ljg0B;agyM=t8|I<~mXG`T?~$r1 zWI_Lt>;(mel|77xHm`hZ$3iZACHIZFZ>#%;-KYF)a|g_EMbu<4A~8CkBuyiI=GX&+ zl-Z4sRb*8AwvNF^4!z`*(~5_^Q%75hp%^g@TWvG2e&;HJ6xD;fl&Cmk>!UX$hRP_c zBH{@Jngq-(;x$3<+VqgJb(AEb_e!Z#n=f6iHPu=oYMjZBFCnJ?1MJLIAw5=o>@8bx z7&YZ%^Ce%oclDgS{XpP|GEtt@q9yrKmIT<=2RqELv>>c~*-znIOKjZtl2&Uo5O_nV zkIk~`V-wwL1=)Z>iO1LCr7_wPGjJMdYT2kXUJh|%9#^6ccG+9;#Y0rceHmYr+e%a+ zcfEa!t<6T?&tCa+U!1K>iWVhg|5}pMWQPd;s70Zu99Y#*?&2%??TGewT=@&-hU<~! zk1zyP8X?F_sk5Fyn+bOG)~U!kmDu*RZ2b&TeI#VX@UDgHC=J#_x~8!qje1CHdIVDb z)+_=6>ssER#@|9HSbHMxWl{ZO(MHbw5wguL+H;`ludda){RB|;*GJKaxlqM<&^eU9 zV~V@;#k2UT=NaC+Qi$N*fv2PXTXScSUiY? zKtA`EF*Wk*g^rl725!2d@1c*XMS+8m`>bdC>@KstsTuA&PyE&*%(zQubgfogdXc15 z91#gdOLd?rjwc$gR}f6cj&d_v;qvVGb!v4qzQ~f!BLhMtk%4Rf(oos(N;}qb!AB86 z(8|bJ75^@BO-0_`#wv^>#9}Dkqv!^Mz!;?)g4)D{ zO?VaSG_Bz7c8z1P#4nG)s?bpt5fyh35{=$yi2_O+T?FfU3DmQkisMYwlX$!A+M%nT z7jjmFsqv#qv6E!FVW{?P%k9R;4MTa7b~pjyQ9ozVV5|GoW?eXi*9zM{-zHaY$p? zAd>F80>`oBGQO&m;!-^1RMF%VD&y1KuoMHE+)SGR&PvWB-6r`^i;j^{=#*Q67 zO%-w<_f$z7k&cbHr2Ybq)jE1DU-dT-B$IMpz_mO1Y^~o(pa$I1O=uqKcM~L#b3%e` zp;QOfE@T=6A*pawWtS{LmfynoXZfY!Tje+*!MELgducdx?w+(K+~dAF@9$)YvxEX& z%IL;nEtdz$w;arDM#Y61g`v;k2je$uo}%$3<_l(!^umeE7yXo1hYrPp-1IYtI|gsW z{^rklRNhNV;kCQaqRV$SLxVGuMgc!2Opofle0(7ug;o)uR3ev zBK4GUFdEO9iimti{Y^2EC!v7#$7ZZ`HZ}YChUKqsfr|oc3tt zJ34m!Qt4NorSn)X=aTm5!Vh-rxJTg^_IE^QFKo|i!(xct!G49$8TBMuo@&isoYpz< z#WY|=hKPM0!Cu8U9@2ET=U1=lh*tIxq;V&xw>^J$Ux(jHo2bs{mmSgX zBsP0`@liN&V;`ME_?4Q;1zc;bE%A)_LK>Kjc5${)*`Wh$=j+44;X(R$-7_deMn4wO zKYOiyrOK=6%)Z3Xn-pYCHyHw;uyiJoJ3*n&@s}?g1{89ax@QR< zMrOc$f+2i)_vI=0BcsE3XojxKhPCOsoUYJPbIPMO z!EPs2(jaz5(8jn^=-}|8-J8aM*k${*jVs~XE8p#v3p6lKqq#byzfe(j^6mEx*w(S0tgzfKE%6H zRa&r0!>)8(yHeYfev=+o1<7;vq%Z!|fqT+MOKps~R?PuVMH6! zZ2i{(@E*RxB(YkmpJb|XDdV;i-Id+(^Hd?{J5g=VlXM}T%UP7y?9H98Y1Y(Rv607x zK`pH~mQ@(a$}LjGfH&hLa>tox7hK#zifM};>5BFgieq#Udw`TG3RzDfQz>Ry6tbg! zN1?ciP>*k^#&^`;z`OF*h!n-e8I6JHsQ;SanH?w3-B!yN8S-Qu^*gCgTy3FvCBIRz zY~MrnJBk4sN@=lU@sPr?Ie)Jlp&uO2prm7kge%)7xBkoFjezqVJ z+VMMfl2lU`?+@0z3}U^H-j-MVxhV=HNr|0LSigIkf^L~cYyaZ2CyuCV-SP8eTm6Fq zyKMbKtr#h5tDn}kBk+J7^*?Og@l$M-{i5T4llLz0aaU#je}ICmkew-tMg>VF=tR5( zMJWpG6q7QMNr}p(YAHdn*0n;!7V26ck_^oEHy!P+jds_@jY_pDtl5>6E)q=z+M*x< zbgiOGRFoM48ZJUXp}+U%dCqr|NmE)}-T(go|Ge5c-*dk`xAUCmoWmLBDuO6FMO54uIT314lghUtlC(yT6$ZtYXj!JvwsIC%k^QnhQ{XPaS?EvwVltlF zVC&6P3M)?HOl&_^ed$ber`=lCc6M~;JHQ3>4QHNNFqn$kK2#_QprW>A@m+3h+nS?G zw*>vhrMF1Yt1I5x?ro0RxS0)+l#gFz**de}Cz_*k9&Xj;ZhZV98`qU+j?SEA0x2K0 z*{JNPU)3CaWRA(Bd>jo!9bhUo?0jx+&g<0srP~Gk9yAgFH{Y@YW!$Bsc<*OG^St)# z;wjm7@+rB3&zk%>DCX^iK0YC>gLg|E86{2RTk4w`k`!J^4*3?eq9&*3TR7$Vfra=S zrnxG6lgY^WmZd;Yn#8Q~gsiRGd_N0!E?)WD~NV|7r_uEn}`f3)mZ8E|DWKVb!EKE^%;;KL47r*DA;3K^C@EUPE z3=9Gy!{cwXJKoM5n74VDHgG>(>8uZ75lU8^23ZVjw@D4e=!^V;l9qp{{i|Z_Gh8IgF2N-?$J-D? z%d|-_sk!9@jeTxxdPGB9k?lmhs^tjc#NsNXEGS^5@N+I3OB3R?flZh5e!p9rH+U3NQ+_Cyu2QJ&Qs+yX+S7P`MFe< z+pl`8Y%ZYSG=gt~^F48Q6JhnCv&viPY;=&@YjhI0ST{MXMN)$>)_x}@-0xZ<;yLwx zh`FSP48|?uAtROAc%4nzgrT5NNVYdSafKsYxTRyy)3V$Ra)eJSmc>RVMmtC%Ydgcf zkVTYA{?czVQ?ec_8eyY{g!%A91~;@&o$FMmPO_Wp zHz$Zhb*f?8L*R&9=6FD3Uf^l~`8VBYCL&Bl8822z9eh6MpYK;UZ078J9XvMuSR2{2 zGbJiYiB{S)aj@2Nllj*+Xmudd!FF7XD`c^J zfO@6UG)2}>{HjHM>j?g(!9PrMm0qg=je?bj^&@OmKs}&z3_!oR@DOu-9z{M% z_>F>}k}1k)s^{h|LMTK;{1g9RYu%e|u82}*_En7TD5FSu|4gGhR!$5b^s}ELWOS#j zy7yOEWt*c}Yi1()hCP<91w?kyaUZ;Gv9D5~=IlawtrH7VuDOb=U4&NN{rEdhF|#aH+|Oy7m5) ze751ux`{fy1jsm!M=(f}QuHe`(bMXZbuD@{8*Nn)Zzj4SaeZ^cajN+h3Q7_51_ixR zK~4I4g}z>+$Oa-`$F0ywuIs65jxMNIbkmY({?UqkLw^3NFnhsP?yPilRz`8JA+Akv zuglNpz4!$k+}4ck7WcVbFq8Y18z32}=KO+eV)n&c_j9EN7)*k~po4bQPNW>w5p5jz zQA3Ls>}KmVPQKXIOWzqLa;Ld2jQWlwmY{UT)@!s9WO`c6zjC)L z2(lo!Dn0S;XLi}l3+Cr_?cy2k17yww@F-q&+6XgUGx1Np$h!`|fTtc;^L;d~26mC= z`&D=t+r^3GjfNGPDq+QhRgnuk)FUJz+cIU#dbVR!UK`&k&tUL$j{pX*Gb~j(9faAS z$i=@j_{VYacH+?dYj9Nba$H*UHWj^sZxx*uXaQ|mLEGwvOOynlL%kbtebX60mo*5JOu3kdrEvA9@ARbag_FKDF#3bityV`> zS&F}!+|>}mKasn>)lTkaDiP#PnO~s_LPn9he*?Gjp&S5ASv4V+g$db?Uqh>`3Kzs` zqK0wt*5d#K_70f9ALw{>;`-GAki$qDAT!eHZ%X+gSOR2GzBDdQ2FIpK(2Kffy@PHu zY-)fIHQ2znr{Ul6x*oo$FHucgBciyJk!^T2cK@`fnHqu3PQm6GH0&%9pGAV%S1NKb zK`8~Ds34Z zsB3dWR&uHc9lDL53l^TMtU8(QKJh)M8Mn)Qf}XVH?n?ArWag~3pMH^VtUmASEUFi) zn~>*r1_2$YK&Hb+>pJJ~H1Q&u_&(-Vj|g6Wjm_&4tfU$F)i$dT2p+uIg1QOT)D}HT z@>Daz2iH_yZSd9>J;OMxYIwB-AI*CCTR#b`Ci<)xTQ<6~-<;1%Du})?z;AQ@Q`h0B)aE78m4o`G z;Q9G0ujjM;nCmt^`tK$pKY-)9jV6qK5(k6z+y34Z8~r%0*m#k}AW#38AQ^XVSY*=l zZBymx6z$vddp@bs(`QQXl?{CNQ}&+E`SH;glz6_v%@p%K25O?-n9c}F~LznmNG zhyYnMdGMjni4g|<9-I~hr6jYBiwK<|)+2~Oo%4o_jYPcX9{UM``dtiTA zbAA=CD3h1t+Fp|$Nm;1}OVreSrKPG5vZ?y0-P*n08r=0H=L8y=xXRJ`=9@K-qW_0| zT=Sj1xk(G`xqeHhb4gMDT4Ww5zlOplbz-zN!f8F(!y^vM8rpS=2@PsVKNT3_#qL=(9O}s=@^a{^Dqkrh6FW3?M{WDvCKXTfLqcL zU4S%gb|U%t7dWI{uq?j-Fnf_NvjrYiqVlA7OpwC;%$TOyriJ|g>8+mxQ_ZB|`7`*a zT$hoZGm7|eP{OVQ~WSVXMsj-ASGTj z`chmmrX{N(zr+a}Y9f>Pr{JM*kKqy!gK7Tus0w5cET}@jI4cL$hVtvlB9&(dypZ24 z6$=|BeHbiAF?e>ymX#AocI%4CNOriI9MTKPw41eWNM_KhHD2WAM^Q&E=UI`X=W#Nu04+vpH<=h@@R28fC&Bvy)8;D%zZd_U_yA~R^>$H@5HsYiu&$LLi4w?BD#&L|jE&b-4Y?xK!c%YQF{ zq`0Udg}nyv)+~8l{2sR6!&!LtBp#H={j-Ju`o;k?$TZiTbBgu}-ox2d{T|MVyoZzG zJ)Hg9u^JOkv-fazmVS0RY?nJf5hb88LJ}KO$mCpwq!qH=ssQlRb*Lv|oq{(KEW)`Y zu&wha*j5K@t2{<8CU8Qap4N%dKa1auP**&5yYo9^CKY}so>D&EcA08lhAfAmuolOq zil4nRNEIKw$fSz5H2Z4zBo_hanqJe zAbs_CnTgu}xWq|cO7cb1p#+NbsqZ3vZ<=MKPhOi;$r3);f^yI$D!*a{r}=na!HV`)eNL6xmH zg!b9S?TPNKaM0E&I2`624p$Z&`%qa;)ZyIiBzB+zh(3iU$Rz@uAeXGQ0nW_&nGV8! zns5ms!!DN~h-{JfHkV5zrj$Pbu{2<{fpHC@bOFCa=P= z1=CiU4VRzZ9ks9+H_$KRe|Fpm%)ZgpCw634g;dT0?}jY{!~y`{!jYD2SS~_!y*R&P zjpTFKoBfKCc|Fb z5u3IwxF9-fshhT(wJg6F88&PciIr5zu+j=$)lU=1b05 zMzs{fN+jsA@tDwK^vb9>F(!QDNh_HVUpxqot{zkYNRT~F+mL@22l;1FEdL-MVv%1y zPx4PRie!mJ%djo~dWPeMCwfGWgK3K$Gak*D#kY26GbfBBa@HhKWsJLdg##nMQd>*a z&^*qFO{L{)G>S1Tm!~xIwN;t6Sf)VBB{*exEVl9XM7Q2~+Y>o|!ZO7aQ`FJlHvBUr zi%}~ork)jk9VCkpVxfjXvJm$!-;P1w1dI)rA2qQuZp(D6_rF2CwL+YzUsg!kk{OrV z_$<`hvaN4WdZ2nhj?>g~i?!CJA<*mjLN85=-CSWV%h7V;ibw4GXNJvs4l#QI7XDWg z$V-?U@S7ltITNd=AwiN84W^>`wl>mIFxCqiETI=v8edxKA*WdT?FYMs&ZM`6k=Qoh zsCq728qR>QI0~kAmn}i{PVm#T zd*vl2hkp;pr;MY9Z-7tz&;o1^WiP{oTNr~ioqb$$eZs*-$c|N_7$>%AX<@q|Q5+4K* z(BNs=mJY8)NWBjFn$b?bH7KYf1hk4Vb;PiFCe#rl_@%kAaslm8`E7xJlYQwj;I~k9 zMyf7!^wK=GZacLOM{ZMaGjg5G%~VWucE`M}r!FN42HC{qxH;h#`xst+JO<1hNIN`z z?Bm(O(I5LqY|Ea%CeyvOE_1@8%U?=G$8!kn_u0aX<1vi=(K83z>9Ze={h}@Rc%r9E zg%oOsEEw{?>##SdfMu=G=`&lS%ad)SEeA*=M59~Ums&a zra553f)5!cbbJgx_8aOpTjv_`Clobt%m=xtY6fSY{XyS!$NM@{~UE%Bu7?s5@}i4v{8x z!OTuJAa9u1BI@c_R&jGPEa7pCNe`eYqR^)A0wLQeLVjVb5HyWTx0`6x8>KZvOn#T* zEgkU#A-&QmeZQw~C;hufpCSEeCj#8-kwlfx(<6;}I(7Cm(xplFHsx=!a#q{Yje66> zzm530#J^SfJ>EB><*yI*c6fPnN&i;x>j(5S^3{_lzs90w`J4A$6pk&l-g$uF;jAF8 zUVtWcNquuPA8U!TUc2RiWd5wzv(40`hIFbSv**v{T9AzVY7l-Du!}qL3zk9{x6=k# zqc!cUWzm8U<>$X9){Z?Oh=AU7Xq6alI+7l|i#*wASRzoCi^8`&zUx-dQY(C;T*60jWel=ee%6n}xP@yPw%{TD9bb$~yuk-qfU!bgr{jN2J&%zL2&Jq2`~f+BG365&@m<*WDfY0|%qn%^pfn-1#3OkVJM zfqxA6O)6v(;H$97fDiu!u-_UP$Y-LL3+ho9fAVG#-zh6LCu?uFE4ux@w+t{P?2T_= zH72P`hCV=Ur_dY6WLr}qngZ#4o5s~XjlZ0jMouZk*cD_GMrYg#jIk@IZ3!Q>@ZGR0 zMk%ms3n_$X8he$Zu}*4u@GH0cHTVs?76t5r(3bzsgx+d$usaw|)Bf@={DPqK*BX=j z1&*t2k2)zZ$r~0Kll%;hi;J`3n8f=mrt$okeg6K$AUFOiiM369*~toc7JlkSb>iJm z)|!c)5&D;nZn68(xYoINK4xc=_3`0BC_n@qz%N^)QfcGyzTQd)iOY0>;*qA~yNdEe zeOQ8``)rGTLoNxVsZz(0A)kOD4?Y!*1|`0pT`l$xY?ILod(TF z^n6HJAt)M_3`}8%T(NUpSp6xRS zWvi%sIcciS8{}?XeglHwb&p9t+;!M%@<@SS<1w!Hsxat9heTe~-dV}L&epSZ%Sb$& zNTgliYp7G*?w21;qewdl@--X~M0=~(_~%iysNBQ16i?`YHSd3&YW|CZOj94@68dX+ zq?Eb!*t@jWENQf&8~vmKw6`C>n3GNqY4cjgzQoMbLxtB{I5ZYFj}2*gV`o}zDw6L+ve5b$ZZO4#$1tunwk91MDsef z&Ra?fgQl|NC#@U0@cOEk1o-p#qj^g;+N=4@&s!#I&mx~oqIn-t@TV>KLUFZDJ`=O` z>fnX3nBn4zCaGPKsr5lLes?-C`yEY**{$_J{Jdl)Iw2FiW9C>g)cp<~z~0Y*INGjD zCHSls7K*jMKhqf2b{uDAC%;+z38C*z9`iSf(FS~}A&xn%XpHe@9FKXv#h@IymBf-V zq6bQ!JznLA&rR8{+)OL%d_g}SiAGG~%|#i9Yp-OPx}-zulBF4VK?hwf{iI2Z$(ICT zODlN1YQ0;*q22Hy)1ob;7dPuD{bSvfOs^CDTuOQ3XG?8m*Lwi1s5($kxr=zYNWZ5B zn>6rLwbERuv$>eX%57Y0I3ShH#t=Rb!bfnWMjw`+&86+QP{>;_NSGkpC6Z)mi<_||KAR%j%j$!ID zHw-7`b;u9=YgmjXR!$fwhJO&?+Qj<>R;zDbszjLFcH+KR6@-l17}W)Dc`rxn2IX$X zY)Dzek(Qae+N`Z*SGr2^%HP9>kTHk51dzjCv8O=-5|)y0*KvY8-^`zNfR5^@hPEoM z#?^XIW{XDKZ=WYGY-|9A%k&FxN0@4*-va*gq7nW z4AZY)CA-H}GFSJb2lHjLh%q>pizL8!b`|Fah?`Ffn}R_jwyM!x&C&hQ4-%=JiabtM zy04R!ZdzY&(AUCw`BPuTRXC>~t)R5NI5L0)n8&tUAE(UApMH8m6_Ter0QyW^NOH%Wq3!Bh1U4C3!bYb(n{JB}N7xfEOQ#*9?3`&$z>XJqCqO+IL zy!W(jyGoD&j)B@c6Dh}X9W@Zv+ff6N0f>{xLN#%M-FNshCJSO4H3-Lj=f8j~TELvm zhn+BZjS3OWO`I@ziGoDNEiA)h#&dQia?;gk$w3F>^N{R9hT^3yq7i zx2qbLEAZ)oQJqb=TCoIQ7gA*uPHS((Pb-gC1=`K2fUJ5-1Rxu%XDBRy+;*a3kh_g< zB{2hLl1Ky-S-OvKmH8W7l?fQHZnz&0u<@3}^=Aks&uCaAis)2_`?f9X8G!8>4U0)O zA7B_K2UO*7qU^B3W1%=uO~&Cp4e!)o0MD3=hqbsta!ergzXyJlyc(N84LOA@t6PAJnM~WN)psW=uh7R{%PMD zHvK7l)V_>F){woXJi_WybZ;1+=%h%r{K9slo59~7{3=-SYO?60NSO5SUFOF0aeYI- z(m^NJm>UaoK%^#r2Z2T;Ps|i&q@|2eF`|$&7K=h=Z(b?#*hSdO_#bBxdHh&{sSFBD zmUf-Z(#mLYxJpY6Uo7~Rmr+IWfypqOR5NLTOg1S-TM(j+8ailUzf#jV0j+4GfL8D$ zU=?k2K=lQh$fLpP+J?I*g}Y)=(iR{P&CzIcbhS;P#Cc7f)&`m~?ILtYbBsIy>S}?z zE#(WxXPYEIN*4nP$;oMtk#=|GgaB~%Omb8S0J<&bR-GvPs3p|^ zS+NwKSMf}rI{p^{-heP;?4b(Lb9*l@)5ES|;PPEoJ^;*M4d@=Vo2_rSZ z{Uu>&&AAbGGWQ>_aF;Cds7CS9(t0^w7^7!mhI1Ifzo0LRD|FG|3Vg)<^SDMho7i`>4q>%L&>MM!w@Wl7Z%TTF#F(#<&e*7w1m>~bg;NKk5t8IS; z%sBA9d%=u%A5!U~Y^I(YW(0Y`&?3kS`-Bq{#2Z4};|Kw_7dEVTBV7JX&CzdVl`Oy) z5IW$O?mXHyQaV8yX;TEmE6e`1SE>(Ci0F$Dza2(_fI#C#>g^Ds$rrCveq zG4KK%WVyZv{m+qFaO}NkYf7F-onCHLpsr{d?DAk|6ir$r|Nd015;4?9a68z!MVxbs5lO5|1^C=i>TNGv&IQjSEfjigfu#VwfL zK`17_%7BIgQ!_|KIU>EF+Zr}`5fb)zp9g=daIE(-zCu0H*Qq0Tm&(PT#1+92$9fhy zHEh~2t|qGXG-$iNtQ%f4(Y?_l>f|sU;h*6${>k{Lc;Tbsg^%+8mGJM|(AO%?G)MK( zaFIfBrk_H81C25KGoX%#f|2PWT^Oi+Vpa9(7v%?5rGeR3Rzv_U_2SmCZ%lrhDK>W1N|4Pi0hQv>9`MSD5TM9|s!N=I#y1!L5anCPfOgv2Ca zi<;3MA`FcrCQ-oKaTJ%eIJX7JbU%H7mOheOS1gdE=EB{g26m@8L`E>YSpp`@R3V=U zWp5V4&fwGFrpVRHP!zucvaE|RET!)BLh|m)TibyNsL_EyELbVuo zk-2Ldq*j(9XDO5J3dTgwH;^v&Xb51G2oPiqb83|#z@(*1=80Qa9B>R{u3pd*8dFP6 zqNfqBGzw}23f{Z_%N4w*UBubFS|y!s2~)O{AKh3eW=<$4uG*lF%E=FZD77w;`^F4{ zW97&5|L}-zaS?_>`QOD^HL(ch$EXHVE_t_nhpb#^%`j~f{)`fccROBY$rhZF{sSK% z#()?^B%RFKjr2rU2OxF|qmdRAX`bO!>y6G(IZJ}DOf{Y%hJtO}bqznFETUm96Zzgy zKJQ_*3<*u(*+m;fZ}R5uE=sAFf<;ZtWKqhRN2^88fZVk%OI_o#6q{{;vG`lGtl_QD zK)>UwUMe#cC>hO=6Tb?QzaAHv(s$ObX?P4T82W@9tiWZ=n$@baGos-lN*Ld<0_3Be zL6O9qm>qh71hr27l=D1=Z^SRSv|bRd$`n3q;h`7#(1{+aoIYFSE@6?^)@a287r#>! z)LDbpBAPad_c3@#$nDG)JwfTR{T~G;r&oXwyqwI<)U*=!S^3K%H5mL2{ADrTeb$Eb z1Z@bI5lS(INKeCi@H#O~3T4nsiC+RY;?Gdl@)lql1q9P90yQr>b*3O%WC-*;MBgG& ze7{iNbN%~pQKvVf+~9@U<&dvC@&FS=f|qBZ&#p<7Gz)g3r( z(e(zBB-ON0(0zoKkNO?hjf^!+nBcPmSAY5zn8n{~CVg)V->4k7>$A+;9Y(wc@J%9K zoAH8myKBpO8U&G$9V0aYR_C1zfnZJ^OG85LDn>-6MVsG^A1bW~Av#r8s}aW|^yL@g$a*BE(GlVy(BUOv*d zxgR>ob^x~G_zu8{);_fPe4JP0L3wWS(g9XHU*i-cT>(rt>o<~ZRYF{D( z#g2Y!_tU1vXBX8Bv~>K&^l*=GP+2&8H$TqZPB)zN%a zN9Ez1w%XhE6i=*tnGe0_5>!hm8ZOn+ z2fpe2PM(U7d@T}e9aej2F_iXEm>#z?@0Z~u#e2|5$@cu6+;bu(s^v_+If##HUNBADCBr0`7e zs}1s}E!}Frj{|>`#qWMb3X9F1rKb;z!P|~R?4k_SE#|mV`J!uFO_N(qj0Wu4vE>U- zKQalu-fOV0whCKp!|OdWs6Zs&%oz0~7SFrdD@Lvg1#!v(jV<>lXZ=x!mw2d8l8pu> z(a3rXw|wvc8i7SZD+EIkRAChq(WX_V3>`9AsJD)bb}|a$V}^Pwv9a(?4eOm?v`>Fpth;=RAR_`>dxkvu@XWgTg`ei+Ju|^YRn3wIf06)FqP-e^tm>1d^fN%`@cFnKRxu+70_&&@vhOW|K->T|*`8JC4P}Wb6}$HEFD8 zhJoh{P*5B112!smWv0enm5s9ki7L;Y%XBo;aeX13Awa+|GcjQz&uF-S`$)L^1MpZH zV=lJAAv6r=Ni_$<4D7puV;EaJ7Jrj>}M$i%+=qE3I1ybb<>z zK?vt|VmKeZ*uRu!QM6!T{;YGe5mZs;fa1MXn`l~-^-}?F4%#II`W8rNIQ5>VE+Lo& zL9inqFV)ASrDzF>hf4^)M4=g7Lhvv8h@Q@LKg2;UU6eV&bPuMyvg{c&XR3say8|Q- z>3gC=KgFf@u}#Rjfe#4KJUes5Q}DABxxZyXIj@wRGoI+trSB7mll&bEDF`Xh7hIN4 z8k-sm&dwR-g+%N;U4Dpu7DI5U_5IIG5XD695j9ks5?W-SF4z-z<-W+WUy78BGT6Pq zAk^ZS9JovR^saW=&H+5Z=3Gm*fD@{&S|=Tn%4*{We9z^&w#pg2`QZRwbGRD7cnTLz z2uz&r&u`Za0dSr$>w3o|x{NJ2XGHn==f;-LyTW<5vA>y^5%o-zoqrDzdTaUf(z_Nh z`h)50cAh`~;+P2;+Y=ELQLj4B{|j!2nmEtTiA+1^rt|!7NIuVmW#IY|*(od}yI9-) z3Wunq1Od(j1AVfQf+T?fh&hT*Ta_kL#|$+{3pO&67PPurQhm;!+#Q)&+f~3Y9WH;C z^>UanogEU#pd=blZuW3fL8Zv6a31b@ny-C+;@#gzYE8gB`t?a%17Y)`LVVj z7{u`f!7&yi`JW&ihPvSeN=m>_E#Rrr*DN3`2&TMmStreS@tTG%ngTx_>=KpPmRpIV z_bkoghl`ARmlBYT>?M2MxeRG2{mOLm{$`DAe$MBP{+&c{nRN}Rc@IsQLTa9Jl&{qH z*dt8!L?%7urc8u&stP^j_8k?%o}bYUc&HvAXV=f3a^n(lB`25Fk<86hRmozVIlU=4 zjrJrat(ohA=@2M_#RcHq}7I0ZVmIN*FDt2sIM~w zD#`5JN(&syv0Z`Na`iam&={yN zUG{Hn%&E+L&RUwY(sF7quf51Dif7%<@noN~WarA0v{tG8_$Y6wm)GRJPWSn+Sd$nz zYfG{>Lmbb#EtJ#PSFgevdz0oAZKxY<(zr@q?Xi9Cbf&0%?w(u5$;3c0*R^kYtAKFz zEjv{eQOcSmD&&(`^lkj!E1t?wRvsEqa$X)#YNp8MG?i1U>B_i+Q5p2%=(B-H_0}tWs?WR7H)<2vJMQzT;b#1kIE;2ZPTrn)jCvdS<_*KwDyZ-E ziuXqkNhZY*-H=))pIgaqA*a-lJr=~IC=ZizDGte$H=ny)VTlWKR(+~y)u;S13Yw;r zjeQ;F^tPK*Ok0l2?fFjtut}kG-W*Sp3ORCV>rAoTyVhY&U#A@2Y_ZaEUHICf%Q($; z0jJq6t0%^Cr94-v*K(yS7y7FCDSj5{yxLhhxwd$r+O9J6me%~GZPAjMRnKE}X}&&r zvxrF5P3vH(u-)y0iu%;}nd3snD=>I4A>)gDn2nGo!7sql8ViGb2IS`o`6$)CQ}+aYItR#=aVJ0!$Z2HBE`jS0I$EKo`;L;?9|!iP>!{ z6SI5sYQpS3iNuN78zf8+F`GP~O9c3i-r8YFN&Azkobt zg?}hH{6ndkH=9!^9X>iUqOniqyiJbYJap`F;~v-Pz8cGJ<3~r63iRgYb9HzMHh@v? zC5pJ-#*bkj_kzklec%zcfySzGKcC#XimY(0%^Z)Z0WI0a2NKW8^`?*&bk?Kjgm z4UbxE6E8mY@l4@}Be>yftkw^eKauFUNy)lbHF2<<{?B0jSo?`#+gH9j zlsR@cLt`T`4N&q;S~BT-=K0tef@+)gJ0$sz9?$WYhm@oWFv2Q zWZ0C}r8cv$`8TA3>8ky_Gv?j6+`GXm=H!RIsW*W{nB+jH=$rb(Biian-usbL?S($o z?qn(IQ*XMS;%3hkDl68t_1vO1F})Cn!=7f`xfAA=6hSd@aHX3O6(bJpHtd}^Fah(@ ziR7$A@*SK9dUt&yc^>D1oaZ(=&uw&`+h{yj@FN&?OnDg%1g{*S0^d)Mi)^ap$@Bon zjD|U8G{P~X(P;%d(l%LIl5--dF_&}zuE)s~()EmClYRDSi3p-eWPm(tAQFu&De*$%Xi=5z|YTE=V@IfXGtCstdxSiG&n5 zN)c&AOeanz8qXP_@?k0;AZ@>{4d}U1gSc&eJ>8KzH@NL`U70jfG4ZQY0MUV;=Nbfw zp9jUro9Jm%DLU5&h6Y4FR3Kr9d)&o5Tv|Bdd~Pum;%?(Mh-VT#k7&-iSIrHGi)N$t zdpAx&T!RWtwfNp{(6OOjq5A+Nahv?P*Z>xLqu?C^hsNm^0dco^=qmU4a$ZCebvhjC zG&U=~>*Az9l62RFNK)zBZtBv(&5fZoV|TObvh{b`mKh$Kohh_*z!El7>DUbOfBYJl-U#6} zu*6lr;RCfIXHZ`R47JVq4fpf%k)3mY!tS7p^$g!X`Wk2mOB-e$HU)u%mW4Mmf$-F?FIax0oDs?o~jwBn4{i>C33z}1P<8a>t8 zcvs?zA5dTG2~Q=idJS1x(brDW&hF63%bO(TtT@IwABh>qhn64CM#A5stb5)ihqmtG zhK#NBIo*Q-cZd(pWbV?VWLR95mq{zO&EzZ%IiC_H!>76xJ|&mw5BjYxY&F5Jru-n9 zq=`At!S*y+Yjlvkt~|3fy0{Jq(^>!eLDJ@heQuMDix^f-9*60iUIBn5av}_^&IO(n&u%Ii4&H2K19C8T+$TfnGbo__# zr67Q*b7W0&5RoT2ERFn>gP~y z>B~xbJV_5#Omr)El9ldN*inQnP{Cw>IoZb!CEz3r;H~VZO3$|GUqaj(Pr0jlh zZg~F<+Pu7>p7#gaOB+txkJ**4`{ub-x`){CA-MJ)8*^vZ-<&b&o+t5A8E0|NlXQ%w zyP0VA8Wg<$io>MG7gT6lDT!O2%}>P;w=q6ljNOvL@pn;%;*B3RqKcMBg~@7OCCFN? z-rV)3ZGZ0fIvv)aIf&n5d!;I5w^0}($)|y`*=>|pn*;CxZI?=cFAcW6$JVbbc*w3bG6foGodQsIwrf-Y55!XeYY>%KQned zyKghul}m>05?!mj*!?1oqt5+L1*m@LNJF>hy#jPQhPq0(Tg)VwsshbVKDZguAM4Md&`o%yH#cbO z%|nf{QBEQ_%&ed4(S|6n^hzE^>mE`Fu*Sy0&_+ISH+&7Z{3G=eX^)hW4Ji}fI5GGF zvl?~ZI7zgcP)WGO0R<;}ht265u|$2NR!;8-4j5z$14%gp3iicl^>SPh4gmM{_-hn3w>)b#NAQlf2nH3LRDHAQX43HyPU{h?k$+Cfp4A1?eI9Yn)UD~ z4%1hX(`XY#_MM(YwW?#9R82`c!%nPbJTxy1bfIW^0@DK*iiZ;)72l>3Q&yscOtMPI zR1qpFNh~in>$g`a{b}Q<_b9%3(pV#p+SED~6*tQlg}zz;83yZOp}5ZCY;8mab&9wh zIN8Yi79I$nZC#92u~Bg+Nz@=UM3bgI5^HO>sHiRmt%|+5T(E{k-pyWh@I;n^Qlcj| z4afToYIFe11M7AN%okS!=2u@DR}nxf28k#A~pJ{szxf(np2;KV@6{h$w5 zfmkBDLyVtj zWpzuC_7|!_Gp#}P92xDdI)PsZ7zRDdwf2utz7UI`8(m0 zht$OYY-k#HB(AuDd8O?>q&pK=-E*XvbLh=x#*V^<`YE`37b=5qYt+oH$hHgTJjuHw zl_0;c>d8c|mmtk|OvHDTUi)Ab30`s81hi;k_9A86dv|s2H2vGGG3Rx5IlA*6(Gk9f zy|cA>lBA*QDAga)EWhlUHCMrqlKe(#*!iazH9|9qYeX4=u7nr$ki@*|StCyLSE?hA z+q3LYPZ_c7ASnP{p~@p`(tRocU)s`H>cEs&5)$=(g5Owja9Tp|)n2AntrwH`W?$~j zPI{W!o$rP}xwBMDBh|ZDRFaK=i~rr)0f`tPsITU}}4edAa7$5ZJ}oe!RAP&f~jw^$aJXGjaDU$wN507|QYt3T+kn&+YJ8 z>&FSQ4uLy8(7?1x2>)}tPQ4BZy&J7)leEfTdAi-{srEz* z9N&l3=m{c_R6T`0TQY7cO3*6w+RDMP+63#!)&s)U%}g^Z2*dl?xgCPBXsCoUS#BS< zU^6zz7iUs_^{QePAyKd0JIF$fv@CeFm}S3b0EcRd+f@PvTWVWnTU8~61whWqh__X1 ziI~_{O;|GRgjO$sDQ{H8vLUWw3pq6#pN2yiqLzq$stte11@Vr9cPB7Y7Q_eRuOcuo z#Ld>oKFugdza0mZ95J84Vg(3`C5tnGo83YK$-r{#61AyD~E1=7tw>!a^qUgAuCF}GAC)g|n zRgMuHCnMg;z9y=r=NWg}1@lpF%AJMg6qD|Bn6nKGSgJ1@59obPD#{ zn=lpn(QkwLtctu<+ozO}kXT<4ldZnS9ayj=x<`?;_b6qHAV{G%Op`?fcxd8-lvELE z68{-1??iD)G*=Fm$?ofNp9rt|Y`&O^Orjeu+)&m!Qs9Z@o7}`81tZA3muu}0n0+{s zsA2X?m_S?A{7h(OIPI&Nor#`pl^|c$a7)^C3UYmN>C#`3)fLH!+~b{xoE4eRizP@ps^5=Ei zLDcJD7BmfeR*zERP9Gj8*OO76xc|-s#e|(=+NlCbuHKqyVtAtG5P7r;w;|Z_OSaYz zX$AWilJ;0g;L%4kO_&jwf@t}iDP{zpSTE8(Z`Y4P6=Q6zZs^h=$gY0^a)-T#YQ>(( zVN!{zMq@&@OMBeDlf&`?c5D$VO{71C@6dZTd6&Nk`D3MxMC}cqcWZ$+yj~1#NOML) zp#uk-I5fhGtkGo{OgO$odLqWB($2$}a-KaL&cR;5efdi|i0x8KvEks;CN!Gg$)_Pv zG=F6#`a+k3pD+5Z%)77Ca$UJbK1@XLQHah>Z3YRew)m#yG1Rc#N{I1##C^g{;&!;N zA14S~`>Li3bF%N_F5**;kxam*^>Ux|@~RuFtdjCo zEx*+uHAD=~NZhmw@Fr4B&#rC(a;9fj_t)&su7drh$fg$Hr_}C=U739B3@SklZPA~~ z7m5JKN~GH|`*C~iWFaZb`y@IgcQ9pUbz#fBd{k)uv=Y*~ikir2ED}<#5@a+sGQJ4r zHqnSF@{*pK3j8h`{Yr+E-+pw8tn@ZxSSB*_2nngM&a<{)+qW<_4B@Uk=wc%5= z)IOu38is#sZYIAqiNa=R`MDh7Jb*W?(vwN*_g}p?lA8M51W8r7V|P(O2OrhsY3z9fClyRpB4`UO zNp1PFPSDPyqL}_CdS0&!6{5bhrOD+(wo@4aql48VzL}8C*{z{3Y3*KhqD;uz3Nubb ztiRlAx_j(r)FYZt3quW)=_m*B-S#|C+)QZTa3f|lpOcwSv3BhnZBM%PxOh+cT|d{J zG|kI1q?`~vd@AJoNs8B*Ho-ItW>xij{K!4=!l0kbwlLTa!^kNMgWpxw3NBi(9Joh8 zRa@2b3{FcM>LxaGKMN+uT4414=@-|SOM|>!+Y2t`V(Nlk)+KuCEmlH zqt;FAVZU7WBuw7Ju5g&JCu*$4DbsI2@78E5>rv~jD{`ie8CBJkK02|ll0&`6?U{oar4QT6S`?Lk3%&W@ zF?+W?wfpS7qI&i&HJe|tmu&QK*z2u~-sA~{1Zt>#9(87-EqYq|#wh?#LENYQa7D0Q z!6WidQQ)MxE8n9g{I3A-+n?NhfL>NTKo^764&LfWG(PPZ`KWv%KzFgY&_Gnz*5`&P z4w-d7vl`*Mko!KrESX5cK?vEmp|LyBG8EUzv6EP97)U^JnCMK@q8syb1AHTNw4~i; zFtT^VC_;HDLP$c6g@V_pWh0+m{yD&>SPP+>`Rw%1;Y`%F9dEmPNBFE)?NQ4PKI{B* zluug-R4#a9HarxXv_h{Q8pZl5;blV~vu6v%wd)xdpfpQlHx6|Cs`kl%DOk@K0Rui-( znjUz@Y!d>RNgdO``jgR@SkE zvHUflr(m)4zVAN4^?pNQ_%&w8y5Fb8=i&P)f_5Et(JMfNiXJa5TjXMKrE;doS^mBC z-SmYOwY#((3|f`d&n4>m-c5S`XZn1>cc%2Y_ES}TzHH9qK4&E*hCUzXIZY@@zF(jVw9X3|+f_>C|0$ezlP=(GLn#3qlF|wdcz$Q$%BT7)7 z1Oy4&-vLJ)kzHgef6DG5e2$B_zsfWe?YKkfyz@EqSS)r5gwEu<{NT98_sos+Hhb3| zE`8^RnAM);R>7w{ViMfTaNHvHAMOfPg`1J{uz5WedD86-FCnJOZ>4%E~rGVC35T&)b(wTIj$1( z_lh}>icXkZ(Nm@Uts=tbD4dH79;U^1kwG#9j1s`jH{6RbPE)WizC!^oBH(ufgf`2} z_ybxVt?ux0ijr%i8UeyYhXH?&9Iln^RR<2A-VWd;WGtyEOOD&u_rB^SnR1 zexr;_?|9_TZNJmUKR>_KU;5#Hul|kG=69ppDfYIyBs#XY<(-ua zfV+MD?)I*6x7g*bojFFF8S21~JE)TrEXp%Dvfs5=fqQkbA{+Vf+@hH*Vs5Iy(vmSr zhhkp)uXv#hUB$UL6|BA1^4)+IvSFu!FxVwMz7F?|%i*kv`$lo33{*kd8xi3|Y;Q8B zFD<9BZ^WG5VH{do9B?<+ui#C7_C?QCrDH2eMF5q;^LauGmOdK^($YtH!{&j`l1Ll# zbu-_U52;XD)f=U!H($eXu~62#Ej{-wzo6&V70a^4qf5Vko^%uB+Amw^Q`6Y2+>S#p z26wn?M|RjLu(G3HXSR$b^^)C|^2~YPQTyf%sVROqhD9rjA6@M~4F71N-11i`#Tc_m z5d|A}t*(iKD66Hi96ral3(Sk1@rsXVjpg;{)LUt|G-Dx4Jby15uw8~qHr^E(vOy{g zS_O^IB)VV8vSQ-Usv4&j%0O7{W2rXA>kKwjTF_A!Qp09B-ht|I(RMF!Oua@YtcW@P zQNkw(7{`@(-OtFd^>0AqlUpe23aH2q{uYXd*!QoY%{o5gMRnklaIL&5V-5a-r|(rt z-^_}7Z{nNnE_E7BjdeE)4Yw}QGqpb(`GiI`p}@a2oFmg@N1LejbN*iqSEZ^OWEu%9tvvy#Kd|PJLmbU14 zy>ZQBL|Q)yWw?~Lt}QyZo=1yPZQSpx!&aBf<>{h~zAkU)L8A^H{9ej!;46VD<_|r( zQRYO-2sJMf8R5naggg}pdnyq6R3QARzz9--F{F%9u$LhIQN`uMY|&$%=L-#o>2Yzp zW+d6`_!>;_hDshNXlG+tC;_B6wiX4FgJzxmL3*%4_%XM&+%4Yw&J2|MJj}x^jF2#Tkj#x*A)E~jOdP2W;;#A{}(-T<^Gg~Q5c(5stdS@ze zbnRRVwA%-=(LETQbXm9^zNk>0ArXTLv#&m_+VuBR+#0DMAA~VD7ZU z=bc}xDj|_O+_Ls6D?PFL8b`Q*ArPMlUIyr5>}x;H zjgrg86*Mekz-I{BrJ$YV8m`-c0E@@A7GHQGD{5o6kyBZjOOGe6_=0R$qAg}B{S?Zn zHeQjhSW9Q;=qkhgTX`KVcOPTm?c@W!KF4D3l}fsM*M5nfM-R4R4l^3e95B)I6a1Ly z;*D4OcCGvOhJq@ca&#*h)jOhiy(CBXVQJ#Y8NgF`paNz$qK2UwnjCq)*#*A4k z7Aq8$5Xi%XP0FaHv($kpt|1xZf!}Jj3``9WD01K}#D-2Ka$S?kpKUZL@L&Q}FoA05 z6b`Hkpd9>8<-qfp_USqB=zY7R+!PLcu^F5^3(5_t*+Bqu{Fy@=A#2h-h%LJ#*@3>U z!{M6FiblO}#KQYb85VQxsrYmLcc!pjfBJY|xb{oFG9~Ms48}BE`?JqxBCAX40io%H zU17*Jw+sP&ZW0cCzhWCQTyCYdV~5+KJ9iT(%Zo8%4HP4ojW{wnGDi>FUgtZ^Y~C;c&hM!#0^uJ zsNO^0H{t)TFHXs-+i|IAqqS;+|6a-BR>J@E{C6dpwCH^XTY><-tC8KrYx*sEB3GQCwg?h&}rSPj+XuF>4oN_si*eLOMFlM_UEIyD_Cjt z8RWDYP!gy+zLx-jUF~*C%1~7*Zm}< z@y(SzZjr|998A5AssxENPS|%SGWRh|=vt$ZAgG~Z zy0GIYGkP=89aU12)cWHyHSw9A_*@u1x7wYZHRX(LJQxFuK^(I^MwChZ%Sb9~s45d9Zp z(4o#(jLVj~Pb`RRok|>CyV3%K`YI+r0*=;by!3@Z6Gh~8-NQfXtI$`6qiL@_9Y+R>aTB<|Bu%*trUY*t9s&s3?B7* zsw|_I&=4K^>7%R)Po(~J*5BImKjsMk^Mm#k;VDPtyKK+@1abaYaH3j`Q$9rx+PG9W zos8sk;Z-_&s7r>v%ZJHGH&z6qij4HNN>D{c`s^eb=?x2ZO~^<+Zdz-D8|Bna##pn% zMAx}bSdOpWd_zt6$~lv)p0iLB6YQw3ONC9M+f+J2P@>0n==POvFJCqd&8`h+7B*Af z^5#IV$6|WD;iEo%M;y4X(>M0b=>g%rp`^QK>}yGsqkgk~5XgaVJMfi1_mD}cqB?tB z!F7-!u*8WK+^Cf~ooaOrWlpAx<&VI9V_Tf_Y}eG4EN(<8#METtR^6T)pU?D5{gS;M zq+u24_4ankx2bof>gqFTug~i3lQJjj6FnCF99FW5l4ukk#C04;N+K1dL_6`rleGfN z+tr)Z)Ot4IQSS@+1s-j{A`_Wl=$pw&3B?gBfhSCT0|9D901BX_iZq5)$XPL_EkaLp z7!l}+q%Ixmg8H9qhV8HG;C@HGqc^Cz8!KJ!+wYD zerThko=~Z?lZxHs*KJpAbSMJ)Ben6De&Rf!jv#GxDDMl23i+fbIu^}<_Y84TSphRk zUcqBw79B{b45#||n%uY3fTwq^<+Gbb>BeA2k%hs9I8wT51Q2ODE!1Y3i?#MDQ%Psc z#bAk^C497Q`ON{R7ngm6JI{4{-A#3^C;TGOW4pnD&2+Eg>Wk&~an&2fD78<2#PM_m z_I-PitHuU`Km|Yvqmc59Q|&HW`8x9mW#)uF zd6%Dr87cQ)$&9r52=dn+AL{-lS7GgK7B^9QKXrKHt+qn#;~)0MU$FNAs~d zYF|@+OzW0n?G<-e!SBU!Ta!D#se+k>8mgbz$sT7E@ToB3M%_1xPXn(J6@ed6@YIW_ zMwEq%v~fh>2i&!t>`d{%NA462Hy%e1O4M1e9#l5o1s&O<#h2dvXP0dSUAaOgS*`Ga#D82 zk4)|O;Mt@o6!l=IpH+>D@8vrXEI|EsQZZudsB0=A7|biMaIFRf0XuDu>j7i3U}tZU z%bF~1FogrTYB$H$?&XJnJ&i7L#EY^)n~t%9baSbj?pnJF*QW6?B!R!0DiN>oS1_nk zX$yTT6N<%xMM#TRsIYe98({K*Hx-?HcgOQy5)xW}lNz%+`&V1~35Ac1F!j zZlj!3(}kQMCd%pYYKjq1P)t9cnjAhi@@X&-LTHx zYd5+(xEYsqqpgyHk_*a0AjQ>K$!79P)*&-wjQT)QK#mLh-Sulau;sP7&BBzdb?$5O zp{aM@T=}A*vp849x3e?2h>}@Q>Ag5ZU%sf5O6O6WA&8r%yyOWNa=?PND%N)2pF;~OP0KD&-aOb z@&AwDLio6@ zVSo<^k)i5q-}30LM9RYwDNlsCf@k9+E+}Q?rmT*X)t9omxzdz&q-Yby3TkJSu|ncV zdOg*i@vqgN4uG$8&Iylli|~csAwK@nJzDcW*B_goB9FxXe`J2FKSRg=HT#1J<`nzO zr85QV*EDnq=C|e8#hG{~j4OP4M+nHGPQ?*##wp#P&hkOqn3UD%&)h7eUDNPS}J|)h?Me#P5VDIk~J3Uhu)kLLzTY?>agD zz7?(6=z#+sIHLUl%thqeG_)P3P=x-lg+5M9p?I5pKgu^v-;NxOx+W=yleReQ%kT6+ z6+~c-bc#`55+|J>(pyQ~r>K(pR0cOe5EAv-OBASStc2dpI1J;k3YuNqp+L3~NGOKD zz^O1~aBCy-x%;gBTokKJjk$icKWPQhZ!0O;JEFK`->4<&Tcae6y`D~@^I)cGKYxqJ@9JBYDiP`yWvx@oJfGApoBu|9P^BO|DJ$Z%l5^xhsWF+f z89~#9kLK3PNfigoS8TE*-oH&nl~U-{93-wEyj{+wK2|BD?_Fa~UzeQ5z7Fe`UD8gG zUFIvU!9ld_$H^Br@*DMjjbC2*x>iAb-{cptQoNFB(l+Ikj>9&ZrkqkoIoOx%rIZIf z*tMQadGk5eNdf|^Jyq-^jFwsLsXohv zD$$(2aXE~fmr_FwZP5xW?JmU_@6tMAhAn~T!al@&mW#{ITk82~({<=)rujLio%AX~ zQ5|j32ar@+Ozy}caene zKyBu`z=sz9q#Mu7;bw~`B(NCnnodBWSSf^6+5jyo)!e1U#E{^=wR;zDd+o0mKe>C3 zjw5lEOJ|NEUGMO8}2PD)8JXou`FX_H<_Zi?-aHz@S0)C~{EzZc$ z00>i)Ic=$Vn}_ZAb})CP#Ox33L~7-<8mGl9 z*QIqF9Fx$VjV`SZa|P)flefQMR|c1o8V1>8XE;J4im#|Levr!Z{l04 zfk2~F5|n(MMuR!A3CCtTBpR?_V;B&4BUWBmpNff`dGv{GfGHj3^cpE-d5;eQpI0Eg z@g&|hqn(`Eq&U92?-JfgajQS-=>@hArDW<1GUE^c*iG_=H-nu0Ql5w8ld6O zkf|Xs25Ujmc|bC7guox1TQnN)5P)m0s8B7c<_wAmoZ+9o>zu(LLKlV;6%00&Pn_W{ zfW!L*o+>NgspMd~QiE?cn>=xYFGk)}74oKHCReXcK)=-c#$1zn$dv$UAjdWo=gnn7 zhNoZy$SPM6*W36pjBTDw1DMa9j>jMQd9A2ZOk=DpifmVlHd$fuyPPU4ir?i_Srn-U z)<~wp^7g)($V3f3$V1YcUT=7xTCAN9>#3>Zd10m=pT;|h8!(tbnS{Z*cH;ccNdso% zrv^hRL=|<76}X?s{Ty5Y_BF11!RLc7uPP{!`yyGH3!jn=sXPLLTgi9vfbTu{mM;FQe{nh}o4`(U`nt^7u*Mu?dF2(P zn+h{V0Z^n0X4K&wDl1uZN?>v*Be~N99a@S)YG{{ZThZ{W&t>sILpAN(~0Cv-oD~l zt%dc8WJeQ&V~~%qZ3E{VvC%l9fLt)+7`KoHQo6%=%XU3Dug9^H?Ub{Sa$H<$48l_o zrCd%*QY{iM-G*81pj*zOGsQDgrI+=hL z#?rxR+I-vo*@Ga=EfgjQA0>d+25ONH6NKNc0i81_NJ;dZX@( z4!VZI{x@dITD^2Ku>=UPa+0|&8-!TM^&!a_wk4}pR~7M~$;Q%GSB*9>=mp* zDl|_~x%4m-U*@4(ZH68AAUmmVP~sxpU0&yuFfV z4KeYHOxJnjYb%i-Q{-o98)ZJv%N+eyT;^Fs+KWsN5kxz-R$@+6j9z5=BSCsT=?9{3 zYS=}OS@rM2@rKP-QoYF3MNlTXQzxU_6s8xMZdAc&P*;=PUS#@)1#rCasnSX-a$n9r zgAad9KQ_(bZ>vYFi63| zVj*cxU%jRA>y#VAMeBAouPD-}A`u2DpjsSO1f^+WfB$V4hf4%WQ8JDj>{66Z;_#72 zst;C*AKFB@q2!#y&6RNrxd+U5{k6ZtMZ{4V?7C^AA(%+1{WB>}ty*KF;7@yS3k$>9 zPr|%it(M4L0kdI4Ox1^HZUbR(i>Kl`xJ~0_}a(I+kJ@co23A9;c zFWCa!bZC?97Z!@Gr00+>F@<8*zTe2VZSJVt@L(pdG2=tTpNnH+RY;V!U^}Xk&JQlc zSMb96R1D8C6B6|;vQ!2(jIjU_0InerL84A^@gA5%-zK}%#^;&mm{mkh?PMKt4j!#T z1~i&*3u> z?i1=2YI8(-rw|-z&wRZ}oBttg9-<)VqQ+)kZnnPlZI|Ou>ln5$JqA2YJ+{fPAQuNw^o(O?d(B@Ai$D9D<`b0^)Os8yRe!f+$;HP8?)N7W(1d%)ka6nhz#F605X)IFYi<;hnU zP@`TS4QnEUGOT2q?6rC__2X-ehFNZEmO#eMIMBGIIOSbPk$kH$5IRB19%;ikwX!7C z#81!$5ez2MsQ;!u&1a$4XoJ0xTj({~(EBYlIMu5XB=osYS@AXD6g>5y(nYH6aViL2%+)25r^yC4KUgoqV= zYH)W{=Fo)DR6`?Hw3Y#nS&{WbD58c3R4(*RW<`#~KMqq9uoon(p|mg;Vf4$g5ezs_ zQR-&i9)#N`bdC)&1xEmi5&lhPFp|$1W=_W67}0wj(SKE+ne{ygvxAk9;(*#Q8*nf! ze+H_S*;1O>y5*syjNHHHX78NjV=V1-3b41oP_`dY7P)+bx)lAKTjn7U!;Eo0w}s#4 z=-Th$P)z+2zV)~(#6q}%vmGBWGGc)ncF0#P=6v&ykLsygBBa#oP6%xj2WYK zt%RIH?kPoGUwj59v*nlcaSOPlK7zD0a-!Tyobn%(&fkH^7mY^v6(#wi=tiM;HzN9< zn26RBMRD8J=6JZx@HOA-!`*A-vRCA?&?~yy7L5Y7nxD4lhs`uhgWpQST4y12TEi-% z-qK&83_s z>KzaB8}*D^IpS_xQIyXc(zjkf)7XLpO3C22 z45bx-SUZBeAjz%5-jFDO9TgM}_Ewr>@;7ZVBwp`=mf(bkPWl8TtZU(+)ANurJ9L@@ zikKGaXIbvK9S7GRv{1W?K}9Xpk15FYCHJ96weAjQ-)XE6i?dIh9FN*x5N9+6NA+T4 z^3Ap;yUwsdM^fS&3Tu zsh_fU(|y0U^sf9veEl6w#6Nz0$B8(hd?M~^&pz2i41UXoMkreeJ9C1{nx2t~sBzUy z9Kp^ZLCj4-&{9*7>6Qajr8BWpg8*{~5!Xu{4wRrDN|y}og+yx5*c6N^p_{Hq2CE1} zFj_)jy64mU!Z-dMr@%u#!#4uJas>rX0;%ae;4bAkS|clY=YQtU?L1CNANhMj>v>~ z4z^NFL|N@bl%97GB)drHC!2gyyr$DhVWNtPcLqx-V3HLeX@y>_lep55xkZwScl*c+ zFk|8%nKS_@!p5DgbA08(A==Y|H(R!E*U;#>i0$IBfo-Y)MD>v+*(R048xqOvv5AS> z{LDWO{FuG9Loe0eh(2Q12JZCy(d+)4is+huqV9oE`xcdv*{7D`=g0K{9h9!DV5}*-0e%b zzcI2{<=QpnQg@Cd_6SSk#Ldk-I~BZ;|A}_a-PLI}ZHEL`{`jmUZTJ}-`4oTC-v--1 zsmR~rZhuC|=LgfMzoU|Au?5`~jLXdgdf`)R(TlveR*~y(fvH$e6A-;sL>1n@*(hXSp=foQ?<@vdw<2e*A zwv$9+b{YliHOqv+>1h+tbq!M;70f)G!VOP?X>pPWMbp#j_!5IoPpfD0Ra)JJJWavO zGsq*hOox3w+h`4+Eq>5EBN;TerWN@>78q+wpA-LkBpp-`5$I>tp!p-32 z)*Yi?>D1@c0r#?k&r;U1ran(#ko`}Ka^wq|fR7^Fycd4par0hiLJ@aZJ%u84=PSrc zyrc=D1O?>6W;e3FRm#!Db*LzF* zOtfi;-Zk<5?nk`tD!o8xlgJ5+4~6otmwL%A;u!uH_CU8ijkdny8IFiw#&FVqO#mP? zB!^dJ)Or)V*)|yVC)}C;21TWN)ah_*1n|tp*Bt!L4}K4zcS{$MQ)~k)p$)L&HAX|P zJ*AISA@b;GxLZ8a1dS+#-{RJBzgFRO>d-D|WhebAtm=xi^0yorCxagux%>g&A)OR~ z^s^@?xD!|H9aaYkylcEa`7y6Mt2ABnd2qgz9_o(c!S8aH+djRCV+^+(8{9;3TMSTI zVA$RuT`6H4wn0dP`@*gm1Nl>7)FT*`K?leSIC~D5Z6@H20f*DHsQm~C=yJf8yXT@8 zhPnOO&cl{fwI{2i=w}Tdbj!wl5deQ3O$6I)bVIO)TYc#skqfW;Ue|=J!Ye&ix7_@0 zpTVvmo?Hj{^)shWr#J`iMAzNCRQHV6J%_ZsQg@8t%`0`sQ2(lv4A2h_J`5Y*LZNV5 zhfT)fpiP2ibNJfECe@m$`h?4L(DNG@opFyP#=VjZ2E%Jah`bskM#fPOWyd4x!O#fp zp~FHpL{yOqx~GmIq^z(&x7q0^8Ir1~FH+BJr9?Urea%12`8?@!goP6gVRJh^5K`O) zpH|BFDip}V%lRVC7k9p>^GR+XWi38NHWUcz zv#Cs!EyAd<a*6IH7Hw z&AEOy+yB&gw;S4I(=Q}^yQEDzS?+(*HuZKc($TSA4O;hPeocYlARB_7v^nd9lu#OB z9J7(9Y~FscB)il1UC(^te)a}v*YjTYdO<6^XVf*nmMNwC*^=dE)`nB+0eG3F6zLoV z(1SJM6q@7`9kdF4K}3r_YDPQG;oa0VDN@y|EKEIdy`g z$* zYk{^^&U#+cx3J-V2Qh^We{J69-^11BiG7HrI>s2*F~)oCvU4`0$nR?d^(XQ8GKeE zPcrzgB{&746#)s^cfhM&wA_X;d>Qpq5`U7x5Be<7ki-wpk`$;mq|cg1BQ+Z7v#u-3 zjAt!q2(J8iGWb*{nMi3u?4lKT8q&9$ZZO&&CL%7<8y&&zU**>i{%e&>Zer}BP0k|< zo?f(B2}a3=i+)02@Tnj2n{<*{GWd!_Gk@b*0yrgZN98<60p%*^*{Y5onsUQ4I8Gnq z0y;IxpBkRfCm$yXatr&W-Sl;44ue?5$=kK%(r}9yMe*N_NY4ua5pFiz9f8kS_ zc$m=g^{V&MnD8_ibjNx%*4bn%n(LXP6UeDtcZ1n<%uHr3j>Wx5L#)=T?zZHX%+I>K zxmnmn8%chAGBZCG_cN}*q)sX?f$zE1EX!+zDVyQw$6yk^tE-~Jkn`4@bnU%2sCs-NNl*G5#`The(_x)^U zkhd2A$geU~mIj2>mT7HFsVPDeEm#}-8jkM6MU#PA8mOg#TFOsdGEhs2<-$f-Y&9+R z1GQ8a3{?RkZ31v7C8fiLc3bh(sp5$Xo2~Y8WO(gG#pA+;^-;=TV5%PfoG?1MKJIU9 zSYLZIf!>;K>$Z*wXN{$B=e4oeJzV|loY-u^bB3Scx}z6qL<(z;2o@28$>Nx*^)pvQ zeLzvGZ{fnpes-~?C=ULo2RI#DYhinJywA_9jg2R9lAfu6{Haz-SOO(g;1^)5mbj}$ zH#vvY9cmxI%|8UK@gm)^HYFyolR)`f&9Cvt(-L}Ud{|s0EH1Lt8tg@Gj@4PB3mKl8 zsj)h*CJ7GLG{m9>$eA=Z+0X7%C!ngug7oK?mqPj|)m{SWKZul;K-w3+Co^+)j=`ls z7E;h1!$Qz_o;-c3GPBagQJ~%;zim1Gf!6E*?!&t|(+R=trT#6<>_FaaAWvo%$4qR% zb>rr)haykuH8b8I8c!~{wV~^qD+U$5Mby-~p=mZLG+QhD7w+bQ4>o`Q4Q9}+eYS;s zAVt0HXvb_n^<89>D+v2r4p}uPOCyux7}{MrWc9Qz9I}KSS{G?!z!)`%+Ro_Co{iet zaMae8jN0$0IvM=ZXOMdNlZ&ZG3pyp*;=%5+7&Zf&@5*K3TITF)066#M3X&W9$uYv|hIyxx67HztB- z)V&H-9n3WnIY3Fef;}X+dddv1y2WJ6Y+VAd~4Qi#mQtv=2@+?SCGm6!oHv9o2JL*unDZh z*yP8@a&LwUtZ;#?7TD8xxcu^cYx5&{l0L{%4Z;Y{~;=P40tsmgk=TDHcX_Yjl+E=LON$`ZjcOIg{{QF3ovzL;c*YD1@LHC z&2c9&^a~%5U4oOgSE+(!pW~bRyFCi(`7gy|1w~F^yBl<4xuR_EC6iL>AoU`@Xj>`| z+Lo`%wC~`1T=g)=49cJo)@D9CE2U)v@^Pim96K*#_*Pkx&~brrmn0r$2+bpz zF0)Y6viC$R-z`JtyCsUl<=d}Fs>Ho$9zW;L=I5Ll{G8=C2iNjk#+9|r!AE#38dS1b7%|gqq>Na0vtWuZ>U`m>sC7QgN#cZNBeSW+`34ln&3tg3 zxGm17bqKyb=Ud@?o$^KOf;cp#*L*#zEJ@FDC8=JPHpiY3q|Dj!fhek|#xM?_HJ6)3 z3gl*yIfb{I1VhHX%^ZhW+>a9ogMvEmcEKgcPO`wPV^_}j5(XZr4-F~b;_T%^>RY11 zs_i3pK>1SnW7~(iJ$sx0}kcc3chRP&P=qDrv46@Vd^U2|BBE z=Pb5jgP)=q&aO;;r2)q1F2qxKqdz`{E#YU~$ft<5{;>L|uY3mH3(ay>-lB$rXca_( zeZp8ZUxgNkD;LXL>uO!#Bstthw2VEzd~1w0XI8%-Pq5l<46dGP--ps820>(GR$q{{wl7nw^$^oHfmF#e5NZp^Ar^7&CmoAWyTl>{ zz{4!H6)G0-BL>V>0gJG1>JyxEIP=Q7`{s291Eg^@i&z|4qJEabJ;9NW2* zPFo9JXLn63HpI>laR#rm5|wO~a+Q~y|124rgFGWIj6OSi;IZuS>a!r#AeqTZfluR& z3d(rH#9=*ca+j@3OnK}PRXc%Gsr&gzOnAskZB~e*d&%Ig+;2Wuj@{gqSSL`u&ZPMK zndRA75oDE(O;$~cWX(^!u8&faDO+1}G%2)4X%}q=dEU(C9@!j5o9PBNhIft8KyOEk z29^$^vn|w?=`pP6ad%)1qkDv@W%pj&1niySw#&ELaJ$EFd$n-8aPKw5aMy0^%&WTv z3^$o5v}7yfjnJBM3}^Jz{LvWBXc~sQ_S@zDOpj^}mbt#D7k2OUViCrXd#@uXe$Y)f z2*sEF*am=muWFgo2(nUj?{zGtq|FzixXzeJR)oLJR0f?3sG~0@?iq?R5+cN&JpdJ= z0r!DJP9vrXB^i)Y)7b0PG_Ud^b^P=DxEQ;Ii?IVRqE#w@OUUMgGKv#GB6UN=4=Ey2 z*9zBIu96~kEqqGe;Ij_hiFuy{ZtWT`s*a%Y5Yo7@oe$QURc_fpvu zdL;zPZQYMHo`*g|BKR?F9Q*=2RvdPLmYQ%wrp^1Z`;0>-F)1K0T9iPwr{+2Dlaoza zcsMcnVQykl)-s*IP5$?tEU77~bsee16a$?&r>D`*F$O zhl#E{_dUmH1Sa3-bsb`L{HSo{6Z_|9bnml&0?p*3!ugTUaZ%LI%7UGCnqVO!`?xKjuflN)=-dTijOX&Ci+O%R zO#W6OM7HX=Z(4zu7bCx^NMQw$jjqgh6w6#nP-pT*J;$(upMfzG8ySxtkOq19-1q=korTPMHC`rwB*}_vO}FFH7{zc7Hx# z_;JVBZM(d}IqLqVO}*^OSf{#;`4+}P`v>lOXLrTlwsCpDld0xle?K=BO8$R_jGDU5Sk+1v_)6(7ZbH zrE2A{>cc5t^eTe?HAFvH;3OJJ!;)x}^(dmke3EEtSQ@2WDelf6M|DaL@mFdQ89B@o z^O-6oV(kn|a%-zgBGOF~RZ9l-0^e3ugcUM;bZwRMrJb)2A1Rl+lo}DA=RW0d{me_b zS2td64*meaDF8JzW4z$sL%6;jkxz|EW-tJh>4+vXS8yC1%wJd9QLUWJ4Uso#BfxSA$F^H4+8hn#uAC%2V@+@XP+Bk8zldyUIlx$E8Jl*_*aJy)*n%z>p?h4bNft|WLlE(2c&Vsqdw6!t5**So)5n$ zIb-Vsy;qP#XX#ICGxU-CShXB|2(=qM9~?;#<`^4h;x3Vo`HqwCqdB<{7sSSPJn4762b~T-(=;AEtM;?~%q*T$8bFclv^vIc@GT?sHqo^a{i@;i zqtUJ6>6P|p(XBd_BaEqU))7Yi@J|OH9iS2ZjI=j>EVga}^o4}A(fqe7iW5?G5 z8~b3(OZY@x!FEbAJ)gtpZpCJLZnp22X?kJUCA&K9^;H?WI-6Oi=%6R3-*tohCZ+QD zBgRa)%Nz``&e|Io;%Ze(9N%lU$y`FPXZ&4SaY~FMKPe*p$cdK3eFIKVGHpf5?LCWhs%jiJWjQ!p5-ImWIFr7pgqdcHy zIiBTbFj!q66LcXCzb_*Y{tGEJSTmb0tgxa}YAN@}Rg6?04H1ItY564-fBkm0@s-EF zeCzDw7~VNK{X8nC!d?Q)=Bsp1UlnlBwCvhYHs70r7YbAU-GwQi%*L4V+Xf@vVV&fH z%5E6nJoyjp2Q%MK3g`Pt%;byTWPN^c@G-xO8Q!eN&aCNBaJ8ZR#&F7?8YawNr5T^e zMu>E0F>$ruUguC45E^gJ!+y=3-Y36h-}m_v%lr6F1TT$9;8dl|_NvIk*2q^mQ4^Yj z&|LN_#fH4@nKA@0xs)1K9K-9;<+=p#^_7L;ZGTHLgHm>;YSB11pu?CzL^iU9qEtlr zoSAXdmg?k_#hnrP!a-XRyEtT909NUcZ7w=;sR7HkFZjjiT@xXsLhaD$8`fSe5_CfW zY2kW_pJltMyh^!iwW#Au%+$R7@waKit=~6nD-Q8x``RA zfp#1^*H#~YxT3K7_^4uJ!@7nT8ODC`92FAc*N*m)OZA1Cx8}aLtfFFx-?cto2A87! zmFXzqbQ(C(z%kX522^H>suhGtKEEc599dAUsM2}1a4kQLpVFgt8_%M4M0c!{!6{Xc zXxkG7iB>5{AmArsbyAS1Rc*^P%2IbNDoBXhiquXuaWc7nlC&o3BC0S|gw_A6$PQJdA+^z@JD zQyJ6KN6yHrn-ZpEM6k}w4RrCP4r=k69l)w*P4=p%*Km%0Fz4uO9=th?2xGHU_%;i^ zS@i=%DiC|pe;4M*g0sW<(cNaVVAdy?1;9B4*z(J~0QoDeqyqE}bxl=iZkd8&2)RcUmpB8C;%>do@>iCe7 zlhTx|Ui7nM%e{15F+-9Jrm655l}sU%k~N#Wu8HvWiG92PD3Q01)BnWxRJFEhC$bOp ztNaGO!uc!RvQ{abA#9>fByA{m+t7gm*90f&(4>_-(l}mWV(ZDw%?@|htaZ9u(QWKhtlay^hbffy8f zbG7b+$a zBakl%{n60h8Ttpz4b?@^^it=g>1Cbed4M(%GN@Tn)C>eud(;pkWGItLY=mZ!i<#Q6 z-p3Vsu5T$M6a`xcM+-aP#VJI}Li|7>4NHYIt-?I6EX^0@oU>g@IejWBa}o0`oQ;ul z*e5wZs<@B?;r(ZABNpRb1ga9)53d*jkZ40YSDCvNM#bRAOI5VoD2=FQ*(haJy3vJZxXhPB$3rZDX7^INSA8B^`Da7?R@zAW z!R2*Iqx^(380XR3vysa%$#G`X56+Bx)qCq!;KKEHkfu!S39^32#YVh8jWhUW{97dk zX2-t2r$FXE+ZdAh<1ew4`{B49&Fd|O#HT4HxX%xs%#AxssY8iH^nd#KQXTH3W{%o? zM%QT%_W}?RADd%YkSKRi9lI}R$?nyGPzw!b=fx04pbHHbpq|yKYb=6Iu&HZ1adsD%~FF= zy~O3deso!SHBM{@!Pa#nSu^BOlFqlvPQgv3Y_qN@0 zSZFZzc^$Q3meK2OQztW3-+T2<%)|RN%V-$ z(q-R^z(u{tRB4(nHDW6DMo84sSSRgMV3Z;BK}@5t$QjECr{|x;VgASYyxDyoRAAPx{pLSiRsY9roo$0qXd_lz@-JYmoOq9 zYPBLQ$|Rbdz2t^7oFYj>-)fCbavm<%S^d`(Hks?~rV6SFhH|CM*x&m>-8&DbW5@pW z-vt5{`5(h_M&P|OWEE=cfULrfT_bCB1X+*BFA&tTRRfb>-POE!o^f69*ZWbuo7+bc3RR zyMpfwfxcB0A=^qEqBc`B?=b0l7$}A$%iuO^W?knn#NqAaL-5<^mq~lss$a}< z)WMhJ*MeOUI_!{I)FH}JAsynl2tWhP$$aX1&^;MR4!A`XI%X1QCl z+@mi5MOFsa5I`zj$FR|F>mip)+pxRFm1m-5iFJQVLyaV1YoV=P*9ok~62^3G`NL}H ze%nTwIGoqS_k6p6r=r}V075T9WdH8aZfOM08s7>qYu7tcNjed(WD%scZBPx@qgnOt;uPa zXFI-!Fns<-XKEa`mPq-1b4{kJR7lX+Dof+UD54_&dj@3xpf#P8~v@ZD+f-R4QGw_&@DkTC6pcSJCCf>h6ufW74-=20fgi|(){ zI}pw*IsyHhq_^j5Ui3csS=BC{yFwpDosZ;%0pl-6jchhIOPS`YKoyNWr0Z|}WT1qFRLZCtqc#ja zVk~w#w6aTdy-dy8l%wWIUPOBY0*y!>Gou=R<;nk^C7hb|`{v;J<^ZcLL;3HsrCZ!B zE?m=Kf0%0;chg=r>wxw!6n?+%fXuVP--Q^#DkL~g3qT4lmNV>BSUNvt^2LidZhQH?lS298=jWSz@eLd& zUmR&MND;p#NK(XYla-W!2Q1*F+<#lZJ+HqXTz`k)l>6|tf3I7@7k|W2BDW=s;XmW8 z61D$tOSrILyAf!Jh}hOi@OQK&ymGPO^pV*DPGA165T`$Of#LMoMalU67E?UQ*y(*H zyGM{+8SA9KUd`%G*br(-^KimzO-?gu1_8;yz2XrX!#tu^qK$iQx^S&22j+9gZTgaE z*sCy?8Z8O=a*w^OJd>_9DH8>?s*0LEuWKtG<=2`}XrHLX{NN>Fa%ug#s9&}QFBeuHIKE04Pzz%iT(!Uwg1Cag>25U7F(Tw;NC z7Rj@CcsNqFf>cD1{U1VtW!f#|Tu^KW@cHJLN)CpJXDg1U#&O6Yxb^ah#y}lje5wFn zt@Cv{U)1?jAd2_K#(3S@d_8TJWGB}4DP=%#dL3i?Ll=oIEWXfKN_h%oq7OI#;msosh=w1b~E12 zt)i_inXI>`QxD4%NxcgX9q4nuvCg-|eA$D{XSOjI=p)UyY!z|TtSYJb7^Pfeb*57~ zJI`31LF!~h+-_A;vkpc{raIH9cgquZCjV=~M+}@*8uYgnhe|N8rV^D-C7)2>8Y@1X z>cW$;;)4`dfK`b~r;@ZwrsC5n-O&Y`R1hb#u`$ue2E{Z=P*#Fl{eX+Bvnsu6j+{5%*hvY%A7~7DaYsiMt`~JFJ61&zMs(nP z*#l&6*9b&NRl>_vN6Su`uDqVhhg~K3Hr2UZMYoNO9opVvP|*%;RzX=)(#cI;_bU4r zW^<6MuzP#mU$700TkDj@sUULbuDKH`jAYie;`z6pi}|I4m&mDc_k<#;4(IC?gd?ex z^R05eWzN^4Va00vj_?$8F**5Yix~jhoriN6J z{~fX-Z5HWZ7!0c1ZF9aQq$Muue4WlWVDJxFf_r8p5|Y7?C^`~Z;|L}cR{40O6bEU% zBao2; zeQcEjef)E94V{W!ieUyB>Z~#Llg=7lb&MzL=xX*>l3f~P9bL_S17A8T8s}`V3oDv~ z^*YPSW(`qyhaP}Ua0ZCxgttt4jOG+D%#oMK81RR?`|ElIN-k&!Y|IEbHI2O>p_LT> zv#6J_n*LDJ;BE<6&CCI>X4)1MyZWen6}mXBAWwzYtz!)4%z&IXY-wSH;B~$<7O6xt z&rAJ;2AR^(-FzT1H&gyqn`wSe{3g5M);j$sv*ByLKK{)*PK!0%}{A){0RsGtX*61alLgEKlfrG3Xtagty`6pbO z50R#R+r}|o_tCoO0m&l;h;y)fnW{T28C!zG2c1I7{9Xj3Iv%GqQ}l|G=JR1uZzaLD zl;BEf@7Zpp3A~h6i;p4h#Y2WG!VJH;h7u1_!fe%dI!)w1abZE^pH~x#{2s4ovP8}C zI;UI4&ML&DT%Ol(+|swfVo1C%0PpAOfR?&Jwx9}KRm2K;Zn>00dZ6-m< z$z^p!?2T{)>q!J6QTlRf>;)Fo`ewMmRu{O0Vj=Qv@~v4n4=0noj^Bcrg!%TZ``o1<`x{xBy7bgHRaTwZg!#lm-_F*4+DbtdQ&t2wOYUBcmU1M4 zfX><(T72MYumZUnZ)4#4w^TuSG@#K4Z}+*2 znXrQhMe-Ubzq|m-FJ=<{SwTIA@e5iho@C-7bVKRVMsj!@m+v5TnId=`7Y7P>g?wvL z%9~DIDG%#MBYkH6R`2GR&mMh2NN-V+8ux@|jeAftyur`iKg;d{<1R@&%wTunfk&P7 zXjzLnJ*`%>Ybr>9k0;LOC4zOGj6pMIw>1=TmpAV6#$DdH<>iuIN?ZXq+v{Aj=#0Oo z4?lTTsFH2L%iHH|yhk~kAdoWW#aH(Nd?}qm6Ut}?s{qa3a223WAiBgH_XK)d6+v1B zJj;jav&^Jacgx-F$k=1&PCxnw)EAmqu!qr86O}fbtpIy->z0VRuGXBe3(?)bWi7xk zwh}<4ZD1r&iN#;;ZreCgorursohBU@NQ3?lDKD%fPU0`?s5nhm&gFS z))b|`o#w1II#8UKLmJVY+E0kw(x>`rZV`rq7h4=&^qG@gW%+I$uq3w*h1FWzEfwb2 zJ4rq>xuT#y0TF=9S@s}xc)-hA$nI8?vQw|cpX4lhr#hk#G_h_?_43%BVr z8iBW1W9(TuAhODG++Z?1*GOz1#f?4F+y>kYQ#0pk5B#%{a)Dc<+)L@K5TZ~?0B6v4 zwOLo34P9Ke0vAY+Ez4?8sw-P57kD!}7o(I!Mn9>(MOil5zzL)H)NqBLy(6D@gr6Ji znc<3j+GHcULJlJlqNu&&aT(mWIm;%=ai(VAB)$u5ae+fPaFSN})~uV2lgVDjZ(t^A zGFe?k39?-btok3)!zCJLEbiE>G1gRi#ar|dX3~ZOGcm&G3hPlZ*xVrkn>&`uv0N|! zAIv0*!-SutII!3yacwg?4Y^YbQ&vPu?S$Nk+KSxCwa~!{kgxqH*PhIPlaE0nvV=XG@LW%(S#D$to;8}ckrRszWX@DjY z73LEcI>ffM)GrWDSwT-rjzJIQ!u$jdGa)L6`-cKPl+*caRm zWUR0twOJ9jTUiwOW0y>YrBnCf5w^H)IGaNl-XV^y2DKQ9vq~9ioONF=lUR-$On?cg zHyB$ogQeV6eTc5W8T`~>)<1C-l5!`8W{l%?Ch`x)l$GG6)-XO$jCM@ zv0ly0R`!3;FmJA7|ECsf7$^#dqEKVRmtLpsu9co##DTo{%Kby_qTN82~KrB0y;Nd~UnAf2X{rEy>j zO~!?`;zrZ$7FSxGIi)Lz0+IT}a$0w*Ug_SI6pnESQyY8_U`a{}U%`ia0_SbQgb7c0 z-EX#!-+8IALW*z+-+HAUfD`3GeEWetyk5GP1MCuBz%rXZ?CBZ+LNi1u2aci@KNq(} z{U{0%1J2jx;sliZbGCi5E=^D(yU%+mT%XXOB?^%Zj{Ch&-b5qK0LQax?HvWRh{CJu z5Nb&e9mhB$ZuFKrk;Cn8Z}irj!m44vYyZg~zSo&>ZR|!_V<8EgO(E=7>NWKZk^r%> zMV0#FQymRI)vxeV@{ax>vFZb+wiJ+GYOkrwHb)!n(^KkSehb?_w?AjwKi{2e+dn5= zS=h?C?rqw+NepkKG3}@M!SlR8&7b_5*YzttRt@tWtc>6#(Y6BdYZkJAZ=0Ajt}>_9=ygta0iZS*fmP3 zr`fen#!D|s{Vj#*Y4!;!n1J8YdV88ZVgcyNKb(8idQ|$J)a&nmIp%)mvKM!;1y**# zK59SAG0ozBSSKh8b_oBqALb6%m)M{?`xRNYv(K~b1m>Pe)oi#8W zbhr$m6(z=ZhkVtk!|>(b?GjWdCZxoi$GhZ48X}=d#lk7G3P%E2WsY07MeN-IWWZWY zw}Oqa&>WMqo`wvoo|K)PR1iWb!iW#N*KPb%Dv?BP++&H7?8~P_?s?UcM6DF}E2QK+l@{B^c~0M#PYG(W}9*bq~h4zQt1F0j!g)E?Rf!K!5`Sw zIG@2kVsDQS*H$(*H|%PW_xH zdZ>0${ewzaco5KQWxJS*GQR*k5>E4~&`i)S^$TW(& zGKC^EUqm){8uPa?_)R_r{{Jb{pG4c?_N|*&G7Iy?jC+<+Ht!a|6x^L3i9gsK3L85Y%8S0pfv zEsjkr1YiWJJYjBFkd+>4ER<@0@S3WjVs)z8H9!cW}1*TfwrrbPbSC8Y&iK$(S!+1UMb_?BPRL=C3+k9AcIhWE6y zGU=h9b1zHTKH6yw3>u{_W8Go%RE?=_eij+#m&NJY*cY-TI24)?;eJ@-0H0fwUb+G7 z@2j^No8jctu?j=6%HnrwoV#b0lCVc;PN_vIN>O%7iMVm-RFcr1N!(?KyX@6HV+``k zv`fgo^W)~Ew30#Y+G5Tv<4M9BPzU3%oqdS;Zaox-L;2-66<+skI(UA=Y8;_~$BpM$ zQA3nOQCL!7$1SDJk7gN6I8{Y=*)9mN$H|weT7Q>Q5;*OA);b4USn97tA`hmujlrw= zF<{c)osmqgIBKTn<%Eddclr$9u}5=ST`^fUy|YO1ED z=``?95Wfb0Mqd#2r!OlyVdqWKbN6u=%v- zeB>{NB$c2`aIc7Q>T0*S^M(cW<4&AdiX$h*{xKzy!GC2E{qImVL-^tv%4VcPaHy-; zS4I1g(%<0=y4!v*q2gvvDQoKY9lgEh{cQMzvSQ?YHVt;eqEekgvw|_SkW(4sDQ9%} zdZ{Y3zO{r;bmX|78?vz`pV?+b?VN{=Hs$k81p7#1}`PgL>KebV$9H9 z#3YG%n~PaejQP1@^d8`=AFz6^DaNc-jNSu$!o^%zjQJul-u`=_k7k++xg);DlhGj} z?B~MosF05ZFa~0UEm=mkBJI-cTM-|S{mETs?YR{wz;dJ2_;}zYp zPD;_8WI>>SFh+48fx2$K99K)6OOemHoGY&Ze)mub0ikx%$4<-SF3QV3!>Z7)rpm9q z9zRof|7&rqPks&T;YqI1iyr9K`X)jPmL3X&hjnc7S$fJDP(J)tKK?DC-^$0o6@UIF z(YVlnz?AH1+!1ibBXmVv_+ZyaAM!r=DalMa`RUb1u^o$DJGj6v3^z1mpIt})Z+)p0 z$zB&HAAGztIXEf%VYJKPBo@L>prHvpZ&qPA$*p*@!$q7-3MVmGAR`J~KHqbk zOmeMH(ARx)4JjyUI4Oi#>2PvRIZmo7*+$TP+c$)hGWn&dbZ^|MQXNyVgb-G_)T+Z0 z!rs?jQdeRHp@5dHhn1q`=$o4NG6*|C%Uu9wXK1<9F60Zr3~fTd$Zu$aU#*tEPp;7N zpu#xmcBIfUFTHGeED1>mLcbM>e@p1MBJppPKlovD@QNAH3Q;$h!gLAIHkbnIbtsDw z)mCRvXKqB=?wI6@qWKQy&Z>p2aq)Q;!?K`d@S_F87+j35)WqtwYRV0v+^(?64;7m3 zEwTeMWR0dhUpmU<<-X1ii->jEeQ?3XP1z>&^G8~&aGNqg6D@Co)=h!bEvTb+KD}uP zne*TZNYRhZ-B+Bs;k*K8{ zjwzERmK`v8a@!v*$<%S4W+;Oj7ApgQORA(Fg2g7`giUd=0AHXLUlS+LPm2P&4PkV- z+hXhTUsfB4W%^&JaO3AO75>Vm#uD|9vip>d!jRLt4fExxC1NuXowL+hzKzs8*Q`9H4Bm&)Fq*;4srZ0Tuk4&E8t@NOIg0{(? zMpVIoU_&sN9Cue3H&0-0AyX$MnuWT$^vam9A^2JU(-CmPplI__^U`BF()>|P-qF^&Eu@DEI^=5^7NDI$y;qb&PT#wRihjCjkgyB>^-?9jToyv7jwVU}2ag z0gR0psZ|x~eXGO(#1Ln|j_)>oZ?{Yhv37YPlq+kh*Y*!Y8ob*tj&;oh2%Po+B zpiaFR+>4)aT7g4XoK2qwO6ns(wXDY#9j+t2toX}%qS@~kQ*=afZETW7f?B>4{1{iN z)ELvdoR!oayA3zp5IfdevoarX6GWHPqu83b4XIv%px-{zUXETuk*Ia9wJ}X11`008 z;7c(r*+nrb^6wIMP`pi z?p8j>^$VTU4f3xdfKFE8DCupuWIJBpxASe;zz{yi-AVF0_#nBD1G0W8ww4d2{Q+)R z#X3I6^=k^q%4?q`SRDJM64>pNe0 zlb8DD-l|}&o3M;IQU~V#G%kXnjQZ6XDzF#Ibq%3(qW|f5WAK#7uZB)&F~cgH8ap99 zHFlh)x5iez&`>L38-C)B)%QHU>x29GI)E>7YeFhQxQJI1TDEx&UBrqBR&)e6KY>M- zmXGRffU8RdFWm(uQ^tffz)+Rk+&K8e-#%x9`E-* z9`(oFN7D@`khtzMn2mnFd!dCV0y6KKdK&w7-la?YuDiIh{7Y=`DMi=(f7X>{Hbnjz zU{-x&g*IvHhkrh}RT5^DdJ1Bo>ufOS_YX!DS7&kZ#^s%4-t9((f>*?AB@wW=4<$iR zqJ<>fp{B`O{r)GS2(E~-;{fe^5wZnw!FYha>a7i4ENAG~+Q4a@Mb|WT>86HD8-k|B z`u<#`A>DMA#=nJ~SGkkYO-ZHGAYK|YHIpRWbdJN2rWUy~(oIvzzF=B?|FdLYa6Zd7 z#;>@vYh}qdJDA?qkiMp!bhA3@`v*xk>spc+vNX-J@lQ9+Cwltz_5Dv0J^covg)+g7 zo%|YRq;C`zAkj^d_3Hccq`S!kY?={{xGEM9(>J>L*ljSpG`LX$pc;Rpq(Y?<;)NHy zg%@t^s)b+K+Es660I^mj9s}AgcAsFs@X^egVM_F^WBHa@v!nGCHl}TU@^CcRl@H??#zLaSNjTX;=Ozc?EV`O&5rWlX`$_r5Zk<4)hNglGZsSWxnND zV%Yj(Ze#}xM&?jswfdMl9-Q&<@glvfoFH7)UK&g}5@4pM9L)+G4AV+nh~l_#Fy(a- zh2h!>k}%f0KoB}1w56sQpk=E%WsDPhu=cIyHMQw8&1br)U7uo)U|@$~;I#_rv~=_I z*=FhH>vJBTq`ThI35$&D-q;D$gheMfEFxSy8^jPU8Da>Ng3pU=sF*!NA}0_Sl~JZB zMS@ey!!jgf;_bzozgfmzHF*c$C&&&cN&n*vB4Qn*38mT809_o%WsQ$1X{Ku4mqa#r zY+(4Hr%&NH{Q?CWg`-%?1?RONJ*;hM8OUTafDJ)=gK^?!5#SjZmUCaVatd2V+Eo;J zc2RZtUk*`jmfd9T8?s&E9oC@IAIi;sx@5n?v(pvrbhE9%dX1x~j)(mh9>tW*d+V)H zN$-rixx0Fr-__qb@&4RD{dR1){~@Eg+jCF;W-M%wMWQw(XUhn??MApE<~;x-ZeU}Z z41|YK8dZ5|y2hF(&%n;bHo|HHlTc$T5Y9);JwYEgnB00{?0OdI3`nOzYNK0w05D3X z2uBpe%s)?jDCuh39bp=xPd*z#XjAB^&G$Yd^r?F1du@a6zj55rS^jt723_ZVwn6s+9H*v!uf?z*cN0NUKc9tp z;G(H^+m>_o+Na52dz>L|(8C-L7%e{0&wRYn&zhM2oGj0N^H;x@wf4HbI^HVE>4$IQ z@i1P;VR^0fOXrL|n`dw4EhA*3r1KP*iFTG%5YXI7eJ_U%+)EhkVD~pmep-a6z#~~Rxmfb ziddE#D{&CwbZentdz-uK=TDNSk3e}^NN33xTe|w-+RN&LbLJ$1^DCQ!vm(#edpDdl zUccwC?!U-S1Xs4!r*D{@2=?Qmy9UsBstj}cnYTbBPV@tgG|#Lo80f5}r*^VZuMYuH zcDNrL5D+EDbPey-af#ka@Qbhx`Je%u1^ECi&3$H5X@-*>Iv|4Qhy>5{!d2Jtw8nt{ zeF7cuvA(&Px4H%W51HVpjJt;P$a5#7A$^-oM@9DjgLTD z0JHoN+6IYqhaq#=^_UtvRq>XN_zL3XS30F1QZ1}r$#W`cYpLoa#VcKL43)@@At^|C zQhjjcociGW%iO3n2j?(e*D_w`FkbIv_$m|Oke(CIpUxhPc8~`sB#}M9=z+c^zY3~J zE}7Kach%L&$-nm&X-WY~z23{N&ehv1_4N}YPFO4GIL^0?-eF~*@Iw1L{0V>TIM|hISYh0`f;T`mJ=l|4r)hf&F8b`+n@A~-4@VG<_|Vc z?8}QEus%27CSxBEBiuremej5CJcvgC1Z-?{do|%n2oUQeMl9NaQ71Q0PX|V)iJ-&- zB+`+q1EX8;<2BfE6w0ST6tmr0?75n6wn~9aBoRy>r<49ZN)g8<^ zj!jS%UR=pj(>=uJN0L=|FgjAl%3sTlr;7b?dM2WK4a!Xq znrZXI!bKJC&`*A~TE$2p1t6X{2Bx{jtV5a>9Urh1H^(}0HP;;G6o!4JLmtlMdx-~^ zzJL>i4EeU$mz=M_zX$L#_KtrGZ{*n{{!PN&vhNG)XWH8uJ7?j)pszLspVo%}Ef#*@ zL!~fm6$`H`f;yZsHYiy~S#l5bm1e+j)x`pOxT#n(;C1N)aTNdVlXi{(Px%IBR$03S zGiwQ;vLl$oIm+QkFY$0}1M?h+)k+8Bkhu;hH-ly~7iKFM7n!k=&ySrXo{t=E%-+(G z-k3J&hr;w?(#Gsb$L{kAf>~aCO|hg>>f$gK_<0LWQ96i>($xnyz{sz>%!y%FhGJMa zj0LWK(d_)m#>Cw&B6fU#gKBHG4fOSfRA$&{XL>(YN+FC$Lk#P z9%{c>WFJnm87A(f4l;Jkkn>a~wjOh@4LM@#=GGaSX2@`Btul!2e~@+J{Spokc@Yy- zj7ejaig?7x?{?a3d@Fy4sLyW4RS@=JJe|0u<0?oOY3~l>Dxu8g4nkS}JSr&&-p#?4 zk>=nd5X!TnZU#TEEy!%u?JpStT!P~W;J%N91n_QxL;#olA`G}qNf9@?EdYAR>r=Lx z*+QENtC>=ngS|N;OHcgFC?Ri8*=hztR6+?g=sTA=K23}$;TsV+3RSjIrG}{1DB%U- zXDH2?z(JCSb9L)V@6s;2cZ<-1Z4G0kCgg@`gqcwK)|}9iv`) z>a_%>ELS*`CWoN zmivXOjS<7-3*iBcQODAi;^Evi-z|wgorL54Oei?tMMcI|jN+Mkok`AwZ5})qt1V;y z=em6>v}D>huSu57G>_-mVv4`AG0qQA1xV|V3}e^%f_m=$rji@^KL9=ax>+?>Jp(>75kMPjIUH;p@`IUV!%#@qI%S2D zTS1xYhepUZ79zZEEd*Jzd#N5ifYNqbUH2K!u)9MtVC3`6MoL3%4{HFi@ujew2`p!> zaz$8k_1KO$H0+&VyuKUQe_8Je&%g#sJ+_{JfB^Z z!hzHlHB|>vkMm)tPLdGVtRK^HPUpKTy!0oP0HD<3N&rmOyvW8qt=OKc`^ngf`pfT~ z)b;qJL}p*#+vGnm;6IQv5JEp-K(y2eMjwIndu#Tx_t4u<Jh<0YX@_9@sVG`7UMW2h9LONOTvfnld+YD&d;Xg8mQ`6n zl_T=jg(04g$Xi>!T^fIyc0fULHcN%%57D1pAo*O5xl5LvI@&N5@?A+>LeM?K@x@N! z0x6L}bWQ@+j;w7$fvcBr<6}xDZSEVMPGdoEjXGik3`hnpa1P4zkeo3( zA%5`F1p$h4RNJ(HOBXlMd4D-guF}UQX>q(nE<;?C43$62iBDg9#xSuP$6?}2%o^Ki z{ElJ}{evxriAHX1KV&qmmBi$9UyHQXooqOQxIOpa<|3`#z7twIO6E^3RJV6cYi*OU zQ1pC?9s%8sIGFrqX*@V69Vx@XEnb&s%C2xQ5yIcm2d%GmEA{U=4!$0pby9yX6^hlC zP}x0~`ir#H9aIPx-#JR||AIOsN$OF3r0#D&-CNU)!pDpIU3-#0{KJlYx;J;c(;vRu zPi<~L*GC1hD~ACpJ6oCz5s{ ziMWP|+{Yl}Avyf2Vjzbf|4}It?J<9TVEtd6 zKetll?r3A}%F=jfqwR-W4u8J;_4)HZ9xnCKvk1(kk_JL{Xcy0gV)7}>waVSa4wF+hfQ~f_^?PZ;KTWbAa4vGzRh{TpN|jS zdnh{@KGcOr6uZKQ$cj<;@PG3A&gJ*FJ^Qzl-v@tX*tj0YVdJ~+3bFA;#ej{Q*OxG? z5`84`*5VF+sk~FvA1DafZ~OjVLf)}sg0*FP@;k~qi*Ff)ga0SLoAF+o=aS#GNhJCG zT}Ao*n1cL%cqqT05z6mZUFJ{Ta`l1|xm_~%>&ol1C9el_E0NPVub(pBNs#9{`FzfR zp{ zCQFo>!ZYN1B!{n(o`w^_Zvk-*e#{W(xkn3#GyD4?MLSwCP_!3*P{fm+(zDlFzn%4+ zzZiZzbw}y=?}#63drRXB_z}w7uY(_Z{UhT?iMmr!!b%8DW+9D^={SVlSI}jg?Z9>% zuxbpNJPswkwFfJ?n?RdiJz{9{Jsd}gCaw+9W=JuhP2ajA+Bo9Eufrdy=PnT;4ITt{ zlG31arv*{+$pFSdx4U7Bn&|v7?bq9$8b%8C)7mpC@-^`Jl0;t0-Op%{;?wzV{;yXR zyI7aH6GWZYvHGwTDpmvVRpgaRbcS-!Owe8*rwLP{`e@;!Rv)c=a2So&IUg-jneJ4s z>M5;k60&!=FPngsgV(DB+U?ZI39_>ZvS5;k3MizBLZsqd-9)iQ+EB^jqm~f1Au)_F zrE>6Jl9~OS4kcUlbS-#Ds*@+MO@D2Jj5%oelnkz++9|+~n>$Wbk1bNc*iLVxK%1>P z;b9xHN*p(23%(l;+35sn7re^GusB+y^s&0QMwA%?nen=-*g0Yw5=}#|TL9zMpVz&e zk3^{x0RniHBWxk9s4DN!9>^HvpYW#8{6@u z-*sw5`>)wIR8j3`+hJ?r_SDmZ2bP{+b=}SVsb|}dW*_2sJxrL)>~lOvaqU;Tt5ZK6 ze6;+$tLqbyiuNF>Ey+yPltQX}JM6VN(^M%-StlpD7DOsKss@j-kM@`%NFBA*oQTCs z4^sT$BNy$vZ-q|E%EIq5C>HQtXKP)P!;?=t@vgVNO;2PNRQi`~%>k{6+me~nPo=EE zgD&+B>m~%`T)06|{^aM|kJ;9J%5uKPv;&{Dr>szP`!7+lJ*Af~+ShrN>+^$yn_a^X z3mg6sizpuD)n7!L9~kS}>`wQ<4>#y)Z}EJO@!mFOOh@$&T^s3u*ZpNGFCXW56wTc> zyANXHt2532rsdfR6_gMT+ z;BgB^%M2dVrl0S3J=Nl8=b(4Iu~FJq!@?2H_Sd&uGz&!nPRp|O(4@wkpGkn)P2){T zKR^v&%qD^d%I9N-aOT`$e)c<-ZtNDj?$m2P`!H6?IB(zTYf85NCn^QGWJbvT zA6KfwVXchV-ZHp$7({F;?{8WoIn4fOi{Ewq5Xn0R&CFMQTa2?x!h-O3s1t8%*~U+Z^`@?7!FWj1&TW|OWIGhX>KI!?R7TK&)W;6 zcoS4(1N7_p_Cugwr-bzD6uNiY+`?Surj)w=%F1?6VW#|oz;dojay_9!v_sl=!My)y3ym%wV?2t*S1t#N`0oKt>0^(e%#&(d+)9QMmtl=-vO~{QlCM zJ(=GN3aLisw*je=e*de*4IlLIFsJm}nxZ?S>&=-3lZ=Rc!SxUe9R(ux-(&+{|5E*1 zfBByD@AHLJh5psTQUAr!b0NY4i)U=hI59KiH?NHyq36@jKMxwl8Q4$6lG`sUOpvCL z2@+1yuagaCTqbYqe7^1zw(P;daK0Ai$KXfRvEhS0Oi|658+!#<=EjkUuJ1bzekkqU zV2R3c@0L?p)wsxN$=C^H<^y@P)sJ{VM(TlDts$`ri@2z zGr}fqd39{XwkF>pLisbqex|9_q&T$^)voQy%!gW%mk%My{T9OU8FCI*d56`3?|<;R z#owEQ-z2)$&#v#h9sw?&=(--^v=Q4iDSTtWTvi#G#|Az0J5xy z;KY5e2*AcKMrOS*BM#%JH4tW}M00PN0lVnh;PWg_?lEBN1ig!9)rV^BKU4eGB@0xh!A^*raIwpQ1- z=!11keVsm{UR-U{^&Ky+_Evv@h0Ob-UUeekRgYmoVjX8C@-9R~cBa0L<1T&v;a06g z?zyKFeg?P5`%wQ#@b9k;zmv9r0y1_z6!7NHX3wVo!7Mi zSGN}KM1}EzCo$L)tzY;eOS1~EYa&q%!6sVK%{uuAeBPQ(T`!L5IJSP_W+lN8>`4iG zU+hG6XY$XzuJ@~HnSGnm`_!VdrpHiT+eEJZ$1Cd>K0by#_2V{wvvS+xUgt@~o%UXD zQ6p|y118V__1Rmrio#q2>L>Mki@t#O>VvXAUES;o9Cif`?iV)bwN0*UJx-&t^SEVY zA6_hbYI)g>uUq!90RG#gay4}Dr5YCDmep`Xv4-au&yv-B`R=&)!+x7f^Snt9xRx?t53LZelKVF>fnw*{-%2D8k!GoO_|*dou~Z|J-ja zprp}JDt3+j6?m;i{{gqG(MN@izV^e#246el^@m~u<+zf?npELS zq1^q8NY^W||@ADYZE*PtcZKde~gmR)gMP~AoE-Yig5QeHCHocoZC#Ia+p zny&iV-hSG9uRW18_XC^nD2*k|4Spqr*|`p8$NYcx-UL3%B6%NAAc25{Hz-kDT}gD* zMAQu;8U!Jc1SabQgPbY?9w45eB!VtzLK4Y1%*w9t6?akNjk_LrA-X6DH+XO;h~g0u z*EbGo0OeHX|2)TZ0L@~gy(nW$neV~h(K&Fid~ z{T~UN{oGcVu^*{OEf3m@0CJy^xG+-TM6Nr?eBXoJM&*OqTTeB_54B;db|AJ)NM z$X<#@`Q7Ag^=}ghtLyS+7BA+ye$kKh`MuQOkIFtN!)`OgN?qio;YIdlkxr4-E)pWE zz^35!5>hEiqYy5Za1Xv=B|xmyCDdWS9gTueeE=mC*d-jTOW<|KVI@GU)FtpyWJd{i zvjnGDX}5u5<&#*PgMNFivk2)2bRfk_yLBHRziQq8!Nu0i!8fdIh?TnRqY>pOyE(k$ zN7pxGxQjM1U6uVYK8o-sO4*1pR7{m!tKkxZiY`S=pfwx|5J!T*Ug2LNtDK5rRh7R4 zp|Q$8;u}_F2$fQ03YA20U|57uX?K%zsFwqdf=smpA^%+d)Xsqj#!RB$0wYPh#| zhfuj1G#MdP{sW8Da4tHZJ&^!_lq!#UgaZ-9MJFMRgNx1tpAkv?Cuj-KE$i`7o!QBX zIazgPa{?DG+RloZvs&_a?L50?6Pj?*M&-%XGoAdietGg=HDUbI$vf5h`tn^ne_LNW z_~UQu>o8XPMWlA%mt9}aen)7b>$?{H1Rhk3P*f$q?D{%ImAw`pMZ06AY%szWD&{r2 zR)Zvl{0}ka1D5Z3B%nNbPh_S4E7sSQSe}M+!spo)7AK7TTfLm{Hl%TILf67ZadGn57?Yg36*NECZj%WWA*f=x#4r0!nmQ^ zcNa90%W=LB5+aV%y8Q$3gDWeGTJ8vzRTbsB{k_MyeHV|xF)X){gWFN5ji4HoDO0Em z-Lz#lQ=Wi}d&j95@5d1OFngGlwTvokJvSD+4H-~c;8eZnF7t2zbILe~4oY`fg~TD< zWk*YQDf`dS`b%C_CHl*M0)Xf*DNtpj)Q|>K3!qL7<|-|J=2WzI3ZTzZ{(}HorRBdv zRbaYHzlR7$1*W^(hqeMUry?#B0_e{W0z(35IDd zH|{VMn9l*85EO#p0@`ex z8d+(+;-@P838;yc{tn-;N*|&ElhmmoPtlNhFCXqyY)pZ9uBE_CwiK8&Q0#^EZY}mZ zf@sMAc7nxPpOWT837N+sjf1sjNS~m$9?<=Wj~c^ef7ML)O%?MnV}!LnYsK_lDt+&= z^ZZp}!sSa`QSIEn8S8U$)E4!w&#%}J#!(&er7>?rFFXkg=Hiayauf^q&@%m2j8|xx ze(ay4m+4%*sb%^?^tWS~-qBvB!@lAdUZ2-!_PAYhCwr}m{heQBeLh07$6F&SaCqzU zrR;F7&nHH&&$nuhvtM%@yUmBWK3@TNijeNrCCrX2;V{?dqwNyXbO{$kmT-vc^Y}$V zJrCofC~n|=ifh{I^KWbh^ekLtKyTqo24wf&A+FE8)l&8acG(*-c(T|Wt71|XTeW&i zV!&NaMPgZ>pNKfUKHnyh4wb_Bc9pSJDOKhzmSI&s#PvB3wX30iL_>ZNU&S*UT%Y&1 z*XNxrrpaBO*8dkNm70P0m^XR>ASJaMWgyVxX6zq}p$?MJD2BQW9p+%DpZj%3_E8<# z5g#?YMOMsBDkh&X!cecUVzw`oVR^sG%9AZI4&5i6;rumXf84mo$zSVVzwcYq5P#MC zj#TgB|MV`fSW zxW-S2F+a3?-ys3l@5?0MzimIL1`FWu_4{{doy`-oWhA0_;!8;5;E8Wn=sr6~_1RGb zkqTyOUS-7$RWaQdBRsLM74z8}GWrYuW#u_bVjMiNk@b6QNz>f$+qc8GVf6Z)OLOR= zXYQ9(`O$9w#Tl}wH(w5Jk64W36q&s>-5vD zb$Xcn@-MbtPeb7_IBIaco(40DgD^O1biMxZp{&=_ZKIY$HaKcaEt2}UEP|mPz;y}^+$qh8Vx`w1jf0gImugn}wd$=Zd{jRkYsDlY2RiLL z#t19@X^PHs|Em&HXy@51F^$TT3;)zK2mR-=Fb?WiuTujG<>r76WQQr{y4GZvIP67D zCnfFvrwa$g*-)r6wGEbLlwyo|i_Iu`2XjcFd6LB_X&)ZVAGta?V)mE~vd1H7kNKNA zQ_NWpLf$+8_IQZu%$r^kGKqiLBCUVjeemq@5Y?G=FG|^uq6&(vdr8?~j~i6XRJ&HE zN(?A{8WNk=S-!iFfb!5mWN*Z3sS?`j5>AUO0aB$d;h^k|x;`i5^Bz8m z{E$Gl z?TsK)D!2}NqYG_)=I^P`q)$KWT%~hESe`+_tk8#jVR6p~rCm|nvo+E69`xJ!#$VU|^2Eo5B3{KuQPGX#el>nr`Iwa5-!A+6v6_PKQ8DK~VbyBA#DD|# zKw|SasUv)6BLVqgi^xj36cEpY<7jk4@;y`QT%W_`o_TzkFxsgHq~br@5@XG_(o;gJQ<2Y4zs$KTN8#ILvRxvFf zv1;|C#DGJ#L1Oc3%eNms^2-ep;Mni}uh2i%VL58)ALG~w7N;B{V-UqDSD;rMobrq7 zb#J+KKjNdf-UKV=78P>|V}w&)X~q2TcNzEfv#mV6C8lwEpN>~I%^#0?u3`Qt>*pJc z-_Gke&ohRblm8|!{<`ww)>(@GJ!sKE_t6pj@0Ys|W1K2G)h>IX+)D$U*kBdY@^4nH zo|G8!KO{E4wtV~H6aFUw4*vIFAurZp`5A8SvmfoYxnO@8i6}0(2x%N#aMQJ#3tpu9 z>;!^Hg?7`t)ruLTV)7UxTyTgL^WFc+=&ydj%5$N_9H_i#T-h`~-1l@7{P1tZzu7ui3QEh7=d4_`tW2S0p!gzmF*RG%G15W)|yvSNm+m~M;_ ze%RNF`Rp$;`V0SR zHn)D+92IHd4pxoB6??c3z7BOs!O2p8V)lhnnSW$km6)&qgG}nB_!a@vowKF8p%&g=M zqXQ9Ax)<3?k$`vB%i{`ebxl65wZy(M9>1ODs$>4r1p+6=DBr@=X;yTZt4jPT8IxDi z-wqA!lVra|jwe9b@RB8LiM0Kz1k@FVY8mDg7{S_}xb9P`!22{Q%kkoURE1aTBP2;e zq!szE6pG-0BBIX?o|NMM;f(~G@a^=dkvihmH{JS`mdYP>3ku&NNvs-YjR?yJUiqqrbbXTqh6nQlCZ>JGIOHRb?~WJnD%^J2y^ z9^{(H5Z2IWr>^Yucy&!HbVae{%;)M6EEQG{uDbp4Ux07vgUpFD?<3A6wv`X?;Ehgz zXkK>(5l1kF_=ph@|eC`-t1B=UAUATCS83v(# zz(jTFt=v9N;^F1uzL+|9FrlawZmX>`*IyM&T)suJ<8^LUG2=?W`Q)kin6UeN<^ES8 zLb>!6c`tkAN10Yqc(W_&*Fo-Ab<{6S)h`7FBwF<=16XY>W{@#0Z-oH+L`GE@IdNgU}`jO&6#xZ)j>!qH5l`%|ua_b0wyA$;gD z476g3X+t^2;7|+im6$O=B*lN*N=$VB+xNADF<}HAz-j#H7%{n)UA4pd9Ylt<5k2Mh zKkF*ycX@6BY=Ce}d_H*CkL_B=jB@+hoH~?IzPM_4>3lbwF@-s9e{q2XlB*XECs+3; zjxt)unC|t>V|?*%)RT3jWc48H7`OsseepA*Qu!-bK<9O4Nsz3X(2E`e=*Ol?Ti*r)$ zM!Gobba$8Tk1MG@eQhzH}97(WA)5;a)1^7 z{7f@{1Bww=%`w7l`LzQP56;jA-C!{xbDA)rX9PQPtKR5qyim zrqGEm*G8qX@umOI91I2M0ZW4g)gsP z+BCk5+Hz?4G85x}F!&OG6!67dLwvcso~#_kaC|v8DwT~dsr%TOHoja`D(x=yTKMv! zwA&ZItxaSd{S_=o@a04TIV60!dPrmZ<&`^Gxx?izfBuP3qWQ~BSsGuyU5fSxZ$Z=e(slEp;matD|H0tPddRir{P&12 zXVjCG!x)Y)aZ#ykeA%{}ooVCC(ItW}128SAX}Uz(?TfE&9a%>QzHB9sL&BHGT2Oq* zt*U=MDx1j49WK5M-%BXb_|l`N#+Nr&BSC%mG6~3G;mc4#l*Hr9ocJ<8dI$J&r`q{x z&Yd5@m&^J!jW6*vhlVd*u?9OBe0j13@TKA%;>%v3PGr_-eEA?Mm5ndeJK32wzHGir z@Z}UtOA255OS^sXf2juq3ljdafIx^ZJQEZlzszM&gYwI5TN>jp18-;Ln!=a!M1Co1 zP<}aENFszU<}p3uQEw3;{`zhts$8Z-m;S&D3sue%97+66XvtzNY0@!3l}ot2;E&(P z4GKY(vpqzWdgR?m`DOj5hlVflEu#m$F?qKr4)`(@w~+ecR{?dJ#+N6eQrY&9U#6uxv4RHF-d5l_0dSZ zKFV-=2c_kBbBFoj-vu5-4t`^Jb73>!P09-5&0p)u%Ha&3mu`wmW#i3dxLn7jA-ngwG5l5aP`SghcR}6%2Cl87zQuQ{4Vsv_Cf!-!!#u${mKU z8?;}AwN0kkY*YjIgk{!t^8;MS1OAhVRZkXHOz!&EPa*z;dti`It)Ey1*H4+|^E(K| zflv3JukopadGt!e2|jW7xOB=^{AVqY$HJ%Q1d$SNne4=;InrU^KdY(E_s0*NY2nlJ zSgjub|LI^3`sncRX)^HRVDRbNV5=B&%~ImiMfGGIi5>{YrxT-6+4$6|j-6`HTW3s^ zPPh@%mzuX8luq!)e-s;8M~9pfM<7QOpKdc6kbeZ9%Eq$Nhl@|cw-Jg1pL%z-@o5p> zD5)2pN`O2TK3yq@lz7sgocJ_UIt=)9H}*AvPYK-k5dJhg_Wt=r?X-r$EtBX z&O<)~pNba~pSA z>G2F3pH5zcxcczv3m}h$Pj3n$CEj(rlRwRu4g)@Y4y7XaljxTPpWcF=;DF}4QR@y5 zpZvg&gTbetcLSel-XcC-T2Iz!d^$5Km5oow*RoS>e7fLP>4ZN`u<+>_=>%VV?cT^b zI_A6f1ad_2>6}fC@uwMMSn0#Xr(3=u6bC*HINQdjbqf(!A3psB$YbHt&4NgYXBRs0 zX^eCj@aaD2UV%>?pr2O!=@#e_4hWx;*EEJt4ewXv?ui_u#^_noPl##63c2)}+=& zdIv~zITWx!nlGt`6(-aTI)$e8E53TSVSFjwidP!c^UBt%o^{J|gR!qSkHJ34AMFsz347o1_yS z#@wcm$AwM^oUUUZP_e~!YzxM=B6#y(xN;RFX1%`>w!d>jLGk2ZOkpC15am@TnQgmA zcAN9%&XTQlQr8W#C5-KGGzt%I(%iwX_Q=)MU_)=gquUEyTxQ|*dYZm_{DrOp4>~=? z<6q#Kg$R%TTI9!j(s&A!3Eg_Po%RAm6>jtw^9w)I8%fh#bL2ZiEFFxb*{&7z(7!U z>{Omz*YBM=tyk23xBWhP$<`I2&QnlF+rZgvmCNS`ZIu@M6rH?Rp3B z5Y{IdKmAU{&)}~38J;V+$}}*qnJ(WMu1fjNcFn~XAO`~ELO6@}fg)x>Uj(#g5imt5 zpy7!WLENnv<{F4Nrp@;D?ZvoaxQ#RoI!%L4)1ZxZBQmm$c0*F|b4e0@TDq4j+oCW6 z;BDF28yp0{&L3)?jUbZ1187-=%4Smmv$RD##-RYUr*b)aejI)kW}DmBZwpqS77~ss zTHzlf+?bc+8bl0Lv6?gpaRFjUH5x~}&qgPCkV)hToII~-`g8`Ool)+`lf`q_3!nTmKFT8Er}Jd8fGy2P4-gXX|9W8RgMlhQZjWz8R^Uzzk1 zA5H!}B1a?fb2@QHmG}jEF=w!`q)QGfVb}4C;TLSz`N7nhTYe(f_@<5H4#3cUAhLT^q~S`O9MI zHS!jr?Cbe7@w_$(Io?(7>dmckn5`zx-D-`y)rt#)$0OO)hf$ksKiV1>!%GueBFKjz z7(lwKYw>7!@~h7Q##?RLRe#aECvXu1D;uoY+<9oHQ`|Gcg}$AR8tZ z1hC68d4UVo!P{PnHP6V~6%4dkPOL_SDEDLy56yvthYaGA;X^&N?|RR7c>-a$+`iVF z2vPtzO#zm9Wf2|_0lk>7^(B+REaN8j#>CWtU&O9)iy2r<4Lsde7*mQ7fq5zejgyB* zZY$Upgo(~gCHrD#be%B%E?>(F>9{RHRV~mLElKg-8`7VW0hS4j&&-kWM1A`=m|J= zgT%aqj}m+{pe%|&C$Wcf$OkM#3kMlQ5E0PIT$vJ#$+OJmmS8@ZUM3vF4bkjYZf0DE z2ufuT(VmsLiGl$;$?c!5a&^o>3hx&l?>A=AwHz#%v3+db0H?4&d#VI4Od+4e``82V zQhAcRtUg=YZ_WraTxFgblmT-vYZAS!`C7)GQaZLv4~!t0dGeUDq^S*Blehbf!c4 zVtloH3Zqbn)H5W?9E`P8c~wzga>Uh0lbPchThuMLY^O1!Dz_|X%&g8_QJ7BYDb3Se zF|;CBkt#~%43%OYBOr{jUJyKB&Db`(cvrLR;+@TM6ZQiLrc$C!*i>qn14iQ4ZY0#w z|42*v$Adn=tQ^|IOnF8p|05T}5^kgR~g z7&>G`#VI4qmG2B!m3(Kr*5I3s0q6zX{TyzTLG1D7V&K)(Lv*AF8OI!uDwFylYA|qO zxBxC-FaVE~?+g*z7#GIBZn~(%|I+$WTcVoP)l}Z4(4|@1~p-Y zve8_TB`e{ftRO7_l2?gex4>wJc0z=dHn)@Zw~KT6aw!kTFkuoJfdc9P7?K~`0{E<% zw~s)VW&rv~fvTGU5-O&@9h%w>O>T##w`*Zxx?0uYvOn{dbmF;DUe2}v*ZU$4|*guHriES5@36d4%c#vTh0!R^@>Wg5JJ3|QppjGyexj#+H zD|-*pWZ)R|C^@-8Nuz8m0y~p3i11;QK_r|}71*LFWKj45nX9kR&Tx4R3X;uXv7w%? zC?>Z`2qw33Gzn%jP-BkU|0ZWDxBtp9Zoj`$oP*|@3r8_5A9Kv0O7kUnjHGf#Qjy^C zJvSFlkMC8<=_{{dL{i@*MAo={mtc0vufvETbN+6l9S%SgK|mb^VjVQjNb1dGNqC8i z$#5uUfAef(#`3({yf^;=T zyS_=-|J`jQv5KHe6zN5g-kBT=?2eB^YLwWUseuK(lRUoiIfUFjcc@XmnVshwghiut zfjR7Sg%PU|?kit|bluB~@oSWmUzJz+%0G~>G6{PEVLObw$~VG?FQuKB(Z=m7MjPX* z3q~6SRXv&;<#!_yTG`B)S%oIzQ=L(5lwWU!mi>%O5>d7TuDotP8s#2D=9hnKl(7PC zU-=4ty5~+b%FjWJ@FYH;nFs7iLT6^=`TOw;=?2EbyEbwU{N>o}g-9FI8CKUmZjraa zSq^NM``c$KIxufNjZG~Yo{g1Lk4v&Mb9!D<)IYm;QqO`IU>h7g;1EvV9R}+?6Y{&~ z^hhZBL!PgFk33(So@Qs2|0GZM$yvr|>%g?a zV*=y#96oPXkcRTtj-Kw`wG5~qgn@kNn;f*5!sFe(#CyJM1qD6Rn7kPIYCAGvy(PFpB|I2aLg61R=!ID!$lErv zQo-cwkv@<{jGfQ|zp+Ifc_IK)VB%^?foccN*L5xWU2f-Kj(0aGo22ScyIi>#Ls|F* zay^J#NX7Qn1(S~=^millXHKFIvy?6{|Fq>t*kdaM9t-z$2()s!vsUk1xj5{8d!>(S z2%Z9y`ChOdFnT?Kvu~wa$3Z9Ly92WEK!}>;HJBD48)tGr$kwv7VEk|oB^bLK6O8|b zOkANubjaX~e3#`?FgD5_M|eq)GO22>11W!Jnd2_o77WaWO#S!}k(@JzS~7JDkcU_Vts5>e!$(*$^(PWzi#t*B z4ogIb`mRr4eL&PVp;F`q-Qy{ze9#$#T?iEyv0bmsQPIwufIxwBq2Kg)11b)L5_G37 z_CQRLw4(e=T~1Y5Rz3m=^XoX4#&rwz!KOJjmBW?e8tOr+k(A}e@KDt_rBJ&CHQ*`; zx-^K{)RKt$~5y2=o5SoWdR!H8mi%)^L)-+u2&58{&5=BP_)5KobFX2|o_WzCCq17`u@wluoAWZCVs7tGqf zEc!ii{UGFHgoehKegsDzs_NZ=H_0nbOz`)SXUUgVf0a=BE}kXdANx9AB{y&yL+$o9Qn`^%rwYR`VP9!YwE|y7jnK;kCC5W@-7oVTE8%iU*yTV zkSDW3-M%+Uc|8Yfc2Px-%-ZSTuGYP8C13Gb@>cKaoBJ{1yk&I;zG3yatOR*b=+qpm z9$A@7jj89sH@=hoz;(%2?7~*>22PUY-(GhAV(eAq0WFhiY5t~aYrRN*j4tlf$l@-y zid&Bnk!DMA%i$Y;OmuNA6j)xL`vcHOwPhY$Y|9FK!`gC%1$<%{@Ee*2{(ueqG7bF3 zNZ`XP;3tFuPiz|a1vYRP-%HKgMgqUm0#2jeP`6ONYoc2|1xVE`k7(d8;v3d2!xiue zqb$G^u4n>ig$?wN8t5Wru2%?oyx(*=F{H8|J*u>y+Bn<9k;44c=Vsgpnq1 zp2!*7?SY247`1o5i664^peOZbrJkkFlL`q)smDT#Or_}{3#&vPhR@*_3o0d9&epnC zs7ug`N`U}%b2Sn{-JAjE66M5c!9bq(z1msudVGb}r0}0pxqK|dy_~kdn@4i7E+=du z{L9f+XbSX{i27qnq&U_J|2I-SRm?VFTB^X8Dw-O8p{kmWFO}Yv^dp>I#&C8a;TAA6 zC)S(_-E$CuFkyB$%7*`iv3mKVc`b>SShe$LCK2D9DGdoz=00Sf;$034cuC>yYX*;DYph!iSXV zvx6T7o?x03JI%47G`lt^$$u=aS;NWPk|k`qzW%#G`C7e{1?f_sYV@;#=83C+2+7vN zvq$huvh$y#e^;RB zGheiR({apC(|vd@9a5W0dvE1)vO(NU@mp*~E98ZkZB>dv?s?9Tio z^yNwHr`O!2lT%^}=fZxP=epOgw|enm#{N#l9;0Jllh|PLA5aJBKQ`?k`_EfzPXExc zywQE<=r1m?Lj6@^cnUx^Vj7d0MXTBJmHWd4pAs zg`Mg^Nvah3ywqfVcps8RR3FYHA<0s&JP9uLBTn`wfG3uBpug+`#`}xrW5K@^T?NdW z>+wIyv>yMPmC~gT5r)TiRvK&F9!p$wBFK&T0pNZD@Rg_VQ))s4Yfb**l>lHZW&BH~ zY!8-Bo{OFH1jPCJq?_bOE6HeR41(EV&!po%2pcXJJHD=@0T9$ zF7yCr73v0{RD5*~-PJYCJ^mh8Q*Q~h$}3&cA-~(#g{NO!I_b28n8NQb@x`46L>jnG zA=3K5r{Vmv^i4JZYm@zX-cRhoagK;lHi5+}%@{znvOv|2KsZ(en%%h%e z7#V;PHtTML7b|lt#dpDn!GeWpD*1}s)cp4!9rD#|43OJDd8ERHSD66Q+T&M=JfP>Z zS9J!?XY-j}v9F#<_I&o`KSZ7)COPG)1VB^rl;EQ;@$GljKsw|pm4?)=(>yI{g2^3G z2bt>T>pqn^se%5Cn$Jd>tCvOzQz6YJ_VIDE2mpud*CxWJs0V^SWBk)x3^R}I4G(pW z@lc$&y-V@#6iwJxplvi^8}%=$TWH5tpjHw{g=AP=1Uj*{PWjXVx%eUrk^qLy1ZKCv zN$&20^*dJUj-uWSA-)z+Np%)yaFN^VO!L-Yo;kA8VAw`OVYS5L9SsfCOF8c#jg%G+>OAgY`l-&l;>S7^ zi3JXKc(p0rKB>H@^I#uWBR-`4qqdJjJw%VUNA!3b;{dnY9%JhImOw(<$2~8d8fhOl z%NZ($fv}n@$5~4mmMZkH`FTHOE=Lb0{ihJ8&E>MtSnQLExg3QQu3tYvDumT*E;pTN zu;E!LU)*~o-`TPsneFR05Gz35^W39Z;h(UMCUo@7NG|*YdOG?8R+-mgmygLTOF4mx z{YQa{-A#dt{l_Y%iMEuJed6ur9zQofjPjXKaww}#3}1O~@Och(o_9@tdW|vVpNLh@ z*3zsn>$TWCJn#j$x23@C@m71hRn&iE?Oz_PPy9kX$T)=sbX#kb;+z6XYT?~mysEgO zeS*KQs1p}eZ%!y(1$AOD*2sC^R(LD*&M5RXHHkQd0QHW?yOhe9Z?h^u0wsHuD)B|g zVBR%amG}faT9w$TWR+GWb{;)FuXHO!xue;bZXXz>r>L6rsfbIdUHg+meTcm{X#7^G z!cAZpRc?FnW#Nlic%WjKtG^Ip33NIrZEA*9fy_n5)KlPb>X|l+3UO7Sg{qCED)ej6 zQ20l*>rxBsM;v{&i(ub@rxCDJ6;dy*>W#xNQtykRfDf>MPjUhu+cfZYHt_dBSL~Fp z@C_f4ffn$NPT+jgtBIPw0#K^vw`<@PQNRaTz!w1x?QVG$o+f|~v4J12fp>}mK3D;t zaJ>dRVPvB~W%P|HxFSba8%IQ>PjHI}`h>4}nK_`-AsXz_R(=6Sc18Q5ADRmK9UiM#oH5F`0JE z1}o-f6>}2r{7XfjnhPWbDw|Uf#J!7sR>0B7ZGJ00nm>)B!mk)vXiQqcFWB2iRG7x3 zvvhc+4lj^!WeY@`YB!BfIL|}LNA{xieG8PcOW;nZBe5eXMxcS4Lo>%A7OaeJIc0?k zepVHH6SQW}x}@NnTwTCtsC3(idQd4Ra=c~V0gF-!AG9c?K;g7NO_Jt8P0GwE@X*|p zi%raS8Q5Cvg5Y#`VQzXRlR`&VC7_hHmD^IXAb@_a2H}A$T9*XQ@ zj{oQu2?uUim$F!lL7B>rlIFtC>4_206-uv{sp^zTAJDq=EJV_}l@JeaLbnR#PY}G8CzDmBjRj^vC5&vw}p|ta%KOH-JJj5`P_DEPM#G4m| z7CuYi#=_^-p8OzMEqmG?MT`b^e%gac_nOaH0Xax$D=8n4&?qHk0@65?l&P&Xc~o2| zBy|-&iU?k7rDL8~G50aXg*uxLSuy9L0;or_ou^b{l>8i8Pr2DKq4iW1V5s$!mZ2*V zpx0Bf?D6MULPyC^Sx=QtPHHCWsWO3cWs1i?xe{s(Cct`X#9*j~lyyqrIHk|f2QRpu zV&=lddUeW?(yP;A<;3a~YKElT8hl6MTZttqmZ^iPutZ%$1%*;nP+>tE7EwFBHI9q0 z9K8rj2`$1V0VdW!C0GN&aJ{M!ry(Y%sTJ6{P#o+GB#y^<{#n%E z`YQuX6$>1r>?F1RN<)S(t96FjR-yHml-&!aKT@`=zk-qLuln@^Gc0hj{>q3clGYdG5CJ4cz|dbd-? z?$J!i*cnQw{yQ>xu&)`R;l@f?s*2e`?NZzE%+Ok)$ zf})jgayKE5otYL%UE4xPN>&FbI*avl)97$0Z4Sp6>FO?6H@0^s8sHekfXBc}eBGEO z1b<=PN)CgG=RtfRA*aDPTRA%gjx~ljuMS!C4i$VAz~u71IfvPAXtuiF!6Ti>0zTN?2CnKIs7z ztvJ^}X+P&xbO|}v4hhijeUxm?B!N3dyS@0|cl(Z(5#&(!j|m%U&JWxTiyrC@remm$ zaz0|p#T16ScY>TS>P_IjW7PTy!`)jAceOEvb}lmH&m&28>3*!9US(p=8eAjAcv9<( zvd8QohB2u8J_cd!S&1~})7d`RV~m1(Y4@B#(f;KSLqQc};5JwOlq3VD@D ziyTo|-c{k&195D;$NP$!s39OQEB{egVCDFa?lsCOZjK56L5N^Iq9%IE1&n|YF$>Wc zQK_Gz1qu;~h@=ql5Mr6wP>CO*7vs#vQiv$Vm-tY_FNhQSq}G{8F{)OzrC2Eq1V&UL zC5_%$Tbibir0jtC35H&j9bi}(|Lg@J3iQGa+2Ibs37Z1fHDjk(x2GQfy?yah7#TH8 zZ*#VUCH(d2ZP(^slHQst4j)EuUAqXq9gB~mx3ZZ3L3$fT=Fq&@pF6Mf_(!e4PGAiL(6jV@A8s1f z*1|P7Jr?Koo(p+qSW*HEv2ouw^q=^^sIS}igRRD06)`*C4Tp2<*j< zs>`e15|Sj^#;i3Inb^xH&l%dukJs^1FRi zn49Oj66R@xFp+=gPXFAP@-jQxmpj+p4N9cf;PLp1=H&VM;sF1IBzJn9yW8&ZPs=8u zfcpwFM&N$zJj{pW3#qq!9;IuDz*WKIJ$X>Vmrh=zWMVWg@j8hOCV!3??9btrvdN~3 zMVk`ybZm80zCY-grx7EvbmYJ;hJdv9Tw>No=H{Q{u;Gn>S_xRoE5zSISbg_rgtYFo z4z_I`e7K6uhjRrVR%{FMasYRfY4Y^O_Rr+9fQ~YRJ{UOs1yH6(frtu-(8o8nALGkM zZw^{xo?kCy*`PZx`oRhTn+J-1u(Ir|>BrX=*is+;JoSH#(~tkmWft*FuE7>ohP{6# zh+n!4cu+X0s~_q|RB<&*Uij*<%jqdcgEkG3Q|D z=XOm$pXd473jORQ{cQPB)6eY(Oh1!BKYc(ySOD4d17jAUpC>T`IO!+%$|nusOow?D3M37+l{v^kLoX;Q+hZE% zfQB9d=%(kOoF^Nnp))Lf4bWZ9KjHNA#P%afKcn^oSsn9F{M-=z+<{Bo!snl_Ur=-~ z0x{9_vr@-qN9B7#$DEFs2I=R{vlabZy-U;2^%5ZT(-UEbKtDsK90~eq2PnS`{k&FE zA7r5)nNBf>rEqsP#q?X+E6Z%~;OD0Uk?&eu*C1_a`EE?(^4*wl`f5_Xn>pG!Kh-DS zy?pio&rjx~jnmeF$agv4A5r-(eqT6!J^5^izK+j|q_3$G8%z%Ns*k>|*Rfw9Hk>Xm z(J{*r(;$8Qu9Kp#W;o&?@?9$l5c>LT$05`7)ki<$I~_3n%z60m=_hXM5v8A{oD3ZE)2L@c^z#ue9}B00 zz7iWuUWl0J`6*q;J{pzJ&@odH(;)qPe7d5aMRl5fR!V@-&%+2ig!$>UKOG7BxfM`; z8TvV{UdRVQKhE{hfzZ#gg2w6RdUGgs6_F|ix!(=)UCDArALJBtDas@XQEM8eVwkJM zyKQDh8c68j0}}5>{k?G#>SWo=Hl$xzVjfZYDJQdc(9fw)h3Mx&+<_lXKP@FTn0z;H z$B)u4)IO=CzpJA1t=2Jp5z`?3{LQ84XUeyle!LPO^fMe`hd@6QCLRg;=?o~p4E=mK zp+3mJApNwM&^Y}ZasAv(oVICv{@LL=;Q1$QR^!BTAo5!o%%qN>elB-gIQ@L}ScrZu zJ1>%cmP>3fxff!h>E{_8n;MnxJ{{8%F%8ns<*AB(F8W5(Po4w_{hWlbL!h7Yi;e{S zY%8n}%a4S9?tywC3;kI8Nl-~acOBZT|Bm`sFZORQN>Zmuc1u8SEc}n*j&3HEP_^tsr~ws&+-l}V$%9o^{!N^{t|kVH0g z?_pZm1e5}~p*!9VQiibD&>hJjR@8ibuIw&x^9^^T_#_;=>FVGcRo}BV0~3zQTYe9> z1!2_&TLEfJ*d_J`ZQSv)Vbrx_ij)n<^nk2$v%bU#V}f4%(ky#DqtFz@ny-kOm(}lg-&}0xjV*558WhzO%EBQhq0fBZKSdq^;&K2agxw!;lSI; z>bG!Mi_Q{ycn?XGg~NI#p3j~oL5PLJB0I>oaCm`1mW9Jt>CPQjIeUgp>;xw9_5sE! zYX?|39G101f_r(NlxD235ET2=Ca9N_g@Cr4pb6@6)HSVmnCEqSC2Co{19#Cvq$sq%~g%iORfX{aExm2BYqr zzXF1Fj`{0*39#u^=C6gD!ltF2Oj7Usbe5B6Cwg_WX4vgJk}CX3PFy@g&zUoV1)Ofiii`VI44eZQ6v%D?S^RY^~L_ z#h$RKEdOYsvb(^xW!kz>`A@<}QQ5T5Eh=kp+NzUoiJrDZrvEiqbS5wtVnOu(6o zjpNK!qr>M)I1Yy3%b9BhzK)q|CA^r|c;7QIXe4JYTw#D|koV|vItg5i$!knIMO>LR z(q@!X#T=NtM&c`uiOQkwFROV!<)XayS^^kyY?;ZjyNiepZuynSbD~)cy<% z3FQGWR=Ltxn+L)oXu71Bt)^&*0%TsC!D8gA4Xpg}8;%Xp#*J`e2D?H=Ql(Hc*o&V< zlZQQ@nP9_C`iPS6end@RNoUD-S)9)v*{IMyOUZXXF$w4cvoFOBWt^Es`EGPoSi<*_ zP=p{`zT?S?`sXu0fs#IokVNQXGSi0B2k(T5ppPL80)1qed(PWWYdz+`U7=GXe4r~U zdp^&%kVNL=NL3&qZ+RbHX|9Dk_J>U+ryD{gL&0u@N{&x6I5BNqwV?_jaY!K{BRnZg-GWWxziZ7Kv2KHjkZ5>Zs%w)Ubv%hhOyws7CvXb^KiMqbPPLxIq`aW$aW6MHdf(L)zEyilxOsbpgk2 zR3HxzzVnu1TvlE-k*LE9BCIt}u>|x1O&m*9E8wO1zV!;#3lvQ}go=24*Ag2@-|bpR zLSCS8>6>8*3t3}WHV?%uYcN{gZA|%0vYrQ{<(_VDTN)?BY($O#c3`=@BA@5J-1A`{ zj(;d8wFZT@Fv{C7m}eFf=Dmt!mt$aA9dUxtsG#Fm4=DJl-*&1=GD(J*4uL^r67#KMD`D0rId zKJCHvyffq){m^kAWvoqXTZ2R%-xG?T&r_DYAZ3aSmq|%HcG}@<_>*EZ3{bt<4fajah#yrL$SNj~XnT)>*DkyEV&u1EM}95&(G8Mw&W};@<94a3JwM8N@9wpV zurd@c*ux~OGkHOl^YB8S*TWJj{Bo}M8kQgLB~Z?f<4c zBnVECN~#NW%HsuWuP@qx^e7CmsS?$m#%bAOKYR2NAQP7h<>3q$h@kfZ_q$GotF?IfX- z98LvrVLjmyty2Klq4KNMIEG$J2I@t^b(8AR&kZ69loqRS8HC7#ASi7FXK! zLC0!QrJYiQ6eF2ZgvffLPZp^=gR=`;N?%lu^)!R3)-EZGw6|P6+55 zTsA2L^^Ef0i;p6x-5|*(2&#&p!D=Qt&l(Y%4)3fH=P!4Na6n!5xgtST%u&TG1f(<_ zUy>5>+d)bX!i!iQY9h*Eg2ZZ-qy&D5GfRP7Fz=e?ij?NAV>_wjATSa}2EO8OTaJ;I zV)1NN1k z)O|)d6%SG+5ftmpwY9u_Ls!4>I=vvAM2d-8Jyih?g-SB;(CSH@spH*^l?;beG6!F* zr({i|?pV~X`*f+TMHEhIQVAkP!*%cr&8ZW8y<>&y3c<#O>bfX@dwdktO?gkS5po;) zt^Qip0Azi&T!ZkaTE3!c32^{&j?}Wy)m_O%3z1$O@>_?yD_5ceosv%#BIzeoa`n0R zLbT1qh<4{ea|lDYQ4;-1vU2kd6RhOeqg>88i5E*n-J_Ta3B_71dLVmN!e5~~6oiLy zqKqjWJ58z!v>(wTG;#b8&;Co?BHadZRycp5>NJ0R8?=W4*(VYpZK@rDRZBzj$!SCu zr#_wM%N_a$k*_+g2%9E2OV&T1{QHHlge@hZb3PGzt9L%BMnA}W@)k0w`D7_v5%bAQ zc91=vJi;KqJ~24xe#ewjzJyW0=G}yS_$LMn>N_zwem}3(i+=)aNbva@<B|j)*I{g39CkS6^79!TZaD!OS7n}ffD`K6#ik%WZL0R<4)rE8Y6Qv(ixNOUN zYX5hMBD<#df1eFY`0MWfUUqc@`@cj3+5cTq@BS~l#1`7(aEBAftt;4{klPQqTL=F2 z_$YGgjr*;Y(3T}L9Va==bhfw-MlPs?$)K31z%KjLt2@y z{(&TFzT&x$i1`W@s1Ahymqg{t@IlX4KMoL`c_n~BvYd<5?D#qG^4#)#`8Ix%ohzIq zj$l12oDX*iaU_8K2(c_z{yF$4VmWrHMJ)eAd%Lwe745t(jsKt8+dcND5C#5INH6j4 zf)n=K^uuB`H#M+csu4tkd6KnW>PrOo+Nyb3FV%_09a}%{(!Idyen{e6FF`dw330rc zal84-lcYd9?a7i>mQ1)KgiEGLEWmdx?wB|yLOcHn>%t|Ix<*2j^P-*Kt<-?CnUf?W z%hNo(a8sBjZ=#)vwUaULcX*C>M9Dtt?h5ZH+cdEVDH3i!DsT-f-7#K8m3NA+G?jd0qgNMi1Nco9(dKboI}sCj8-SidMqp<+elKuvUmj43GW+I&Jfyw z{-I5hs1p#3`)<=eGNveP^)_Q_e3lpQLlCEFgTJ}g-!q$?|DQ^uXUe+sbEg_y# z-&~C(O5R$*#KPdo#6{oysvTtO2V{2eTl#?~oce*~NKJWbH?kkDjUC<(Z_$7lR`8wB zLJ4?_&_b5-r{be%;l6( z9-4SuaAP6VUU0$|!)|_qo#IdtDEj1`6flhNzU}s1>bQdW645(b*DJ%)18_RqA_XCsdOMD@O-H2I2UE)iX^UL-fz9+n_ zukf9|e{sb1z29L)udeU?2UXR)zIQzw-j8v2ufMlk+`GK`>x9zPvBmphv*jJI93yvi z_Dc!bJ2$(1JuBK9l)q9?UW`%v3M#~Fe3#+NeT+rcgJ3DyC73|@B3Nfc<#<=u!lzqs zkFw$3egTB0w~P0+8h--qRZ`>{;q7J$)^F3@`i`67yi4b7c0~f<@#&@#X;jp$sCdlC zc`NEsR-fIb`s`wS)JQ&x>zX0?`4eZUm^&Eb0&sJx6?4+pG6F$-RGyn9#>o${pQo-z zm3Qv363b5Nyi=~P!P#$2EI6KdyxSkQSav@HN4dLWL1L7xXPj>cc;^tgzJ8B+%ySe; z5aMfz>+73|lf2WJCb)~d(@LWNs5Bm5_v|+@rZIUX!D4)FJ41Qp`cyoaXutQA9b9TL z$h%p+>IvWC`VPqJ!(3^3Wa;nYRu4ydR41$A%nlIs!OGEy5lOjt=@L)cCM+o$kmEJ8 z@fdHm@z*8UrR#&i6>=Nxt}l$@)@Yx%y7&vQ$e(e~dFk#fqxX02?jsV4o|gsp$L1-b z0v?eRcqr_?f9!MSbGiK`_?zPNxcodO>s6c8r^0Pr*Perz{dRjGd`Ww3hW|QXaA96r zM~APaH=v(?%q82;_Eu+Cf02+~x)N8m|Eyks%rSCS+T31YaV%Owj(-fkq!0V<^bp72 z2wtOb8AtZYR>7w^{&^vef1!2%h%o+cY+bj{j8Z<(hnkV`b19Zq`-$TX$VlFQI}($i z_`Y9QRYJT!->!=A{+{qQPO=v7|DA??W+dzo@81^AyEc<|H9D`5JMKgcR7cK-i?wI} zgw;O8`+4c6%@mBO#RpWv47-HEx`Z*2C4_kYS>Yv|bU-C=npSQ20qNO>7Lg@{I6Uo9 z9BrVDVAH*{25?mgf7K=MHSDlnvf1&?HuMQsHx2oA8!|Q6tot7Yax^}L8bcK1f6?2)2xqvY)#<;DR&+)Ve z@>HR=D$m&xzM9bK~wY`LTF=uF#c|{YpaMcqf0xT}ET_ zyZvmG@i73AtL+C-s-NPFVoBQdOXJUN3zNp(zTt|^;{!EA3VPDx{RV4wGj9DZIgnfh z{*dP%#F%W+G@5_>RK+}{VxTlb4Co`qtlOw!rXfa4@p*i|_8hO`Y{|bS+0jUU;CmX_{o@_x~4OU=7T?9`fP|3_+ z11N!gr-nT(3ic@$?D0<6qnn0(g$4U04Lc(Ww#$Oe^O1JFsk~{T-a#ahdb5q}6CQ93 z?~_w4*j!lJu%Cyg3D}QYu-Qh!hAJ`w_Gt?Cgljb1370nk^AZc@(Hf>pV1{_M1{ive zV=*dfbj-ojW535iv72gG@5kIM_~1oyYoqKPFlzmNF9mC=PxX^X7= zk;cJ|ds>A)sj73AU7Zi+=!(9rVxDD;aN{?unEtAw=h%5FCC15(m3^K=o)7V3;jhAv zA2&;G!JJ>Rw6$D%UK^Op-u+5gwc+|<3>wl-}SUj^F zV%-30vt|9}QPDH%eu%$l@Ob4&SnDkK6>H@dfP=N35n`=j@^)kV^=<+ue|=O+`8D}# zhRt8kV|63>>)YTB>-h^*>pV*9b5fIXb{VF_7!2Sn732e5JuH$7$6FP>hpSH~{x!s33$dIfe{GIOxEkiK?`;rXdEJK=ul)TJnr*$J zVn%JWVwzhqe^W6R+j(jqXCCm^0wg9!O_QVu=!G=q?<`*|VOjij;FI<8*QLne;IE$v zXiNmT2nWEE>_Q8zm>X4$hcUumFSla0t(QFOP+v89WJ%0H^H;vjnpgdGypy+{Xx;A= z;;$nc;IFIx?&Poai@o#y-2{J~`G*}M?*7JKf8(!B@RZ;9tD37hkNtA|wf)lv!e4J$ zD=etxI*SFp`LO1%1654?`&P{3R?Gz|CV-D(TV9C)_p1XK^4Aqsz~@L~zG3+uWhoYa z{o#>%`Rgd8aq!o>tU|w1g}!AM+TMye3OP{rR>lZ_-TRPk!e7=%%x!j_8i{Fyzm{xW z!RafGk4jM=KF6rR$n$Q?` z8*SWDHQE?iwcQw31>MlNI#^O(Uxn##e6P+`%#}7e*SNjPqux=4@l0Ook2@|h#`Z`b zZH!$u+9+I(dVIJ$*Zb4x-11;y-_hB|f+falRk<+23g)JM?TIyU^@Y2-2G25m?@nKy zJ--*~&b2=FW{-X_E(Yh47v)?qp;ys@z=V)I2rz}aM;jk5M|4M8airzNF69N!?$Ro0 z3?z6|rWUWq2X`1_n;S_NCmBftQjDZ4GI*f87=?shZbJvbN>|RzG8;NVw!(3?!t4V1 z1vTGd%&aofRPPySmc<9GCFZiWB}>@hVXLa};R)CGlx&@YhXqsAGQYPkE(FaGCbDeTAzno`Eh+X#&{1;H=X@}jPYBz4>F0J zkEZ7*8EG^mLqD@ua%dsLenLO9kp!R09)wX+f;0ni>xo2LOMMgCgURVZJWo0qTMEAxcGAx4c(4U5;+e_FsbD-jnTJE#zZL2Sa-Dox9&bKikc9GZp<9 z^ZNA4?A@#A^+3Abk8liY(UtkJU&-C*7*_ULLG5jz8XOYa53OW^+t(kVH?eE4hvz1s znfLmG8Xz&O(qki|1PBd{(z#VKN|mAU;q%i%WY|(e|NE1($)0(DGkgA7zy>pRZ8KPq zO~RqULUg+f1loYd-9YRxW~t=@gu{^u6Yy_yTg9+&3}NCnx=C3?mke|7iXXA|My#^x z)2Uzs!!#Klzp_U+7x%)IkSJ|vOkpxhLxXtCUzau}FoZ=R@Ztn>!9DE#iPsQ2GpK^h zL``Uj{&Y$UsYMn8-L!3FdsIQ@c5D(6@K6FCX8|4w`ygOfpM+&CVnGJ%5E^-gbU4XF zFh4lE<(t)3w;1JL@w7Z_{MZG}99_^NybE?MmoA_!Off5^I?AdUyMV!FRZpu6U_A)P zB(jlizp@}RZ(9l(8^>xZkTj+cS3PLj^-UAIa@8PpezW#;;F$+Tazfnxqr98ca)DLL zzM)!nW-UwJ3w87)Y~%475Y}>K?@%q@r8`v1KQGfA{e-I+AR4OW5JxSi19Esrw+gT2 z{n9~CShZZY+V0Cgu$G(N4b^f37ONgmGHco0QOjQ8wKSLNT0YxoEnn;r>dS8cIlPwF zEDr0-Z>58t)wSeQ>;0Hl40&9;dASv6IF(eZPj}!X<1IYKGTdm_uLCF`q4sAKfOMT> z3R{m}4$7(ho+XkS=xa%uTY!pQbEJ!{T^>p&52tK8MHg6fpCetf+DAg^HnU0HHk?UP%#LG(3k?sdnm4Ss>W z^x%rp-7Sr&54A$DKQT*U<4WAID}psZt)bZ#0r=uQbeHaiz43(@HMcMR8XZ|+B*piI zn-Acjz{F1CMoW#Pv| z%PQEPSW0(@^4c%9xxC4?Di)`#Gi^rC81rM0J^5HAJTZm8BOjxb;07Nf_odw?Z9fB7 z^T?Kp0ncAiEIfa56wiP8chNlGQLMQSN=wTqo@Eds%aF3PQ9I?JH4K-z00MKr1l;$g z-?by6h-5Sz8vPJq?MTshZj_$_4k7q!l=X3jbyo9WOGS;wk4{kY1z{qCLA3x@*b9^0gIxjX}D~K!Ee$KfuF|9tvO?ZgR=@O#?yPgM6 zt3^iH9SD5`Il6R7GF}@RSF$6M$=^51#^{KyRz&A>B%&NDtC+cQwU;6u{qk^63xRy` zf2CV6_d!vI$3LSZiMf8bEzG@D+zNBg#ANKi+_vIXn7bX*hlRNt;I5_J9?CpJgT>+_ z{v^7k%maod3D1dEhxm=e+YU$}H^JJFULzOuX*%b-+;{QRn zlu1Q5Dp9Q?S>}^cjw;hDZdK+I%rcI4J|$k&&U>|+6nYEXwPG&qkH1KRWjoKKTgtp! zm6@+2S!QP`N0pf@ZdGPdc$wS5&e)%9=LYR&nQy>dI}k*HzJ0GpsGW=GmNHXSnIGv$ zmiZ>!)_6WDZdK+m0c`hePe++#r`CA3V3{?bP?UMO2FrH#qg%>c%nJG9N9jnGnJ49_ zcAh0}Rpy*`!}|7K@v3&-qTOugrEmwvqQKhjp+OG zxa*y;0zVS3D)3G1W`U1tH!d3JgIT66=L?A2eNvRv>g|)p%fChN^t!jHmA9lHBIgub9c7ucbj8Jb95%L- zcIZ5Yu2i9S+M$&UJ)NA)pLneudM`t#FjP3vS?8RCN2rsGvX9UXL zrCJ;c>e~4nN%abYz<^;ufbTSYORr+382$^UL;sCvnTq`J|5}FLl-Z374`8|+%FV(T z;slO2ap^G>kkF6QG#h7sK9NH$x^-oNo_;V5nMldARh5zh_(JYak9qZNdxNzD(N1iC z96L8YaP-_}fwps71X|B+9%wPQWuV#I1d$!yBPWZN9eA4w`fZEE+Ok9P2P`$l-oWs|$PH9WgYydI1pdU2R8BXX*TPvl6unYbRn%2yN=1+9 zOs6R)Gj&o=c#UD z=k-%gf*+uq1V04MT9~KD==y8~;p+N)s+`QQSvi^E-*DE>4Q044l;NgOhFe1!ikN}> ze!7I6p`qCq8k(3;hB#*6K~J3_Ih5gdp$r|BlT|te&cNk5_0ypaeKypgFNQkwRX77@ z>eSsrse6V}_YS4*tDNlqgxV21?T)QlV)xThEMoFushgk5&o9oZx86Kvj6tBQU|-``1&=kdOs?_EZ-YwC!2-TxckinN=l zSCm*=Kr6(%aqi-Z63a@xqNEi8FES)sHRk2Gx`7D)?BpqKeM=6g@hPAgdF*tQ$csW| z;S0f0SnEi9L1CIjg7c2y8UWhdYoQ+UQ&EJB#gc}KNw7~sq3&r6bf6%5D3vlHHuoWe zfHpS^YTX2LVqshiPZK5><&%W9Py_P+vG?ZjQ50$1cmf0lHTGl`jmi-;Xd)iL6GSAz z4EDGahzN)Z7+1vuMAQl5Dljk^%{b1=?uxo9Zd@;PJx~OL0w!Duh#K%h*j?kX+Cxx+ zA_P?C{eGY7p6Sj3Vb}Nf+dtlXV7j}`r=EJAr=EJM>M4lse1xI4v`MB%DxHDFJkiV6 z-@w7Kzwz8j9WjSM1#J7YSXba5lHlJUeL+z{rPwd3m#O(5E9ZJXPx9uY=vik{pD-I6 zO`pwAj=8i05p<(=k!c^eTQyFwLfqRMS7Q9}6jWUFw+6s@iWr z?c5!}5g%4Bc=RXc@Fpj1l|hOJakH>ULOxb4SR&ZRO2ODD#)l;!C|hqefe#Z6I8bl( zXPx=1Q&;1o>N48l6hf2z)!&$Lx9TqdUA8^}bj0#8sw|JwVtE)uO$!GU`0&t?dZX!#EGH$Ji>bX2s5y7k8hy7ddl2ps_AB0szME`0O9B@6f*WgQ^RB85dwi&agC9EvOe z9xGn&6-DxJG4_0n-IJ(*tFb%wzBzVp6O3@|roLWK>B2Y1?m8=h_#C7yCQ}cG6tx%4 zhuz6o{wP~EE+2=RJC>fUM|z=#Lr_LP9CK~L{ZA|)JZ?TN=03Evs~#-Io&7md-}qrk=ErfC`XQ~)e3;|~ku@lcgKo~Xk^ zkAygQ=;3?n@$iM=mseCj}lGBco=%^S=O%~eJO6_>@P>YDZX`VyaixdkRkw! ziz5$-WA|~{M!&BxT(@fzLTdFmj(FqvzgiFR4cP2It1!H!FzDeS+w^xcNeSpMk{ZY&7wP4ygulPpyzJFhOhp)hAd zVcrH;b>4=mJzWZ2)!|y~dv?`^tE*xiT-Cd(JL236+S~t1T2|68s+8r}+gD(rR#ekV zVDjO4qBI>rlvlu3dKTX4UuF>>n8Uo8PCOhni%ErEM`KxI6I-63RZp=Z)KL&Xp8QB! z0j3hy>5;?wjA61!r)oV|spB=pU#Ld|#S-Y!x01C) zvigwq2D~-g5=pBc(7b|ZIV*s(=XWu3=njYwHyyP$AkVUGEk4(I476Q#0PFOMoCS^aW{7Y?1;}Z9^Ho;L_V^c#*bSW z8wKdm&6lH3t!_SB`Zs;oYs8PI>dj0fHSz>GK-*>O1a0+rC{j6RI{RPtiweJtw%_v3 zO8vAG2{hL?$NN`QPBz2%;{ip-; zPb&D~VEE_Ov%o(ip^ik-)|}HaYYCC-xARX$i&P2ziDovLB67e#M?>9)x>tdRBAQ)y9)N$IIkYYQxvcp*a@yLZ3J#d7(x#uqxf5+o(X^{+ zV}cmZr-cL)0f^`xzX;4@;ZB2Uw#Y1Kg(XzcaD1}KIJFB90X3&2#h*nL=LQ|hV1Ri| z&5Kqr(YzYO#R3#`OIh4&RN;I@a>eviTKzd{(gRlopXN-(p|dSO1jb(sNNHYMJI+2L z*^W=CHaXe>4Y#cZOoHsjBGVqR?5w6lMBD<8Iad7*G$9abp|9-`6S**DM!cJhz*^-d z<8%{vLieh~b-U$FX9MnjIoW_$n8eY5$q7`WdC9^z8)-vR6X0ByjF4KF;QYP?(Lly2 zB|ml-RBCx_0aM2tY zbR9f#HE6tVc-9)qJbl0q{!<`8&&`Co*YS?d_)RVS5>$9Dq{J4m^toA>TD<~_>9?^U z{z*fvH#ozU;_q1$>5TA)B|I@TDY#0H_vwfll2Z@NZ7*tyqsvqAA?dMAFs6 zwR)s06j4K;nD4#;FO68r#-o119Q14>TX3u%z4Zn?SoMg+hD$dYx8;Fxaq{$^NMh*0 zX#nR~ANCPN(p?rf#aE3V2T-QLc^X0C=SrS#vFT%K{v<$U>Cu~tagi`~T1HCi^~m&1 z{tQmIyRxES%?vNEVaWAFuRDHcI6Ol^DBkQ;rlQrBiNkn~enkWRt(66l&=+pN-m#Kh9aH@ca9f#4 zB#qD`cezwQpH1VsaXmMEadamWqW-}|=0z9T%LWZURO`Ar$?Y)Ps?@ z2t`(B8GVlZUQOJaO-G#jS#f@OSAjdVG^07%P1NOxG<`m2D|+{|~K$q;Y~9snV|$6Btc z6(QCGQ%Ty;r-=h706uW}#3tI+;=JO?Ab9dI<*jeTtou#?Q=V=*ZUHBR4%EGdW%dKT} zg5q_N8?fhDBV2R%t?@}DjBzh!T7 zehWng@_L`h>w%`c9tigTFxwg$o$?x`a{MoffPXRmTd71|Vg7zIG<>wEQ+ABbVD!kc zasZ@97xCeX3P!ZccKM=<*r0|xsm}>Yfkco?7cD2$h?FGqDR{3p-%IdbXTBHXy~=#Y zc9u|p?>S=L)^g@n$k)6De6+?DaOgCbxFzx2I|iMGH595^td&QNdSDe2t6`iWzIFoU zlo3^aGk(j3#L6v#t#cI4u#5vcf-iGDH`Y1L@`gKQ+sHkX!PBQyX96Y zk$2z#n8}Ljq@}r@Ds+$HYIhO1yr>c1Q}6^PWMsfAhHNFNAUz-tFlb}3g=;Ed^gxaW z@&LJVMuEh~VHk%*$v{aNqo!yX^8!E(l4PNi$nQXU1vH~PNrP3#=r48%f{WQ?b%*uVG)@a^ZFYQIwBezN!ABuo&ytwZ-nxM*l##IY9xX zX$|Khplsu`JP3md7U_AkxKF874kB-rpEiLqEPldxp3l#k5Q$u| zRWwIJJg^R+88U_k@WwIxwh=Oh3vw}rU-+F!MScQ>OPDAktJr!(8V^Y-WrZ)!vapgd z#I1*+iq>>MB}iU0O`j+$!8d?iT&!5E7?lqB5>YW2p|g@E4O6jL#2Sph^iqb64=o|> zKLTrH(=hy=-a~ceb9AMZqgp#_6>;Felm)l~!WVt!5rBs=m~F#o+%Uzi53}Tn>u%5^ z^BUQR@K{%gZ6q$Xjl}tQmm+ecn=tw=V1YUFs`zMY^etzpktoq&q+P-hc#TMaqhSA~ z7Wo*wpJKG)+Txrm5ENX-I*?_qKpk0DAzzE;%VU{T89IiU>9#x_#d56s=x7mi(R z?KOViqG&Z(s?wubNUAEhPAUjVK(q+#!8#-@s=*A0!v>&+G<*(kYeMJndrjzUJXjZH z^=TK;;H@wq?Z6Kjf!1U}n&e@h5b6-Akb^IZoLo=62=3S@6a#x+0X7v8C8{~fYEBRJ z^lt~w;4n{C(4(UCY8l|*9me&;JE=ZcVVvKPWCK02h<)H66}v8thh=*eBBN3jKr&-6 z(o7+}fJUI@$UKGeijd`u0fY&K5UoV}{B zf!~qv%*J~kfddmWxu`LB9JCQ`S4LDaNna;wj8b><5eA_M@t&(VBG=>yCDA2io+vz7kOU-IR4t4!P-$(U z5Z8#JMbnH)k4e3Zj7?!6=!6r4*asBiK+%;yBs)Y4b!~;l+;$pc@5dfSUI?j1yaN># zfPsvWpqoVRhF}G44^J-OEbZKZ4Ucocy=8&O6D-ghJ9>pUl<4#H@;SfWf#00b6r$Se7o`(Q^11VJa0}?DB=Dx%KcAY#W-EWr7kLi6lwn zl;Cv%A#*ET+da2VzUI~Q0g8fJ4tgTDdDxkROi2!NaDYGw%TU`2dZpkDRd9wXI71aI z9Mb}kZ$Ee-M8+P%Ud;a%dXrwv1}K5`U$wy$n0A`j`R4;QrQuWyW3AJSRcJV7botx9 zc)_mKsan~?*p3Q<+$2K?%~QU}b+t(i@(=J=*3iv9-irPhM8aQLe|)-Id-TT{Rw|`G z2B+771wO#c0Rwt12K4a~AMf@tJfp?lLO4)CqR}HtaS0b>p|dLx0wM>Y;kuEUR8o5E z15M|}k;)#_xs*?hP^#yXq+PLH3^QXavB`nx)Rt1a#jZ0ZYFUw}K>>LKAXhMYM;*Je~v;)D)C5FE1%88`tU4A|3) zpbYM)g|_1jBUc@Oq$XFGE~<-siuzgE4H1!$fp}CF+Jqq>nN7hod8cBmqXDBLXJ zGluk6DCDe)e=dDMtrl8_NOX1yI@^Un_RJ5!2)#+uT(_}M=4XNm%+H>x7j1x_P~9#$ zNw7&QaxM*1qZ_Zj=yfoAER!rz8EAb`v7L!q_AiWHU!e?B`QRbFvVzk%f-IsWk^a3N z-+DByrZd(;lqn9>v{t@kYsRp9AOj=Nl^2PWHj{|`uqbR8y?()}U{LBuJ#z^iyckHC z(wD|C?*0lcn}P@?m?jBMR|$v*V|w|I@z^ksKB=q|sccZ{$9LlN_M9H^igjC^X!LY2guiWD65J8QcUp=Js{@pJg#-jT`l1b z%nd`319O8ApfGnP-fYl6@*Q-G1oUrU?)Tpau654}SqPHR;<6u(6_7(De(*b%ht#=i@BtoLsA5~PaA4w#;TO)w>Vd@j3APu0U0 zV*}y7wSJ(fvTYp=I)Iznl`|vOv2f zM)%!ziSa@fyDTX&j=#W?7@^~UT!^4LP!|S45*hyhO?iJ3Oo(Cv5!4rWL^ZZRb;kbD za?{ZY?ac-02nlzfWiWyqXgLi53N6`qvx$QN-^f%$0K(=lEi~eAsm0P|qD_Dix{R2@ zES)Cy+F_Dt|M0$fRO|5mO~M`UK7b$xyfYA>;PvCp2JhOA%~Y>J0Oh}tU^i@WO0e_N zRr_0&VA(2i|LtA@?TYNtyq{i>a7VizMv$Z3_aH#En>w1U-A{J?At|=YEFei)s47ZA zVkZUEQwF_Q`k@lJe_*=_P%VM|PQ=N&-lcq=0Iyvg;+_Ij9ceT5o zX^wh2OFc?PSsD!XOaDulUc{5kUvB3R;TAy!TN$ZI%Z*Nf)(IOZRaZRfA*T@7C~_<@ z6Z(DglJ@pSvgRdaNr9m`s@w`Z;1W>^##8E{WFeX@BDN@BX>7)YRSxa5?14T)3IcEo zHRDbdxqrIv@f#_j1(T*pxPwl<&uB&`pCdrg$tR4e5vz_6%0o)K(#&>UbfTe-27-zRbkqa!lRX)mI zMR%?dr~(TEv1i&dxTK>ctqH9k-3)i&;|l~i_+|qF6h3P3M!wl?N5g1-InnUtw05Im z{&qVW&Ouw6p`nW(Ms{=BrIr1x+F>f7w?xZZz74QQ$b}OyK@1Y@g()`$y81f{O?m_QwLZEHt(;afHGw?M*267E343IsWj@G1fn5}v~w^gebXYDOw$72)y=N6V#WI!4Q= zcxtpJnggi_MMUI*02Uw%7Jn(DVHDYb(qIiy!bCPqVMUlU0^3PFQZ@`nPQIwES)D0< z-H4|6rEtb`Zk~G1%f}Nk6^ncL#WqCzVjm)Yxe<};dyObZZqC?j{GE=+B1(wK^kS>f z+gvg8a0c8T;*`^q5l`-n2dtJ1QL?uSdSEy2`~l*eKzjH<16Vjc!nq!}^Cmn6k~*-46K9byh$6g^ROUg#)1 zV~5Gd0SNm@r6smN$j^tg5PLBMIS{f50SY1S;SC5`_=6oGi}>Y4$ky8|gw*c`A&fI# ziP;e{h#HxbyO+<~5l_hd_eB^fjs_8k+$TbK*$dHEBPv1KEEE~QD?}}9s<%imk^1!JGo?wYP*Z72Od3o`wBR)7}En8mK4dWq-UCOqwW9|aOH z=RyG}+v>r&k1#kKQV5OC=a;F;n#$qVvz$PF?r3K-Am1U9_HU_Alum$*N8Wan9y$Z9 zj6EqvKT{TavH+0qH?MMwK2wKm*ogzNJOyvQ=-I2c!;BOi^d26@o$rdol;k2YAB9Ha zi@a1xtWjcqK@BIGb_^1u)hq!j!Er1hEUr`M-y$633%^A0%p@>UVO_#9Y?k5-q@`+@ zN`@Btfb}rhW@8afd?ti(1{BMH3?Nv&yp-m^8jlM-hHA0vlUqnWHL_3T782xLVeF|* zsxiPL2|){msGRbIS2S8LIY$}Bob}RB`Id%uf~CV3Stes08#)T(84HbcoBoGRSix;F z=DoW+IY^AB1wg^&pN@3gtuxl9LxECQIb=e;z_1ajjPdt<+r{!)mX6lKIa7z(X<*3E zUzx(4_U~Ev!ln-ORinO0SBQIFeG%VS&X~NSV7Um07x9bUcjvs0XTDDs*{r9L}T$#BvR+euKEcGF}{n zp2@`nn!_A?sLqiEe)dl^t%pG8!Rko*@fK(v-I#H+2^ZFSv5aC?qbpYsmBw%d58Fhx z%fwR)6~hc^oZVN{5Y?I$MrSNPfSmYA*5TN$g@)5!i9>uSRnkRA)f@A08z~aVw{-b^ zl4D*%8#q~nI2^j}KG`yrl?{}swp#WDZQl8q7}xDkp^LKwxXY2$7-Mzurzg--i(QGb zp@g(K?|oPpCqfG_O=|?WfVV-V#8wcJXxgFM*pGrKH(sR^fvJ&`_KT@LUrNuJFq!*{ z0jW<@oi&PRrQE6`^B=hHxq>Ie05CG;ivKzyfLY=xr`VxY!qY16_LybsDc?~Q2 zR>r~t<$1{n8GG~0ol!U@EPX32oLwy+A2T*rJ9saV*8A z3+G1Fc=rZSYm0z7(6L|4n_(L3t=gJ~>v9!7Et@YORyF znNkPV@;KUHQ%OEh(8yQV%ds^jPs`|l5#I%*ATkHpIHFUasX{XGg#ldzN5+t=tWli$ zG9jV9tYDZqq8I>NoR6O&d22#fA)iaf20`?S5nT^@G`MN0#zSI2S1p=?+-pJ);S1XW z_aGG=9ejAB*>854o=9aE?T^+cg@~yR0mGR2n5j97_FM8kmQ#y_2; zSDb~PF;uxlxyIAfwN1@iB$%%H2MrsOho9msZ2Q81K)idV;3pbD1suQY1R*n{^@*l| z-ijKDfp^xS@yWq4_KVu#gQXK}r^OaZmIXyQ;e}kfi6K&*?k=I(jTjAHIyBX>d81L9<6BPV}DlM z!#T6DOp$wyFo@OCRkX5lpc)90!tgXz#hJMIL)n3W02qSB*+K&2q#I+!DzMrZMJ?u} z$lQM6)aGsqIkh=#U&n4t8I~;8C*puHwugV9ke5@MFWOKyKqFYSIl(dJz)7hICAy6< z=y>U2c-ZGD$Ji2V+=MzdO){lT`C7;9cPs8>XT;86XhiLDRXPg$iTSKCAz7NBV({nEL5b2D0GHLZVGh&7H7$1N6pN_vdbq)iENy4Vy%M`%lxLTM)kVc z(Ad*zuP+1J9w9`9R7)@iIYN*FIYO`m!A0!%7}O#%NQX@g)l@Kr4uH)(BsDD^+3Iy9 zLXT9hg?R0xUeChoPJ*2=G@Bjx72er_M@FRF@G$IEFlmb(mpjL*WYkvz$+BF&M1o8j zDP=xvO_-k{@UR+k%IAgtZYSrm@iKvz?N>5ieu3W@qw2=f5~#Yx6tbLUzAVw78okpw zlqr?;jr@`c)m5$uN~Ef>PNuhejhi&`epIwTULGGba1r0DG-^Uy;Z*q3!`nCjV zO#4CV;Q;CH&^DTZG}aE%I44N%1V}Ic!;Yl|{BmMx^|*Fp>Anx`Sjub((%PqNSXviz zV9AvL=}ZSmXSW3DjP`@H`vW^jAyIl9v~-Rgq;s7hbxMF#xy6p9H~Hnn(qG568%x(Z zK>A*G*E>LZ>TfnIJ#9F!)FA=VAO}c;TY_|0`$5{U(T=67T7u-ZgOrg3Nh^C-EGB6+ zo9zf%%P%K_W?b2B1YPKWW&^K|a5CHMCv6A{V$YC`|5FnP@;N{n))J(KE7}dxf(>>A zUCo^6xCg2^To2 zgeD_{Oxp?#6?d3mscB&(I+!UhvrwU72{!P1QLj&LKNF7h!qpH8605n2NEnID@k*rY{up#xuEQLvsKdsN6AGn`>LP~ zcP1T~_TwrF&+D@Ml%q*1kAHj_>lgjX%DjO~^Z5;VlL^^(Vq)$y*FXtU97oo}@5NjV;Q zji0;jmDzQjs!1jXG4gh6tZ|PjIy6;1ohzvgcO<7#>@TDDt>DI*Z9+|r2rxOZ1%PCR zhJLIsQ_8(X&c=Z-qEn?WW`-uHr?WoDHJ9=2%j7gTY%5y;>(3QZ*#WiNhE<7i;zTp-NN(qO+6T0rsx^$kNN~OaQG`OvokdR0#s#a^*jhBjIO+zF z#yuR+6H_Ac9m;$dDlP2HiKn$H9X(0DXNHbe&lc1<#fUTe zI8lE?dA7PKpPc9!=88QkQ{-rXEjOoT=FHtDAYuBvO--fuUmjSV8-fm+sbY^2%xHSr zwQ^qNU)NX;URPzI2WHWdjQhY4ic<}#NTa-NKgY;xB{J>8jR|r~^mzIO40KZ6m&L76 zT_T6YN_OIVvc}Fzui+w_7OX_VG(N@BN^({RY$dq84{@vZY64gc;cD+yCCNUT_T&bE z+^sP16u%N6GtkIOfGp&xR(X@dA=6kZy+5w8!CE#>sJX4lcWf4-*;6faFIkqN1548| z4MH#p)3Y8U^W=jiT>gK$2PUix3?e&B7Y!M{z33`p zky<8xTcO^f_rN!v<@j;3TY_*PT$Wq|;gZYpqv_Md@qOB8Jl^b$B8{JWfK-=I^NOaI zsK^0{$j=!0I3g=CEMQk4@O$4puD*jh7QUaLYh+!-uLO0F9E{%#YlWcEUsEK%LyUg1 zN~nV|XzE+|MHl}EV)Xsrp{0XW=WRk^#*9N{l}4(>YWVQyWR=FOXW>EBgF%aY0r>s} zZ5AR~28T#`m)>$O<^$PI!ERUVlb36w11aTZvmJbV#Hwh1hc?k_{zG(peqJu z{J5U6?Q^34vK*6EO}xHI`bi1K}KRAA+bqRS=b~# zOpOK|lwz{(0Vci>Ygb@kh0br;tTCBX*ck@Db%HlcxOD19_AY=|qdC$sssl2{!%N!e z92+IVSe>d2R(7f`y(mG|{@Yuj=;#_ij#5I4)w6Bsblg=yMdW)*r?fl10K3vDA0*1o z&#}QOn*MV=A7T<#m!1qfmIp-(?~`3X$aCd8>{z&agZ$#F)vM^OIbN$vF=b~8vp+}S zYB7(?xVkFYnf}55nZy}49?-wLqXpNG8k)pUNo)AouPSdRm7m6;hKZ~pnjXJG6pd+P zty8lXWD*(L;t;8v6J}r=^Swqsa@Agv+-M5fJQ7VIkA$Ocd;y@g_bZgk0()WkvSX04 zeBQD6Ei7Mj91F1!sLp&|U2UJ?`Hy8YNh9cX49v+xt_@w9X_1dS12u5@XVRooRFnE# zX*Fr$(QH!L7XMM&;+2Kk(pvN=Ii#QxMZ*I&P7*9Y9#22q(o_YA>JV%^bQA=ad4ktJ z6kS;_jg^@(!D1q^7%AV(gJSt*MDBE|$=CfGzB0r9D)aX+J|{Nnr~4{jNC$A$^h-&8C9M4FX{A^qzs>7GMub96h= zYGKqE*yAnzpR_FRpbZNdj|`j8u-QkpE34VfUlk%y%Hq!E5S$kWlk-PrBD2q_zNr}@um0=gJ2Xu zPkwVp8-OP7FWbL-byJRC`c_-Tkf0BSo>l(_e=Yhm<^S!^w28Qbg-^zlOPBph?Nv+r zMsVQ|ZFIvy;?FGa%wMZK6MqXI{dMF)U>SFsrucioOghE5`SG?&ZJl@K99Z6%@CKjy zmHOM*H z!}@*CU;gv_PJFNb%YTobO+?HI3)H@-F=U%M3q3b>lCg{$dJu%Q2A73PTQKHkgeAD^4}VysTC z{aB9JKGOPmO=c zE3S%|PW*m1EQw$EtYEQ8{9fAG#BXUnR_rpdm1gEz_W^a|u(sq!}htR@`Y zu<|#ee=3Y4to;5nm|u_JKmyG0fHX@MZku23OlshK23I?e!`;rKxS_q)`1VY0U7cQ= z;=dj_>(C+f=nza!J&kKT7hq#Sx4#gGXi-H$Wf4j+wr$64l2N7vQn7GmUNAJ%LAVd&v?0yJzA5`s6@%PpvW!dG7jHrt}Rpsik z&)8kUBW8l|c;OT9i-S)lAdN{Aqaf((q@owI1ANwabP@Ej6||`%Zug0#T|XM#8sTk- zQ%2)nqz2r!kV+P(IMm$qTI|iy+?Sy79>}<&Ot}2VnWzF_Mebsz&~AkPLlIcDDW$lW z4_5}jWBkp)4J(I0iKhP!q=!|u<8UBTt+ExQk#(6CqJ>v6FMu#GAcm_@ZX@bKC&Ddx zG_6<;D=4$#*hVoc{H84v2_|foy865rMLPb;1+dxX$I~^LOS}?418lt zq=g<}fye{YUFOnrS7^ai(ji0Dcvv$u51;6up}IUtrs7Hq60l^=&>Ik=q#$##E|lW~ zLBhnJkAVl0-IraAmps0d;3c0gQ}9yE8)5^@l`*UY!~|OD3_5l-OlJvzTk$=DN?0zk z3`Leg7BUotfL!r{V!1kiqu_kW!wZPJGfHzcoa2jh@kS2=wlqDAriDM%b2r>}Ivm2? zQRw416XqQZQ&H_O;BQ8bWlo&6ia1U%PBi_q-wKIT;-OZzdUhmcjwB zpXrfQJ>qraZiu77IKe$flzLz=q=i)P2w!gL_g|a_%b`E*X*?T_v7)*#>Q*!API+#D zTggwV28oN+2dh^0t5TGK@*DNIt28aP0@8Bj`sDWz$hwv5Te)u!5P^Q-c2hET1yMH| zA!LU3(sL;nn|yK+-zvFi?|!(lZ#n*eUbAq!BV-Tqb{)4`5-5FQ=rEi%}>>LLpELF@8;LW$ zxm9{_Z^mt%iX!<0>G?z7w}hHKDy&%ud(eB$m;{w~NeM{*nZmq?Zxsl9lUu zT^WP}?zX!Vw^{wh2Y@2!U8I0`x0%R~&!#0T&x@fYxGHNr%Dp~Wt|gyP3a^_C4oLCe z=;h^u>IyBnbQ1R-cF&h2C$l`qJ+j8Nm*}xOc_$LsL zTH;G2&C=#Hhw=#jK3;01HoyB5QqI?;>cz|$8LR5l_N&tSZ`7dPpeJKG@Aqh1bj z%c>BYQtHJ*MJ>~D67na!^aD?DdnrVtlGW;-T|&c)?l1_B8!8g+D5xZA5IlchM;QdA zAgln@p%^UvRN$fGXbT_em=t8n0*r~3dYF@>w52#_W-YY^WSJxVd3ksis4?*aDcE=% zd#+pR$8vwHZA4sPoQ3U0R4V00J{wNue#T7%BRNwnws84-w?gDeycR?04^t9|ycm}@ z=3`WMYwixXyAh8O7&@-N#RTKf!cmQwN7N&_4wHu*C=hkVAI}#B0(WwIpcLc@R%fH; zHn2*d6jEItL25PRxI*eTKP#l-Of;{JQksPh%vAx4b->~#yc@vcuH66}DjI}sdIv)^ zi^skZ&7w|WG5QNjGDc&s_sVZP zO<-_JX;;)b<6_8{E_g}%Eg8QV(1i+UNErg{Hkx9#rj9|b?a5rH+krmR1o|uo(0|;* zo=MyL8R|S79hRXQu}-%d(Fk5eXFdN{TO%M3IMnoSc0*(lrmykTHJ*K`;@Q@S0yOpu zM`jQ*?wGm)N1|L-BI5{&uMwK8M@L!_6cJ(Ucj9%J@v2+TN`yz#Uz;H~j^LrF@)Cd- zTY$`ZWYCX5oXRuh8a>=4k&T_r%9|uF%F6c{ zi4H$a;f`0w@uE@HSYT~Sfq?bkMHX0Z!ba23-73eJhzrOY(ya`}SbVl7Kg@fuZpzaZ zkJ}fR9tX6gjl5=bnA>>d$sgjoR;FT!fP)P^`67VAg_G`P{E@InHSZ%|WPmqvhnr0) z@kVB5@s7Abdi6J*`{8=;vqudD5gf+*8-gqI^#PyxkKa|;)1B%Ejo;lfE5&c%3hvqZ$Z9g32?(cwA1@bo6*yE}G6XP6GX<&8%; zgxCim3V7tZQu1N!ig&B?pc8}fxc@{9Rl{ASy%2rA8Qp~D%C&v8r(kS*8fl4wMA*va z>%|Fsg}P-_pH)o|j0SA@vv8-3Gj{e|?~9~D_lolh=K@!wH?F%%57C2FM^mY}9g0dM z{e@Re?W8wASZ~Ak80%8=6v}hK_eFV}&tUU7YA)Dy5w2=Jgo0^G>}xdM8@Y%VH2Vjm zUosQdE3-Fh;QHRY?-Tt2{o*sxFM68#MNdGHEl{u@pkK^H53wKEL5E62yju`upazP4 z|6O>|`7#Do_1AA~GElwr`#9EwEBBK2mRTzq$twHqD(lI?J=Krny%G`c7Qe6)n_ZmhKNbl}RDv-`Fu_dlFG+AOw_?DF`rRnj z8@<;>N`c1jjXc0t5R&1&@!m|law`vFn#|kbWj&0S&!S~mzIaul)9Sc8hlzMe{Pd^b z3mBKgZ^HG6hBx@Om>Y5FornUq;UBNC4e@Rx0f{&A#tp37PNmyeS|ojuBoitO zdMzq_NTT7O2oyQSRKQ53`-UhgO@ysX+Ut|_SqzI{6RI^YoyYyadyG|o1K_i2q)E7U zew)ediel~kmDtZBx~)9QQKFHnx7*go%3?_eM0DrNv&k zkLA}uqs5K0Y#!@~t;JpP2^nPd2UHwFtS!DgyI)lx0WlOS?mbLEJnxTLRT z8gP!}SL7;;1PUL`Q}3K3sbO=#aGU@(_D=*S+Rhiw#|lOjU`9KNYbu7o`2j9w_AbL~ z3NEhzqriOhdl$_@=vUB8lQH$%?Qs}b^E&8nGL;&;>q^sT9_kOW^!1ME12$-7K|}_d z+@&+9k=&_Ekt|d1ZIs?*FR%{^RF+5DqkgAH{Im>-!3MKIsd%%|_@O<~ARiOq!g!Z2 zw^0lB0g7Rr)Rz+=8k+i5^;_F7u3fga;TA6pm@Wb-K2&0#;Z2X=zFZ8lFbSa=7z};c z@a-}Zx9#RWn9WHHO-s`>x3WAluu1}7&z^&)ws>haMRnxG?7!dasUoA%! z!Ccgr_+7;(zANyIF!{*rzQ~pZadHCVnGQGDjYPBY#Z>ZDjC5H@e?kEg-dKPflf0k} zR}n}K8)I>`H81*w5sWtl>)xupe1DZSaw20c*@X&-9Lz7D0=2!&g0 z;ERIc%)7I{-e6BRkaTQvLX{TeMOTV@$p+Y{*l$a}xCcMG8m3_c*nBUP1i3q5)YT^%FQqfYQN2DxHuxSVEUy5l+h)|YWWeq)Y#Ko`;He(G=)P*%-c=7fA zQ4jvop>#x%sCT%;sdPf%ycDhcNv49F$-|SI*?qayxElnPz%@n=CPNe7>mU5QzZcUi ztyce_UjM)#Gr;dz+M^F(TQs+2qPpgPlIR(wUjwchY24Haco)4C(frQLHYh^(QrHv2|A6)|whNL4|4=(j8O zh~kZt&lcY3h=<~h8@HLf(H}uv2Wa{>Ild?PgTqBV%GARjz#ixoaiQ4bJYVECYq${c za1j;~WVrB!n_6d&J?eys7c2ezBcAKQ9;v0bn(QIv#J}E-5faysdZ{!_@R4?55{D4S zp5Np+QTv`f*lXW1LFwcJ={m-W3JGk^8ILQ@z|bCfTye(Or0}=K9ZURMLk^mo1y(!(G@S$D?yHAs z`*ksyFjB^788NC*BX-UgBY6&z_YIG~F+5|E9EY2`v8n+_ zZ#a2`pJCvJ(Yh}@xumds*bTmL&vKdoxhzVqB=>?<(Y5dgOxCavYoci%|U9 zh72ISp(}pW2J52mFVY!XVn=`uAi5)G2zg$Mha%5);3g5>e_;$fbY2ddl%;+iNPd%n zTG3~o{pwfNXGWpG1L`wb>+OmDpFShv{eMEAdG>*yY~A<2r_W41U6^7x9*QXzeQh#D z8GiUTc0_Qz<%iS0}5>aOm9M$@tL~JB)hO4fijXTaJ4UTZ7V8$kG1wh=Q~9; zmtoYnq7)g(Xh`Z^kQM!^8t#Cc=8`gpoS0&3D9<9NeUR9eCUb4}7^*W)O@?DgE1Jww zOOts}X);TdyuKR(c&U=tki!SmWSATZJ7jeZln%)2PxR{Tkk_W-lQA@*&R`9?7)f&g zP2ce38^hx#`DAgMtISiO>Wzm~o&l*m`Ub32myd>2uCt_h^;U@T(dpqCBFcT?2{}q+ zx6t-mliAwrhk-Q6ZSCIqcq!`rZ!cAvUQ}uNh{QfK&_89`?;y5;WJv3-u|F$m4bfZ_ z{-$A=JgbYsD~rNweBr9t(U8^JV)v}Q>X#ZUMQ_YsRq+>!)s+5r@2}9 z)cLAUl|F(~G-kK5Kdxbt=9ai7ND=@NV(W+7%%Lz6e=07CZ zbgVxu*mP9sajtd#Y$$7|_Jt$d3~U?V_{eHuvj{fzEd8o%V`+Wa+R}fJ126;LYg`(F zn?cdbiGn_SE#|D>+=r0MMVs(YY(*JLhcAS!;d#-%b?C9SL{zvr)kH!NvD!C7!l7zD z4KFf(y#X(hXAEAlSPW+BaJH9&S`Y%t=^WG1N9O!S)~{!6Lk|IMnCzR{n`3aiKf4~a zn)Cg8{s~Mjm5^prei&rRvPpF^>)#`@{^#(CS-%!~g5jaHTG?E@c$eZwW<=2Kw9st4 z>rqE7S*V4@)ia7%E7c>fOW|yIBSN(dXn9ti&BkY|nq?@2wNybPW&uowHlvL+3-C~{ zG7CWURTED_;H?>&McjK$LGmzBg2^LW#ImGPRNjPd?n0q~ltuwsN2#HeNcz{abIkBO zGrU;BAJ3);qp?88Gje~n7Y}rA4qkogwXc?8_9+AeOXIhq=htPY7X=4PV|W#y(8G_5 z;+J2(G@COM^tBQo_h)~KPsIL1QehtqZJ*P8VQY7cnxE;YTa}MpHl&nkY;S;hpp}-( zQwc&MpOmL###GFY3iD$O%~Scb!s$Y+tfxP_hzcnNaYd+A<_9QM&HWgY&l*d-;bEAr zl1QOAh<7k4?~)Zq(k`sHd*e zdw;Jj?(QzzG7a~g-vZOZC$)%NtG%&-!GSNayb1GXN@=IEEv1)ei{J4=fVjQY+Xv{; ziyKjNVfp#)!t%RQ-QGAlU)@Bzd$S%ra)ci3w80x0?}oJ9&;;iO*z;Jp0Y*c$@St{g z!O^KNyv~?_4F}!`%v{hi*eYM|JqeJ}s@`w2RqA|B>KY6a&Vpp@dLqZ{z#_8a)sFZojzwzfI59dRTDVn4<8z zMLhfRksf}BDnb<)2}%Q)466*cAA!AHbT0DeYG3rMDA)cPbnFd8lHH$cf$`8)IGcF$ z&)MET@t?l?3+~t45YtmPV2;s2$0k&5+1lWa!QL92lY*u8gYUfYHCovr$O&7iclXy? zfT|`Ib-mG><9h0LZ8lXE9-)9Gnc2aD;r7~S|nEZ{&-?~k@ipQ7UHZ+Pn==Ek)IM~fgYG+Jn^BF z!d2C+#ts0Tup?SBPWF#M;pMS1EElcrrLyK3Uv7bO4;r;oN~a*=MJs}X&%GZc;zlBF zjy>+3<%u|K>PnwfjvJ^RehG(;F!>%X%fiTU7OrI>gn0!^j$uPk=E5(ne3*zrJ%z#z zD+g6iYMzzSaPDX4;`bQLN?{_|Zj8Uvwv|kp#Qmjmm|zmLZ=Ab=vWZpY#G$!J`oKRV zHX2m+7RvCCQ&iC#F={28^zf7!nXiX2-{rQm2Nn=iEcA8_?&(lE0A}685m<>p&3$hb zA2Jg?oYP(fr)<^~ev#b@#w{DLl?w;A(l5X+1)$+7Jl;G3IgQnyuy43u3Vi2QjmVN6 z=5#iA(gwBFB8%IzT00LVjZUV&!=B#iY4vpI3*PXD+=mev;r0$3ne8e~r(f}lVYT&Q z-s1K~I%TivP0dKrU7DUCF`WMDw*jQf%r|4-NF$tAY z!yAfn_h_?E!}fe{aL-Kbp1qi$=D(5ZowX+gy?k31-hJV2s8gW=HFUAGP}12jb#BVw9rfxP!MR65(>jB08zod+E-nL z;VKrf9Kf@PIy@a99B+bv5(e!Dgc)Xjh%(8a0O1512tT8>ZG$k-ETX762-!&xxc{Lo zEc^r$7n_jY9E3gz5K5=FUHwus`+ImgFmR$(ed*w~st%ZW|0H?C)!q+cg;)zcDN(Uj zb~d80YWqHDqN^6nz?Uxy0uYUIuTi#XdpumdAQd*PTNGU;IY5WeR)s~ESE2m}VuYL{ z_s0m>oWpyG7W#I?%c}hO3sRlhmN&Q^q7Q<;}*FuiP zCUFS0GZ8OMQA{)sZ#E|Cn_wal7s>s2`{SpVQHJo-YP=j2KkYyU@>54Z@94^Q^3$u# z4@z2zC-WzWx&8b!*(@Sp7Lj1-cJk9{W_}zkc7o7;erkY%#8&)*rvnS^_n0KHXyf=SxOPu2Lc^V3-$S^T7hj)RpbNxN!P*}2-{ zsab1iOf-3_qoiwPaQV=^Kb|rNm%&&FS4^yhp8eUX)Er>m#jv7y1IG^SywNMc8yIi* z#}C+At@r_#R~{Tce2xs{had2CP(nNT;YsEPB`q}bCn%x){4m}uVoGxm+Q|<+&HUUk z>R`$C^TQVOGh6X3o(?RulOG;4^S@-~Phg>a{4mzcKDl}I?cj$jGcT_kYJ(p>!pM*u zU+DVZ&JWoeEz9Kc6ZdCT3s_Yd!l$|QwirJ}RK4MHZAT@rdmDz{G ze#>pbrf!aXv(!WNftR^T595aAm0~=-%#BkRP(nN4Os-q*0>>kf=9R;E0gsgi3mP-2 ze)_^QG2g-_Gi~ubqMa{ptTMW2i;ei|0-W#U?DD6E6SQ6dykb$HOJVt0nX=Us2NuIu zxkKNI0=K&Kys&{~L+F32gCE7?wFSGs(gL%f@P(=hWD{ngR)_=gSGh{xlKr=v4Yz=N zmAl~u>{c$yZPI4*{$s2OLdpFGY?QwkN^X-Vxqb2O!#>h*Rg3X?O&j_y7nt+3*=M3i zoZQudyp<^YgDAcG2xWEndn&y{O{G_loL)XQT_%fJY^WA^1sN!ms_`T{uBOuZZGJg+ zX_^XGwHEq22wo{&)!`2UAJ}x>Da;OTo^57dkBGx zR5LrKvVyoo%llZBm)_BO#pjzjhqb8Kl0ns_=eLgTR5R-ll9eN}QCMD>eUeqBR`!Zg zaNo-h?1O?Eu@qdeVLcp=;Q84p{vt1?@Gx)eHSXyir*eBq>|&~~qR~FXw*jAN!M6o~E7<&pcamF}d zPX4}D_-BWjFE+72&KL7hlAU#`4#YZsWFYI@gC|+%-4?6^Os`B(Z_5eenJfUz<1q_J z3}yBKeKrbcd%DyJ=vhRE7BE;Ggc;-D{lM^wSwJwMLe))rrJ!3OMf{SM9* zwb0KPH0&JEgGLmwnZN;)oddcjIAFi@&*FssQO?29KQajY{m*+k{W)Lk+H0RO|*6W>c|`lv2>1i3&C!9;!VrEHhjBM-_7$ zW3q{ZITF*--kUtLW?Fmofulvs-eHVeOqqr2wj!OOWy9A6Ua1D)kx1iBrMR-vKn<9o zwCovJQd~R(=Jq&7#`PGLyivFVgR>7`ZWB>8019v}g7uCtiSwA25yw3TddC^i&QV0T zNmS&X%qS}IZt%pygod26(5@gS&Xew2*-SnDE@2M-24f1h#RNN{$b#_Zg21|fT`h}V z7%x}|d+?WfhP@@)p5c4?)C3Hr7~k8f|0|5R zMB^?zo&9i{RljX~Z&vqnW@fA{A#*}wK0Q&hRyG1rkZzoppab$hMw!{;8nN6&5|xv#`ATp*D^>7EK^WonnsG33IH)PzP9H*^DYl z3ES`_B_tFEFjPBNSm+0tMbP}mA`&3j_-TJDESH)2Z)^?%_$dLx{#RIdlQBU!u{j7f zcG}+x%ZC_&ng44%9asQ6nIPvA*iM9G`b7Qjtb+sh6| zRMG-g4@qigr{mwY*vX&SkSS)ZD`km;N`O%JVf}e}S`ICXD<{Q=94L0ZP7fNx+_5TU z_nHuw;XaN>OxEY4>6faYPVhY!qeQ|`nXiI2FlfF&llBXOWc>aRfiiyYHI^*q0E#ia z9+Af1t*_Tq3gb~L;1cKbN z1Fr&&(Wu&C@x9R~E@7APGo=(h3)G$VSeL&Bo4r{kR+r6Ccsin$9-fY9u7and{(Rte z3wby^9nJ29Ed$tskcCFgAP%GHHS(}L9mNEEP(xGe+gPK=33s{00se&+7^5yU zeP%ZIKSJGQ*0um3HeEAfRUhZ!@QpK;>60h(v_JO=NZ;##&XBAdtac^(2p8AenUG3KUeaiiE)z#f}IQFQn%Fz35(EDxDt5;^}{q`oZ zs(iD4klpBQ<}|ZyXqK(r>5~Zw4}dj63U3y$0T#mg)uQ7AKqXxhSoGC${{&8G!gYWT zn;K)=bL-unx^>y)wG;15})=3WcVO$!+Y~4I0iQ86r*DWP$8Z&)MhJ+uvxhK7M?-s;nog_J#%8ht;YO)NF43<4Y(GC z7gU~t%8lFMwHc|g3b; z;i79Q3e(g1hka zdf+emBxW69kAc@OSU3??C^+^DO%KIJUOs29>WE#Ao)>QsCB*( zzW@a3_RNPHad7a}ORHBRXR zHt{IC{4i^``0$Mj$%o?OSSzEjGI_BI?XLuPojU=f2q*P$HuO5YtC1b=QUgj4#1vHG zwtI2n^j9--yyi{|{!CRPr6yk+<+*eu3aY?yxt=>2=$VhoAdfutsHc)X6W4flqaQ|9 zP{={6^@8=UN#7ZFu(29>mG`&ZsY_U=c#t!^&vW>&)+Zg~hlr=Z^xewUc%|{L7ZhJ7 z2NK8n>a1<^K`DY5`Y_;8srl^dXt&y*NU7X_S@|1;7@;pos@&~Keim;VaFn6A1nL%6 zmT{1?KdA7GN^yRn$8A9t!^WBthN zOEmY`?5pYXqXgh-x%X#t_XW?<1?2g7_8dI*;9C_aCq)aP6F{0l_6bYR1Khso((DRi zFZ!VR2I*{iZeKjGH2ZY~cho`+NR1x+A}xIL7U)!O>CyY;456{=Jc^Dob)XOj(~41N?2kz4i#{y?Xp0}u zhT}*e=taS!&*p(kEp#bN6n@B~O*+30y*oJf+{735X)mqtZ6ic+TxRg?CIXt#HN{6Exv33yaR z7H$HxM5VnQAx?Bq14iw34X!~!6QuEm>Bgv_I0E8^xC|mDiVGM>8q>B{af!pI(YP=& zGveR^#sy6PA#M=B1((r?;@nIe2%s1kCGY=F-FtgUrw!5feedyo#4s^ojP@@ z4u{6wG8_wQk46OZvb)hs=+rY2e|Q~$OKNr=aPv{Grg?ODMtJI-8xQ4b z4srkvW}4@qw=K?sGSZr#poef&a1mZ&hfbZmyPBq{p`$8dMMHGf7OZjB)10f-K}WnE zWZhjW3)c9N#VJ^gEKtPh2eQ&Uvt@!BUKtj#n7Pv%KI?#nq5@EgkLKUdapXfdAdRz| z&Ewbz=okJgDwCIRAHOopGV46{Pxe)?Ud8jD7L(V0$GLdI3rhRuV zP2lMC!BgacqlX;La}n-oR$PtTC=Ho#njtSHx60x3<)VT~;k$8(9hv|R@hFOiw(j-Z zEzBF^DqEh<3LJ1?rsT3f_}~6P-z2UlKH*fvxTa^ga2(bj`Ei>89EUr3;5bF?E3}Hc z5k!tdfTfkLz!fpRUGrOxfWJw%D_`bfN2vrYhn4%hic_7Bb!2**U9XSti4c8^Q%uBz=iIGi7m^i zH-Dd-Gd$d!0aG%*4a}{}Xds8f3g_=6nM?D$Mj1(y{o7XjXI_ z-^Vmh*#WnE6u{yL*jLdgCO{+vC7-n@{2wmOPkTD1b8Nvt3-uOCJZ;5Px1p(-i#ma~ z$Vx>NgA|0~$>)WJi39QT$6^Pf7W$kU%2V)14L7h`X_eu+b{iXMm4k+{H*xkv8p)x} ze{6}u1~^e5e1#H__?2xdAVg{qO&Argsly=NY90h%B+ZWiYHV8_b5UPL0J_$tZ_q&C zCXl**?b~? zwP*GCuOLT)v6o zfpB$o4TF767r6|pOs-Rdrz0t>gdmWHKWC;*g77d@1n|L#z(UvT1T+As0+JUvay|5c zni+~IBS31v?RC5pa=MmjU(Eo?jA_x4wv?dyms4Zwhe}Fd2&DwdV9nbQy*=x{yth46 zXAxx+J}9CD$Ho$6jf$mJtY8G_sbNoK5rrZU$BhuBObW1P)OeyyT)!4@7kB&C~&+C7o`REY4*)^S33lT4!vq2*L!z_r!37?36{iHZTO%op8T#wVcN#HY^^18OAD!T`j8I(fG-l2i=% z+lQ*PIiU0${Dv5C(^@_pF<_0<#hh12#Q(FVmL1V?A=IDnZXU^!;v-D*rWqts=ls#$ex=snDWLgdU?+>-*t;%5*ES2XTb2)omg!(yWBGLJ%( zP@NW{JtsczqlI{6scgm-kQZi1`>w?8n|~S`2+z+3Q3BzGlvIK+xvrRw&+uVpEY2V$ zjivl9=OM)+uIhA5+DrKfIX#U}WlK-u3p^LJFGpg8fRfBCt@J%!xomgP?AH+lmCx|e z!jns}k5K<_i!~lgvdTWf5%?PAoeS&;eT44r8rdQc-l^Dx(Zo82#pOG8%4+ZiukefH zQB^08>P9|%)vZF&swR0Djq(W9;lb+!8^99(@%{F8{;r#(T~`nL7-Vd?r>yA(1yu9b zcuPX6i`iPkP=u-?q0CY&`5pRHplFLeTE%gU9I47d7|`X#14&t9bM1D_!4auFuZh7u zn~~50d_arX6g^_FDb3lu^CeQnFSv6d_7mFN!1z9c4Z_<7^(L+K7^Zq_!Z#!|+VBb= z@dhcPN2=BOraBFwc#}tXe5>YFd3mcO6K^$BYu@=xr1`FFf^(8a0JIJOT|`7Pb=a!g z9jb0&skWA>>Xun0mCCG^+GNgWE%dM|-60{fG4EePHXF*oZ!{&Fk7P`?7WAY-(z-NK zyv7^}_5tpse{D0_z2=b_A-BoQ2AilzgtxKktyPK$>FTXXy~UR6sq#u-b-qNgu%wXX zQ#kXfwi~Xq8lzk`2fx8(J@4@$5!7<)Dya@|wenP{=)dt$3^w=(D;ke6#JlKQiFYp^ zYVmb426h~~efmKLOqy%yZ=F?H{62~?9Stxi!+qY|=&8r?v2GHq{Vp}@*?ihh6%m-U zb!;2zRE@4I-{Ckkf*Y(Isr4=3L|ng|_w6Gm575r%!(zd_~TK7g;r7dXam4sBA;jq?HT-3x$?9SZ8edT7Ou%Z+~->Tf+-m(Ba z)BhCNef$9iR%-Y0kt*p3oBt=<&nlatDTQ&Pd8}XQ)E!SBVozX74IJuERMh7#$ry;q zes9Dzd9!^bjeq!-jGwK^+aGF&&xiY1W#2}>qWo-*A34dB>+1T>U%5GAdaBeCzBDV_D_BsPsu>{p#m}&3C`D2iso+iV@c*IAsxE zN28Jj;Xbr=W-n$E!YvRVA;eY?(H}vER^Gs%NI~`+(v#uRY#=5FJ%aXgUka{{!k9k| zKb8ajVgRrXJyT!~JdEtqUk5)64e%J(C_Ho^J{Qo$tKxNlGg4ZBFSvZ&&poL{_z%}D zvKN70s7=W!xCgseNhw@Q6oAgKV(^3MNExP-F##JYK%I#l7Py-6x0z=_6o6Y!?3J0z z`f{;Lxz9xXu|LvW$U$Scz5+L*z>OxjmkMWcul#~E^Vhwaji}~WAsD{i@uiS51Ci7) zp8ySt>?k_+mKL7k?KQGZS8gZv8Y%oy1@(yQWSljR@1xW>nm-aas)Sl2;@SmBB_^GS z=A@Ox;^>18u^b%LL&Y3}qiLT?ANNgUI4dk=4kBw0593-B3*~&C9e-@G=YFu<zghjtGyAEN&gJ3SR%{3C!DZy)02;Ur1fjq&`Y zq(dwRFPGAo1J23X>t&wW|08MqVQjqMWvetk;u@4B3l=1J8OKbzg_mLDQ{mvFX?>P9K?Ar-m27m%KUVi?VL~-vWW68$LJRuD5 z@}?SMIwC2&JQnUEUi`oB9$re(|9gU$O@+WqLvP~cv?NK{jq!Nt*CCdJm-G|j@bZG_ z(p<|y1BI9Sr125g+Orbt7$XP&G^o3H8F+Ilygc~)Z-JL7Cnn(K{F@!Ttb@it$-4>i z%hnh@qhy8!wZEFu=>bP(NdcWh0Wp%al92h?lGbQ{jaIc#JtxlJ(QjVTpZ` zTJBwVCU7&?McnjAlC&dkJ_pd^f$8Ap?c?Kcb1QU9AVLJRP`J5Bh!Ao8B}o=6NchYO zW!+Tj7M`-r*|?NE>H6sjOH0r7(+mNU zdyb4X8&BCnFnm38oshBt4~3^gLS4dBw)xfpzYb6T1wP`tJ2jrh4+WkIdJ<1BBuUy4 zPj`2S<>2X-W8?62)cZn&(@6x8bM_V@L|jElvT*QJ!c2ZsJl)?r)q4DU?BxIY_4sON zQ{(XT;xq?OC-mwBPv-(W_I!4%0Lgudj6oYu4HZNU@U%q@&FN6%S@I8-`(0RHWt*k_ zQsIdVGloAETlyWW$L9kVdxE1Z+!<%KL7fTtCrQ$dI2zs|mV=`~w91Ie%NySj9Bl^; zEcr(o5BVoa77mV%VJ5`U9k}+oEWu6cQczII|JspuO`3KAWF5o5K^zYw2yp*E<_XIDS@+tq|xH{yY zBuP8srC*0w4qnn}-4Vsh3k`yo<)DEj|48E@|D2Xs$5{CXn{nO4%VoKp%vXY!2N(Pn zc$sok0$$Fa;^1XnJ4z6JUjkmXLfvoU<$Y28yFZe#WaDMB5C(X;_gx`bHX>Q_&&)33 zCCk%2y!Zzv_C@M>YVj$+%eJNfr)&d5@WNi@(eslnO6b{PnlM%Nay-Bu2hD>^cW8PyXBqUg`l38!y!Y6!%|c zEZKO;7s3E9m%J?`Gx1P(Ipg*&;$@RN6<)a46`hYlWoh=VJdV-0)rd3KFO2`4_ zG4@BnOp*t%=oQ4@h9`_$CKNz53-xR;PxGQynHS_?FEJ-(&j|a0)?g+rpo~q}zk$9T z#@qI3q+)Z^-in_2_Bjaq)L06A`xv-a@)ty0^(Q9|kw(1{p>{dRH;&PkGVBB~z0 zW}Vm}mNN!}hsBM--OB_}8sMhJ;J&zojdnx zKslvz$CgKddC<3=j6-bGZ8JZID|N_)9&|p8Akb&Opm&F%7b2>C8Y~b*@9r^|!2CLH z96mDH8i#r2LAw&i;TWZNAH2#2y5Y{kZ@~PJxA?Hf;UXap#^J{j?3xG;rpDnCSWEDT zNC7~EJ7NKXxbw7m=9T+&HvVWps}9xH(((|td}Sb^=3APBSIxKB!_ZZs3_K_%%HffZ zaBoNu+f_wcJ_2y33IY( z4M62Juf%~FgrxAQ^j<5skcV~h89DN(%9BTR0UusfCts*oy-9t2p^55E+TsgA_{2bJ zSLZ*aRNQ~xp0!|w6wI$^>0DOTYe-6;Q6_IylB=(3s=QTuB-i;l@>rNBkHrO&eDOp^ zfhCc=&7Uieg*EEiB6(ER$-`)rN2rMpR38sI8JdjW{KDU3@tcwP8=Iq{GKp$PrrxR= zd8=loh%h11S&{AX>PSX&Y%iv&H>+1CsyC}wi`84IUM*l0pGA4AAEZ}cZ!{Sl4|B0H zc?9zdYq=k2?u!*7il0hUcnvAuXDAw1N%6iaMdNBI-dC+?Jm15TybE*Wu{cj2OI7hp z6^$1c3#k_t$YXwW%isE@OkO&9_WB7MDq* ztf*?V8&xXdQhuYwa>MUw)}e~gDmY$QTa>9s)O0f&9 zXdcUP7kml)0^<56_c-hO2lCw=lvf$$%Mj9XFt&*o6xI^{Jj*o&2N#M~&EYHm!3CM- zl^HU(jk-kKikfe~=j-Y${aHvwfEr7g;H+Cat5)u|2$DgDq z|AD!C@Dm~AnG6IDR3}w2mHF+_Jhv*nNZ!i`kq~i?Y#Co$A{D38@KuwWS9{sT0 zx`3d=`i0Po+G|tnw2=d06a|t?K$sR`R5>du3V$RAy$y11Tyqu-H<(qQ?rw)|^85gT z3d?@#S$Y@-F~4IFtJytR0MXuAhKWbrhzNJE=}L&}sR}ZsZ_FY{b>r2~I{f^T`q_w| zGt|$fh;cW5g@ap=IZ^~Yu*!j@;-df*#x*No87t;;yj60%Kw9WfHFHV7;II}f6UWH`?3-DJ z5rki*K1gdm#>3v_QlAGU#TbxPWP0Du3Z_rKy^thWz7)-cwvSNQ9ZOytAMH*SP;S@7fM{PvKKT)MC4Mrz$ROA zF?$ENl2(IV9L~P4>Qo>JLwT%>m77oSPgV>l72sZtDKC?xG?I1QsD6&c&*|#tWc<8c z{k(Z)=uR|u?`Y9NLSsyxx!_?SseGw|GW&!`Rehoe;2tqh^~oE!ifmDOScnJ2KK^?L zu@MQY6x>E(dk-VT!}gJRNno2abu5K#79G^OhTV%GtrELQGB)={4@P8tUTJWcIn@Q5 z4KFj_*-A#iF)#NA5ZemST-1Q%Q8c&iEocUoBh?d;5w4c67)hD-aQwmS;8EWPeb#al zs-rs7NYa_$9jGcYMt8~CCj3Mvs>2Ulxm&Amx`wwDDsX|!7NSs$up9uEJb*H`bsV1^ z_#!+dsZPboVl{@OW3`HzY7AMOdo|`EDu2Fu5d)6nQmd#ygyu_RIHC1eGa&bt+qs5C z##n|k9}OA=c~mdL15h3*jkxFtE%V$1w9F#7BQ3;PY-2jVhE-=qjH&94eS>^CohIob zWTwC=Bg)JGWRk~XtAQ_8&s>Lt(HKM$#6eW?49mcvWE@DFp{r1I@@!;|nq~+?=WiyH z8b+jssHijoODI)sHbPrct>aBwvXoA{wU9S;?!A_6Z!c5gixz^7BB~t{f_VzQ)I9nY z%~OmO+gJ@ORA_AJIDCr6KB4iB<>{sV&Rc*=vzlOzte^S9OITA{5>qW_Gpz!Lz}lPF zqPhi@GxCyTR9K5|>`D?rCu>-E32#%dI!{d$;GbH>Rv;XNS1lNJhQjW!;}md*ZPq*^ zcWa*W_R~CnJOrvG9UA<6ECYF66gB?PDFUh11!zkNGn_yE9eIgAS+y|#jptXJk@)k> zF%Q~Ygw*OZozoCoaB}Y(zh?5&NJ z=A%1iAJ;Hzaz|@DGU_Z)R81Dl`Mu_>!!e6Fbo!g3(_EJ1vlPBYnY zT93%cnrA+TnC4knERV$#CCcJOOyFB6{KhwbuDpqeEWpbgH5LE^$Zi72rT}D#gBRa*uv zw4JKjifLNI8yXT-UL(;0u7x+SnI^og6pbHshh$}7whCO<`pc_oh~g0yahik{a@-bg zQbQbVQ{SY9P)Mt{GR92Cx|5t+T^SmKvTgZ9I6?WgA0T%vCUSzI9hgy=t0UrMAUY&b zQWdQsKvsq>lq}Ri8UYI~i4KO%AoOMFhgx9aY$kG3 zH!r?&Am=Mwg4LdN+(PbGd{)}0@D>KerERk51K({{U`yEB@XJA~$pXQ-mwaCgEgj=?9#h z##mKErSPqK&OKhDv}b*&zEda$x9`7Ia!NyUF^lYnH>99A+kDISBIz6fWir;ms?XUB z!o*zk?9c7Zg_v`a>dS1UMXXLwsq-Z9jYw?eCyF>*q34_%jlCZpp#Z6(}#A_zn~viP!`n9pL%Tt{bI!MPFmpX2RQoMPV<*VbET}hcgT7q zz4UeQk`#z2AqBdhQpA^OAy>Jzv><`BO zk&4TRt2#**EJ*ad4a@`*c=zlNPV1ja-#ZRYA%4C6!6~#ojkN>%z3%9HALg||adu;t zzW0^CQXi$xiB{n^^u3Qg9joun6XHO6y>PyetqBjM?>z+?%9Q%vP0gL^doAmOsl=MI zM<6l-I4!)ZR#L|;NcAe{)tfL z!i-3+tA&6eGKk0D1SzTp`5-|nelAi!DInJ2Cv1{%KjRAiiW~}WY24g@FVmT`3Q;x4 zIE*RUVy{Z?lg-r{721T^c_QZBsaTzsG03Zmj=A;#X;J@bHU|<*6CSO4`@V2NWf%-I z@4AFj8s>OfBzciJEt0&bdgh1%JZq3UW_C2xi-u-SfJ*#=9IS015A3C{np1`>$j@75 zpp7071;YOrEZfIJJbi2dK4_T_&6CGHm3&zDHDr#Oh@V1_%uy)LsF66M3Q!upz#_}5 zE*J667gAtE*wantVgx4xjd~^oewVDIl zQBPT@oFL*6uP%QPU!$-+*^WSYiWhe~u$|6=;$d6cPS}pG!#yX!+wTzpeT5UlArad> zuup;+hziZX&jR(c0iC%4ooS*o+W_f22K(e<5Uz>jO{w}E@M@@|C2&m!VEK4jk6&y~-~)H#--7w1Qzi(npONSe9ki$fzl~Mlt$X_)=J# z0TNK#*VFnK)Wgv5;hOcxIeDBU4$(YkL*aQ2$hsF)HPl}~Wz)*$@UCTQ&yU8GcJmW5 zrTzDB_LO%1c`%Rgy~6C(PX4lU5Ri?i_ickEhT(bv4|QouHUvP>HHM!^qd>rGK#*Xo zdDS@<8)1pb=${b(!r1r@spN;KUV2#084#jJG6Rvqj3Qs79x3uPFAVlfi%T#jb}5m3 zCqBrVF>$pDE~+Tsm38B2IPmJB&3*taS5|qx|H1P`FU*Stx^Yr(y?kZUn}8SY?m2Wg zVaNNJg5f6f{(bRg^g3N{uhZLCAsT3mTZF!7#vLn=o9Hy5-W&qoS6b*@Y;@`2|LJRS ztI8Ke_22d?sDzi!Z+pr@{CGr1zdyB)!!2_jv$$n0KglE?!dGj}ybr%Z(I#R*4~a3p z7Er><52KwMQvMuOd3^&K2}|L+R>ZF>3d5N3N=O1T+FJGwL}uhmd*uGet6+V_cg$~5^hcmM*8FyMb6F82k+8m{~ZBrmT}2$ zJ^Tfl)hhtf6-9>R1tVAFWq?#gbOn4pqCuw71A{kevx7_%$<))=A}CEy-=&9F27D_6 z+ORtF+drUv^FnpH22ev&Q8`$yj@-?h^`51RV7C*y9xC6NUUF+7+@!ORJB5~3A?M&F z5V@%>5IF*s2>M#J*~JWE1+cGyO1}@85v8vgY4jTeL(PD1Icq?*!KKkkYzTy#^>$nz zys|YA{t?=WTOut4e+9^mj(!^#^taJHNaV^4+}9Nd@78_0X=HdZ?l#%y#^d#Ncn;a6 z`#`B6$Q&H}qju-9G$O^tmi1quMZp51iGG1wmf=33$QZAF`k0(jH%3yn`F@yF9fDw< ztJiPrrH6-LM3;Cn(3|kI;3e|HGvGIHM!tgaF|u5buIB=g@fgDwwlguj*7i*+8E=(5 zGE#D&8H@z_W&{SWzUgcv@2SoH272LuapBK_@H(alMuzvzC<1kg1~=YxCZhDxW_#Hk z=_^MLE;+E_rlXq&;x(>6+IvnrNM)OlPjGOR6tQge*T8ZQunsl_2%zwo1;NPesA~B0 zqWW)o1$tgtz>YvhLF1&8io!n^)o%jr#+w0OyYBlkFc?n8=-OOYk;D`Awm^6}48_bp z7IA!is;7T}1Pv%g$N$DaxO4-@$~`!>4GA1SuqZ-r&+wpYAn4`=dj6>nKlPT=$?zQe zq9TD!DL~YUOJ89|=c#11-6Z=bxsE%=1?K0M|6%=U5^w4Q|oo#yV~sA zVUIwsg8JY_ZT53`DT-XsS`?Yt21O9qpf)!$oD{AUUOg2DcT+()kauFlT^nuV}LsyBY#E= zsjgI{`Wh_)Sqg%KH*4iQH*1qCfE8)@BjGqtlGLBKZ4;kMUnIl&7FUCTyv-Q*A4ip3y*0)AG91O(^0fNMF#iW(Dys&e;Ge1 zBkqNM>XnOOsNa|s4DT%BnhZAJ?*#^%+U${_4Sz>wcv^bR4Tir5j&*bdZXPrbVbL;N z;hSdgdf^G>cMvfgzmXX=fpiR8bzVge_d#*DdjKAc<4CYcAG}7J14AM!@|Xp>ff(q? z@@4cES>)SL1g2b6Ue{zcj6gZRkgqp;sS&sM6McS>&CpykjDpka zYdM+WcdIp@jn9X5dl7EnEpLH`(sJo0a#O6zb!|58x4^jjR-1hmD;CD3Vz>ZTGUVLQ ze;>HC-beBH4chF>?8wJ5vf{0}(GNG%+uWVJ)y2wN48F)1)mmiu(}7m-dR|(lRbCAO z2Qb^vO$lcloVh_}W7_CE?M~QnrE#9sW>=tyZy6;BH`<`5J?%NJsFGQU;RmGGAgaw<;0bWCt%Z4F>Zp z1cKkTgW*4&!B45+7bIBnzJ$RvFhlI+k3(P$?}XnOQ)h6g3Vs&B02TdL7BYB(3Vut1 zal8U|ax(aA75tnXdz;yl!CV6(?^P1K%lvXU!uqSQqfreEX#h_*KPJt3sqnrk++5DE zR*n|K)sFP&1G+zWox$8=M6hX>w$Yr=;AJX!t;GI{JvNiU+;BwfSFK9$0G{npbvt+?;txO2*VFU5KgT)DKj7# z85Bt0`Uj)`al!Dapzq&7?JTS^J~guo6|)Oy{x-@F;WCsimxy2D355UDM8<}UOwH31 z+zBFM&uLg7uG|gI(Fy@|t{(XV>UX|XziAhM2~N8lC1C~*s8Ry(`SC)5!%{iJ-_lPv z&Vup^%Br4Zk?pHXUv9xKOl{PIfq^RHYG*G7j2&0$x=yXeIN#+*rypR29F7n$1P-rt z2p8^>$yguQZ#`8EvVui+*p^3$pJwx)cEA@1KnH@Sk7+f-5*p{$C%LY{+6yxx zh-2=;c2(#7t$aJ@h>Jd`P1gi*T);p5YB19Ce(o(=Q!J#{%msPy;b66x%EuR~b+7@s zlwYvI!hOE>5v{Fe1E`71KGCcA1`l{pWb(vRt9j;`jD0hv?KY4w2Z&y7{%;4zf(^g- z$UTj$foN}`Otj6B+qbkkU*CJr*1UNs@kEpxUB}b(n$wAM+tW0#pTx)RYje0Fu;Xxn z18>wT1L2!cEAYE*=B@*5zoS~{De!OQ8n;o_Kh=0w&ZbN8E9z|e_qMa?xyk%XZ`I42 zAsXDs%m%O3%J0IDF8Y_w0S)0_dNESa8}2GR0_@*G z`7nIlMvw*fUsO;Ag5Ze@_V?<21K&CI^~Mi#BL=7Nr%&+4!#!*5fO!ZIofaL@?+bO{e?+Vp_16QE8ijqT3VRKYR|lXzrqBM8#)5F)D8J(01pE0gW%^h<{XWWiHlOK&aZ4$>1 zUU-lEyn0Rs=${7+${dv5fX9aDdIo|%v?g^Whw8@j63Nv_Q!kmWN*m3kEwF_OZ*AGY zqTmvS>S|buB2`l$0#@Pf-$vfpp7me^tS&M~XlinSDYoMD#0VeNkA-@e=i z$!aCm?7K_1L_4p7_tu0*pf0WBxoxMoE)3N=h@F7C9pY z-Irpd#Eg$u6354kMzh-F_}Gc@VULZ+kit}BBkhNHl1{CpIFNv!j`%KOo_m0A)9ig( z41InH-w=~#)eF80Dq`?$k(H8_B_m;(uhXCrP%AEqoS|R6!9)iQg72T9f4vhjpGVHq z&sxb)eem{@qjyxC$_F@$SH_tqN`ONsxt=D^Z7g0amF_Q)`dpAee`g+hAi*_vk<_N0{O zi&ddIgp)Z!4z1BGCm4^P3oipR^5Iot8uKv5)To+)qnd}Ej3wI<60GtJtDFp%ZwaKj zaHv)?b3OGCC2D^SCW8fF;@Cv>FTw-4srX@k9fBHE(Z*c)GN#7-VC!bE+)xeQ!%{g| zlkqDy=$IZ^E|P7M+NcT9Lprx$H=!u32`fb&L{o6tPE%+Wy+ zkN|#wS}vzQbnJn-j%`67z_tWSVfP^Zh9?`WtPx=T@hUNW=82_ZD@q`DEF*{?(PGp0 z1$Z0^!EG(@LpwZoA`6*r7cv<;yb$%F54LXJpS7Y4RpVMjzkC&ndl)u8*tMxg^`DWS zy_V72-$Zh%v8q7&xN-_a*j6)i0`Av;36zPh#KzEn3{Cka4cG3d$o24-cE{_v92NNS zl6b(g7STr5bq^{^;n@G-q|`}M6s7XhN_=cY0ULp$&dGrB^@!^@{-*XX^fYP_Fs4BLIaEg=%o=gQFUaifi#INb zZ*0M=gtc#EXeJXy-23xUZw{WM(bvetx^3FjIy7V|#1Z3a6H@EUt00M@&n8duYd7H+ zogoeQAF4a#z)9NmD<^3st3Zm6o5+wId1I14^i9dgNrl?$E3}vE3W1gO;RC)Z48KR! z;D9#$#|;7Fqyp@<{D4tsxyj@zEo~4z#gx_!KW*ljrLVSNGfvHy$f=}llJ+qekQUkq zN|GFt>Ajvd5ao5~dcMH1K)qNTXM)=D_0`uqsy&|_v;#eMbM%gDZS<7lrhyVx z%eC#_$Oa9nVKof*cD|}XW3?y!3Tt4!lv~F*PF$<;Dxvzw)qMS@YMcS07sNmmRLIlO zJO`)4fYTldr^7NduL8d2RU=`z=2c?@42^>&-v1Q1K|vt=tg4UUdh-a*1Qodr(gat{ zeP}hhGhbZJs>uLD10NAwg3*Etpe7&h0)2J9(IF)_yvrGV96ut|k8 z?hgGmiVad7hOq}05_K8he53#gUV5dhy9FlZ32+i-MUs^4O}gJ|!jm~@!%DOCS<9s9Sn^={zGOo4V_);!-OxL6p`RYwd zzIZeYvO%Jpw9@|g@SgpFD&pS_vA>n%l97AqNffpz%F!`u!VZBo1qRs)9gKERUCS1$ z(UezbeOCr}*v5P~w%v-pgmILKhsYhY=7SDxnfb-(+&gO2VSJ2CsWW@@Q;y-KIeg^~ zY-lzLqoFtgFts-#3mo*AE^l&w1I}Gcmba=!@=c8Jaqc3Yfu4oBJQk0YD2rqG#}SaKvpOW_vyWg@N@b<3khJFE4MGnt zT16AniNVGP_*z&y74KY4UtmX|Y(eujoO#196#)s%O-CUIz+LP&WN-=x)es4@#6`4F z3H^>~Bko(H<^G7>IVtx($(G{XI4ReIX0Y5Z>e z_~;-vidC41jx^5t=VlBRvHNO87lY$++)-74E?Xo$m-QfOq$XOk+9y-2yKLzeeA`>A zI1GbvW$52XiUTECe-{oQ1F^7{)G#ORFNdqxGdA#}9-Pwc_t9$>|Ww zsAK_fTjc%#(?HaI{kaNZ{_S_#%)go=6ZY%>+LTzwm^)81mxyuOI$eC+np#ob5nTsYdu zd}*EgQq{;twf>99m@-Gh85)L`H6E0SrHm%l#N6?+YEE7Z^3@RFO0ih90sEKVn%ASW z6dW-d0Jb=L)lC+MeUNPy&c16SIlIkaZ&wjYZU&jXa~y7X7Nc7d)++d~5Q@3qERSP} zl@k<8Y#9XIYLA(IN_2VVryqjJK~TZvAg9K7rj2qUk%vSUDV`8(7oK^9bn3obC%cAJ zM(LI&qsCn%4@ftPaOVjIWW6_9t%A8W*CRb5izb5QKma`n+mV1v5aso@BGjbeI%9pQFHFx0T@INRcApZ+n(}O(c9azS$%NVCuwP z&oRH@jcOoZ4wn19uyVjpNN%F_kl3#>Hdet9)!|N;ZhI*JP;M`vSK>N(p)+T=DsGe) z%c*mdyj+nxRr5V9Dh$nIA8L43bQ_AL0$5&lGbCeX2`A0P^wK_2%Vbh-g+$ddtyQt1 zP{Q8LF^k4gjIjL*tChIDmHGHPKsgY7U65ObKkWiJkAJ)`$m)*-?<0g5!bmJ zFN*KnS~j3%X!&$>@9*@8o{jB@9P|GDrCeHa%=?%Rz~>Il zH=4)`07yP~ldrrxSFJdJHiDo?LPddS(qT)LwWQ6K7Y#sjqtbP;&q?SX4kh!ZAA+4iBv%aI488fL*2QPuFEgndXtZB2+U>sXjievM_ zKav^q`YN27bgoySg27+V0$qw+UbXQ<^Z>2Y4EMkJ(-<=qqDh|l$MY;EOygR7hIKVg zY~xdddHmaCzcU~nLV@AOHd8Q%YES0tNmN>>A;enCjsk^=Gt2M5fVc_0FMMAOi1o0j z6yfhd$ir#|&&R9~?fqBn2x8x0xG$TT*!xWv^b&W)Z%?xQ6oXT5zfJiqZkVLqxHF^W zV%T72XiM%S3Q*rW2?IRuRXF&w!olMT2jefn-D<_qp#}@^$Ddc}zuuyPIpZ>RKPkXg z2Q%38Y)1=o$0!8cF(SLU9lZlGXTTa=t9Xw+7yiV20vD#BlVIk$jm}%2Jcl}=84YPA z!-Ouym{sS1&!~PRBR8*i(Ut^#5DDrwq4N_7-5g~8bnvOzuPjVwvI4VKv6Wa)F91Eh z?n~VkB|le#P!QR!^3%`|f{?!e50s4KjC3Ko%0Cu)BBgY_*|vdu8-Bdw6c_Ij$Ehk* z_6%lY%mC=96C|X+ywJuS?zW2J4r#!l<5XHu*!o-1T{G*X@9$y;5%-y&SR2;f_N*7L z!x|0J9&QASh-cG0NrTq&#vJI0Fatku2cghh_)0&dH)r{Tpy(S&APMMf=KPDyGC!&b_8eo6zZC^NkgbnCsrVWt(=TEpoc z=@ZT`%^$}Or)yP&k{dAYe|KBrP+B@Yj*-qepAkmn*n)$aTd-kJX-bdHZ6)DP=_6&9 z94`k0>&;py{}pG8n@3^@x!3Q7kY_GSm@UK}gD#q}-=YjM90i>vE8T+TW}`v2{C|zr zT$vh_{QpsGb8ojJ$VKdiJNMT3CU;l^ymoUO1uD){6ll3t$v!~F;LCY+#EeFA@=?Gx zR9hGi;ThQ2rmf&k^N4*>DY;j?415yzk1|}8opH|Q zgz}l-%`;G`S2-Cd-h3)5CFApZVPOQ*qxc%-&4<_#)-wY9cTbO_ZqX{gY$oEF0iZC( zxSO^^!y1Hi4!&u)4nMaS%J=6qpNtG3LVvQiRpJ9uB5blby*Wc)WTTvDjaxPd)NnHZutQSF2bfF}WuPY0*FEn*fycjht(YM`h^{FmBHU zpSSjR0^uvLGJ!OUiBE}$u(FxVydt=RzaWDvLG7L<$&+gsWSRqGrXqCnAWWQvwdAPi zblO2LIslDXRgv4kKE*%U`orY(mK{ojC_tWA8I_JEJ1cOCgLpj|(So=L`pnN%%*kn` z#YOEa0)sbcp^Gu^7lmhFW#dmHs}F=th^p+zgslz8Fd($7zKXFT@_U@#2>UZy8Yq`f z<$ggKGQsy?HX}*4% zGpB#axu6=NDuQ`L`7=b|c0B^1KPFFuez8p6j3#-r?^04r73=iv`~pTvyi2K$3499| z$zy(vJgVo)!z#hb+RD?qgGt@UnYDubVND;pv6SUQshx*tg|$i?-)VaKp-VlXMrno7B#)|Ac~qxK_BeKw85|4M z{bZ=_vDglIm^m)emh6JKsaxf>%UsbH-54HU0LGXHQ45QE*VP|0NL^~SDwL0WaWo9~ z7LjVa`iPY=hrGreCijQz<#zHl2RF0tO~IR(Nlz_?Yd##w9O1~OAm9#F>Pjx zQhTaWb-t~VL*QykS82ncu@bF{1PS1FcD`Z5?#e;i+j0GyR#8hifw!4!6$|l`XNOR| zzc!Llw`U!41&m0l5FRs^okl8x9akVnW|r#w@&fa{hHV|rFURs{UQaz2mC}4K<(5h1 zoPl#YDsnI?Vln53FSiDcC~suyYhM#PaAw&NWVgU@`)-UMI31?Gx~XyFDEtRCj+$j| zm6gBRTsBYroCByLSE$FG!`rx?ZbS3A&@bZ?jD;csmr~#|59V%=m(f^Pc^=JWk7}N$ za~H_-S=kfUJTJ+=}1K zIxHDOZ_5SspRHD1c|Hz9$$+uwJZlBX9;dLIh&6L8uVX*4f2Oh&N?=NT!+r@NSF2xo zFyj!k+2=_#GPJAkHOkPMFSQ~JVg#JuzTp@8#e02RzichC`ehTyr}*04ve9ooE}NAV zxjD>OF8jEXQ9u~>1!tHtJV`Ls9o7ucz(85^qN6G$MkjrQZb9E%!M=eV4HwHfBzG&) zKunwnl0yrFOEog5Krb^1{Wig)Wx_IW)MV99jL3fSvVurR0Cmz&C%&)x=^qoNpTN;b z_PGA3^K|SVXFggati+j*$Th(avhhfmkJx=H-*dX}ESAUaTf#cZuv&nx(e69Sj!?x2 zhI`%hztDYG|0%BfPSCCH8_M)DtQ0H!Y6C~rQZ+5bY&8>?|2L)fFJub4f?JF|xfOXbK8LUaMRF@>alpS8E|@E4@hF$H}~&@UD_asPJ}EFQ!`Lp9$? z&(2!G{rM4OcbaWkhq>7D$xbsDYce#tRb`ik z=rEd~G=r^eA_%f?qO6mL;H0w7hJA>=@dc4F*HL!3=wm>Yvcol0$tl_468y9fdrgBv z?8b{BJDiR17*#O^(Ck`DZ2Ik+X}% z<_g%cyVeUHH{iFz;|f7pNWsL94l(AkmwY$@?GbN9(8e{Po0u9fWWPe_o+yvSQzdF? zqvrclo#wl8k>(p;15~Tbtc~nBL(^(3TQ}HzyUeg~+p3 zko3|O(1q1lSwwO7Y=+>jN#&*BSEb-rE%3{fi@$JhC8uaDQ*JTGy_FfP5AH3MZn18# zr_BvTCz?c!M!vXZiz755)`6JrZ$1J`V;)2*fS{wYsP*;=cbah3kDT0q0 zNIQZxzi+!L9`PN-=J1tq1K_*@H2|6u=lArr1pfaw^ZPcJAh%H=R{?lWR)-1m`%}m+ zW_~Xr(@ZkI4_U5I_0k25&h@MefzIalrBK-J(fl5GEs7{xF&n%7>dyRLxy(VAi`5Q6 zdW3sQ)SvwH8hnkS>pR>=DiMVL8SV$Kj7OK@KJD^2IQ2XufuF``Gsp_fzpoa4`v2Sf zzWj{ebbcS+Q_!jytf3fecjx!E9>gAW8OE3ra?$A%S_P|G#bn`!q8%77%ur;~$~uMF zG+g}G>_>PEzw6&i0NN#fx4?X^Hj2*f&+lz-I(RHS--6qDGLaCEpW5?W97}iPmIIkji)EzF>!uB=sm&X|HJdUKIFHX-`|6R8*=4uKEIbBUu%9Z7iV;7 zGO_m)wzAdSbaDK|&P#7(V*eZ%OqkexI8c>1u^%KrwkP&Jf@OPRe`TqH^PqD9=dEgD z|8Sg4?5ii*6Z;B|w^;q~iRlcOYuh^}lV9`A)r%AHt#&SoMV)d``EA&Ppmo40sDh`ua78 zq!V#-pv*zj9H#IVzD7xUwjJSI#T2$oNF-_1xHytdI5nQ6%`r?tCMj1#!X&j4&2ko@ ze%ys&Efa$XwU&xwApfUYdzLmq=!f8cB?SNR5d5tbZ8z7OC+2r)J`y^|Y1ab=9BisZ zs}j$t99jGmx-#W*Go<8wxgZ;2KjnRh{n;GfxMf~C#`aF8+xc!Y$0J{`g(grBh<%1o zHw;qP!tD5bVs`kEQGIh70W3K#b*UUQz61V*u>rWqgW3 z{BDvd$@li%$10ar%$iWmkBIts=;Ot?YrBk~#}>yb-H zyI`a3|L#;04tB65uw{dFt6018*r30u`8jFGj1AabcB3Qs;kiNs8#+U+F#x$CT zA8#4W@R~J##?3g!m`1Uh**RaVX0pXefIkOWW7|-&_R<(lV@e8yHu`r9c-+tNQ3?u$v zafZ!ovWTz$z}F~eIM$BvzL3{FE6N#;M+goW-1ZPyl^ig*VQd_aIPFBmBS6wdT=m<8 zt9~)S#}kXN6pN!%31(L@x2R`XwD=Ikx(j z(kvrttKVacOfu)gR!$w%+WJ2>JK`FYBnuWKcg>GuCLt_v-atr#t^Ohg#rV1h%>4x* zRUII!o68peh~=-p4%YY$P>nV6oM`Dl87bH+|Nbymt~488IOuSLv7DDqYjI0Y<4R|g zT5<|3o_m#);%bC!T#b-}ix80cB*HtLHP97(4wJn|tuCR~%O|Pq+==WFTG=>5qa%8OCnitJ#QZHV9$xb;}4L+4QsRd3p8Dq|W94mv!eaProyv!5AfMjnD7n12v(JMLbu^+pL7k{{Wcqv8y z?+IQu{S$a;C?#G_OOlk`7>}2J9b!3nNv|O>9lShQEREj|6EcOD`=s#^*V^Y3>lkwu z=%>e#Nw@HF!c(dE%Y%oqa=(_pOnHL2#PFB%H#vA&cN4xP<1Yimh)(gBpJfoaH^b~H z%3mf6VStx=hY88Dp+Z-9nesyy@sf3C_weFhl-L)k=cC07ftPtV5--^zj011Uh;)7z{@2CLNXH%g_kq7cM&g}D!PZ4 ztmhKDGc{g9{{UVl-$1;43h2Zmp}e*uUS8@D%fZVNkC2!Se;F2##y<29AxFp)!cv$H4?G zikAgK7~o~iU?JJq(`>vv_gxq9GG=D?@Df7*?+IRhcp7-wa1HS?GD*^oc**S$%fU+~ zHi%>J@&crM;H4)>qww;CG(O_`>aU4)jFETyFq3ZKWuuXbzdVhdf?to9nGZ3S7`$Bm zse_k|yaOm1UIvMQo0^Z@_>J}SZdo|n>*?u27~th`sEt7~FBI}>KAQQTF5=~I+}b;r zm(J|rdV0+K#J)%^zr6J%@Umbs@p4d-q#g0{JpdLDNCz)p-A`gV{AD(zd^CPNNTcv_ zgET(kdOk@O4uAQanRE*;1AmtaFD3i4a=(_pjCzo{#Ng#b7-m`W%L`ZITQa<)g90{x z`IihL_i9-<+j!B1Fu=?8Ckx5i@KAUeWp)uSKitwiyd3^iVt1y-OH~cIaxvK9l!#>+w(MDE9A;cVk2TL=TZoC%dPhFAk03NMFz+eN&* zb#wRd@&o!mvC~t@yH`F2yy#aDFRvy^+L6CJ*ddmKms$73;bq{N()b%e8ikikX?((@JYIqyn*OU7TGz`(KbGD8NDdx|Wa zZM>LJ;)uMf4HA;g!$aX^GnVVg_NO}NcdJUfhnKg||9gU$lOF|MvM(cE?n;uhBVMlP z5X-^Kx$xW(!(X;R%Ew?ii^>GKxa})xe8e>%Nfr)X0?edacp2E13NPR6%gX&){_=*w zTw?I@)LI8G124z7WO%s&1INZoPzI6v3|Tnac&Qb_fWLfsoRDnt@it!G_^OL|x$?&D z;iU@wzbANcJq)~FV9QkBd)EF zB-SxT-tEs!x`me~uStcMm-c4mel1?^p{YzPUT$3D;AI;&&XeM$aA*`S2je&7-Th=N z*?6fG!T>M-JVr>Cf2@s{d%o-2MM z@;JPlaFR5>9Hdcr86b_1xGqhSg@c#7nMt?svhCPZc**VgTi|7P1#^kP%hxzbYEgO_!X z@*%$*MrDHFrCu5j`=>L{}Q`U76FIiV~ z4=;Wru`g1~yNmAvUgi}OFZ(1(+7U0`0ATTebnvnkW}PwoWg4V>;AJ^Tqxj3E(s>C$G7}Faznsz7MZ9dfw0n5TiX?VtYP^IhftSgnh?h?Rop>Z9+YflDLo5d` zPfU-)%P^lb{sE9i;bo9C9`-LuvT*S7C^P96UIyl*;x8w){T6uHPliN{{IY$igO_RN zbb^<93>+IT)iQ|O_sGK8#!H3}26#FC_d>E-JQQB`Tir#xEWRWaUd%K!K6>HyM4mO! z!!+~9#hJJ?5%-@12Z^1ZO70EL0dDdF#LbgQl6K@Xw|9u;;O1%?CPw9$eU20&TnrK^ z-0XzTf_C47lVsuG<_cy)+FQvsU0_~zLnnADpndN*KM0-*c!>fmz>9y$9=rf_)-8K*mO*>)0`m=6LdWr+ zr(U!0lx+^gy_rfLX7|w^ybvvdJ@^e6ST>%5GRWL#$imvjQ>_pTc=|G1NICgH8&7Xw zIo}0&INQ8(d?$E{-uI3BAK=lLPT_E+6waQV=P5dUbZ$nen>SPY-`b3`5x8oqDAFr; zWiQV@@}jrnf^G&zBOc?~85a?bkZ(tX_EkY#I_D{j%)wli2IF_w(BtGA9aew(jGcA3 zG3aa4W8&)f6(~iLJK)4>hnxp6W3wE!B6Q>M)7QCyY2gCI@ z|CiU^iG$kdJUv7QwfEvjczfBqWd&usdlYK-*N3;2t!p<%v=_dRQMhAcQDjJNWq*zL zZhO%+@K5^+63{;_$SZzXm*Z&4`j*OZx!$OI+6Aam*)Emc`m*)yyWe>}Biy*-E3ND{ zriNo!!%FQqW%X5jraf3!m;vXuFbPR3`=3hvDJrx3U8LT?)I+V*=OmVWRJ814IxV{$ zUs!f#V%bMW%jRL5jLVLVjqE@jA^@TUD)#&@ZweqL%#=G4Cy|3uS`tG%x z+bilyvRaNTD&LYmZ7lq0-*^K-KBBT0?nA)^{J2@sKd8=JzUc=>?6oMprH6Q^<#}oQ zW_QEA0&5OPQ{%B>Z4RRhjMnE>&}9f}xdrb}sP_}Fnr*onaWBOObI98q!FW3n!9cEg zv>mVwpB*o?SH8n&Hb@*FwM!5u#z*ZIXlq2#atgwc=kJ)s0My0|*Iy6ILy|bkI-HWV3alQZ{;T`oMOX)pRFid_`~K$kaQ&k6inq1U zFkJd5n0H1>-#21MO0T3Nr9Gpwk%BY5lu9P-yW1%On>12HQFv|8w^N(F4ev$eJMYlu zEX1#ZmoZK5lxgx%{Dx=iK)69WG_7Fws)PX$ogXo-)cp7}eiVlPS12=NJ13>O@Gj1f zXInF*9yw%qW&eF+X2`SAV2%J>#v_z}H*to9yTZEI0nk`j))+GY=;)C>hbx99{X0zm zpUTm>K7U1>Op-tjbXjno-o^?o8y;SU8Wrwtcs^rzxQ-JgR7a8pv_J7xh{6BR6ji9O z(m!OScd7vVJ5_)hBSjSott`$@tU?U@UyX%-0u|D&^nDYn0Qg%~D4o$c1(u<8B;p5n z68=snBtw5f(Q~Y{u+T+XqQ7}~Wgz!>r(mt(bbP|hxu&439W$pJZ?W({?;RWdCH@va zXHRW*1=?CPxLqs%Gk)me&K;Ku}{9ts_Kp$ zdP|W(27Yhud=vaXGRo833>gpe=tXK(wGEHv)l6=%fFMkV ztq5w_2R~LJkkbGL@$2=RG7+{OAI<0NxA`iDS#1Z5#y!uk(qAVJ{(cNClq4F3XwDp6 zFQK+4`Id|*R-O5JFT{_Tug{d+QQ0}FvRC1u=Ialkw~(3qS@p%>ms}KYz9wIi&(|MY z(z}<^yMFqG8Kr$#-O5+UiZ1lvo(_oa5bg1^eVHD{!--SUi>%0wgGu`iHEG|0v-ooU zzAvuC1tvo$!p{#Q+HB}#yel?TnPExmiVb;1S|=Ndy>Bj+y#6yX5P7{H@+=H*SNupM z_PTh9U5^}U^P?OP1rqxiNJ4&8jAz_X$gw2IU*Jc(Q8tH?$4c)og8_cDnnL?8@}t+R zEZ)a6t_lOKDj0**Jk8&9Kr!-yO@3j&usvDo((ss3gB1-pG$~0C7&o%dkWx zK<|f0Mb9S~1hD`wU!n#{HMmvEIZDcj4uoUyt_H&M5N2fA zYR|`9wmS21=j+vD~5=UR>FG`tf72-!!_JKGWGc{-xUqPm9Ht|QLxXVtV?yBjXB*i}|#V)a>*l|=C z;gVa4Su|^9D@#(UO;H;?yF%1P`z=bx=^;nBg%7S)h<*2ZL`~z(gp3-Igzno_(6I<= zxeZaSQ|~|X{aT2r$KZ$A`(;Xpc-xIPDvfq5u>zJNz!6CEY8jeD47~%fV#Lr|NsYqV zRbfqdC^2-D{pD~L1m}*-J{L7LPm(Y1iGLSIUnHty@1VM}Tdo5UimuZ&KW+dNOESx$ zSq@ccZ7-81T4)`{TKO40N{`eH-We1ZuMl-dF-vb0@*UaySHz_g^c*3sIMyqy1p?B{ zcV?>GDj37KV4_4YpPtq(Wx$yigjsZX5oJ~pcS*3)=csf>h!TnUQMa~>v*-!M}Im}ZlgTaUr{nZn3DVqOm;olze^?nfj3kzw!gwgf+WG=3i^(185W`Q;L>7*2 zDWMKoRt85V)*+UkD*aYxDu#8)p3V3MPcpP99by=2oPH~z4s^uL44$$waOe=jP-k|& zz=xrTB*%fBgz~$1SE8ti0w$2bGde+h zM9_j_RitVyg&Dyr5SWbQcpOD5)@rG!wc3Xjt016g0+<9vji?p1)qt-vj$*(H0hPJG z-`eNQBM)As|9ijh@{u{`?C08Ruf6tKYp=a`jhx93e5o4|eJ1WFygZ8t;c@{FTdevU z#Vuaz#ABx}APXwv6-X6YZXZcU zrTI8kt-xnofmMTBhNPK$5q(L7TH0)afl>ug!fI zFO|61-tWh6x)yo`nS4eEtd|9tEb5lvQ(uaov@|UA`3809%j5%tTQ6_XLigj#=>Y5g zN#7^1uZ8}EIN}IB-~+aR9xh_>?wEymMu_QnP5#k6*Af^Uufc!sj^rAiVR=#mPCCS? zbf4jnP@GB^`9YofhFF}IIWpgVme^07Pb`c~Xc{>wEz*j#Nl>~kDIBZ0$Eog2hF3RL z&+(cQ-mOxPLV$fFu0nU`j>NqJSCnbFmtu)^R2HwMUC0FJL@6ZKiz`ynBlH>QwlcZ& z;8L8BhcbXI6gUd>r7nEI``F~#(ZQ%ME7G5>4CgfTkwNu4Jk+4lu$0T;=6ZuMm_v*Y zyheld045~oo|}~QrXK89Eq_v~%dXV+=Qj7Gv)J~lRqy^sX4*R_CUEAr&FFXH+qPeU zl#+Coy|-WCC|ot+-hh|A)L-T!3-y;L@Jtb%lP26<+0${%x6BmR+4~ZN%QTl>{sxTlKh|I$tJ`>NS<$ZZNIwPF5(fph`2#JX}aB&{fcYmoNlk` z3_{X$y9;}|+s=M;=j!)hwxtc8Rez3W5Bdu>?DprVT~%>>rGc``#+L~h4d zrzDiiTTO{Ci2l4&q%DjulK8;+-thfvSpwA@>+I_&Xa~+fAlbYGvaq*bVqt-!pc6gd zgkx^Bg@yUA??PCZ-b?sMZe~JQxK;)AN00~$HR}6wep4;V!;97aXYv(%eSt4Z`5!w0 ze?mZ9SQr4Iv|F`k5>h1z3y(-{@HI3C?|3Br0|Hzntyx_thrM#fkQAnc9;J?rlifN&*NNyY>X>CYOlgcf95^wUB-M;zjs zZrBsBNC}SU!DE!Np~m6UkK6LaO^k`l7iCW*WU^$YWi4Wrz zX!{1@GvJT~{;bCuv|70NA-Ino9C|47r)hP?60qHRO@sY|(0Qq6Mo9z<{#dx`r8pD;S^VzY523 zGmCD97xJNQP7lq)d*?g7qu1-14x~;CkH{!9 z=Sv0Q>I&<$Z!jCps6eJ80TFMz)_L3Y*six-%PlK{`Pr-4AIgtvd?l0v+^1y>-7pSl zS9qB%rzT^4_Z{i^Q$9q`AHhyo{FwB-nRDBC_R*TafLPx4cDrMaKZLF2-4j?j^hTJ`I5(>rj;5W5ESpD;lTVi}~n5f;@i0{qod*P~J-3YU-XpUeI zy+EXj2@3B?Ju>?yjdt?7U{a`K7b2A(P-G#ywX72N~eVu2fMCp3>k z8vo!{x=1Uk#xe79A1h9pXLsP{(GolmS%JvWT2ajet!VsYEjLug;M}k-kNIQev4Cb1 ztteE>@^VAd)z>`rRnISXh~pG-_u;EP{73wn9?b|*d_yIzxFZ~gW*6bm>>?hTz2IGj z=gxlx4f`IT8I)fbXWzmH5pz zR}inpiV64tN!Q9l?q#t~J%Y!1FOZuh&UWIQhfrIT62E$N+d|~DL1FLv4 z`ZKtK@p7bkPS#J=(cbH0c{6YnSy1{V!(Zu)p-J#@*cwVh-Rb@l5y+NVZ{Lp7$uY3- zp62h58W@V>>R-5A9an!EO5*JGZ&;SAL!U~0Nzzbh9ct}JL8V8=^hbY zNPx{s*d{I5{;;JGjlq{Wf2l+amk6Y5z^69nC5cC1JjcUW_+yU9H~`QTdXKGb3jI?( z-c^rx_`q5Ax9r5gQ4FJUX@7)SHIV({Y(s9`^_h$F+1>DSyaYp34Ut`!n{zHxhUd4U z8r%!Op0b9tqV*<7>~VI?9u*^u48<@%ieWzMvynU#-)3u}g=~hoz=rSriv>QW+YNj# zGi4k;Dfstq3Va}(2@1a57vsRklz@-pBG!~s^pD*8qAm590Ly*Jkx+ln=j8z2rXp{u$|7Ddaj0sLx9-|rbL;?cx6j`lU zh%ZlF%zv&dfp9|639Uqwv5z%Kl7(HXOU{pAOvEoT~v8h_qGk(*XLga76 zZy#ol^&*eL9@VQbEON0{|F6Zg$$)Qb5d)SYVaE}^gtpr zE{Kx|TFDGf-Fwrnb6HBfU2KT;uUDmA;I9)@yBO+}y*KTWd^R_mR++?Y>$s-cGwpQK zvEXH$M|9+Y??tFONgV>znPNrYR^tLi! zgeD^!zNiO~j`m)USJaN8iE2gRcqOHGT zDGBa?!mO8?ROgvtsmTZv1H>w8+8gFaQb5^~&t`rX4V5M{Ce44kL!P_WRlyy={39Qb zA(*PS&lVwGt2;-ud)K1#_D9$Mnx!PrfreX8zw$rm`tyb)x?T&Vi)7$%f4hITdG|;E zHnNmNgV?_xt^VKj@07C>{i}tJQ02%kV84;2#QBb3e`14!eS1RrFR{;3tn@RJvA?t< zd1Rj6#*z7|l6A`VOS3Y%HtX(}{|C+b$r;Jb+OUny+Akt(`j_9t90@km3e=b(E{4M> zrzon9bG9?(hbJJ=2u+fxeJ5tcPg$0=aU2)3lxY(_GFs?x%-%4(A&C*P*i*{z3=q=r zWrVm^(j4NHh!wfFgldO23`aO;s zf@hej3C*^&wcr`z@)4e+psPSRu@#lbLO{|i901X2k?ml~|F=Z(`sJO70{5-=KosqO z&nAERXbIbBA{{N?|HQBqQxJW9UUvfoBY zpU>i>L<{{BBHaHiky~R@h&*s?_gv47ZUr;OZ)7qiGgB6WjBqMzq}&0^!(<_+krVKG zje5NVua~ITD!f*y*THxtF~dqxNJ1{eJ4vmdDdifYup(fKc%!hQ*gJ9;u&zwdhKLx* zPq`5HCzDZgg$jhm6)Ha5q0=Q$1Z{kI)E6xc_^MG~3)R=P>T8ku3aGCJ^%YWIt?FyO z`f9_MDa%ww_#1gIL7kWjN&d!p1z7A7Ac_=7x#wY|WYiBYv4- zeuyvxA`V3pO7cTIQp$kwOO+AM8Nv+l8It*|-z|}`?=zAOB70PtV-saD&PvGHCnW~G ze;|s<2W~0f^5Mw0cvn|`n=2$b?E2f;sv(kiPa0w}L!u$bXSFW2nV3k}Km;j$&l*As zLut(5EICa%Q5wP+h@N-j7e$G7vwIsyqSEQeZnJYBSj}(3C}o8g`SosYSjg{X&8Tpor%@h2m7h_QD7|0r{+#vHd%^g0Fy-W=+;K z37>d|r{ftJAmZ{K&_mt}82(-#OV^7AlsF~I&9je6&p*$Z1J)}CStA44Y3Nv&K(i$PMv}VCF zUeY4>3fAo<>Dik(+Je{2(^ zQt4(%(|uCmVd44A;qa0RM*Rp4hY_HUxd)J~Vgo+lC5@nI6oLT=OYR8(VFBU!g5r|V zDf6}#Itq|Gb9a_1QrM9ZO2ZeXJhqgy$R@&5Av_vSx-;N`>SYRm2&)o>6rN@&-KRO! zhG6^$)wp732(?WXCX5YE(J*s zJeHC{@=7y0giu+BHva$@fuwE#$6=2F)-c;<{qxlyVv&66bGFc;up^E{)aMj-CW9wH z5Sai(?W@qja+T1+GBH0;XaQ;$%Ga)DvTy+bJ&Azc1xtyeQ$zemRx9w@H*rgg|7vuh z3vf!6#5Fk4s{_Vi3>?M(ICS-DNG0ubkK$`xSF64=+t;;OW1{MNjsi&pIL}-wrCNa zO`+c*E&EZXGUH1gz@dNnqoV}X%ak#sSKthm#5634qTp_E62Vp1fTaY#FzBI{o=SiM z#aTxyp3knpml7lA7pSiq6<4CZlo&a`0$*m>&$7c8;2ncnwN8c{TS>Z1Z5`M#afb7y z{^QJ4z}P(G7WcmGzym?#*dUu~!?&##`V3)Apfd}BO`$c&1LBhnjLC>QWoz&}Y`OMF6-SfjvlTY9{{O!)Twq zyzD=9w-7{Tk$X4y26SnW(@;ku$1LI5aF{GWu*6r1jZ)WB{D6j%+Tx|>9KyiV3Dx~KXF^S5Cw9sq7OaGf z#w6AoM@t7yzVgWFhaYR{QY$^XZv6}qd*TQ>BRD`Z^;0d z-7`m_H*eDqF^Fc73))2nlPWPKffND2WYwwU*n0TL>tJATT#I1tLiw1)=&?%Z`7e$S zht;zwVD-%J3k##Q{tRq7Sl2?gE7udE)@M8=_1L~k`&@yZQo*r_6)XojjEo{3=RX!; zDSPHdp^7%NvvL`I$3Wg}yOj|V<2ECd^Ng+NlF%H%pDZi#OI8qe9abV#K}Nl7?-lMu}2HSRxnEY)$@?K+JdCFHBlt58)}Y zCmEu{V%ey!lSF)|KaVOREmBX2VyjaW`PMvS1t~AY!)HFuFM3Fg1gS8nI-I#$K}Bgv z@$8M(^S@w|mAfuv0lP%TvP8oB0`jIsT`wSr1C3Jvrk*GHfG&?vd@WU99Mt$~QC}tM zYrXoaz!xgA@C6u0GpFe0~xSXLYAI zwFke__3&Sa1FhI!SfVXi!08CoAhPq?z@}LJ0#52$5n^!&f!@jAdN<{vpbEfk^=j-v zl^8tV18^vPL`qT7rZGwLW5FzMg3x0 zgStm1KrSG<-+77qRralny@DUJH4T8)Z{FC#3YP*hFo7reh`U!U!dG`Lm5!mL`=)3E zt60`<#s~06tN?#R5qN1m9h1S)-66JUy8Ug(8uF;O$8k=4(Ugq^;x~dl+%PfmtsJ%SRQ(5k)~6s#@yY6?A!575IO)MEi3-Gf2&>;ryAq`>q4K7OA4<=)`O z=`UibUXWBDB93}O(aC$v|FLAHXl8-JiSmY3PC8I2*hesYas-=;=iwIvC273iixy8j z=(~%)Pa`d!y9~_7PzBm0sUmijx%;Nklu%P>|5uP_#qNmSK8PWrE^ zUqLQWRlm6B5#Vs4BTLAm|GTpGj^D7o0@soSgc9u|=x8k)QoDi;NDGwe#^MGB!}%PK z`UPq;#A0rS=wTd~VIss@t6`em+su2zFENqImJBRm3&4adEpQC#TqKav(YZ$u*(R%4 zfw;Tm6ZdRBsa?Su)BJrQXs}4@)q}S5Fi^CC6}y)gKp7(FK6Y4p55*+y9$&k*=2Ct3 zSCr(PU8{PtXEYRGbV7}eJ18n}|83#k-Su(R(YWiQ3YII=wWOP@6Hr;2-=~LbC*dXf zOVc&4ORD%?A2SZ-p4RxCWkUw#MOLw8kXlHsnzy#A_vZSeHIJa=Ha27^FF_h zF$sF=bpLmWHGli2*gPn6MW}C2>7`mXU9X_;_vT)h(+fZ=`JaIOnOM^6e1W=;(^lp$w$^JU03fRy`28tzR}CI=>NT;mv;{by)4~8dihB=S-a9pW|vfPdf9wn3ccL>qBQ=K zmmPYUEse+h-$!EoUg~hZ=YM~H<`p@e=w-x(ae7&~u?Kp|1qK}c z^0f@2oR3xzHx9jw6~usEX8m1Imi4qlFIV6or9I*=u5b5^UQR!vb6@QGe!Is9fL`i9 zCcW$c>2xNwuJrOwmsD|jY0OTcm!B<{#y<YVcT)r_lKJRG7P3$DvM$zx z+=A=Owpf=K`-5IimLZWOw;X~Kvh4Zj-nJg-WdjC|LoX|25as;+g=Bgu62yRBF8G_E zY&{-oK05izJ*1a++xLxLT!(e-bRB(x^%n@Nqw%7oQU4V@*dv@1I280U_TQwJS3x?v zrPA@80ZeQU!Wr;9mq~PvW8IW#_m(q?gAx z?Hj$ki~jH2>ARvAPaf!{=tI)WgWY6hH>S!j{w}HF^l}xPYm#)Mj3v_ei-8(NFPo+D zxc|GGB3O~o%QzOYPxO*;<*w$V|J3addU>OFCwh4nCy(3wvkF!~Ngg zWMwy|(o0pBRB?JK?Uh0=>mcQ$@y7r)ie4I}@worHn<7||(94M|WS{6|_|#p|%j!A% zgI@lS*@<3)dYoP|KInm7F2ulb=;b6CL^(%^!tAl--3J6QpqH0%pc7E0Lx3@F_m8Wbx9Scm-O@$dU+I5J{te+a}K@S zBaO%X-v@VwF-hM2A#GpjWzJ)}qL=$>_XoY)l+lS^E*=@Dmv!&;Kre%U0f%0GltGlU zNfc*?UM2}*KreScCMe7QqeCw@;n49tqL(~t-{{4Y+qo}xE$=SIEjQK!@EpVa-`!;G zN-v*+z)}e^PA_latn;M#XbPl!(90^IM)8-cr17}_yPG0dk?@zbEM%Yf%f?rBCBIxV zbAQmwFjBLbB)=SkeJ}QW^ys@i(932F9EVCk|@p&y_5-JKrdH5A}F))Q2b>W zcG&C@y?pjr5A;GyO@VHf>LxsGLk<2Tn*e2SW`3Ig5FdlgoCiSyrR!pO2S3WQ{dUIu z1Kai3sH{Lf;$}*m7uLt%>Vhlu*s)(fl95KcV8Io(Sx`6AkvuElirfop)2g+R1JRR7 z7DMGqVxNvkXPG2BbC%ed&0HK3mZi_lFdBm!dg%tRi!)}m&@W+;)nhGM9qd{lq(HmR z9C`(+_ZnYC^I%+ufWhK$f;WyeQ>I;WdX{d&-BHEx5VjSPWPDmvg&Ug_ZXZ!JBo@fi zgR5|PldCZYzFa`b0Nr>$2LBsRnzZC8-befjK#M*G4E*arBnobi1%|^g4u?7}I=Q|; zAdY?JNx?W63;)-|T0;$iUw|~)_2veqYS*=AavnxHf3|KkRmR@q)i$PUF^e-ka56cS zYN5f%6Ai)0OB}^-e#Lo(;haWg$ovuyMHTrl;%EmNo*(ou{tVB07=xd~Ws!Jo!|OfR zgNfHC0WcR7;Lhh+iU%-{l!m7T->niJ&ifOShO3{nK7*bQ5xUv)q)O=;jM{`~F@%>= z!4KD1KBO#NWBvJ;Osfs7m81#(1?lAAS0%Q$kZwe*kKl7ga*>Nk#;Fq+lZXSaDXvDd zziJUU|5nJdwPIM~n>|IO5?Di@#EL|L*Lc%smTE<(7veN%PwA(6`1C2qWTsUa+k#*8 z5_>j|6g!+}wT{Hmt%1#v`>?o@vYsBCQIO^@=Aqre4L|6?HMlyfz1^!lqj`;{!1Ix^ zPU(H&6HoC5U$Go|GhG|eg()0RTT3mI3>GaYkBY(_Cgw_ZlCYuDQt>bThM24=!Z}cQFCLqP>=Dc>i z>Y;UksX*Hz=37>RA32!VI%Wr07k1c?w+=^PTJEVpD2QXYn<;hEWgO54V;kpM#u~Lc zKi0X0hNmJl5Ye##u4#A?K==gTDb3dnsTX4>L%ZuTWHVhacS#cyFo9}4*aniVv6?&` zB+_LwR&~(_RmNtr0n=mg?%dFw2o;#YnJ}ngSz728ywM_V6=3^t;hif_>K~RA`ia1Jc;{q#xZduZ`XT6?5pbV2UEgJQYHgGBb-L;LuyahJujT+| z)3v-yg5K=sv!0UWB=rLU%Su&-80yK$H_`sy^@7>~Xh;q=wNv-;plm@44L?yq$i z(b8Y55X1gDzo2OZ`|GqWU>H(2aM??{>(9t$y0k87RDYq;{Pvya7vaVkVVOg3N8w<% z{^4={tIpd72Zd3oavA9`_THlO_9|`BF@aCei}|Y0Zv0A)iL3Ef9G{PLn78-jIJ7cTOGb0az))aq9jsLwJ|7*ni zj2Vm262)xIYD~MHCu5@va84mMziI~E^y^&z2zvd8}L|u1&e7sUlo({ol9aajK@5InCP#`kCPZo22}ItwS+h+@M|vu zM;o4gn0)$SyaNHQ>}Hv&U_NtwexyuQf;BXdQ&j^VXr6_{t@xx<02$^FOiAROEl|bj zuRH%4tl!4{RSP%`{r1;5r3CJ&aHd^tU#o5(2i&cHXKH><7(xt-tdD1=W3+5=b>ay& z=deciSk12exl-!`ykFe1f8aeu;4SS5-i19@vlsAMcd*qE2kO{y=((=`jH&Glg9}XT zvF1I=zsS1(JC304I7eMK8!fsCA z_)OfKHr<^&C9~SB=m1x`qZj323N7Oy@cwYW5_n6-uS)Xg5TQDJeF;W5%7V~YfxppN zKC{AYaR-Hn$g*;d>DUh%Y{6ynr~(OD#qm-Ft|~zV521pjC3chy-&pLXz7RXj8eu0D3!r2L*eI2VRZkX%ppFzgydI*W-`ylzw`#xQQ?{tD^ z$xGwhoS7^&?79`jNcuVQjasbFq0~6{rx2eN#AihupTr%c-QGY^1q4*n3r7erP6KG= zto&;r-OBq`5rs_;HohvXlfVz0qD2ry%veD2lx>44;uYW}iWT5K9l%By``EEMV@IpN zygL{uS40$@nAc%dj+-?P(i^|%t%r{&FKuY|Y0nHP4^-;#Zz0ag zoBZCyq}1384kDRTej9Ad2;7T#(fS|&7Fch6ax>?rCa2D+^P-~|dm{Ffp`c%=f&$O$ z#(LnjfOCReW)#l(wV0$_$KbKN5P(?&q`Bz&dO^!}Ypeu-PzI``9rW`U#H5>G&PYM` zTAXF>&%*qc^KGm*=`L}6J?@+$?eS##V!5S)OgQ*s_LM%*9kFH<(Tf{#ursjeT|rdh zU#(pKCI^b9w@&R-`k~r14+oj4hn2?kSTjzU4*r;RbBWKq86Gk-^QpU)K&jKU6)nNd z=jhGNGV8zn4bleQDg6-ITqJ~oec_wkl`m%D&1-za3%}=1K$UthmOb?MfBA0~Z*&Us-ZBiv2Z4yv zb=5|DEKkGn_EDF&{D;7yJ|hYlLgtI{=!4)OKBcN~8 zYW*mk0MEUFFJ?|;rwuR)j^$l^FID2zlv|+rj>G~B8$A(zMl;cA!y>04lYoP|BgHHs zob!e92WpAaNt29t#B5SA2a(tcP?GgG6(fv?Lla>fkshRTWDqaxVUR(l$OAjEl#+V2 zM@Ua-iid!sgc8l2t`&>J64^{Hib^F;a0I4jvRoA818ZqQa%?@yyN?xewDazl>wYA6 z5&qFhf6RiAm9Q|#W>Pf=gvSCg*33*&RD;hP3+~#4OXjk)F%59VS*VA{F9r8nDAylM zMp9=!t2REarXisWVP5lGceQzCzJ7LyrD-8}Z2{LuRhLh9`$u}4&n+N!s?F=Nyul%b zY5ohn!PQyb<~|Iqlrf{5H4F9Nj_fIyVdhw*n=_VrAvu@7=N}(=1c|hPAV}Gw=QS$a zT?!05%>qjsymN8;Lr1#EnLf?a;VJ#vDR}A=yfF@vpx0O4t!TIgp$A$H{^0SIN;XG#frxZfNEt7t}M-Sj-0sn#V^t`~V)6asq|*uc4^9Iiw>a|G^D(G+w>nG+u4QM>tNPe6t_B zEZziz((3vW4r2uq>)}kVZuZ6WcgZTu{Z*@AU0dVNg{dgRf4pwI6&no-+a7J8N&*pJ zDWnA3=%XUgPuoz&EYy^B^I&f{v&r@sE#HZgu#t&M_!2@CEX&Zt6&TBJ1~!sA8;e8R>oW!$5PJs%17uOJXF@pnc#GqDF!7f$We<6 zN8z!Ye8C#bR1S$i60#mcM`;IkE3d;pPUL+I;OQ239;$MIy46J~4dVFfMm|l+e*>_v zkNIm?aD36?2To!gje;l#rh>2s^2t>lz4w=V(w{}`Mm{;~7r=DAHR7gj<&!&s56CC4 zZ#Sn#9kgHF~Z30pRV$|&%9?n z()!G7ALkMCo>qK;_%Fh(dRaH0h@sPvME~CMb=bTbTj{@AH&jZ8_CJfBj9f6OHMl#a z@6YtGuyjg~^0TMpVXRC?;}aUemv_W90$pU@oP6chwD~EA#HR_ZcB?N`w_DkOKdvC? z!Sd`W{Zz|xG*waT*xONuQVJGj+@AhqRv>*<rqY%^OXvd8){DM*&-bEW?fGPXRc=9{l$ zFxD6c;kr_2nybeGaaLQIh(yWc4tac?pWTo7Hpl&)&{^!*x%E!>ua)&1=%`-NRxvZY z;l*twN{wv$57Wo;#v-}T#IYnfZ%7F0lf{|}gm;pUo|G2F?dxtRb+QWMHU0@D6|N&_ zFvVpE!knrzre@;akA6K!>{{LJNT${CqEtxq&3dr?oXQs~@Kss7x-$3`?&^>k3=1a+ z1175>W1Dkv?FR&gnXCb-xbji2|f zhT-uxG&1t7B4b?1G!ccNE48|*EKNxHHN=hzXAUD`E^wl;jE3q*#q>dqdNHQZMm-%@ z?^Km^L{DBS@TNc=#9>wxlN;0u<|BAS=Ao0hgu@YU%;>--?C^|sZOm34I5eXrTIR@$ zXcDygMhG(Pq&2O0FmPPub`t?U<3@MBG0t6J_}v(yFuuoDiVePeJIZaCdbIIItVyfe zL0p|((-C-1Jykw(eP!9B8&IY3?)`e%l_*cqynd4i5*L$`oypD+F9fXb*yuK`H$s(pAHVTnk zD0Lz}h~SSx8EG#_c_v}W8KsKwuc%yFfSgS78j^6dK_-I?GaXXnWOtP@0ec_@GI35E>8I8j^Q5CtG%ytkmTL;LfLbgFFCWA2|q_m&O@dk_J zQlM(lGD4)*LdM|ym*`osylr)Wt+A~@54L2$hS3VeCi4JVBt=KbS+q$M8$%wRR;AaN zQk~_Ga)6_Q=uFuM<|XzZ+fSBDRVEv(1o%h ztckZJk~=Mtu+_3>hr8F3=5wEU(ja854t=d|&G4EtvV7%VsN2UmKu-3WSV;j_i5?{O z{NSnzJ=omf?+?Qc)@)Dne@(N@d}u+@PedPcy|oFTl)s|YJq`jwZdkuQ^#2wGqnJ;6 zM-NY_A7!DuRbWX|$VB3c!HrbeGN-gr^MRRTyR`~V(b2c8wbuyubHJP|`Nt=#-6sB9Wp0;bXqexGX9mJ8Ouw9b}+ASd$p%O(BF>g*+fkc=hsaTHR|@ zGBFg1`vk^e*9FEuBF5iu^2E}u(d^VW@JjCT5S&EW(}xd6pwD^--4Xc>%Yjz}laT-a zS@=*f-ARSjJbW6`79^!O8Br*!4^xQjI|C`WNRx9Pd0!1ihbhU~xU30#Yt_`@X zR=|U#15`DCVN$84Op@CAU^n~(`pWeswKHOBS)$!qj{}vuY8pz+(1W+Y%6%wI%C^w( zS5yySHg@dMwG=XJc?w1Y;|*;|Wf@s@UoK@IR=PFv06Q+6Cqe@zjFJO!^3ce#bPl7l zd4r;P0YuDmvyckJx(0f$IR*kFWFpOdu3K|oP@v`dcq2VbKZ>VP(uy8Hxvs$4Lem4T zF$F#m%3C^NACly_;XHQ~f+<`XmzKa?B@11JS#4C7vR$4T1qy}kDOaV+Q*5u^Tq!^` zRhDI_94_RLgemg#)99qVke?^BS|vY!4e^DjvgPMX5tERg*V~av^0Nmub(NprJekyz zDnCOWbVZIB_4$`TN6B<(gM?$ zgX5wB+td9TO&nV29t6V|J1q@&Fkh=@zZcEM%Q#ZTukfSQUypbQ1Vg`!qip#Mg|Z`+ zoOTRiWHyGaDVF!RiVU1B#wqg0XHSxm6l~7a?rqF!@&6Gc<#{!z+VEI@66&^Y!U)n1 z6agIeX6&rQL|=fvBCpxEu*q{Kjw8KI$@U2Sz+<#~QSyMZHsLBSPZ4}d8X!;Qb1NQa zM9Qbu-)3QPGB~3>&3`jwIH(1lHaiVOBu|I7q&m&4NjGXr%$iK2=CoRvd&VO(64u}x zASnz2q9?1q+Kn41&Fa$(Pp(mo{P#?JKTMKhq~9U^%}rCmczKd=8n99Lj)3We5=9s zM%_wDg+jE+AXW>+0ank?0N9F zeYSsR!sj@y4_}8%;BEUi2C;7Div7T7jNCP`3w{y{JQo91!pg&O4pBf3L5nff=!1Rc zxzMDmTdgNF+0h=bV!BSF%c5{3iq#~^jJFRJpU5}Zd0L5XZ zLUCiA;sVe3%xl}Q0;1J@hazC%UGFRZB+y^vl`URly>(fU(hfHwng!OUf$@NkeD|5+ zBhdM#+#-BLtK)*6;wA4Pwsalx$MVKGv5z1YY9$i*j2$d`%kddJ+Dxl|j`_@-M-xR3 zbTZu$Y(0Z;o7=x{y#h>9o%%%seU2oUZkT^ZaI~M*=Od+T zsaUJy-~8OAe5CBb)Wyx3eCxd>ws&j_(e|jXfoh%nfEudOZ}%-f&T;cJ;HKx9E76hp z)`i`clFH6H`$J}tnpV-4Qi?`hGk(q zkXe(&20=R0g%t*87Nz-*)x(!!#n7`JWkOkj!-8oH0X$MySJ_TMt@T0ukx|ndyTi7NYZ8{dDt9qWCyInV4 zvpkTLrPap!>~>_HOsAfBXXl}Dz!JJ29X=gvaUaUjsV{vT@9@*6+9`rFEGR_aNNs5Q z0!a|d%X4C}42Et^;S=av^fTO%1s#8VnEgn#C7koyKZt>-K^~q8Wgxo#II^<1e%ihM z=I|H5P-9tV{sZ_FJV4U%W`TOv1LLh!7*ZgK%OA;;I?erYeg(|bOt!vwW?%W8%KyYZ z=Z7#-v);OHpYyBwkMB0Wa7t;pElKJWTVS#;;GP3`AdE#nh>T>xyasZ(1fqXvomST$ zUp{D>n3Iv1Qxg)O5l>v6k{GA0sKnKn7E9o$k)bb3I}Xd{2nYVPC5P$3?+d04(6z_j zpLz%^xZYSxAR-G5E2F*e^qJQbVw8?6ge?NqakU54tzF>2_9_A#*!(jEE}!WyEc2OI zSxAQPzIYZ{>9CvQKc7l-3G*S#CCCCeeZFwtBsi1oVcL?j;5Jh*?L@%4ed=+vI1^rg zX}lUe)D}`eX!fa{^f}_1Nq^hBH$$wM39)8miO;ySEc#|*(z0-u!4EXDuu!&Q-Fn;`d^rc@Pg}cWHorEykNCR7^`e=1+Ev>vhIZ zq+AI%66<3LhMDIL34Y&atg~Ko3i+8TA}VVu42KO>KZ|+p|Zh|P!?a? z0=vpP6j5Bs8eO^cAYuw8o$!^LViBdQ)8YbQ4(Hn->AwJrGH66E(9M*ne@K*u0-}RE zMcpA$IgA?6De4-D;y?wljr}S1614$pfDdruNyB+L@no^l`MQB&QD0T4b5v9yYZ|

    qEWLqVToqTEHI>Q@ND#=q?KgA(=u@7*;0h(rPkbRH7{zhw3n*E~)$Q+4R6D zog))=#PmRM?QN*rfArRoC7A&%9ojev=Z*R!c5#Ra0yV-2a#&K_v5zFh8LR3()aEy+ z_EVRvz(ji|{+__!O8osBe=+=3;Ngrg^uCU+*OtH#yT*SkEHHuVbn`s))o`b;vV*LI zSm!tn=MK)Zuy)@1%~h%BAe4sHeMPW^`N3 zxk<$wlPHFc9B_rft`qCn@oGlWQ>}HPA5TK%A#!6Is5Pfjx)2I>)v%xNUM3_!9?TaV zPjnyKfVi;?njmrEJfF?h%dCTs>< zrQiw^*GeaLjl@!%_B6m&1p(dG@MvlXHuTZv46^e#M(9k6zN}s^3$YNhGvQovM1@}d z@l*`c>ITtzt>cfSY!hg}8ewa+HwEEKJ0L2(N6zayf z-Y}PaKh%F^59p2!o-rAgBBiu(X&XpP3oI4~@O$X3R)%fh1Hf?S(-bP@e8RpTJ z6b|st{vihVw7SorMk9FKuyll2b-fD!(_v2P1r^3imkSeGr~GKp(1t&qT1d@w36+#!t_x z&i)FkIwxcGx5$^h(wDu_+qm&)EOQ;<%YF}Q!Tt5@oqF~^vDoR>v$4+ALb(%Cbcb$a zf?;718Fr+gg%$gwiKf`A=!*3|1Bir$`0VPX+MIjAn4AD^IW+MH2Z72LIPiShi1pss zYbdA#$nFKjs@(_g;cY0^v%b3cN0i2hAHBgSs;Gx$#8-~x^Eow`hY*ggx)zk7`s6Z( zp>$t*hgNq3g1hOUDo9n{;E(9lD^&v3RY@q`h&m+2?N2)?GE$NHxk}MXyZuQg1-q{= z(7s*a7e;-Sk*VGOh?61{DUv$4U8Qhow?F8l=+vcf>0lYXwcCH=r0AVWJaEKgiay%y zcRDFjyBV(G=;lw)LXQrv&fbXLJlV&t+)$)vZ$^h7t!KZ=o`w}3B+%cR4L6E5w=WwO z0d%u$R_-vWa({$#MZrY8U|nowZ|rHrAZgChf)miO7~_x47is8+-#QCc|Hy|f=m#}a zhi@9lt02iSI!Ym*Tnit~#sY0lKftSo-C-men8)?-=)+Vmev1Lmp)zzsD#q@xNqOi- zgeOS(1S~&D@=t)ABQR3FS6y(p6aFcZCyj`{PWWdCPZ~i#f@w%OpQj=jOZY1Mi`0=A zb;2!#Cvkv(Askp74D_C*XRq`&euAO(Ifm9pGK#)98kjx;m`0!UuSR_K7W9z2I{O{3 zwibxM(5mV18HH|vC6Zd_7-%38{w2bb+NwL@QG_RPmvSfkt5h8P#0md86?WcpE9GxW z#Z6x){2PQP5%G@>F53~F=#p;`jCO;&Yy@}tKyeqMpb6B6*4o_V3!n(xWrv=a>Bn$#lfvk_|{Y$+~|aFOU1!> zC%hvC2fWE)f8e06;9z?y4!(D2Z$~N)B2M`CsW^Dg3I8D#2k$%KKc?a!+iBCzR2*z` z(#KM9@R<{ymV$$K5WGKd00W#e&17nGZUrBJu$2!}4`&9r0o1IQ&E3~N=_}s=ehLF4 z=N>iHEENeLxDzJH$Q;y6)&{#d1G%z+$ykl zq>p;(6WIwHB$v}pfKS!j$q-byp`7v?6g8O20`1l5J4?4hgQa5IKu@nX=ZR}|9auJS zbQC3@z$sfBGqgImD`OY>C$UvpZIBEdt74%~eOeWabK5J8fBDW1vk%b576phl4My)ye&=Tr|^D5%UQJ>hUssA?#*sDK0;9}M+!OIEa zUCze4g@OO(Hx2{ed@vaJvz$DDB`xSy$zD*&Zj2{;mC5c@$$DwQ2&F9eFQ{amc(TWs z>{^v9Qwy$C$$p`d9TiV@8TO1AA@2euj}!?{$1t+y8Rt7HRJvetOA zlbK9Svdw+8V6{rtahJ68&+%k^m~0Kvh8ZoweLZ-|jvS?44v6Hbmjfeo;73&72u?BL zOHPE&c6d1`vRS=o5lg+eBOj}m+z5`-VVZ*@udA0sBCGM@kVVW>x(+8mcoe0qiCn9S z?iYDjrOu1ouU`5`!s_MFNS%7gkIqE71ESOMdsuV|D8s8=@R~RMO>NE>7`)F8jb4G6 z!=q!3Z>a}pbK5{Ra_T`&8Ct7fJs2YS8>J}_K<|isF8jACpG%jh$RAW~M?~Ey?a1f> z_&q9`iQj>s>EMhOOx=M+^`o-tugHd(2c9@68*c1}K)adoD)ko_ORZSm=J8Y^x&Bv= z=oG`G_|~C5Afe*<^vZvvS3ZOH52NFj6(Olad>D_YT#_CgAzs81u?`WyUo=;CR!Ptr zl~$oNdZ_FtMnrN6mCvTrp7i$T`rDhfc^1+>7Z}K8gTuV(&DxxD8ROqT*!1D;nT$w3 z3>i-y79KwUKq8_9FF=gXXnM14#SMIE+gDSHqo|61Tr7THGxf5ctrw;bT zx$%TQV2r7RMM()!@f!cNs?rbQDGHdPJ~J04IZxeIzemM$^+uJDpHu=xwhT|*hyH;o z)qP0iZM58us8Lm4<<3BwUV3>=8@8DmSo7n`9=FgKaHnx|rLK2mm=g7tnlgSigq_jY zd=SQu!A6E%7h?>Tu7TE=p&`((0*4_`7FlFv&iaDsFGCa<#SN;kukLgT`!yqnsK`+& zG8T^<&&Yu)vQ$Oxh(}g1GDFq47Gr!FTA3!xSyq5iUl2E8*E5VlD&>D-UCyYrD(Zel zVdSJO=Z|#`qn=Sww=xRDCv7=@tU^X%>n#hMz$nuEa{gG^jJj1tc^CyYmA0He)}|ap zO+XYHK1G3l1Hm7}$k~h>p(1PU5ES2xG|K(!8b)GeAgwz6V=Z{JEXL-XqEcCL3=Cr` zE_ss&1g}u3vQ?@r@l^Rt^#Rxnp!f>ih9(FE z5h_!^c&3XO*-uq>oT~2WIJ!<}Zp35xF+_9`{LsWs+Z!j*pvnjESbiE~ z4nj<%0dENXk`=;206T+qGcoq03eP|#4uCf?e4Yv)j`WiFG6Y6vq6D@RgRsLI!;l*h zGLa#6<4$GZ7<&bKyVWb3Kq1XP0H>4YAf`DDfguJGk{{5p=pgI#mt{pe?D}I8l&y?& zS&^^cnA^?yIyIH{x<}G_PXh{Vv9|rTyY-%Vb(>hS(s`}-n1Z@}Lo{5kf=#7YzP2haXWJOa(i3f-tHtOefa(c<=k!CVM$n#%_n>6zSibrIURn2bbKvt(dMtpdb{@f^uVLu^tImdH?+{p7(S)zB265K z=8c6Dyyf3aJ-x?#T4*9L;pBxS6*RK}*|7x%+0h!G@mwJ(q^kbL!s92-LuuNar%AQ2 z1ZuZ8Atu@{X}v|_amGnnwb9`%wy-EQ^dl8M5BD(wdG=l+dFiCn3}}M0VheEJ`?BZb_7*fE}=G=(Z>1W?qSf zONP(bq!)jr&ABB3;!W&}aNof?tn#{y-tQ@_@ZA+c$13tiZK*RfU6n=^HTT z2QE&uzjR&nqGWtvr)uE@pYfxR45%QGO(LoL6`W6+>Vz;MM{Gr@uptiyu#o#%r2lz% zN5pDxD~;von&+R^d>~r9iLefHZkErS=Jpl0>*cLe;0H?&LsRpmZ?;_oj9y%5htKjf zH`WXWLjkcVNLvCD(n8Bncch)XU9v)`R}KqTVZvp}(Z9ir605AeEmJCQr6UX_SngPc zs}{Xw)q?qDSTM)#kJc&2kkBb{z_ts81MUwURxdz7ORq7*9SKThOoCnf#s`f44+sJF zKX-IBxYM?|V6B`nVeYmr&mtpp#y{4U68p`*K)Su~m#t_}rg7{yTX8X}1L@oTvd1AN z;V=879hu}WI|DUgHKWshvxm{9PX4l&V%t)Dkr&Q_-t-2nwl*#RCp}VIvS#?!%^8$g_h5X(z<$N5(sfBb z(sW&%z)cquu64u#^-j&(XYZDsWY9u`vFVr@4N-*W-}%hY)+tC-n7lV$4) zsv)twn{Z5Bv|LJg)+q(cVgszl@fC%I0mVOPM_Bi$m-SpYb6G)rk+~9#WPX|C&w2d> zf@OK_*%Z#4;8$JaFPo=k+iv4&`cntNpsaQ){752glD`?3%;Ch3jzE2py(n+!Zpt&O z*IUc(PSn{I{`J=HlH<>SN^leYp2Oek`1=}v&e-P@kb<$e7GFemAw$^3akik?^rCsZk){jAzs3l$S}+p~olhr+s-R{d!a9bD=+l zM96(YqU`{r!`c{52z?2+<9*y8dsbq9tP7UjUTnL)Kh_oZArSwWvMk`W`~9(P<$Jt8 z_Vni{|LUpxV;d63dc;XEsXc!pU!kKiydHYk@2f8xe46~DBaUAFW~Fv-1JcUrFgUxy zaGF-2w*L0Qs%Y&VH!pq9n|JB6-865*4{@*Uc=Og)UV7;mIeDi7O~ck+@Z1dl0TcmU z=!3v4Zx~x9&1v~w;VS2Xvf)#7958`dZZb6)tk@S>BWpNTAc%lQ#}$?Yero=*B<_OE z9D`JjYvMWjCFSrTM+qE#;P7*^+qelf*--^D9I*AOK{noCU3zte$n95vhhsTDnk5go z=IO>b$`xw4{-3is{7@ezjHB=lAVE;cxka>yJvVYeSzoN3Mknw5HR`v1huY}<++GP)p}S) zYzjRnc_7Y@|5A%|0u}u@FMwn(q$`X)X}<50KFj*ej<1u_#(OwL-wu8?iFa;~7P_s% zxTrR8gw(rOeCzE^e@DpIkF9fKwnpKjw-WY0EZIk_?MH&Z%Rj?r4CIhQyk4o`2AbgZ zOs(z*z#H7|3Vh@XPXc49z@P5s0YAnLt9=;K3m2hxVTJ#%bW?<7chkPnk!70$Skqa@@;ql$g-J^z`rbL95~^K;&-pS7f60;u`JwHB?UqHDcBCpt=vI z9|A(^&0_3G2GftQOmFya(zPY8d5gV( z`jXx)KBKBPb|v_Gujh{ig6Z7>fUq)BnIHKQ!$O900={mVy?tm(v$Z-Z^?T6t6I9cs zg1A2a|E2avdnHlDCj;P^^BD+~d%mI7fxjf%VH^Ui;e}h zISpKh8*%zp;14H6K)<0FL|-doJa$WB4G;;z~P#B!$HDhA*m@K7`Cj;)+wJNchWrzUS&gUH$okYj%}oyPom z`UyCW0S=*ePU2udGs<@cz5$gK*fs60Jjl$A-zkok$Wn!}M1EC7l~g|t__glYtMm)4 zw;umSIHps+kgS}KV%1?U21Qm(EwGU^TD^gLwLusM)mu+KD5{F2m-|k|ikYBoIRV9V zWiRU8>16BRQXRPN>F%dnp6Z=ujho5?l;3!|Yt{*pHTj;?C%dEy*RukU@B9S-7(6=G z7F&xm8I*K)WebR{6;%2A zzsKDd2Mot3V|H9=3hdE+aVBCDcA7Mmz#jmFmt(%P6)o5$;)>SSqf!*DoFgBCqSb)9 zEGk+Uz%Wr^-zbCw%i4@}NlcxKpv^4BuxPOU$x}+GeU0&puC8Wlka^^MjD{qw@3+t$ zqc>m%9|@{=%>HgG?^NK9z6wT78{jfo7Sv?G-f8}^<%gvF z)@2W?3TlBz6vF+bDdiI*F4*!#vOtLrtG4M`9PlwUsv~bjzgA+PZXTeU7iC%I23r=q zUCNgM^4Tp!LT^ZOlh2`qZ)!Zp_jb-2O|+!1sKFtOhZ?%pd30O zHxx{Rvh-bo2Da%I*2t+VyrxDX&{qw+AEC!Mz1eHUvyZXKca?u zkqA=B4YutGW7*!(m|VJP??Y{NdryGuYcquJraSG`YTE=2PMa4Xb+9Am?^D_k!T2Eo zwpxD*;Uzc&8(?XBSWhBp;{+7F5kuzZe~k~B&xhGVrdi75kU1Z*i6Qg4lR##raL!XV z$A?X_ed7M}Q-+P}b9mv=K5>2)hRp~t<`GIEQ=QXGdM>$B)_xrCl(B*`pnl^_>6BG? zs7~3?uEs*n^l1`v86K)rnkB}nozG558fT|$Vvf}Lu9H1twDi{*3w!J@hxNmES@oD0 z9jmFUtm3(fHj#e*k=<#E`3R9#OmjMI^7*2Mj&FlpVPQ`oX4t@bFcExntJDlImhIM{ zLOXfO%(IL2cbYjwMnjsi?0uP0oSH7`vGY%aDQXYzj!p&bL#=7P{;%4H! zF7w&-#6Y_>_~C9QClIbJPjxoB`i$2wNaYYtPlL~ZjY;95$+Iop`rzMy850?K@naw? z26E_?+#JWp7{SMk^^~(f6!;53MFxkHt-G78?+%e{C}z85fSU@=INn<(pB2Z={j5hg z;pz!CZltRC{4+N`|2z&#h#mu~8WIrv9Epd*kI9&9fx7~0K#E)v@^)8zO+Z?Xvh`N& z{hQ$?g^z@o(G7kl0Kb;?&Vp|`r%lqY93m^Ag{*eQHg&K;gLM>YAlpoc3`yr@bnTNj zL6cqs4xEZ{Ui2@UCUtll*o@?FK&E1xTJ0LqufRwS^qFUpf0jELaE&UKi<+T`S2;nM zQU*xA(V8S>^aAtMYC}>+uWDEXG_NpZt@d$*d5zx6Ai%AgUwmi-@}966v6mjhLW~*f zWp-_))d6?q)E*&O`vhRh*={XiHt5xi5GOIC7kT*m)&fMZ#sN|zVyjh+%(mH@CD}YU zE2FBgfUJ+U*Xp)!E45j$dGL{02q$2WEa^$B{ZW9jr-$Jxa|@6zG-Y4SSB=P+C*M8UNr4L)SZYctE_QWJ*Db`?dQu@?47;}&w1L%G zWC@;j$Dr-d#_Ug_Q=r6O0|(%6Bc?i_u7M|EbRuFFVU|YBJ)Eo|B&AtTBZksb&PbK# zcsx||<}=T!h#5!!1@KEeRGwR%JO`^h-%fV&TqQBqVRv_$W!8MbT6#QhM8>qxPwVfJ zc&tNMO1~IWd8y+aq0)0DXkj<$dt8tB)p{9Y{u}J|h*gt`e=uekMvgCLKkk+BU#mNp zozDdTEZQkLD0xh&vrqEiy&{HDv^#5tq-#m@W49S)a?EIEpbcsnW;v*)R7E-4ajT9j z$h8P%U^s z5*+5W(!~-8^%e%9Bdu?L0reQ$Ozp@|@rkClTOX>I4z+6cI=*^ZExG42M>1Qc*Yy@+eg}=jKCpr^)X6^arMl0jz>u>Fsmev@}y_hzd8Yv@^ zd&B$J;1Is&*Wvh&6H%-7e7}n)8kc0A4C~WQ-FOvw4$y*}m}^&-)L)W$3MT3Z?z&8? zGV}HDbA@a_d~Pe3mf)qzct0^78Y7ui#`;2S$N|g8Ag)py-kP?u{!(1#@P;?mSQ-3Y zt2ADl_WrV3MEZ)?0MOZZ(}KjG4-;ll^h(q1R8iWXYB1h{^F2M8O@J@#?hoQUPw9h*vNNiK*%raaV8wrkqoM4vU|-i297T z{*fNNShf3VZEh=QPn}{B&N&FFwCDR?IMMJa?06tm|86CWe3@9aD0xA;wHRWVtYTsP z2_;Xu297w!TR@OCG@k=+_G%&w<6#wIADskm!&-b>1;T)Y?BYM5?@6+vsJRp8Oci5~ zG1rPJxK>nx?bW?+vZ>dHO{^u>6M1TQPg=FM>-pDgmMLc$YI8Alg6eY)g}9dFi*cSC zyMgn$Bpq`{56AA{EWTU!OhPKEKCtl9^b^1O1~y7?ZvV}4YE%m6zU%ko+>qVm?wd3@ zYsD^YL~?21;DfAEgha7<&`xl4QUWV4@sUqXet?u2*?FB>+mqXLh(mN~;yf)X{|a2| zm4LzseJr}sKY`@}Wh_WE&tbyWfZ9Y}Tc7Bh-kN_?BF!$#$8?3oE$dx?DvHD&!IN*D zzNh62{P~t^uhL6*#@kzrBkLU4=4}BF&H(+7#^0~-_bC42#EBPi=gLN#vLg2&>8kGN z4U&B}B}fp>9^997`Gd(cv-YI;>F7!NSt|asSa2Nt>u1Kn7x*xKK<*sryBtERu1^_4 z*Bq=$!4OLMC+8>VJU;<$PPUr}*i?n%uz|r?eglsT$V3m@M+Ow(%mLYOPVUGv2g)hp z9)@HxUw0r;b1Uo(YNc70bAf+9*;9t ziKB@AQ!cxYD$o-LG}NQ{3TTWh#ADmPwA;~y*<_d(^x#=(ez#oss)Z^XziNjZlIAbvF(J4(WD8GBhZn*L(Ml>8;y4&R0qsWEq0y5{ z*FpKjYv1V6O4$Gs@+IpIE+$}m6z-YTLa(64XsJS-D&vWJc?J+TALdtF?1y-|EtDm) zP$Bf1SHSCGW`RIiLMYE6l)?@Y^HmSdI}Y!W33v~V!wZu;nz{oUD=19ZZS0c- zUtqKi8=)DAW{m{i)Nv{36s(q(iIFhp;@>Kv_qCru2nf60`8lXXrYpE#lpMz66?W*4g`x z7Vzuti<@9R#R+}j0XU6ON~Q_Mi`aw-<~1h)ccFoVGo(3PJoN4xoNCbc6l)0_!xzv3 zelCXHxsC;##$FsfEVfKs+F{$BUTvYR3v(9iPRhL_X9H>i-(HD_GEKC-PdovJ<5f%+ zV~Ay|Nn(-|=F{I}(xmkXdj^{z>g4md@=Io1)|4{cDE}PFfT{kDuya%A!2yaq0@Txh z6Qt$UuU`8&njRO3+Y)8;PP4g_^_!W;BDSAgF9^6qiaQ2_T9FCQ+v2RNC5B`?l6@ zzSP#fXe&n4Isr`rZUHxxDgn1Uj!4i70hj!~&vWjbB?(shzMuc+^Y>%8ckbDr^PJ~A z+j&m)_XG?Y%nZNR+*f`g=%*nt{TwsW2-cxp8hXTjUMfM|cakxQ8n;8h5LGnt{+5RU z(9`HZ;(7L>B@k>>y4@M}4|o-o?$^l6V#;lKeFxgvZuA`*u?!~3<@bBZzY@1vZ^((- zD<~ykOttbD{4Evm5e&xk{a3`Vz8?+gC$Q~tyuCa9tH?&nEJTauDL}^k-NbkcZFo`d8?aU6+Yb;<{YMkA{3>kwS18<{Ndc4E6lpX zq5k>;U3ay^jS}%yQk*z5mr4q@71>&4xhGC2UH%Fx$up&<&@O2+-u8z%tuI#8rnE_r zA=(_QZE}=K+Ppd0X4kQ4ZC;*i^LTM9?kq=W3Of|aGGI!FCL?i zvKs!XeG{s9YFbl|x)~yx0W{#69SK|*$$b!;wsUD5%&CmuADWsiA)x)C^U0`MI8@Mf zS@$6YkUZN+a|Nn{tLZNaxj0^33E%lOKQbHBDKsgk-P0BGDKxJP$i=^tGWH_*Fvt!q z-_gy2SjGeDsq$JXlIN>evS{z4FQ+(w{2TqORxWG4 zBW0wN70S)JF7$=HU*0Jw62<8jas;pKeG*)#3S%XS)?#bUeRLY+;Yj96lmgk;fpEcb zZIsF2@D>)G?_R=}u=g=jOWhhuY@MofbzP};KKq{RWJ=!VB%2z z8J`_fl{$mZ7ho_+^BlGPAejli-%6X@CNHly@#^fM)z)&oR-60p!U>u%e3&6?4o&zW zDGn*MBt^+uN?`gUUNT&S_$uFeHLW27;IcN(IhX~vPiGMzV|Oyb9#rFo7o;mQS?Z8? zxuDY=wT~}irVdK>=&qwpkLnC*?D}CUXnAZM=ZVJbwuEsb@{iZ>dcnV24`9&FPDu*MUDU)PkLmP1}wC{S_ zfFHab&&`xEJgt~jaY?`ODg&BA#Y3E+4OGN!+!w!!@gBpdPz}j#WWT|APbTp?Gv4g9 z@&3?BoXB_&RavMP^EmbRbmsBDvEB>wI8m5KmDyb8vAt z3hL!atYUO1SVZtxKvf>vXy5%0nMGDvM1EHa8b4ZvR1~VrhCS$HtunclC|bSaXUhCk zM#Z{<{Z}e$=fO9d&mH`<*=4B!A8PKmv}ZzNA?Zyd%Ax(0u!K^+U06mywv)(N4#=*@ z(kmdl)PU?&Qj#0lR3{Ofl&<*X@&LjV(>xSQn#=v;NU`xas1Wg9j5(hf1UuaD?l86~ zJHdCK7YONnImRBKWYh6~A`G4V>U(}~2w_*aqHNbIeZQ8NO7Cnl=o1@Z4dZw2~zM69we&iJ^y0sbc}qs6L(!6L&B zJ}J{?+$w%03Gk3_O_mJm2jvrMm^rW`yC>kTzr*aDT}RLh{5EBW=Uk?Qbpj7VSg!`f;-eVRzoeV!1@g%?TepbZk!%st>ouYew@+ZnL4yBv zqmbY^D;)ka72e@MwmqWGI5xN*Wz8+k0dq8>HgWx;K;{6X5@XGV5=RG zPU4=yy8dnC;|G~5a9BX?%EvbvB)u?!q)`+BDjz>)CgC2LgyDje4`@ot#xMk_b^r45 zaf=;8eQ)_lISH29i*gzV5P2SBn%FVD$GeG|3e!fMV ztf*%9%B??EHwktSc7}-eMlrdHQ+=U#HH;D7^l>&A)G-=Ha!EU#*)06lt1@(PLs)Ac z$j;d^fT`J{%;+ducUY@Gh`+zWLmU?6loK0HKHV^4*Q3v-;g`r821tc4p_3Fa$5cBc zFi}3aKlRSvZf_!@*b2~Jv<;>`p$UER+X?n&(rPn{?6OMC{yLFaG{g*L3dGEK9{x?T znGqj_QAL1oNWh(Mm)X8>ic+>yc^Jxefg+7P@Or_8p866&BjvY+dQY9_xUuzKPl@vy zt7Hn3KJA5uXHg!UtBI;2r)A1EBQ71Sl^p{#bKrh)8T5y2d-coWue{{`jN0Rs?Z_kI zC#(mD5%M4!)SFw+UY#^KMk=#q339s#SAf}r9yR=ZCbtua)J+4_Pbu$e2FpIpv7E|I zVLc@N08QHCF%QxZe}TN%y_$eI!f+?ChfGHMa^WE169q}bgY}TVB62v}UPOz+{IJj# z{#{_N>W&88?$MeJ0Qd~Gjb_`Op>C$#Hq>Ks0o2@Ab+nr^YEPjP9dx3T^C3g^ZWF^6 z#|xBWDhzOHFFk?d%CPUIr;NrPG+CGx;hJOu6t!lovroSk4cah!lssf5oetlMDpK_S zYfdJgUFTr|CH0FgcIUf3<<$JhMJW<1Y{i@vapp9X^7j~QsnXNAh9S4VFR%bEB@Vid z65+_$HTFro@|O;wvwFbGYHX$(hN7xK1?l~rz+HLw|DML1=96XHgLfAN@SIx{MCP5r z4w3nz6jxP_kJKdj-QZ>r2LNEP>Y_eL@K29UC&BMv;QCq;JQosdZjHmJ4oUD{pvY!e ztD|(WJV6cn6>;H)hLK|sg0l$6U7hZPOA(h?jo z0I_MA4Z)nF=zw+HY`@p8HLuF3D^ripX!S5(h~r;wR!$|zK~_coYgp{@`d+|?G z;YY#CW@W$|M`K1P;z*#${shU3$!Ib(FUp8HN0_$Q&AOIuCAJJ^G|G&&QA4N9=n_ID zQAuY;T|}Eir~~Ni#kjT62wf9_q{FU!N%J-Jnja@C8vn%ntl!A|1eu?91+n*G9%g=q zDY`O0rne&lP-qcDy*~%fU8&*<#rfPLC+Yljn)!LvlyuKqr0tLH{B+u9pWkbKl%KJE z|6>S?%gsD6LDss<&G^uQxT}c$)ZAE*&dZp6Xrq0jQxS1s)D?P$R)n1c4_+l&Z`d33 zmF>W2DS$p9Ak9>7wsS>Z3oe0lb1h9kHRjccp+hg>v1EFqIQGT=izzzF5k71^#Iz>A-kuDhR++=fGq8O|h4+HXk5ioeA-Vmi`!Bq*dB^Z~z zbhfjDOkx_U3T6WpM?2>zmtwC=EsXMJ86{AFXHqum`<3SV8-Gav;uU=r&Di+76fvOC zKJr5HCibF)c1^Y=qepskVL}X;ip@gW`GJ}7C@IMqFLx5@eQsS@pR;WZO`q*qSRHh> zg?Qk8fsV_lA{LS-QUiE=g{bJR1ZO`n0H52#9{8HwDg&mk=)4pr7gY$vjlc0@ zdNBT5`WvqiU>kqq|B=^lZghdsUyK=uzp>n{BK6xFCTYIjmR(WGMJVFPR0Am`9RycI za;PCC3Cz!OOyWfVbL)A10(0%5X~6UzbfwwQ18uT=+I8t@f6o25dw(vze?@B56D2+M z8wq@$?WlLU>`lHT@k&}{#g{0Y9jhc9xlV`#J2LK17t-#!=hc#-k@XVs?dDWiw>1>h z123W?+#>PpYKfMF2a)&6$4ik(l0SitjzKlqGaU*wW8LkJDF7xwf~OD5L^=V)D4q81r(hp2UKngh`FIMyVcyWSiC!)8(NaZFZr)@ zHK=7y*I+WHV&`Au0ZtI7F*8ie&Jx60zYnER^GAJD?l6_a5{K zp}Hqxo>Vo#z**QYQ4_@eqRGH^S{3;KF4u|DJ%OzrbLfcQ#?Nf|iIf@0k5`v1zm?bk zY7NOs_H?P&>8VlL3Nd>B##CZ7*JP-@jm~URC?TPx2Y@<_KBibiavM?17f0!)9`G4Y z-zVm;@{}#-&{Ypvjyi`z;NX)>co=oqFVJwr{VDqK{>Y>8F)7>gXGmhAO7ni?msvBM z^EBS!oOkg+=r-!!vEVrrt_OW{5*UQWnz8R>umm|K%Z{tIJ3SoA23_l0%t4h`Xg&=ovbZ1-gIQzxw5uadg}6J zu2=C-Kv<}NEo*Btm=1zrSe3;!;}r+-2@{$~*nR!)2GdYe$A=X2B;I z?u|D76DrwmV5!4FAI zZghi82JDNG+y$Hi>_avChLw(6Bc}^)2`yuMhI3G2W|)Sh&jT=`DrX8tbT@2$T^zj$*$1snqGcYsu%GO1y| zB@qBM$MUU)PX$`ov}EFp0jE8UmgTin4i#F~LAqoV{vA>wr*G!mOa12&@$mjW#%Y=uv3XJ3b}+xsz4gfeeVZm3NapDfhtA#lxxiNJx5BChy|R) zDkh&PQA^k=wS*DD94xdCWH4FVBr3fqfu;&O9)c8o!-tzD5GadGnl>CO#bpCbZuSLRdRs(q_-k$D* zm;TP#2bT$O4cmF4d}B+L)jE=SSYDSEHoLT>7zIw}Z#rNre6Y^Z}bO7bZa~}|R`&*wM?JduB&NcGf z+%ND+OUQHk%5_HimgkiP zZXfp_?@yk4n{b`qRG!P^l3~|gla%LNPREB%uq-&HU)||}P>Vl24QFP&=j1@c$#PU+ zoH@`?nxW?d&g^lXVRZaVM};51N`{~U&q1G~e9?mqU+XAd;3(9!%-feI9og$(ZsiaI zl3Zz7H&<(O00NsP6cBkvRAoNhms8IS4Ew&B&|mChY9ih!^hJ2ZLUE8PI!Op!#Jk1$ zn9s}{hS~xKvQ!`BGmd3@6_;K5uL7+T@DNo;YRnBWLyL)o=!bp@Bz-s+U)#yxhjktJ zI}-qaNZ|I*M6Hd2O%SF^BObExwzC0u5Jf8sCF;#Yy}CKOZm>D3;@JMTZe<)A$nQ4x zjt)@1ll`tOk0JyPM9o1d{E+1Wl2Qq{82>R~nIvFwy!l*zhtxqZaL3};`5osFR!RwD zze^veFN^^s04sybwF&5j%Pps>HK5n%Qd}rIe0$#3zzOtkNjn>ElK&PPc}Ytx@x0Cl zpISL9r}4Zx?eb!bIgAN5NWJThz&#LV^0KRC4rLr{7C0R|*X7ZZV)8Q)KRDq(H%Woo zk2b;t>heZcR?tJ2{Y-t!;AZximB-w=L>{b|4OOVAfLLXBvyHF*C?V#OZo96?Ip zOa0ix-1AZ|%Xz6cg@l|C*YijFB!c}DyT1Whd9xM(>-i%sbH|J%S;4_H{G{GMAX-r8 zu+jTh7{W1=SZ@Svg~;5RXA~xf2>pQwO)mh?h5F1yooe(mNfMNT63Txp{U}>x{Dk$m zu^uV`1waSw*j}0tmO0V}4Vo|xA{7}jRvuQxM(ggVUbrGhbwd&_G_cASo-oFD)`UX2 zsCEG0a04oyPj8%f1`$ql=-wD8qRP!r0-{g=RZ=CL4)~yY4~-R4s4S_TxXRJvQJuM_o zhLr#srAfs5>n8+sl|y+w*Nmfx#>aR~CrWwHD0PP_C-Hq+mi|V`9GOyN@0zuvJGNQB zBnA~Q^~sQe4^pnV&ep_6LE$0i2E7~KJ3Doc1x>frLp8eOA=}d6RT=mxjz~S6SW|=i}{HO3BMxx}iN|@C=tLbAY zR{z;-bTVnKqt*fqI3~a<`M)-LBo-VoIUhUz5GR={jGz2f_rKFRBwM_z4`$s zpt4a8_N6j0icg{zPwqyQMM->W>VZr5gG(zNT;g46VhBAZuPiK3B2+2lW9I2PTcWe* zxt-(*i21JDNdlL^O>dc75BJTVmMq_cz$eN(xD;i!JM5obs<;$&x^!`^;?mF*j6awN zZBFlIpOLJ+$7YwPU0WrvuGW5r)UM8_f=f|w>D5aOE(JbTT)H3J;j^mD+G!=9-%^!J z)E*$aL)4C?L<%n5NlFrz4s;R)mw=_>65KrS5+ckT<$kd*4VQ8!$WUBdl6g$WrMG@< zNLqq_>Qf)qVb}fU+u##JB!iR)B4eY(E~PIZ6_ za$f=PV-pjR2EA6qJ8-nl+A46T$(gmN0g5i)cD`>;qfb)L4lV}JdY~_-Cd)fG)vLVX zl={rAvdgDQQF>4b5_q%bD@Lv4^!+-;yQV7oWShM)1N8a% zG=n||ex&Hr0lM%RDTNi)QaQ|%yf^4cCY}_7OmeDVq>eVq6FQ$8pdtscHV^?#5X;#u zPbDRZSdTi1vRl%MB34HlV*S1=4YB^NDm@pmWIB89mdpN2P|KB<#0iC!ulpAx$bb&7 zvWNXzj;Zn^=pIvD(O3TZCBrgmbLkl)oswuC|TY?yI$oTnm7rx%d?kG zkv=S8FfgNrRDW+?f-Xk+ln*vvcewJ0vhA+olWe<9tF-fMyU@Roa95Q+KF3bc4k%G~ z7+#m_&LfKw^%RD;!_JTngnu2CK?avhGw44Fto_9lgZ>tm-#V-zpOrNvTID>%o&Y^o zjn9*Silo^zfWfKNM<93;Xgt%GhbJ3qmpuznP zaKE%FwBDXIMu445=r|rZz}0HF4V3bgWQ%?Vc0G^jf_4zxU9LBsB582n_HkOymu+7W zqNKX&t+kgK7d*TJ$1%rU`1=v9K47@4NbZv-q0QW6&st6aEF&Vdo+#Dfz4UlK&qyHO z&M*A9JD!EsrVBK-LD0=$K|Yvx*k2Vv1!sjm3(ih{K`|Sg_5Ri@lui~@m$Oh9zSNn{ z$kIlXh0-=Dl<^jEI9 zl5fBAU4vAr!s>&brswapulhxb%I4qXmu}al)}Ny9Nb~n$iwMX}OSl}KSfF7AuJnZ&Z^0-vs7F9iG8`&%g1oPmaHP`y@)#XniGHUD^J~E zU8Sj>Gl9@&{^s|x0-pEO(I;}lR}$^3cj0I90wo{SqOkd@-~0l-az)3n>rEL4jYyHa z6I0sr1$TIAPSb&nWar@4x7(i+1SMldURHisbBRlBar`VJFUpNB zTK!Mbs?SfYK9T2!v^+gQ832@i2b8{&uK9Q)2^hJWM6TdDlkO1A=I3yr(zn>riQ+Oz@9Jl2D5HiHd@=EYovT;( z$46E+L9wF2n~JzwLfmQm+|d$x(Oy-etI}$irj!J_7M`8o>Fds}%VDi|*t1LJw#x

    AZ0e$kK#wAhjaaBU7TN+YhS%-Pq$ljRj8od-#xxiuhek5@pGAe z4E)(VT?4-{-*-T^5Un6|;l_I;SGcrLSVZh?*(X9bkbZpO$O=-r+p9u9=+OOiuw++q z*@b&F(Mg0M!)qJr8vZYCRvP1}y#OVM!`5L9nBn-mhjuylgZu%DYwR7FU53Vh-o-O8 zcBxEZBzHASpu!al(6t7O9e&+xbVDKEV}Z}`WlJaH2CW)T?tS9{XZk7QO; zwr#yP!^9a2g!2R8MHt~q^RN_@`XghXktoUd!Gi=>vrjHzvbV{2O7_;C>x*0h3UOLY zkM-1YO{4_J*=28B%7O{KA-)K&1D5;kqrXdoaxTfaRWck{DINl=K&EJ{OL`Of+&6M- zip7HX25CkMP^9&pOxMlVM+YnAj@<7!xIqLhX6N zA_hVo-9ut;6I#GIt298CYdH5}QibiqukK}1m3{E&N)|_jF{CCq=hxm6JA~*l1x4UYU4veV$$dNSiLQL6_~;lZ^Iab| zSqC%TOYvcHje@=9tv<_(yGcKhTe%*9LpN$!1G2mHYR5FoP)Y|=)<@6&?6G|3y zhj)|Bn4&bw;W#mJ;F|lNL^(X|;FL&)4beId0@Lq8vUi=eswvSG$Aq zh5dQ4R&-^~2ab5dvcdlAiEntB+&3o(4q|z|V2dYXUD|#f{Af{{A5>NNiu^$1yS4O! z&dkrY9xERzZqXyew1?^-&=FbGJ-jtu>TC7L&gVGlmSx8n3aAw5SmE4R7WFM%SFfkJ zTJq*VuWCPdoV}@gWb>Z(Q0wrQ{N2xUsaX_9Yk~;-u8WR=_;I(7uXQb%x_wsVMk4fj z%l;waX_a-(C&_y&xp%Bz7h0zF%HkcIZtJ(1iw67I2r8;!k-MvZ619SCh8gEZ27N~H zjI!m=!WH(bii{0GtgMDx1UtjI>&7QYQt6uxN&0~#g@h#ikyIgPUEM>DcT>;Tf~XEc zKJhoNTxAu~>R_LEY*9=v-cTWid<4dM*J{`<%|9hSt0GJ6_&Df0y?Th>FuXB@N9vl~ zmRebrbFrVl`&pQSu8Pnetw@f5&VkYnu81t`E*t(;yvRKd>%hA!4c=Uh<-mLIp4ZDl zUzH6fII%$dCAnix?uuJK20$L5fCP#M2ow)UK(XqqA9Pxc7cvvjbANZ#*BX^+sj@1f za3tK~ZJ+)RAcmzsc9Kk&IZM{z3$aV77bN_Z=*GB*G}H!{E`DOkeX|N39YRIp!ytrH z-3LgO)&X|u*%Dr*2mhw}@m~A$wbkr@wtd479W`iL3+~#Gr+OCnM2VSlo_@eVGS5BE z({DPOkC>42oq#Nb?4&GgS>5J5{f{dW^5lV=6=U4<^b1KzZeV-yc4vgOa-P2N*tE^d z-LzWXP2aQ*Mc35B(@xFRNyBvdIhxgX2*6}~j(;3-^!4;L;m?R!TCTZ#f02XPArMGo zP&C^E2lHw|dX#JZdyx<%O89RjbMkhhwaQFt7{Srx6Tih>c2M%jdVGS?_aV%|1z$q!~1MH2v`Q*1MUV{tiz}TNJR%g9=nIx;>34p}yCRW2E0a`h*zc_BYoWRB=8GT%F*OyP)ZPfzJcF0q$J@Nf5jwnQ*`dDhxQ4- zrfuo)%Rbq_FXi@W8K)g347lStr%q8~I>}=UCM3e|8Wigcy-50hUke~9`>q?3QYb}= zK@D`8Yht9>w=KXezoK9?BRR-kl>AW!vd<;WbWy_o*`79x&BXAsq(XT2P^RUN$x7i?$u|(%P1PEK%*?fB+8!aLcnjhrJG(^tJH<&p zk9Zb8A4cyZw-PoFXT9)5gn;iccEeO_xUTUi7{?FgenDcHbMiFzYN?D$-r7VP$Q(iS zx?lIWRBop0(`7mUU$-aCL(7b(i1FB;*UAy3u=mH*b+s=%Xv8!bRyd1Gjhhtc5+<{L z5+CEk!`|z9<%+nM2TsFP&=(xTJ%r#=~acT?|dZpWTR&^9$&4eMlZq3kDQ@-);y*n7|ZOE#vIG_*aN z^)+BDH!gli55wN+y~+lgW>dAs7OkJlOS~}bJ-t`1$a2Y5yag4)<BYF`6SwPv}<-7aA@}REFh)jGa5* z1N{HVRHP$qqWSg5n^~uhnkRwRy$S;F4*`hTC8z1qAf@d)3dsyItSJ%j%!Kdi^Ze{I zcehZtwBqRPWn+l%)i6@9HT0r$qwIlJ<1z9fET;(3HfgwS*&${QA73}s|A z3~Or-{t*4%v2r~lb)xL`aMYq29a1YJ^nko|TkZCFDBm|zt({SO6xD7xQ>vwlolKbC z28e8Q8f~m_f1?v|)oOwJ*btu+PKa}9N9k;A;9=%zRXsHiCFK_;rCL(-VyK^!A`DI` zL(}^zU4JBYBNJ}Twyz*SzQhCudQd0F;JTt$erxrNuQVpG7+gD%8Em_dlDpT55_2;o zFpN9Jp?s0HwPnzql(Q)c@hZC_?D**^S7%2iS6JV>yFm2wcM?&GHGuyLf~IH)L-(i1-B~O z@*@z%Kk{Ez!!BMp3T#dekHz{+2z?4ojEsJd6y&{DfAa@&W1Q8{&Tppt^)Y7CL;W=Q zdENA^hNaSu%6gBJDzY9SK))xcV&3Yu8t@w3EUNfE+9IrRy(ue6mC9o^v`hDAA$9WQQIiHFgq(IR2*{ zh!3g?;f9YK8i ze+lacJA<@4OyNe?`}j@RYhdmz`mj=RRVeg}oo~E1llCWb>FF zWOJ-2^U~eg!`_E_6;F`MUyh(NhUC_8h&=b&!*v;6c7(eOM=p`Y)}+(3DSn8s8)CG2 zlIapdhG3DX7s59KB1}_aW@kIOe(&mfI|7rW1|D)achXbn}VDIZ~L!JV!|78 zn52ch8S)5?ZufP2y}UYCqUUZqg(^;I@#gW3dxP{AVk+3cXUCW_^&Z)AhP3mACYpKF z{yP3w@;tyR+uR-ZGavup_kD^U-gybp_tz{vQDO9$9|vgH=2PYh>agoDwkyF-5k(Zt z`-AVDqxT12=1-YmffJ?!4g7D_Zv@GZ+O5Wjz9@4tb4F|(y4kSz>|T{6Ab#R84#Zcx z5PziDf%qG|0FDjvTOj^8FZS5S6yi@wTG;!5JPgGD%&QCWdAtk6@8g*Q@oD^YApYLZ z72*r{LKENT;Xr&euRweV=kFBa*ALMpHw3_&C3gaA1Bko&FlYZXNUHGuq=;^M34OGG zc>4sOOVjZAD3GHkJ(~?EQ~2lahP}I)%k*4{-R;=n021!`EXCikzSa!c(ulb|)OXgT zyt*N9S)INJJ{bER6$tV}lEOX+)aZ@A?ouv_k!{%rfIHfym5%qq~i}(deUX+ z$sq;|F8|<+zf^bz;vXajFUOth83xKN4^a<9!6m{hU(n`Ow^g9h&0SfXNjjIA`uG(6 zm`~n6nomf#j?TBD0I@l0F|Jh-z>XY;WDjWvdumVhF)-PH2 z_y8(;VWi>v9eF5R>Qk5H2qpO%eQH^u)D@Dx(Qb|oT@o<74`8Wm@kdxwi4k&yDEBK(Q@B}(oQIL;uR+xua27N|t zQ)EBA0SW*}iZb*lWbMIv7?PPg{bVz9bK>V!gy$9c!c+69rBG{ujz}%*6SdrwQVX$R ztcIX;PmWdkLUAgKon$J5+ns}!NSUF3NEStf=SWSWLcq=NYt4YwS?{%bBZ~1Z=kW201JF@ZK=fg%$TUA-*tUE@rU4bUtF|@mb%@Kf#iI z9KDQj@2H`7P{M52U`g=sJdkMQ39{uQl;4lGqbL3B>ps5!^!`WKW3%!4X!e`k|9HH1 z4Q-bkWzA44n0x$V-qOA9!#?~#$F(qd{RP1k=vwIGIbuR1QV32j@W1F#xC*Bru+ zj3+g@3AMQy-qZ{bYwr|GcANpe)(P1o=IB9HvXQ*y{tRAzuahW}v` z>WX&z_RD0x>-d||ggDWz8ZYq(=7{?$W_u-a-`fQ;+tIqqeW3uG$&7ry{SwqPHw0M? zU1V>m93|(Y!<_&J!y+23_4eDM^9qi;kK!4%HJXEMsH;;5vo~ZarCq8E47D2m%=C*d z+Du<)VV*BEi@nf~+R$-Uv&H7re{a3Atqgndve))xVXz4RDSc3r} z?=jMh(+0KI{Z7ZwxCH>L)C#SNmrdUZiu)Ep#!!P1r}lL7ZAdUzxVu2Ri5hQz@!hXv zQ|UmwW1SPH=LpGXS7NJ;8D}^g!%Uin5C^lj(S1I(1N_a(q`GmTBI6F!BmyMk8ZV=2 zsXQ5d?MZUrqTT-S^9(cM-L${r5`4X{a+grfsfc!Wq4t5rS1@;<44$;la*6N&5g!?`=Um(Lg_(uq!4>ie7(eU-0YGAcui0fC|R z3!jO97H1Qlkrz4Rbi&}&rPXJ7E$~wc2o?V(=Y?f9-ub?q9dSV67E9Rk`}PgRr(Pw8 z=Wvhi3^cCi(N)WVvH_@X*W0^xbaxZlT51`z`|^F={m z&}vCL2)h}w&0TLyX_x)lROT?$l5)ks^TUOMftTzaQue4MraC1;62aM39Q}ymQPsgY zigple=>dYD%I@ZFAE>&ApXaFVp?%FMGNJdNR8kd;#6B|mNXPyTbX!^K zcN=!vf3VCnsGd>Ub?!gkPkDjx1r)*ia(N}!I;y!*?D$`k--G9Lul&w_I9D`2vsoT$ zFS8F74i{5IxQGQ|4~+|*5HCql8Sk~T^*2GS!QK$~c6WL$F ze&Lq{>1eZ0Q9m0AvqFq(nT*4xc*MyksMkr&h>gD3lhF%*ubnOB?b{DbMbb2Vy}~zf z{#^67*%znfPow`Lu$t`YV@5ubs}_mAg;*2_wb^Trl%>LjfMQ$_gD6CQGk8UW_PS2M z!RSa-r8K|8xyyzncKGf`z$DSR!z=(t+2OAuHF=T7`)-D_Gy_mZ%nVJ-b1u?2Bf5(S zndjrHJ3++FLdXcO;uq(=9&SS%c?ZNR-Q~)to@Ur*e=^zl0*#WW zl>_6(5yyfTw!cU?X=7ztyq7m0SH3R_m>mv8KQL_Ub;zG@|Rd&VaGR|L8foN3_ zrw?<7(WsP5#(4{=NlxY~91j5mcacVNe>gyvO)pi&bN@(JRUFCc5`Seba%GruiJ^0o zYhoCjnLu+GE9alb^J&<~ynjQ$j9V#E@$dKme`3~OV$ur{8w95|+XoFw;*=SClK)5| ziJKpM_=Tf2Ou$O;D&e10>gr6*6@11c3o9yPZmkLzVY#r|aoU$?@5-;SWTN~Q1oDM% zV;yzNI@)XhSw6P*7hcUoYBAw$4VzY~ZNmZe;D20{&S!LV1Rdw(1I+01of3W16F0wY z7xc*Qt{Lb9EchfD99+G*Ulnm6c&4WgeL;D)m3QvYByEH$Z4p$!wcp_-#|zi0*PiUM zqnacd-F=){6Jop9KFT?cpy7{%*!-81DG?(nc9LX`(nSIKY@N&;f>qx=6S8_0HdJPlYj(y{?aVNg8k99_eF}do=fxG2@uqgI)a`N3z!b z;;k!(qZZ1vOeR~; zzsS`M1s4F0Fd0}^3rU3*5f;O+SYqj`y@ekTr%K?961pzi-n2T=+$?6>Oo0vj55WWG{*2ewL-0l-s@V_5`TQu>Xn0R=NJcd_o?WEGei}GVHZ( zO0G#cMp8HvfqhU@Qsd7$_8+qs%H;}w%yt$d&Gt1vDh4`xC-<45=#>RxMuJX)k_u*n4oVG6`FeICH6W<0Wgg z_no2rGsfZ|aI%m9p*N?zDYIal58(~Z1G46nnfli+>0Dmf)l#`vCe8i@5_1(MUg8Ll z%i%SK94+slb6JxhmVHo|{YmsxyX^04eil~5dtyp~p@}ABrESMI*vWr}=*;BkjUu48 zFr~~O-m8>-M_oxzu zco>sNj*Kae?l+Ngk){9&NeQi6J~Nx`rJp#g0hz)r+*5-`ieUuSwQ$iNXQl+N!Tj7` z9@d{8<(|q}SmdwGk9h1wc>BgyVw_H#cS_&?xzFIEB&|-5cqa60=|>qlDSs)br$bEc zBI*+6rAsz9A{#iuPkNw_gC9#f zBV5o=`aOPVRd|4?H^G|9YI8rLxHgTaa?ykT<4>|H2nmuI5^9~I!r6!b7O%Dkk`lj$ z@;>@M2>^n9#3u^LS>;M_PX6dqcuFBtIEg3Ur%Y3rg#t1WrFN_EsuAzXFJ%%9BYz0* zVA(_Zs4O)xcwd5l6YF(v`?YL}MJ_Wh8j61rVnwzLy}j$-yu5=B_vr&4l`|-2M=y2Jp(p>-2N5#r|C%$GMMf7*P>v6_M&UgyME zyb$URpVI1)_2mm)kWIABo?o;c*T0wBL*OFH3I%ir;<{dYlmvHCgEUJ_EUB_Lzc>mN z)r3M1@A$fz^w{3^loRP{Sq}?KRKiXtt!K296=rhSk&0pca-Yjn6HfPtaw6rg<2)UH zl*R^M_CxA{zKk)vs%rT?j-5};U}Z}T2^*7YlaiGH@pUV)zgKCdzr;DD@vFC(Y-9Js zsP9J6d|T~3719xc$r?#&3`9UO*iUdH*M9wFiXbwb>TT+^uZSg4Bst{_mwm`% z<@xhc?Vtjm;yVm{e5vsH8h94>1yA)qHSEnI8Fh7H)FN+W0=6H)1|)A<43`g8u}nk^Yf-P=_8zn;|_rH`1tC~P99TEUYIH8EwqbaHvRvWAK5Usp$z^}r=A!3(xOPm+m z1l+w<_=~|cvr#s6#;-6ZKh6c^%ONcR;Vy0Eya9n*qQI2suO)z4WoIB<+Qon#$Tj@Q z(b8EbPIK#cA(t8P!&36y>g3yfzvSy=LF!pQ)vTm|9OsE>^&DWOU7kvRP__NrP7HS{ zzgH{&o)~dr3t(Ig9CFM5smtdH9>&-*{2~bDSSPi>q<*=@P3n8e2% zDPsSAkF1Loed$lCwX@+#KnNK^JgQ#n6lJeEXo-HmmggBo8Y>E z|1dc|4Sq$A1>}f!WR+&OESgxlytbfV4&FH<*cd7bj$;W{6ch+oTqxc)MJ!4dtNdGq zUt6hococ{(>+km?q2Rhxno(QSlp!6LSVj6)TK`qlS{CCcZyhxgjYL>kyafwa?C&~i zTDOtpB{zTDF;wRGMY2 zzN8=d2b48$JE*y%fAF2NbJkVlJQvy=YbFPs?y`c%)07dk%O0kYEnfpCx#Fvc^Vyl! zvQHpGfyn&Ks>tH6szSw8p_{VHtkp;Gx7ZUo$C)*EPMlfe;v37lV>>NJX0-=DPo38% z%&ct_JDI`A1?|CoGUWC7!RFrtn+L8j zL*5wNN6Rmeqq2c0UVr9A#=ds>XK% z!ppF~U8gLn_bSqj`ERkCGMG>BKc`4aA)T~mYl=M*_{)NX>ycH-8_UpJ!vc_ZB-#pD zL*jLTUV?J+K25%kk+<1=ov=aF2^F}g5>J`xeW8Yeddb<^P_Ue*=#%7q1yA{}D)je) z`+3SY5twAH!F8^XqLDib>iKEEgUkt6<`@4>m)ncYywh>pOprYIooNz_&ACVKnQ5^H z4`6BLm1SFl9U3!+>N21;rScSFAcSX<{t#yq6g~^jG=co5!JmdU(v#JnN;n9XD2I_6 z)0$)*=GVa7Zi2%N?#rq+9g9;_yt~>^as| z+vixb-{tPUcVEF{SpM1a&K&EN4pKfOWd|wGN{THhaZ+MhI!kW-)3^S-wUL{9`OF=F zk~=E7xyI|2XGqyV%6jTqFDdfRmgh)$!5>_g?GLW~%4)olg;ch?*;6~!?P7bPi<=qF zh)f;MRTQ1QiHbH!Me@(Qle+L}q{3IJkPck+PjYM_$IEUGO`!v?G*hGvf9O}*a6G-W z&0o@9dz|0X=JRY!_@5VVDytt84Mcn%>({s@w_{oJm#nX|8HWSlo$IBWsKn@S{6X-g z&>vba4hqKyN}6kqEw3Lt@afw)G~vBBA@7AYvZ~~W{)<(Dgmv5TBCZN;5cwyS*A^s{ z`U1>ys7TWC)(_qBb)91gQ5(fpnm(=^&9HRH^MBu_|;@VOIK6aY!4dL zAz4E02()0TF;%d6}52a6`E;UmRD_d8*Tz z?B0S`~V>YUB=a#4h6KAk1fL{IvSTXVSTU zN?%&J84Vgr3{~pi!+AqrXtA#56xTaTwp`}HB2{6sZu^zx#0S*z(dl}__nzh77L zOrYdTOwN95!m~)|=(A33V1s^mE<$PH*r!mS?m;-f~Foq5#hzR*{-|MYow zN_(7u7!wZ>$U`Eg@Pn8yr8L%R1)pXl0-Tb~Rx-QZ)nVfw6WTo5v%Gu^@CS~Z<2YBma5*$5J3>jbb^AlSKsPd; zw(eRftwnOr6oi3)%}2)o@v+N&LW=^CI(mwB!XMfh@a%kwycq)4(DenS=S5CD(bxTg zyw44IKH_Jg^K-U9`*34_UhGSQ=snb+5LF|h)C?V9j(HQ10>(Wx8;YYcd!cvctT;C^ zwy>&XYfWKQXeV&1DtXBwSfg@02m89=OqOsYI9hq;A)|buFKai(?-F>ziqDn`=Ss_^ z0;NPe9SNIIdqtq+Z7V3+BZcaZd9hZbeI*o5_Y3~W{BFQ|m_PJM?M8*LxFE=6#YO>K zp?c?wmzi`uKA_e|Dac?+Tg|`fZ}DbCZ`sKv9Or&}i2feKY?!*8U2DE z3~2t~pyu~fp0IPeLeItzWi`fzGe%_Or4iH(!UY zK$ouN=-pY~Fw4sf(d1eB61FLkP=p8v#RSGEQ?%QwnPWYB1hce>f0I8GFJR^z37mV+12r5jllH|F6@4EPVJ3e98UG$oFbjtu;g zKm)k+rJ|;@Q8|w^d|3Js0zUgraaH*8Y*#BhDO_H}E>(qMjV5I?g@uQlKuGyD1~x4| zIz|6Z!#Kly2xp^9o-MlpoVu!DuHN%p1>Hc?c=SM;Ig5S8YOr|Km|yww5?LgB!-3Z7 zX5yU%LfabF-*i%ZMhgGb{NA9Pc<9aGT9qsF^}^Rw{>oat(WF^Hp%VV!US!y3v2~vV zBA)E2-}|)nBM&Y4yVnzQjOt$uZK&UlYT-jNOg`BYgbmueb-@^ZO+PuadAn*4UKTph zs2TbN-y6{U-a*ap^e1P|n>il}mxRQ4SZp?p%vk{e$$2$|6yW&xIh|Qn!>k1wchnvd zD0$vm_FL&t{n(S~%+Glx!D{#mFTt)8YG(vO@j&R4VAn}RebrZg@Jo-?a0Kc8;OB#D zCUfW14!3@wbx5W*l4ISm2ZF+>_lFobCjvhXVS+euKJ?g6po!Pwb9Fz}x?Ut>ATk48 z#>F0~bN*k7cT1h68KJGzcofIz>@`3s!?$OohUA>^3Sc{-O@zp*;Sbho2#49x5{@}#-G+bDg4K@^CM$V_K*BX-b;NlzB@oue@Se?N!%-ba+&N6jQ(H- zRpqhSB6fpfS#`(7@V;hAC;Xp?Tx>P&knVc+Y8I=ZgdBof(|n3SjVeD&3;86}@11Kk z3YG8$zhveg#yQ%Z`KWbg4$Z-7L2#m~0zvl>dXQlz+jFpCleK!02c?1FLKSWinQQ^7 zD-cY$%v$a17u-IedAs7lwzG5I$l17buWAq)&Zb3zdt@2SN6n1*^=b5uB*ChjeW8s` zjLe$Cql<-8nv!_l3b7(YBU9Zme|^-j{@gj&Tae=)e%S~y&ovb$xmsyVu7&i-PqVCvp z>TfyO!>n1%ht4l|_;tUKHokyNIjcPo`qCfzSSLONfI+HCu)BZ@2br5-*U5DY05!$?pKNZ(zt;%4RL)y zS^bhsdif)2@&}*I^aoqMiXA~4g5T#e`w~kd?wmKODp_YWOuz!=55Xt)vsSk^Y+5wX z7dg)puhsSNYVbX%LyfQy_4~FiC?|5cJWuG^&d|ODy^v@Z1hDlGx#BtU*X9KGG46|% zdf=xz0O$ouzOrsVf)}P^oGhZBIdC#`7fviWjgPX|Ioa+SOp(}f@}}a0%)oqsn9S=o z5(AN|J^sjeKy;N+J1r6|Q9W}g|A}U4xjY?h5>;mSnncu;KS{kuo{>8X?$_t!f@(X2 zr6KsFeqhVq!ofga_-@rZp}}EHC(~+GB`swR_gEQc%Wn#>Tc>`1i2Y}jPS7v zU1UrfadsLupyY^aN5$5{Pg;#j$*Q=mdr=xpeoF>{VdE{*hUd$`NOZZM1wuPx6Hq*s znH}0$7SCd=bZdW#o@9V&4f$k?U8c=^&cEoJC`=W8)^=@qprmENV0x_dZMO6{6piN) z4VMs;{F*2XeCRXZ$Y&Y7hhS5pSZ-JaAW>?uc5%&>*{CC6q{lls%=9NqMRb5@AC;Pd`gP-5X# zm3&|%Q)~4AYxU-aEsF?I)N1n9ycXyB&(e&t&>KeezED@d=UW&25#{OxL4S0qXP4!4 zvH(uCR?kXk+)=LzsYI!Y&fVx$Ym4blmZ{hZa{pU*JjfSS=vL;mRfN{LX){QR39nYX z+n5mTCobF3z0y;AWbAjK5k~`@&wtR*R^y#~^)=i1q2{H#GB%^|XwJyk*$pwy-r4=Q zQsnaHar(TwxitGltMM`lz_(O~nwz&BAVk}{9MMN0*_CfC8z;CLT`KRl7faP>464PH zG21?Kp2Q`yW#u->k;E)A>CSc*Kc@5(nFpfvp=@o}@gyene)0gM_( zel!oGUq9k>k$4C=DQfbF_s<<73mLDG0b3<+?j(JXYzn-g1p=Or$sKTWn|@>Y5>bEZ z7!{%--tlg-CTxe)1UR8HMw98~oz(EDo5igm#$gnDh+D&nKtQ`w6_Nz4VXwXW45xt+ z>B$7(RgTtlu8V#XC@q!KlB4K zuUpOC$sDY?jQC^e;z+uDy7Z$(WHP}9WF;;ldI}$ij}pLeseu_$g~HLyM)_;h`##}4 zeH!Ua#EaD#pv7^%@PT|1KTpX#S73^M>`I}zk+E3+cEm@z{4`OS{YFf;8t$co<)O)c zS%joOoO-KaG$pLn9%T}4dNpKN1GPyCYj8GpM`a7JDSZl6th82Nupe>3YKT!cCyBG1 z#_E2-G0$ML%NMv`m*Qj_Y-WY3(BpQj@%s=+S>FtFT0Z*7LLBmk!o=Od6qYr?;{vlc zx?hJ{qD;O<1g~mg`7ryUxS3oqxBGRzUqFFbT7XfR0(>Pxf##VcrDkhJ^w>krNY5B2 z1pc7}$3#d>Mq6!Bhc211G=5+-BS9P2NT271$^c7Idi{7*v%xrj--#5?-F63L*hat4q*haZZ;QcDA3{RQae6I4WvzATe#b5Hp!s8K{ z9+xpxjoij%*9D?o?c`#DM?5TQQIswnEgN5;61VHcXhWo!#SPyX2}GQkF^#VX=ia{E}aYp0`J z&t^i(!&QH&Ee}<(NYGH0v@d+yZ)^@0M@X1sX|_C6arYu1E&ff5tcDE~7TD{`#4nsq z5nwH}Sw^Fwbz0&B<(K^6z~JN7hxZ>S{|m@+x-|QdyzAh_v&Qpj@owMJ&)aCOW@!8@ zN!ha}{rmh8zR+7v{AF7%gut}Z9On>uOM!3cms7td$oSU6cl_OJBU!(udl`|eXmH(k zi#N^L^J#_k*k-@yQ(q8l%~H*sObhzoFJsdMlDJKb6}2df5w$#LEl4Hi8Ohue>;;}s>)(k(Pd#n=TAM+FZu?%G{wxgM-uvAYlzw(l zzxMDi;o5dCmeH^&vN+i5?xKv?oqVigCD2{=8@Ho@!XBmiboC^B)1D%ykeD0%8bq%a z?8>ZrFA&DhibBs{;&iE_j$Mh6NUzCNJ0q*PU(@4|w(jJfE;w)$bvG zn9+YJq~FQHc$f1gmDO%w<_B7<_p+oG9W3xZzdg8j;QY-ov5=te3w<7ZM|c!9-IvyP zZ(ZPrj-G4y&28|TyM#@dNRGw*g6|Aye&-r!GN3F=-B95It-ncA&o z;zku8&6i9RH#wh%wz>&3B;iOB`Zd2Zs4VA=iqK1LlAJ$-Uv^z4%B=FP(B5Eop0%uy z6gZKkZzv~n%odjuS#gp~T>6G)T>H?OwPSj~MGwMxVfOivqcX*0TD+U}G`7fB@_NlB zjA-ns7)pxeoFJYeC{yZj7TFq^^_%+7`_F$JSj?(B75Z6(>I##^{J}!fi?PYp9FHmq zDPaVg_z0PP5D*3RMtg;#OiNj|Sa$$#Mto4+7GF2_@E$I@Lo|bO8a3SjcJWli8M=v0 z3Z?M5+#K~r&5_hUa8}(Rn9&0`WcymFk^f>#_@_Af4^|K6WJlj6`KP0CIb>k{zFD>7 zBa7SV_`vz!jV%5RFGti^^=tnWKQu|dBa5Hb?}hbi-vODO7WTYfeORfr`gIJ-H4nuH zxu3hEb=fUto{UwVIPQ;S*~QW2o*o0Zc&gZT&`#!w3F0I(7-Y)Kn!5a|$TUv?=mtuj zs~N_aj+j-)_8QIfhf28=JznPyjQ3h+Tq`8+lbx8f0UrJ}Nn$!TxUZ z8B2*5RaD&pW>7j98CPsE7-sMY4>B&;x*)rTecpqu&0Gm~L0-i7bp75VY7XasZF5Jy zFQ;vt`o^*}67Y^v+k(Y2DGn56O6_WQ;Bu93Muwce2&I6%$vZI*wyTYrjj@nqg#6Ux6W+ zEN9s;^Cg%(Q5h~gQC4%^dlMrCcs;dLjcid@CDnd-+Nwr&RI$l+ugBm&dt)YW;IvW& zU7%vF4?r*0RzJ36R^3ju%WS84+p`s-B)7_OIE@qQ`fZ$kdqxZda(*|!Dfd8VNp>J~ zH3wtshQ@QtS}L=-U8s^?Q@L5O?%fg~(bZ@lIQSRw{Q1uvIPS* zUs)B0%c_{`uJU|$mFLJR&lCNDyS{JI^?-%aADWrR+*Z#`u7??Zxd=u$_{G|h*t*Nv#Y}OrFph)@$_hWajPUH;%}e+G3v^rXePI`OJJr?Y#Y^dKF>$E0 zdV<46yh~n}{c*?)9;FSteO4v(X}5>Iv{bR@`-5$H_9e6wYQ_a&eO<0|uT$A_xjZA$ znRWi)t@#N+pLD+^5=T8Z5Kv$f($`O6_dst}TF6_TlpU}F4D z%!@y)6ZHSf`T729z31oH+5ac=^VX|6KTq-4-~2p2>;H6q_MP~j%ugVEF^gwBi>KXC z8C^W3?&7%*L}u|kZWd3V zRaS=HBPqDvV?DJ#xa%nVDucTQl!acT(Vne2?U+VsNc;UQI#>E1lo`|A;a(`V<`_oI ze3|w|11h(1#lTsvrxFl;!>)YGUMNKKYmTT0bp+dhQZ(3kRI{BK>>L2-Vub?Ar({Ba z`)$^;AuinUu{(#I38y#s50TPak;`D+;o8|XhDo+2L~@xIX8q>B64WD-`wS1eTMzx# z-W6LfGc0qMnNd?gU3n6?BT?5{x2|U-TNCuosp|!rq^_eMrmnx41H=ty!+OSVap!#8 zPS2O!)*Z+5aZZ_a1D>hXQ9%4Y(*~mXz)_!8;t>tAWj;ixsn`6zvNwiS% zO_dK@`S8XZ>$;cuu;7_bsiwhdS$nM2(q1sfx^mMT>-Dv=wwjqIz*iPR@$-#Pxm&}# zC*=t<NgAF5I+}+(#9vLK#=)l*0*$#GC5#Q9%+Tezwu>(=Z_=6vN;Kd;v zR(WUXW0|G(Tge!Y%>}xy{KV%qH){p)*d)G}KGttdo%6d?e`jeQrCbre3B#HBuqV`J z6+*tOLMT~3QMbG%_yw%^UM22+u{L@?h_X3@LlT-G*g1%j8HY>?!J?QzfXYQBeIwthxsZ(Z z^O7-7FoTS5T&zl?Tql@f%lK>@;?OPzpP8*wC5I`8bU&9;cQPsVD+*(`M=}gPdpVAX z94f$oE+&Z!ui4QZJ5n$HakhU?hp+j=1Fl&s`9eEKwpx$1Lgub%yZI1|IV3hUP2KJO|*1Ofjt{VBF^%y=ZCtX6r z)?<^WWXuUo&N>~D{VSh%gYO$yGpK%T(elYzXCQ8I69PA|Y0g0x(3F!gG6>CE^BBct zj$x@v)OfN*ym$Rd0wnEvL{1smS4@I!X^s6feS#D7i&&TtpxR}oRQo+1HAA$f2qAJp z9rxw@KkD8EJgVy4|IdU3!p$9&C|cC0u@Xfj7L_Py1{0X*1o48Rg-WZXc&B7Wu!;^$ zMl+66@wE1Cwbl069^2AdQK?7*Lcj|mwu(|Uc-`ZOqP7t5GXKwat-WU^h@SR$&hz}A zljoW2z1RKnu6Moby{&1S6u(B$DpXTR6C1bxg-+rnjVf#g8En?&GM6z0(suQZJik+{ zTu>QyX}W5#nHskC5_iKnK+FS%=D1RsHgnee;Bmw?hZpc2_cR+RkBlVe0IM94gx5~i zJlDVCw5>M9Z;qhlXAeQ^t^R}{Vh-hVG=C&hsnmXh0{Tc-#i_`OdVsEK3wW86$syKe zA>{J?)Ei6a>bz(AejAm-2-ttzg^@^lHYco42FyLw^3GfLD*H`#XrcY-DJ$Tpw|=AS zwd88)uQGga(wlc|nS)Yk7kGAcDA3jVn{3c;|Gu>Cy>)6EyLpLw-`QrdoT>hIji~`IcM|&-}t?=dZy)0sp?FJ9~8^4@; zfQLQ?9zwvwWZtD*<^SxF!x}8{P(r?0s_GI&Qyg?BBfH9zzYvF3Kj`%fT7eu zv@`mLObaFW5H@SE8Oy;00bc{hU5J7WX?KUQ0S3lQdn|l8@fS&EVXRYAVqwgQtui&| z;p`U~=|unznHLn{EtvzDhU-uf@IbEKYxPo-q^~$WXPLQ(kcK?;f3oR+1^w4SFci-Q z=Hj2xF2KEu`VY!sKR}xCzI)Md^*xHW6qOi#ux_hBc`9wuvjv`q@JDunu8ma(@~~~i zIks%U_D|QSr3SWdrTqUEw!H#veQ)^u2t84Yl<6Rn8Dr*^4-S=D{tGL^Y_@EhRkk&X_{OgTy8iJNPpmdA6`ovxmYQbx*iTW>$JYd zU#7=0Zz5h!@S${+ol4*-_86K5O{TR@;(aY=vof8wUl{n$7v;pe31HQ?I*I2Lg@D6A zYW>dEeT4ZyupZXSXzA2-XuptLJ$|fN&qD`_nRr9`QoCUsk-ij>0hA1T{BN@RdHl!p zrN|3DIlzfS*q4Z>_%{!Jn|Hk7#3WT`&-j}d#=pvJRx5loCaEKYybeJ6Pse_-ijH00 zVo*@q&j`y5BPPGTI5qx63Au6A`iC_5#Mo_p;I784opllKwEZWk0p&gL1RQ%q|jHKsHKw5*8Py$=f(DbbiMUPU+Vbi%#Bsz2HN};*(k`{472R zeI7n}>~ynS4gsGqDiinL9iu$>FJqKq>U}qRh=uPM?tX#L`wRPSYT~e52mWg6wvPWN z@X-+aTVT9lHXrW~Ta}cM`vYJ>-o{L;IB#nV<38CMN(3zIE5Up3B8_ zPY2h9lTBd4g)4SXImUoVQ-=YQ!}Zt=OlnWd_jkDp?V`aYB|t*vzI?5>1GCHol0uec<~(JRseEGr zr$yP;Onh_ayVWv-!paP5Fd03Bdmyxt=(7A6JRh=S zaHUChZnf<9s8c2LuR*H0EEKdw@yu@9?LPJ*3@#aRgWRh#-?pu(2*%z-YsAiDT@&(- zboq6Unu(mQ2NQ|dxghztkb1pY!T{T!$;S4pD>CS*iJ|?F*-iv#cN5?VfH-9^F9jix zhqud(5GaqZ=aRt}0ts9>Mmn(>1cI=Qp!eR)8GK1A!5FqrvjZ=@ou8V?OpVa4098gu zz3(QNBeXkHXcyco-G&rHr(t?WcwN)$-tj^n4u_b^CXxb7k41Ee+#NcJ4yEtIb}e$78Wq|FFh(^cCZ%tOqt$gCY@oQf5|CXUu&(BJ$Hv$g%< z5x+CAeuV0Zv3cg}l#xFN=nm&-lsmXSw1^5nEcu$q_$mS z3ZVxj?r2-x6T$eWr$!Fq|Jl*w{G?%$o@tV*8`*vRaQG-y<4t%!6|uFEs}F|>rW&!_ z9WB2Gds|4o8+MUK_fnXALLB8F4&8d?!O#8*PFL+TNW*mgmlUN8^@Q8I9b<3}wzY z@8v@&)+ngJteHYM?C0S9daz!Xjn|NzI8bu;v%%@#L>Gt%k zwDp{eeJa5B$M$qM1!`UwImO&&m72tE+R=I}*l|!GzVr0R@$sGE=wX(mJwpfj){{^w zL1I3}(zKH#F@KciOTUVP1NS1Y)Rzb(bP|#~(N?p;Dd5i+s~h|1&epT2W@vvAzvI$# zA%5}fGdw*P;`h@7S`88%l%5OK%a_rTdCriicn?R4E-jhoMD0G99pnX5XB0Bt9)PsD z-fY&R0sHGG6hw0&dvU~?Nw60?Y~x_RU~jw9Yw01lf$y!4i?2AN9a3)?RyWbX=F}Ax z&8Zn2C9+QZ7X(1cj5|1-Zbi{13Mn(`&T$8{4ngRF!k1N@P1uC>&PQ^FG?CFo=;d?TE;j^0)f-EF6zrzRn>mf(e41LV_H@;y53>?eLsvp#-U@`E z@4LDrvyK+Eq@rSEJ8gq(VH5u37bfoXNq!fc#A|APEH}kTjMf$alf+aj@-K4~A%1T} zAAbq~Wj6(P=PBMGIEi0Sg3NQ{QIeyAazYq{r?TR!(8>a=KvP)DyzPGT1w)gKVBZ%j zOB-aZU%Vwy?|Hz`HEnwAMxeGAD%3?V`T1?1QIQ!=fTm(-H=FEXYBbdn!2Y)7LxL(5 zp?6|r@u`jUMs+5;mGG;=Z#2SLgSYkxVM*e%n2Svzc3}uW%1u+uPW0=OEr6V9+ks~h_af;0%v7?d zUB_qmz-wZ!u3Wh!KP9us@3ng`uPYyN8wWv#uHGf$18b$|5#CTZZGF`8bk)&?G7k0H zAhp(fvcCfhBr7W<`)EAyxZDPZ{_GE?AZv?a788@tJy-RntJbkWcpRJoRpEhKRmNr2z8B474ujA(}MHwg{# zaRv#!;&!lSs0P{dc|ore)Er*`G`2m*YcSU`Eb|mEi~CUS5{7X(zYX3~9B6%i(QgI$ z^s6A9ek$juLK5+c+*~vE$frABX7=XRH%|2y= z3!kb*22~i!W}k8XR^uc#s#yZz2gwmm%>uo=9yx^c=bQ8uObRzvyWJ-hvfQaG$iHV3 z$EJ$U2)U-4FQSHjkJQ8zj1i)!Sp6wXu%j?(k!hks4_(NA{bZPU7tdM5-pdo|IGCl{ zefLoTHq0^Ru!@x~qX+mS=IkItk-!tpwQEJL%r?iim{pueM~Lo5nU}L^Nr8W-*|^QDISOR7JU1zA=g$#1 zL2x_j?U-Lmaytz&iTW*}jFYg!L~>+s@nrUgJfCZ{VPLfV!R!wg>#^JZFq;yr!jh9X zLi1VRV2Ixtd!*(!bdKSuCz3Bgkm-c+X)q_+zZu6gZNrIN(`J$enrkzZgp42B0j(#>cv-BS{JU#dfT8Ul9e9E(?h+q2zuK0~dT4XH8LpvZj4!la5gX@R82uJHyjIrSk(t7X*pu^6C==wV zC03EZ>eQ}6uRd&qVLHx#fX8|U-0!*5mezEptD5f35A7+(8<3cXl1lbL{}9t#pF6k& z{oogz_y=U>RO4`}aoV)qr!PRgd%)a-(idRQ;GJun^aU&UaN>`VK)j)6y%{jd-+wSg z80GKZWPZs9v&orV6iM6hK!TNj{#R))d-nX4B7c>O*7vztuR1nAq?p4QG zgd!&ig0uJDw{SMR0B3*5JOM)Vp$Em|;nF%DTC8;ZRe34x$d=Rf!ftq+G`eUT1Ni=L zk-D^XNY^+R+a|NKjb;t%!8u9Sqd5>YP8Tx zLBAOFVrds=_A9hH)1P;rhUi5it1~+uijQ14Ks{8|uY| zJYK?l*ww39d4UA?bI5ZNlU15wze*D0iC#{7TEk-Z?DYiyccK@=1B@I?ZuidBbkr(T zyiCp_h!8H50A_}1oRe6gXa@HaZVFOM?lZ}S+MGlg(2+gne*DCV&o;}zipTI+sASxb zP{2lmC=gdtb2SnL5G2ie_vDwAd#piqQxJ&aYotg*bClV+9%liP;kSD1wsZZSoL0a) zqzH7jX8RDBe9!0%^_JK$%~{ung*-iZj;F(fQ5z28`g`XM$NqsY36d(fD8M^xI|5S&~ zIetf7o+tQQRnt{g!@!jU+o#u5WPU&hYvh&qsul5%#yU5BOF)o5?gk?U8UAz3{c^8` z>OW~cJ~poQ$|weGGT@1gXlr3Yk%tcCIW1~E>WwmVrQFuq=gZv#urW-szfEYEaKdpK*_Ttb1op& zEMiTO)nFWGZpgMokk`yLM5>HWf#pmT2+F6NR7g2nDHo7ZCB#g4M%NS>dQiZ6*E&{$ zH^dzMHBPesbT@Trh4ciX#~3Evk3>rA^j7T;5l4GLZMteNGbh^FGPC*b$D8>&n^o_f zx+aaz&(|(>%bzc3Ht5GUirX}(SDQ9zJ(xB%s!gn(?{~;N73)Ly ziukjxm7F%QK;3;dGE}F5$?`)be!4fPof>g@%=LTVv`N{J9zM@9%iKte;b=(APhhp_ z8t+WP`|c&jm__74!~Ui>Rg-_=1CWwTR1)d#IfW=qs$XBkG9eBI>?l7+rMoZH zK7}ISk`%_!z`T(^iKw4p8+bz=e5F6Jc2sc#=aZW{0=<&_N=a-3r>TMFaG(M`t+G+Q zQqSvEpdop&1;5MhvFbdhP+&h5_)iAQ?<^uK+ssV`99Gk-$iEVw!G6k~`kuF`f16LO z3O+o#xKn7tt5eV4S=@#VN@6<|SAqY*KMK3Og?HS`X3I19{0I^jx@IIO5R}NbvKM%%PuNmOy;C5q)ik1zM{ek7N_D>V zZWZkiv^>#k*t(TBUA6t^elOjvBjI_hCYYM?jh#((%KTlExvYj(M$T7l@Q=bZ6|ILs z0#>lgHsj2oGTw8x)3%Y6@t$*>wu5L%yk~0UnnL3y!$qJ{$vC~`JfBJ_zikXL*^z!q zGn#3WibCik)}hw-2G<*88p-2np%w+N@&@QR#^20T^d-X?_#Iplzg(y7PVwf`RUa0H zXZulfdqJ;6-?52erU4+|lI#n935I|3H50+&ME*-|jn9?f1x;qp-z{$OSfO^$aoWT! zrP>iDBQRW&NgMRx?MY$^U3N!)CNA;{-QO#8AHUEF0y1OCxwwudN&>az|M9mC*@J${ z|5dMy`Ts^)HPNSO7si2?#Qb1@{5Y7tX*u zfQWB*w*fE1?f7A>!7YN3GA4ev{p!75>wSw|yWh_PCxCDDtf%Xmi_Gbi6^(m}JJ)xJ zJ$|u>8d~`uG|upa?ob)Y=RE!CUDAto7Kx}`!CNmK>I1)v_@noDZR@R1{eEqy^I)g+ zsa>N8y>-ZCt1njAx=?x&`qU#Yo0YtL+;QkrpN^9AxD#Y+y6{u0PyHPF)TfvhAwv_U zInne=u&;Fz8<8KDtv1MWE%_}XGPe?Bbnm`c7yGH!B3G{e&SIKDf- zRqRHmHc~UEXk{b_+rzThZuG_E`q(g5FETLlPZ~e`giru0;7Cnn`dZdRlr@2c{B!AR zSrfUwbDxTRgp%+WlJ9Qpf4+?U&$7#*h2=2zXZ~{dHTo@u<>0hwOJn+1kied+WW_OO z52EeNo#@ zn!|$#b~bb0A20i+=y!B*5ckh%yF+DqPGXiF$_Iuw*g$G&w(+%lHMV?dYTW-53bm#A z`HfAA%J{tAU}Ta<36k$E{%L;CYdaiZF#d3m?|H`!L%!B+$1~gKjLxcH@-aBEkFe_$ z+3C%Gs9>izv&c?wS{9eMh+R{Y@&<$OQ+^?GeYE@fk6~seA2U6IIAu@C+uYs8`t(iw zTF?h6SolfDCU4tK!RGA0H8~4DP#TQARFWOei!n0G?j3xEs#di_-tU z#NE7yG6zua_0YLj5qAqe85my4_jNp<<`&B&Pd&2#8;=M2O+In7pi~8iGP#T-;$odO~HRQa-=ZaggN5u$5foWNXQ*jZI`5D zC9>MLXAdg!kJS=*1{DU1Mf69RLnxCiXpqzF+`Yc_)t36#*eUTM{>16&arj_* zVsW_s+18_)((=9?^_@ zUy<|Kr;5faJ^W;i`C;;kj8N8z>mC5c=;Tm( z2Cn?O7^daXTxVkNB)r=T0*~h_9mEroGhARLtJ|1!=J&xewm^2#m zyI$9#=j%+qm<~5%0$Kv^qnjMI3HuTiPOfv;`)LQ8fQ{~Qn^s)1B^9c%sAg}jA_uqB zcef6+K8^N)%n97?;i1ge$r9a4bhI8_Ew)h_*iyg6iI3(*PKt0Zc@(c?dj0!eCLAHb zD}zDrE__oBKRsP`XiH#2cAy=v=K7Av{Y=j=XXz7d>*iPDQYL-oiukU+bKfw!2+7Ce zugV`)-Wzm~kJ-i@G&cx*!o6d2ClQkO2aPXHd}d|}{-DohfB`}LL7P%<&5q}*H17gh zfvrbc9-!y?`ESXLP3$cC8kx1c3h)x&IePBi@tr3+i{zgwzOw;;*rtY$`?QXe^E-I( zW{p2sI{t#mbU4w?fMix_deh^V-Yf%<*E2f_a}v5#u_gJWW_<2rcoi)@?!pTUte%Et z|KwTyJ0oW+)RY59A^vfhvq)xa!G$MYc;N+;orK}=bgcoMBB(8+3euF%{Z=scow5}- z%w!fGy~Uda8=bjJOx!N1#*jhOiwd24JQ@0|xZxNw%qV0~bml5D5G!}iXW6{8nY?t7 zrM4j)cvD1jT}R&kR0w}Um8j+k2pR&{Y|AVnrHl#|FiREA(iiY@+gEPpv*#r@S$C*Y zjMH~K+a5y_Jbf4Wd_R(uWp;Y4OH|yx8+YHG$tSY=5?LxYs2h^2viz&}SS&aS8kpuI1>{+_hRmnLQ(j!mKSXs>|pm1e8il8BzsEMWS4`mYN=D z7XWOn+b)Tf0Cz&ElCH2xOG&EnEn7j-n@!S=ar@6Zpe67I>1D8kSEbdFDY0>V?eM*b+#2$Gj-`;-tH z>@=cvU)W4N6);;bJm;8zG+Qp>%2U@{CfQ(`E+%+Lo*JhhY%o~NX%n9gNgGc6YtHL@ zwbFU;@|vmr{}$?eqq>pQXEyiGNZDgrbLWkh;+b*$UG( zjCynr;526+g?(P5e*cth)O>*VvyF<({x=#V2E%JCHgh|T_^IaH=iK@n*oO#FrdGGQAN{L29P9G824Ja(N2 zFaZ`{D#ka>ID|gmc>j;zP15Eaui?-txrs{waC*S)NXRA<2a^wf?TP((ws0X$n{>#4 zE&Qf~Q?Gf%$Dud)9`(l&c~vhQdLkib_&)VNgF_z$!{9cvEMu<3z+1D2J~P(OUzhQi z58BL}3GhM)M8AZ`R{W>$Y@%@lWp>eBdctlUdBe2ua!M?;@O8h1R|P!J$P;inc%Hnm z2IH^dbntEAd3j6r+ZeRYksI-j?0Nq8z-}L}soC$*VAIaGxv}L&KGeuy|Ba38N1{wJ zSJ5yhQAf*gB-T>Pg%{`nK3$lowtFcGHHy*%huGLDc!7VN34ri${y3_6*(tm`{KCnP zbcum@5ZM8&PiN^shWwi3+8Rb5Yp%=u9%1`+syd|WGF|yl#9i&_{)pqT8?@Hx_a)LQ zm-Xr3EAgKFTTjYPmpZ0~oUV9*U(QY<(2V)3&wL}giMp||JOHxx ziV_yJ1Os=ExKj`kn-udXBO`5esGd_PNJ|@Jw$Xi;6LN2ck-hu%F3R!T7VBcfOG4?1 z7279`E>ksQoe|}07L~$AvXp#Yb1h9=6oI^``gHl--=&|t!*21@LGTfAsJBI0T*+cK zIC3V>lI&cgo{z^Ob2kr+2;&%7zJNU)oi^#HXV&n~uji!Ct7n;qVT$TWmp|YIS`=t1 zr`ZiEY>u$vd>d5t$wEd6OpQVtSlNAU5QifO@0cn^9(*XLZ5#_)W(ka%Hz_vw5bh&j z4-JE9(l{{6+?GZyl%_R~#n>8fv=hU`sBcu6`BA`Z_?yRdKtrS3ncyWB@~O+`uM zGVKo5Qb&WHu6l#X2|fgws*Y$|E4m&qUH-gJ-H>vX-Weh8qoau6Lr^y*?m^{SrFvJ# zGG?+PecL*g1R_Vkk#3-SQe84rYg+nWrlv}@RM9k7CxQ_&Vv6opWRA3KhAu1=U}vj} zf`4g(HCr@vv8}=M_o3HHw!Cxod*Az|b$SqbXvgrjUq0h*dEfueuDxeg*ZRhY_s#gs zzq6!7dG#l=@l*wzI{kLL8_&k~&x}Ad)Ff7V-7h+XTkIhiIx{a1Da6u6jWmh_EB=WK{#BmN`&ZdyAY`0-c8&>woXe5>GtJkQT};-Anj>tE#JK?h6y_?Y-u1R?putT^L!^g}vI(sTpZV(9BH)=dT|BxYebLECm;2w)@pW4;qp7lnIWUY4X?%X=khx_osRkkkPq^6*-LeCJ1-7+V=`hW5AUWc8wE4MZX)UOl>yNomL@-l1A^Yi zs<;LrFujEQJchWigomk$l~XMoGu9n1MioF;PWZ$9NdHuA?;SxsuSQ9i`;Zys@|U>3 z3oqVMg2q*l^~443clhDhl({kdB0)U6Sak%lui62SMe#nm9XmC+lf0Or<_;fAVt;&c z&D9a^H1=G&6S?_IMbPr(lbMG(G324g(s!sM6~Wuh97Fxmo_ta(3Vj_z9dl^_#Ngm& zzupdtE~NC@mkU-DhwTaeI51q!=KQ|_gv)QTKzJNYp=rN2pT$5poCqHXjenYd0scnd z7|_7qD|>wg{zTbA3P$#0NGeMr%;DgAQz=8e z;*CF6ZPt*7Uk>ER*lSWFwwB!CS^UQqO&}!G{zOspA|jrAa!>P_I+Lq-HbyS_-=08w5@pptd?2!@ zlE||vX|(Q6K>%o5Xe1t)6G_G~_&dlbj!{u`)gLhsFfsEf<@=r43htu`xa{LvVP6W@ zue`caT+)sOPNtiDTEF{U*64{-!RC zzC0kt*|_exWB9l(U^r)7_xPR|fwIAA4D^Y=*{}1>y>&4tEVZ8gwzOk1-=;Kf$Ls&D zDgEHg*Uk5!{-5gGX*-M0qVe(_)J`IJIlq#HpK6MoMTbGTpfsU?k_rXvO@LAdN|qbq zHU3gb=2IO~Qn#3m$_QCKfu@+>m;ssnX+#*nixD8TirL)w3Xc{aLXw?{V=&+BVP~Xk zzH&x-&Te5IP}qb0JhPASSV^5J4*?(# z03cg0rpCrE(v|01&(LZ2>nuR-K^uKjb`+$c9XqkKXr;8E^8=hQi zPhzF9EvLlR#oA8`n3HivIdn-PKB^6EGeV~=Zu2g6ZoH8!puQ_E6_p z@wJ-1mCeZx_fazrVgRvc3PcSJmM%a3R7;yR2q)JFA95rZ+<+0R-lr%Oe@a5UWSB{r zcTlF@Ej_4Cs?{DnD7tPpYS5%eSN%ZQC~UU-=f~{WYkx4-Gz*O{dZ*ShOLXZ+YX1+N ztB!3<%^lsC+=g4KvdC~=;%h5rg`Dg0sk$=|83=Fq#U}U?&X9ibwPnHNB>H8|ZR<8J z7b^reY`xZJsM9S=z7X5O<+%eM701Py@Xv;l^$qRw;DJ048TXLSax)Hv zM1Wf^LOs==!K)$V{}mR-zQ=kS{VK2h?wI!^3xfePr#}E^OwPpLbN`Vkw>5MxYX4Pu20&yH!VBz%mYrL`M=;9osR%k(GPw+7V^(0AK;Nlcz995k0 z7)h>a?nLNUJO_)5(<3gcm zlUlIJ7qURkj@qRU{~qk){^P@U4Anflh!z&q{LAHirFVJv{7=g}wRd^v{7=hU73tOA zvd<~cw_k=-M929#4P^=eNuJAwTP2^%hOMWVXqP^s*(vbYMp)0U1uolVDdc6wvdY) zzyY2N%@yJS4Bk249Po07&mrM%;Pzy`{#39MCPBm=+^!#H+oJ5tO!i#Wv3qFC&rMuW zTLu@mMLo2vgsc4~Y{k&PcHH~#+m9HZy#j#bdJ*T}4kx>s%?Y@+CG~Yq#CRNNf&CiK z4QaiGMjdVnY)STTqin9~R!YqtDGRUDi0e-rQb4CodyjHpiOO5u#+Kx^T-CLtWq;#- zZDU^fHZd9L^M#XFSKzNs0?T?MU1BGv%TB18k5_xDV5sH8*zf8c3C2+&l5m&QR)Rpn=4T%p<-`Svk!ZdoxttE!kS=`mWz{H!$yB0iQfRYu90hRRa-niMq97L@7f z$>n@BGt~ejCoZGh*W__D*%qD-C0jNjLFfiW9x>M<;i_EKJ#fM`C=rVl2NPpuz(S$7 zdXeW5$w_JSSXm@7^i6IkIUj(T0~#vd`0etNB7PL`oLd<=Xj{nnW7UI1MYo2MXI8HX zjv(J!Ex8L37P$Y!zB#+knx-9rhey+=Wm+I^16TPt0}VqWFW>g~hv$KIsu4)gu@={z zgxfLlYTBz+ToM0d_w=y^J4(2eBHgmd{RIwJ%s#IyK9o-_Fc@{8*?h>`Q2M@_80`sl zCAL8(Tb^**xH;}htR+O4R$x6Ov4JmpN4nAY2qhCPA!z(^J!ReI=7l{0(zYIz9hR4` zIPp#7_G5+wm#ef;dcnQWZ`t$Vuhnj9u6?DYzN@t+luoNQdB2+YAS)b$%aYXb|DMa#a&~#l!}o z#$LIqZ}Y)6ocI+DYvS6@`ZZ2ss>-W6kC=GRckrvN;RtLuwW09~D%%Fp;`PE=B=0a8 z=|{KJuXYywM2XYsdri%|+!w#hXjLZk=~Skrg&ayfpwX1y6x^qXHo<|?;QzQv`*t{G zhA>yvhq`quz{1BgC!$fO2WQAabjGS&)uZS}n9Eh;YfHV-*mu`V99Bwc(LZI^zKfDQR!x1#e{W{=DrF}m zb-RY%9Rd^zo(utthEOl;F# zp=9)4B##DbwFKS)YP;6ZB6Kkp+$AX9o~0y%Az5BU7j_!v{+V=z~vGG52)6z+sZo;?!C3Yf4KZlX;YtL?-d*TM}vDfz+v< z#{gU`^B7bTIf}<#C8%)6J1bEj;-;#ee)Ikk3Ut(MTnrT%IdxWi9B4XyR%vuwD8As{ z60Uns4LNl<=etH~?(ohL`SP&LP1^ zEq;vN6-Mdb;K!ck+E2hU20yAY(L`TLFJ3HCOdp zPS~-JN~1T>dmjjrJyzeqNi1d#WW3}JBRZqZ`lNmg+cpZ8G}LU0Mjt=|Q5vRVrGFr8 zgd+Wel~_%Ph&a)l=m+0#J|Tc}RU-(=9_frQo$y-W44GJwvRz24@yUt%CnpaNB~Nbu z+OSZ3xj+rM-WfW{qy~S>l*Lw|xyn^-`k2)91i=58g4$>UMS4{o@`>*?HN+qD$jum8`>^X7gxeUKkr zEctC(aNvgF<9^Ymn9J3mbWiQeAyrbv5R&Kg#@?|=U zup?UC-)MJxle?xV!v@I}Md|VriPl-M8ajT2-qS7Jo>##mUU)~a&|p&NNSUr0X$pfC zOCB7(>~#B(CN{r6i)>m2eqPju=?4?#H~orB)%0k07!uYtUrgg~Zc-1* zzg(2Cl6MlVwL_vr4J3*!$k2ruuG98anq%94rfG6hx9z|-VVlBr3cIlF^E!}KOy_&m zYI~vGqiB##U{tQ7YulSPtZ90NMwTC}wt7R}-?Y1BZK2)nlILl3a!m?aJaH2@mNcmg z-@BxcXCd#T_l(=rg#})gshilFyV*_M&GR*?<$o>8Hi!4%Lw4@o*W69AxeC*Vh{x+Y zN0`Fg4e8zW9BKpko@L;~ncg?FFDRb2qfBK&N4EPw-8SIXC7@(Y)BCi)ykoYetfdTMyqfA8oluI%$!w~p#vGR&{| z0@tuMo4UcnuUuqN{CPXTx|<+{XG25mNk7jna@NPCGc_>oxnH&1dy&pFR*hsA+|2K1 zpzxo7tBcSB`YhlFt5a4umo-(AictkiSXDU|^FXGg2uqZPQxjm-v9+%BM4758v-MSG zL#lMB(J>{}VnA6*r=q3Q-}!cRa_S(U4QAg?_lm12HMw8|5V!GNRoMg}HX*m6|LO7- zQaRCS*sP9o>0kNf-x4@C-33VK!~;cSNo#U#AiRU`wJbltr0FjAn9c9x;AX}b^su)L zB{{^^Hpca)Z`g6oaZ1j7tkp`ZDEtJ22?s3>1v+zE_#Ve*_HZSoWv*AEbol`aKq;>z z!{J8(J1}a0cy9$vw*k*7z+^ey20V}tkQNA8ZX+O5Yu{V**$=b#6$Eq-2lht1h2XFukw7)Tk^zyvjeB>8E6sl>c2{ z`w$0k1K_ZjETC^AZZX!G1jCfqre~JN9VGXc2cuTJJXmjbc_`d0k4jKyautZButGU; z8htWDG|;x+##BfeUDQ736{D2JpJKN5C2-1Ee(U*djEB3r_#_3@?pB@I?B<7CD<5d50^c&u4q8vN4%G9?#p+oY)kwqoX(ujWmsqP*~w@vZB~ zvl=zM+Rq?&8$|9luoqbVtb}a|wVv8_%!7Zy$~k0h$S4JgL=4rhjn4K@Y0>HSgr<$h zKb|evX0*cDJqlXk?jE$lM^3j}%RO);*l7;bXRdA|P0v*=Cd`f}EQQA!v^6jG*PF+G zZGbO89lIR>emKo2aJsJU3lsHkxU%{FE_^4ZF67y&1+H{nTAdm^kd_n)k_J5!(ORp+3a(5)>=PO8q1V!Yv7zAFLLvtKO|5{T0uO22yRnbEOwmr$ z2ieQG*b}YVi}PBw7kIk*-JY&Kv*YRN^Om2L1>Ji%EC1jEJ1cw9CGG$E+^$*Sw7hGp z!dkYWIUe21R~*k(-K}85AC1u+%@st@-t2-9py%;erVY?t0KD$u{Az%(x_~}6xQ{(s zG;^Oh@Agig65#Cy0^dK^fIzoa_9_+&HzN1f|82D*xO0yU%|7p(W%sJpUyR@sb_lx^HM7;xbQiI?Bm~s@Czh z#M5p6P+iFx>|9*6n{4UMRqgkV5|wvTo2UG5jlsK`U@tJBOHUm};%cX|pHq28rBiv{ zZZI_UE;j;YAi2uL$3gx2;Esiz;H1y?KlLI-UEPH*Ck-Pe3hJ?=F-ob*G{-y@p z5aaG||6WouCXj6UI5QM1w{)kbe(c89(3#Pw*~P;c z9Khh+OHEpU+^+?mS*sE#^tQ*;s-`6b4deL`L8G|hh}RBYpXEHxYd0;Gy^<&)k&T2! z^MK>PqMb>8zld@($6GDQe;}4| zH*p9w!lI8usRcCTrE!NvFA0X(N|JwTMfs;*cb zuFX=*K*4~q#(+y&^8DmrYTUk|z$>Bpu6ejevx9=#RQ+?&H?n;~$yb`O?^q-yE8#af z(wrW8JmbV2Q}sY-7aJODGxMSn)b^wXKqopkAvrCn?Mx164m2KLL7TCV?{X4vGJ9$| zQ<5D)iyf9qHJl=&jZ^J@h#4j!dvQ`U0!J;&=sBjJFs7V@4m2U-l}!ZTWQv-IR3O@8 zARV~R6FAXrPTMz0>>DIHi*zq!3r2~VQ+Tm=z2`Ta0{tI2S>z>ppE8Pr&QB8&{8@{}EG<6xB|$0P^A5BZ#IeLkCQgi@zlmbmAE z4I~n7)UfNk`I+_P5i&a4?nVfuTFjE#=1$nOrzQ0_Hl3RohTHhv&96|hF=CyD@}ASS zhzT@{=ShF%Y^_^|3IXkjk2}&|KC4+iZuDKVd@dJsY57dzMaw4?csyIh`bCE4Bra5W zS`oF1P5w<@VEyyl=HB`isW3YsH%b-GbKE)5j3o=_p=}}VU0U=M!>-fZ2-BYC)S#LW zuIO~0>v8w`56TaGYMkx*2fCvtr!GIPVnXV=<2d|Lm?@{Hbq$UXL0if-;#nQ(s3QBv z#fBogohr;i$t_MbR#9RPHP0KGUmsn~_0O|D`l^4)9(j7KW}VEv)NF8Xm7ynhj2Vm^ z1B|nPRH;VcCY4&dDOnl{OdpR%B!Z#$iO&x68?wp0bEDUg%$HbIMnh;?uhS$Twa-*w z>rG(v(sL|~{=@(aF+V0i+;6L##z?b|qnF21VEVm~jSVSqiVdu~nk?B9iH*Wc`ux1- z?J4hfl*Q=x-TOU(Yo30FT^ciW-dAIu{wY(;nXaa8q9BV(oyyZ7i_Y4Oa;*xh6VPDh z^mFSPbNcBnoPMrQ^cS9fAa0GNGDyNZ{qUAY4tpeSf7D_TdgT^IDm6H%Pyh7uG41Vr z%;^oU9zEpg2iavQ67nA?F%*s=1$?`w^n2PFA8Yo=*K7Y;RFFNt|4+uZ@$91UJ!bXp z<2zW<|J&m`mNl)z!xGq#jBl+z{qg;o!g?Je|F!Y`K0faLC*ylDn}TRF_e;eGPoF@{WX*-`@6SE1A!ye4e%0;LF?S)Gv3*1wxOhx(M=v5_QgO*veX^{(K+1?)J-p& zfhsYdV5nSG)Pyv3qx?(N9h{q>$&4!79_6wAdHHk5u5V59I)4zCtU#F29;8LdkTh-! ztP``KEx%}{FCHz7hjIHO4R0qQ4Vq9I{d0*Tx`LJ9F0@}ehq)I*xO3`sxhvWXb|@!9 z_Rkx@4Q%JGH-NOrtm5wcD&{S}ibGy^!sgos64G=MWL z+oJr3lYx3GHyr#`-f<&!&O|+$wk;F)+Wo;|yCFA_%<_LZ0F5@{B*D;mk6K_Ht@1t> ze!cWe;_iF)^G#+xHo4!0E5Urwy%t((=EIa(I8r6o=8ryhehM47jQ_y87Bmg+wj{o=EAzosMySE;yTC^vMm~rJiQrCks~$3JuN-x zD2}opi)T3vNrExTn1OAt$rc&rJlM1g%@?Em)hmDf4ySD-6nUfbAda%SN^#$Pcu5ax zGR}kl7%z3J=;i21ZKjqdxehzWk9k>iDY3zYkClut zg*;haQln^;-nJY7#akId`AUo&tM(gMldUXnk4a$&)=G~%Gd@Pz)+5@RJ~j?;n9gp? zMRwx-d|J6M9^;K&Reh-Q-F+Kk>pL3SqaPci$fi$>U!3CHx~n{Wc$wwVtzd<*|ZB-0T^v5@t)Fo z+|wu4vsZxoJ#q_BlG}_qS?9Zb8)CLg!Px7k1Y=Le1WLj7#sj%WEOKC=sr}*uOXZ^w zcytANPU0bG-OLmGb1IuQIhEpjHEnQ)gwXmFqrUf%*8Sxv*7RVH^tKQw9ZOB^c7O7S zUF?SSo<9-$D^!dT_di2GkR^zu58e<;mH)MoHHYtf=cc7}l0!&(u;B<;&+f>5xi_$> zl-{Eb5`1Vc(zwCr%*D`SOQS>0$TTc1&0fKs2HC6hcB%XdR9i!BtSUxh-Z9A2#Y(k-lA7`%%c0PVl?o6YYEe*SnA z&=cp2h3{w~VTp)*qXHZ8ODco$Px^8*a4I-j7Pq$&>r@7Zz%o0;slBBprtexDwm3`A z9E%H|din7=QJxP-KD}maV>?m|-Gw;E8R4ZK(pZBrEe>Bpsas5WGHaNLJF3I@&2De3 zxulVQmvZ-E0;(U}2Ltv_A%NH?eVj$0d9AJ@e8st^*SE8DN{S(fjy@Ze9Gt@p2{bAka>`9y_ z{PfXLp}@PL+HB-4(}FzyjE&oCdgMi~-VgkGBXDW_K?%mN)=n$}MRH0S7vSEtrv0}E zE7sG)z5=f)!AG>Gg`Flu_>9f6YK?=_^o$tW(!oa0b3M9=&;M6UtDt-cd>Y3 zZxW`Qqg{;AVMcergU|uz#=ZE|t;Tb7dE{t&UPQZ42k9t1^2LY1{IgQVU16W$GtjM1Jh4H1?qn6R3l5CBSA9XEdh^mZd;IM|3 z1|_bdTXh?8q>7W>Pyop#quF8F6=pOUzvC#SFn*e)!ub8!d@+9Lk(!<-w9jO32eu>i zpEDnt0l&QH`F>e*FE@7!P)_uDaCT$3{|nXoyN|{ zxA6U2@vy)loURfyz|g%p9N3`CWkQt@vSnPd#2a7Lkhws>Iv}94S$8bNJ1eq>etu+W zw%mwNu|%7D8U|U>e&$d=VpTZsVoSPgAl~P3RU+RefbyV$v7Mt(vu~_kOJL-AzM>lm zFBx|X3C2w)szMel1IXE1&@j0FMc1E&Ey-5)XD1a4^C9K{rJQf1xt z*bS9^8G|*EqZqnJ%}PpFrOgT&baN>14BA1dISk#JvTXmRx{Ym{BVBdd=}H%a2LI#z zQ0lx2#>8245_!U@gK_FUkQyezQF@#);;uN&M&LOvP72jN7s{={qs#%g$EnMK2Kuxf zS)iX{W2Z;Bm0^v1=Wu;R^u27;Zuojw{G($c`@}z*9@(oX-CMB#cf72o2!+#<6#|~X z6A&ljeZ7F8XXNlD{jr`aWq;EBe@6vl^`oMP3x}|cf3G85))4RLmmRufKo)2tYCte{ zO+XVkrhh1au;JvEm8agN77kM|zjl21DAvEQ z>t}8~h@_9x76k18HcZVoP7KCySl=f9JWXTYbZ+{xq5uX#i&j%S$DrWUdMj^z8`I(D ze#D$NmTktx-|6qb|JkpgQNb!|q2p(ItLPbFJJ~5{vw4THUo)vb5L_BCRjlH>scn1Y zTH$%a1pYabLyFQSxgi&qGPtxPh;|l#^m1`+b^y;qAB@t zOY*&zALlf}bQD)4Jis%Ic*$FOimG{(8svlq<%k^m;=9d>6H+p&`BtWeHAo@XeUzYvo z_|9V@$HjL}kBs)h-yRLKpoPX;(`-=X--LM@8Vo3x6|MA8^re=&`(S2?+UT8OAV1Thoro9)kJ_0j79 zF_J-Dy*2>NwZ_ZfUYL+~sf3G0E(rxz=iUHLEbjewa}umRry|%syE6NbE%%T4TE~5+ z)_%BR&o}uJ56&`J#E!7o`tu_T*!mggrpM^I(T;{TIM_67`U8wpHusu8_1x@X>bU}V z=IWNjRu=M0p#XZQ9Tiyad|n*2Jj7%Iq0H>+xmScz`)Lu_iKSnoo0AQroytj2!Dk!- zEgRCmh9S*Kl&eA&P3`k5Gk@@>p7no!gVuk{TmQ@XGC;wN2@CzeO-d5?zh31_Oqp-^ z0lz59(_#XlL#sJ94WJrS>vMXair$*uFX8tqey@A)D|x=cFU#+r{ElU%=zR#!qqS=C z`_B|3i^H59lW=1-zs-GbilHvWvWCJMtHVsl00V2S2YJkXX55EA3UeTw+#XJ%LMNh( z8-=q+hteSrc|tK^<;+HmtK!|l{22|`zwg|H-7!wG#yJ!v|7on6Dradw?W>*IVcA?+ z{S{e^>ygTX%zMhNMsAm#SKQ+9v{=5n{RVT#lWDNC6xQEyacT%%3{dQVU9%rn&2={VeGUK)(ezSq|*i}m?3vsmwC zu{JZH$wzALrT45`kRtXgnxNC@_C~t>I2RuH6SP}czS~T%=4ggWO|_Y_@IQvI#!S>c zg^7Y#dMsG+q!a{R7r;yr(f>46>-LU1r!vg>kE3IULTc@Cz;p@;5$-13>n5vHm($7K zo#Nt!Lrjmx`~d+HpcYPU&AllM&syo#8R1F~+VY*TVfoHTpnMNI-f8KPwGJW$kUwF=twwh@Q8FEX_Y@Tu|K)-J> z?)Tfa zlT*hcm#4GzXEobY68b3oFqid4)F0vI)Wn1R@nRn=MqV;Q4EV3)o$CQ`8%UI zIq_hf|M6Hwa`5EjO8)h2Z1-By?lq*{w!=bq`unDnZeRzy7w|qiM2H67eQ!IwB35^U zvlM5Guxr={D~Q0?meW?oOM1#KXX%Tf^x1*fM`g=t3vh|086ls9 z>UUwmNrw2YvdDB+fs?q4MM@@T(GPhUyVgm3q}=JjU8Ks}4QQoJ37=#=(L%^52~!u+ z)W!J7Alx+yuz7xfa*Pe1Bo;{GH(qjJ8(v4|5YnnQ!4n&6u5sE9<}<^A0eZWrlIXl% z6}X!s0*C0=m1w3~@*{FMZBj$dh8c(Gq{6hd)to}(KRa``)L_G7R$nzw3L3!-SF0%_ z0fHI`$?$@37EBbJOtuIey#lUegVHL z`F)q)yZmS;vz_CN;Q71^9M$$t`e{5=s$ z9k*Yd81&oW0UQ?VN$%);uYc&0T>SJhr2g+65en>b771#T zt2%KYa>-^Jkv-y)CzP#r?Ill}=Xwa-`WInM*>+BNylyjwr?;*1^s3OknQl;KB|?G}UfvOm=j-RM%mvbB|~abUKR| zt&&Sjl3*=&Gpr07{|~dhJ=N%|o_wTXXTAcYCttA$2;)Jet

    o%)F&8EupjuOP~vC2nAm?-3K`6?GGHHLU@rD<|+eq=$P z!~)?6&n9ZE`vGLaVnd@_jeV=gXY^s?@5~ph&Y=>cVb$o%m1udNQRSGxezTMAq5sK8 zUSsvU42lQ8sLsB2G*PKZq4pWlu|zBgJDz(+HE>BB9CN=1M@wEGqLL18T-VxhC8ei3 zV8YW=A3{=*+kpC949g{>c=~`!_xxnjrNLM54NcUc(zns6`Bt}d@}I13X;0=-Xd(9$ zKB4uVUQ^yk`apI~L0{O@5505_g;f)on%d*eyr(cizM5k%)trUzjOa&=Xo`@BLCfb$WNJ4L4Y+kKOL&ARK z<)L>d-5&J&R*bY9{S!0}sliLkr;+X*P6+1PYEKqgwy8UmOmsv4ASu~^b@v8dQ%#v< zVm=8{P46TV*YeVS$4vfXH?3$=HSiTmnS5lQ!dFB41Jih4Q|^#@1QKp6@-1%@Prx2^eu5B;r4{9JFLv{!1vHQ7u&{n}=~ zOr~U_$9^dXaV6y@pJ@jE2>8lXo%X1S(v-M!zi!^mbfsr(2tK5dLsY=&?Aj~Ln>&>x z?$YDMOo@GLQ}~cXw|#7|j}H4-Vjp+e z$71`4*~c9Fm}wtV?4!XxM%za<4+guEhyHl2rp`+BtSd2sR}-2_Xst7%YGGHRhW|`7 zeQuhK;B5{ONT!KEL)Atk(_>8l;wcjlP9Ghd0d12WKY&LleMgLzWwZNwIp-HzFOgGX%e5d&BWyg&Ln1E&a7lbPvSuTj?gO2MGs?yzsWl%&s^1YD9M;L`4a;uU98DVlrCLWO*Q+QQV_C4A<2YX+J%x2^w>v- z9%+-CxV0wm4%a?5n8$MNt0%+qP3G(NvF7XcZob^3k0FMQXlQJ?sorUOACyqnky%io zEEv2eOmf`8;Wv}*C7ha55lXi_5`5?%4hc89ch3^lSFwR?=t@sBT{!SY5IW*WvsIm? z_jJ|O$QGa*Val0hahfR~b*1tPh|g7(DT1AyLiNYqrB|FYK>Oi2OM1JxXAR_^L`~u3 z1M^KU9*FTsHI=1W@G#FAV{OQpuom|fgNz%%#9j0e!ez02#Oz~^eay6vDfZD|AEWJ~ z+CD1nqr^V8(I5)zwvP?=(P1BIERH){2C40VEG~#xX~r+xc63u-N59GUH}qiQ7Md&v zL^0FR`j_E3uxU9DVj{3c`bt}*-sC*E0evM78qotE{)EHb!d=pFy1GHPI}@FkSK!XF z68N72^Sx76#c)aq?)D+a`xhb94h1h70xTLV}*!=MDLBtc>R-?jFh$z+%y z-s=7R?*H=(%-QGc_hap~*Is+AwVh8=MRAR|r&44Evu=D&XCTQb<*gd`!$NXuI|y+| zP91{eRBf>26ozeQP`w@`QIQSOAbp~VZAhav-n1_KDHGcwg+Fe%L$jABgy35fTawOo zzMnh%tg`-NCV0+hr#IqkOz+FppUZWUFwkepI@*+bUcduaWJYJXs^ZZhT>$r3ekkBt zToe8|)IgH!47g9@9e{frj96d7Ykxv=qOqE=2AE;;$7$Gta9_eL2!PQv#|b?cL7#R3 zVS-&tl2O7k;Ft&on6Yzqm?Rc2Ve;_#XmoVe1wq>Ke+Hy2?j#0jv;MdMX}Y=9NA}nJ z`Z0=tXFu3Vm>mo74F=nvrJiORy@eZcGd@=;4r44Jzt;mI`vHI#DXk64TTKj0K>$tg zCu9(j%^O|e8TnbSL0$tnuOMiKFrEbF{sCb2-j{kh*ZH{@2C6O+goELCWf1)KGV$FV zP)kTMy@Nr7gqH-^_^$hJ(>p}hLNL=Z+^ekj;GOJ;O@4vk^+c}z1a@cWPG#Mn2wdr4 z#aw<4ECkpkQ;<+#u{eMS5RVZK;41|V{ei~D8`^xFuLEMH6uBL_0%n4rU?g0a2@Xu3^Ipf2{7ZeNI&O`?SwH2LkQrgE?Si z@S25{mrBCAq;i4JPh;oJS?svyvGe3YIL>#L;+f@KXC*(kSkv84Qfh4~TIQV@KI3=( zos3Uo{9yRZPDMhm@%`JtUWq1K@=h!Y$aIsiMjx_V$H|9j?-}q0f1By z`t#EU5VpbAy@}vlqCYev=rT4Q18nN60bR6CYN`=i_Z~=2jaGehH`TgU6bBgztl~QR zjXIppVRu60RF~Ze7<8Y2ce|AqSM0I07xS@chCm`3CEDhWoqY*Yvr97k)a=1L^l1@N zKGT`9Ln}Z^x)Ic$Y?w%PLm=79lx#G}Dm=Rdv2Z+)U8N)=BpEUK%}7!D8uTm@pxqER{Ubh*5_cJjl-6PZj}<%rt(PGKEC8^H zuT}hs~+d&vt(dqD6WI^U`Dlv8# z9hyt!@6j*|P-647DD6rrZE$C41GzM%A`w+HHT2YbxBKbSAb4Y-VYLUj+soCo>oy2b zO$o|h0GfO5+QVUyTt`4a4kW&Eza{W!>x=ACM?B%=XD%j z^wBuvBeil^rnI=G#>p5Dv``EXj^A+ix6V814Z0=pLedQPSj+WBD+dICn;f}R7Yc?!FAo5A;c>4qT7nO&8ONbr z0ELnz0mJwTLJ`^3Bn%0$%4B$T4B-UKlj3z&Dyp{kNdorKs1Z` zF;~g@8r{O%xmN8Y`pQ{P{?%Oq|C%$F^aj1Ytdt=D5L*O6$6%h1#{ z8XI!g5Q0s^^(ydkYZ|uo>nWk$Wq7u0WY~>yR8wS~ZJEok=Dfg0I|T|#%ifAtz>sYy zZk%>O>eCgQD-!sxL4frdXF0`!g?8PVsjk@T&I|XIQ-&;p0OR#+zQu6#3K~f;JAfi? z=XEaD(vKFk>~^6C^(c55U>t zFnK^0ryvnYy6~~DqEeO&4-C`+KA?WCz>EVpG+xMOLvD~~NjnqYLLXRvoQy<}*3VJu zCHD$Un?#nXuQ<5!ko}vM(nk9?U)ZkUDDQEUg~;Y>rGf_0zC!=v@+{;W$#gmhWbf;N zaZWe`N-d`4|$kpses2uYryj2GKgp_{7gkSd&+S&j^HvXe+KK%ohAg(s-7ygA& z&VIaRpg{O;#5~+2@k$laVoXVm4;TqVCO`t;AU52vdDB7$Pr$qiXY;0oxspV&K#h=X z2JjnSs*1C8Hi`s>ORxdqa@3dfVLDz$8Lrq;{$x83N8z&p3u`4xrJJZ25g24Aq`8ws)iCEdi5?q zky**E@im9Pf0!-X8KpFsD|QtU5?tY0>~sCfMzT#PK!*_s7*`j436jT`(1IuRJ1##Y zB|97K3y^I7QJs5AmWjc`TqMLMZ5mZ)6JMa(;%|mpOa}H8*?35og z!iWs;0&ex_S2h&maafT{11z)1bYy4_waP?M*kRLcM6TGk5sfr+pzN3~t1w&C^VbNm zxTCHh`(wU@t7M=F>#3;UPTC&BG=?)PvE7_;8rHk9WyE5|M(kd`71LT=`13Qah%aH+ zON>vkJhlhbC#Wrf`BBYK>pvyVCjB|wCouw3A{ik71Hfv54~z==HFD|bIb`x=^%aw6+pkfEEB1Q+s=4PWcsrQJ;K@oEMW`QpMI@1=+xN3lKc;-UTFR}3IZ1=&`iOZ6Hl z42@U~OMp;sdq#Y;s$_25nM-*1d5UQGElLgu;q*e{#%$VHLTm!P>Yi!DT7Ct!mMqN> z;bdQZna&oEgiC`Hwovvkmg!5_f@~WMpT|&poU4s4Lkr>sjbTt*VDpAA5Au#}$WB7J zMr4A;eiZPm;m(1gOkf+M231$seIyyT(cXz#<1b}Xf5(Ml!1+!mdB2eM6gHWeZ(&Dk zgYARURs}?DUt0_+7pOH;i0_^z_<5)om&%ry$(EJ|cWE;0mMjf?v(}3&8p(urmGHU;V zz;#3RNc5`V@u0*;k@)H^iB%*9UnuCjG9Zg3R(LmB=CI3&g_RaY1w!cUQ2Vn+?a1nj zj&EN*5@G-t|5j}dQek`q!AAvk`G6jP#Lr#IgFW|ANFA+xx)#0VSdDw``<|}XqbJT~C42S755EuGo`3=#z=5LiV{S z5z2wAR9=yRX$~cND=u^)eOo>l#X*$LRHfH4_M@T1ai9&9-yEc}t_PtN5NvD`GNg8% zSCBTfX1(DrQNrNm0dBV8^8wr==Zl}X4QV{P$QcSq9;e99_Yze_>))1%iu$#JOK|e6_`U7mY)zZ zN4eAxDnyCkRZ*ukJkmKCMV4LXZzomjr+P#yLh^f8FY{aW!{k@u&+qCVCch#6{Pwy+ z+v{iR1^9npR}98F`+=U&sNr?66@+xjEjJS~#I=1`@d@D)XOoad3Q27nwQXJqN(M0r z$sSGlaXSG}=Ts*J7|bmg>{|(jFD@*yO~?Hhn652=aUt-AJrmuC069_xCTrqg10rr3 zv6jJB4p`pRor7er-}cmiRiN$2GLth0alw-A2ZQ}Nggjcw?&|RD7Th*U$^jyOqBwXH z3yw4*56|8RIGY$5*n4dIC29=RaHH6J3`2;iIuWU9@9~NZ#I^>auufEt@}Mh}5}u30 zv?_nkj+lg=w@?ysTip{_h|qC@J`S-z1^cgOyccaBiOhRu1;_Ufp4iWaO8=_v`#O7> zNE|KD3+Mqbq|wXZ;0O0|fPs$|J@YO1atjvID(&_AxtBpkO{QMfpuB-7uPB5|SZ*#lce?5V6yZX-+du))!bF!Pnvl{QhtzS(=1e2h z8H_0JOPE?9NYxTcFeH&g}%=@htB>Iy`#2Yrbzpa+B(Q!ed z@*qjD6m8P#{~%XETps`>0tdp(^OwG;5Q^9J6zy5~3Oe!T_$ESG zPgQ8hK8~u(&9>oXu@4WovD9~P7X)qONhBUsO%zoPyZD7v5i~J;F1!`raxRLoOzIP; zuq*ae{^Z4P2z&xtL$G{c8Fz6z-C7I-Tj1}BEp3-^0S3PY>3s=Vj|1>F8;0RSbxa@! zc~3Ulj7&g+ui4C_3)8ghLDTfmNhsi4cV0NEbo4B8aTzprGLuD$85;6%$G#0u*r`Hs zxUo*Zd|-Bk&)N?$FlykGp3>-&AnzRw*?&W)?-vkYI4Wv>UZ?garD=#&it--KPS!T$(ZuNBnJughQe>7bZainav8qH3$}Vu!)BptZ+1o1 zs6IObKB;f8-Qk3XdowQ5v^+$S$K9d$8ig2vUr-R>AO;;wons{YA}&{t#+AJoN1_b! zk)6e9ik5sc87{x*esq%045ks1wWjv70UCkN6XfxBGR;uuM?!?NB8lpq>skQ~!MMz1 z(qwE#iS&_9A1(MWyE0W$XKa-Li4;(Sk6c$OBATMx5dz#y2+6+{(QaIHLFaK5*o^`s=}j@|N($dSwI zY~=hdjbt3{HY_+YB=N(hCn4*=%Xr7ZcHcBRjSf&I%(?)IHW#bNN_~tFg0Wo z(xTTLBjDh(iatAgeH$+-G*-|Dwj)doo?$lo3&m0jqT#0?&*ykh_MKUz%3cV9h#=^^ zCrFquEw97MMV-~Pg$X557`wB&4NGOyKa{XVw8iRRYNfyU*s^Z5-Q@?C`*x{02&)W?0{V zORn0KKNGZsc1=RqPnju>vl`+*}k;?nBiD6-r zIugwfMI>ffw`^HbC!{~reQx0zsS_DhB%Xn6X4_ddq$Bks(`gAszYG0H)phxZuR+ql z02kJi16zp#Ca#8!AlGG>#$h$MV9y;pqCaeNC!Nj)cZN&5Kf-H(Ff%2ox3MrU9NAK>0Um>M0;rISz|G~-X zRHxw)ggZWp0tak26xG&q%F~>3zDT)N$t{WbUOZ|LpJAMK}C>|938>VX-97sLxuz$^;2||;4XZT0qPJI z137e>2-OEdKc-Mz^2(v3Md;2bf7&+?+6_Gu;04{A0(!4qYk)ebpWgFxCIy8BdL`Lq z_%AdBWRzb#BMO2!)r5g{HiI^HEzpGF5fP(P!~~plMpD>-28bVIschDxo$HC*4w#N~ z-E4#qR5BQgi5Q}H0Mw@HXzE{>mFyb!IETfB4O_rrkRJ?_E`!4$s~IM&?*+nI(AB1D zh;4>R_eTy3U!IG@_~_=mHWYFgAKRQ4HkQM}w{HN4^$uH?FEHq)>Rw^P-s7;?uwgG; zAf4?3>2f(NeEWKT5F_HkJFJgY7cwwl8Rg>AzO4h!Fg>n;%H12?ie_&msA;oJ8HhlTHJCx?Zv%iSCn zejbhDumNG)7tLYe`)ocK@22YTVf7pq9(JF*K)MGw4B|Rr;N`a9JU2K%zxW=Lk zK}%SS37|)J!lQwq;d^yKvVhlyLsz*kYsgBDXsXJNRsXGP88scB>gam9IgXl`I$052U!;a(d zCJ@GVP&(dI@4~f5lE3MW9e|4X60Wmjhy+`?0a;#3wWEbSR{CE}GA!*dm!rXT(!M`s-TO%ql2n0a+R}|=co-#r7!B~*VBr#nO z66kx3L9z}LF^f4qCg*V!+#=F8altmANmG|u8T%Zpvg?Seya0%b{f|u;*0(ul{lvwO^0z_Gto?P z^YA()f-ygwm$LpH?zuV!fnSey`3WyX1|f_%09C|EeFV&qLl??fUw(wN`k;}tA4I?o zq0Of}M8|lL?8OX=lf7YTf@%iK1XQ{O3iojMdeAq~54X6-_ri?)OC$37GnMM#K@%G8 zTx(bCJ08LNynZ#+%HIc~at8aW+y|t0X)fwP&8_ALv8W1o~%b;5|$Twle(6_8)KAI3ordS?Khl+C5* zU?ikw_2sx-?CkiO)mI?&TV?aj-#QNUtUc7j(K0cn$qN?u{pTE?5AiB^IqPUqYFm0Z zKA#xVQ2Tig`;m#rEaoe~nhtk+uW{eh=4dyRu6KN6w3e5DV@Cr@Z*lIg)uJ8kL)KnO z^JxZv$I(8}HU_~U8kCAV;NiX6(Qbqx)%FzIWymXq{BI=xzOq%YI!)H-EW>x3Gj^34 zNynof-j-#W?Bn~_n4P;;8@xBr{Z>vMM?eG(r|cpcFEZVQv#ut*ik&rIp*pAD`XNxq z=P+4}(VqP|4Dz6Coz@=+M(Mqk2LL+*$F=l{7^BZ32H^+VFStjM?|J7(%q8SoXS?X% zxZMn|>7R=8r0=bNBh$D4BV2m13XwHlmUK4a#K$^9**qGfJL=upXpxfD2H{4%Ed>^H zVY)k;E>YN?`UNB?eV$_oKi?bbkGm-0=1F>3FwAa8@(5c(XgtsB9Oupm`!}QB5e5An zF(YjQy2kXZ6M02aUK(OV+4=^@@Git@&&^S=n>@<&HszD8WbH&WgROTUnio+f8OD#w zQnL3WsD~|D1bIgwptJ|9%8oK^m1$2QLQk6k5eS4P_2g8|=Gw#Q2#Dq#3}h~A=-8;V zEK}L+Q)`b#!#%CWoGU*!8@bqKI~yFI_hoL&W2g`Uzpnip_uPLI)26)DR(@_IA}JNu z0!GTuO{4^ik#(f9{xGJtee5XfK>PjrQPw#7{Sl+Az3lf#j#Ac{5ZUZran3snQ6rQG z>AVWD#tM7!%H{}Xo&E5q_PTfNXD(45dKaPYX)oH(3|5|~t2t~)scX<1DsR<$ev1HP zpjXz_i`8x z{L(rPBC6LQI_@>_s>8ntz5iHsM~#J{Gee}KeUvr9(LNjZ#JZ!#5gXdke$LjSI@@5G z)H)b!Xlp`)9;O)~1mi5i;$%uxohyyr$Oe56I>dB9@|J|e@2(eJv>*LI#x_%HQ24i2 zqts7_{rd>Ae;-Ho?|Z9czq-bD4Axy6^r+#j$lDa>{XNP?FO!`zM@t_7l64hK05)Ka zO0mSi-1TO!TKiA3-hM!>Jz`X2ylPCn&=j9B<)+!Ner~g%5U7T}|{m7^G`giT$T(UgTJ?X{q7~h(j z7DGyNgFd3tr1#Ke9MmzXyY1uSWT6*8Hbg-0{e~F2?N6Y{TVv zwo6FF`J6XBb$0`;G`Qv2d25`L zLbksL{uk-jCODQ6&9-7LnH@|KjH}ooNM(a~1=3+DBi<8*;Z(j5ky+=Nn1&#@!8trB z&g?m_i6Sq^Y*ILzkS%}Dj#}Pb4?f@s;(jI*M{pi`;^)uOVo$tuQ17r)55xNO3D&Ej zlInZ?}g6q1*KU#=Nh2u1J$AP zv*ciT3YVe(`DKaJSf9p20M-@T+R}x4#mYsTzso6#ZizzY>-kQsUStZ@7316O{ZE(S zEs-#YspY7qcIvFdU4~~u11&B)XXlFTi69#OwPhqadtYcowUHwdII=M-JLO4R7%p@8>vs>%z7medJYnA2*?{EkrC&UDVjTU3|Svc^&gN5vn>!b za>bT?D)tv4HX5gwiY>|8R3)$mRcEYO#8fKWD0EvP>X_J z@1kakGklkGN9{3KMcgf{A|Crm(Qdy#ny(Vq)Ezti0Fza24~V8qP(ST$+E4WMB^2Djm3J(-l-}RxcRRmt!Mo=)dmP3VOL_Duem_F*70>be zRMC24dB>GTxHpt{crbp}k>28}tLv*0>#O>p6(1p$HL1MgQ@FMnRhPnR8d_-0Det%q z7|=GUYG8d;@A|5!`l_MzRYU5l`qft@)mL3oUlmthbyN09`#j` z^;JFVt0L;FuBfjv)>jR#uTtu(lIsgMT!)2>eqbN@4&z5I{h+QHc=!8-`r|4gl z+~9QI=N;1HVaxtLdHmRMe|~{F&XoQ6L)T$jP?_J=M>`Rhguti{WGLjj>H_WU?~D_g z$FBcIn$SGH>l?Rle(!qG*z^J)^Q!OotwrJA6(=;@2Oesu-*={O5}sWI^b67f_f90Fz=zq9YlJ6g zJhuTXK6n~jdx=tH%{^!mw)pde`rej%bvpm&2h~6_H9-epw11{OdvY#aDGynjJ)-c> za4_vlxFV?V{qW(!^+AQh`W&c0!$4WM^RE!6hq(nJ9#~W>s3xKc+&*sJgIvNkRL8Ut;&4oJF^iH~MRTHRnAqDDUz9yl)E18|a$4ZXxnU z*Ky@duzEn1$JUMOPV~<_B3I`5<+;%1LpZY_p~y~AxmC^721r=?4SBc|hEi;@AKXl< z6EEl8$aBrp=3Guu0;Bc82f^bLY$iDC1dY#{qR!si4Il2kJmJZ`cb#bAqn`-iaN-U~ zGKbOq_=IimD9$=;dms7uy!Pf%Y-(={K=V7=yX3NqZ140hslAUb>}+p;e|zr}p26+? z&7Oa&y%SLw29aeOu{eWA8xVzj?xXYC`)i6#?QI3Y_Z{tB%^(mozCrT9L4;m^Zr zAYZHE_T;m>h*Us8n>|YkZXAn2y|(&5Cn}{cw%_j9pnb5xrol z?1M`S5cOOQE?|nnYhR$G*U)%n^Z0(yWSf_cQbJX=R+17P^q!|Y%^q(J^_SCrVI z;3$bVm^jzKjuabuqN1HAiP6W~;yn{cY77z~&XX)H0z6uPy-&7|{b6bmq1ZLU6l`{M z>UJjIR4`%VP-#AMu~2zz)8vfiS%{skV|BA69$~v?KX{Ew#Ve?@88@gu1PMz50QB)0 zZJUTp*Z7h*BEzapuF<>1)G!$^33<#f^g53ZV+(bNj;9bn)ghBKccKK58P&;}gFF?=^LL0(bwhD2Y#Zg2HVQRh8-2L$0}F}{ z(QrRfG;U6eD7{O~II!#vQ)LgzOYhC3r09(nzQi&7<*9eDt z#PrV8yzrA$vC9dX`M{7jUt=$?nvSuGv9YITNMaVkW*t zHnyOKO#*i{)AUA9QC(U@zUTsJXoc(O&YeZ-SK7x8T8nD6U#USN&B*8#GpniWUjsz>iydb*KJqUk%Y1L$`E<#LZ@XFIEFw| z9EZ5QxfxAL#WB19WdQz~Dz>k<1Q+#T7hdk88E4z+c^{!df77|cad-$K&eA)MOtg1= ziqj5d^U{dgBYghykMXUgx4=VQ2V;qMj#+;1V1@neDjZrEdL{8u%MhRlI6fiRFiU z*f*AM*ZmD=e>*UyqSDH`{*;3flqFps^e#0BAOO>Rcu^cTp^w1a8iQH^ynv+m1)R5Z z3)p63Fi%Zni|6z_ft4E&46fZ6KH}Vm4`n^=k(IZytm%gSSJ7L|g#M^c8Sc)Ew}PLH zzHT_LfI!|n`*5zy+1q;f5@zI~9vSD9hiZ^5DgTzL`qP9mKlApJ_Fg^__E{RNcPejL zqSE<5sL;`|cG+jjTL<{|O(4o>9CCe9UMs4^x8YK)a2u|w-woESx7wF*42Aj;8xEy_ z?0cXybY7f+e3t7hq;GtZG8RFaIG}>IAelxv3hoIoH{sI{1|VBHhI*SgD%86;4%d1l zNZ-G7#yk~Ienek_s|i~e5Q*H1`_g^d)9o~CTS4=r++FW7lz!NS`xaH#U~fk~pe6(k zG6?N}!m~Nr^CdVdLBulFZ6cM)9&GSf8c7!M2rvkXtX*XMKO#QyS-}trmPqS7N#j$k zbhGmlTnZmyTfy@2B>VnrRMz4ePS=PUAwha@FRp*?q$gQl^%$sdZS;Ivws7vRv(9Ac z3BG%Z?rKUErg7Nl%^B_0TS(N@W`@Qwq$_brZbn<_?PmSg>hLz(8{QjmHxt<7hf&M) z;G1PUx8FcZ*z_b=L&?IGR?b6O9%p2Ys?z(dAO$C&aUBKjmK@hHJgy&n;2ZYfd`!4u zi}`ssKd13?5-o8YpZD@}Hb18T{9(_8 z-$(N^iJyJ>xrswh&80Z6@$&_KZs6yG{G86u^&E2{KkwwHg`YR_Glid5@-v>F=Q#gX zejeuMC;WVrpLTwh@^b+{@8GA(&%yj0!B3G-CD-9f_UR-1*(Z~qPuxxQ{{=sP$0&$B7Cik~m>^AUco;pb93GieiP#M)MN z6l3f-5%MTe0@(w7ozMstcztI27wTKLjaU*r`9~I1X@&PqN@X>ge3GgBIY* zM9a0!xyqzH1s2z3!+-&Fx#RYf4JETuAFeT9g%sqG-N? zW1WE;cdW_u7)UyUy%;g_%+p|eLFYAS2eE#G+G0PmtC?JnLS=!}gHbp7B!Y$R$&RKI z8BlweTPCqzcb&4hPNr?w?nhoEwBJOH)+5J<_e1I_6UG)C6NYZ564t$sphiPKd>}WS zW`QRRTujyO0!r1^qn|Xo+Y^-aZj2^~wt6^PT`yW6Y{NAV!Vn!Do$!(fcTxCr2yZlO zYl>oVk<8J)hj(gs1HEQe_-qrkmuV4q!59$CjpFX*Chd1zO)DtV21e*+7dY^d7FX;* zWM=a1@)>SLR@_J4h_o6N{fXlsD+ID!xS$VxVZ^Z}>vU;!8^Q*{i_|ZqHJg(8aEHs# z59!y{tc?|kGm#`^ZoRL`X=rbZ(rHH{WX~8K6M9jlZ9mn%p{D;50h+rf7}mp1A)*e= z_!a7!wmb9TkF_`Oc6$G%2OUTB+F8VQ)q7tOmDq!j4I7~Q^q%URWwbam6P?pAK5FzG zxGfQN$~3!LU1u;v5WZG260FF2ZO^nC&zBeh_ayLo^~8)4qzh}i&r7#nDFS>ni`60O5g@U6YIprrb)BROdJWo7xJiw!Jt_f6 z00=Ptb3JqkF4B93003k@#u=*XIHLiVA?rQtcHSQS(x=xweJz?huHFV}r63Uyotx-O zxOx^rB-AXi6M+2MOjNW+O{MqG#5*ihi0{`B)0GW)$M%YLj%j#L!}cMq)~^eToUX!0 zT4zz%0Fz8wZ9S@hTB~YXGd^FXPgEVpG{6bez&L1H?j&2;L>b+Mf;+Lo2bg)czK;aH zVPg>h_;$yd@a?%8zzHmujdyGirHlNuqaZa@M|6_*xp;{{SvWwWvb49*N1!Z*vgTh3 zIu+VM(O!c(uE`po#<~WveUPE;%h3Y=UiST1HTn|j?xglY0}F@H(rm_ZVkF5*M+32!06Z{u6%MDUrKD`#VQP$5MQ>^K1e2I->mxFJcYC ziDV;&dfoQ7(FYI$s;@M>g>P*QA(#&lP4+;%RwF)Qkk6ZV^&We;bA=Ll*(a<*Ql0C7 z?H)&me)+xH7x=b=(ICJD(1ZE0+wg*g zU?n+`JIXVrWdoGwRukW$FvFB+n{K(bPI>Frcx)3A)yQ4STP7F-=wr9I>BKoc$vkeC zHPLl*AIrGf(q1t78d+-@S5u0Gvo6{DY1(emR3S)-cif+(TTO4h0GB=BMlhHr){8!R z_<*(#U7NNW{n8s2U3;nzCEQrYqlb3*`!NkZx`z)~qbhdW`e>7(oCVxI5stsX1Y>SP zE~3>thE1T@P(s0)GJ{xRnc%KNFro&X;ySQp5PHRz@Zjw{6j8379}eGw3h1==F!NAv z9EpmHp|pG8O`nj{MP$|WJh7Qn_mlgPyXxAR4Vz&Jdng1|AFnzVB&8$NjpK2IawLnZ zi6k3fiW!sLtx*6AW*|>`F{VKPqd#XYZ_46(MAuOR$eldpUf#JwmGAcvU~3WTtvA# zFaXim<@K@Mr8;80??CHmSnEWeqBa`#;6rEE=_b@*%V|xXFt101&N_6$QgQ|xsye>K zl5bp&tA_O;!l=hpBg#|weI)9@MI zOsDIO)^tacvC;4$J~8MB*on7lh#5C$U9~@J+nh3xi8Lk9U6AW%-FT3HwB=8y z;lxFp^olnz1&P}Np1>JRw;6~SJMK*MC)AFuMq2i!soa%_Y*#btU=I&yH9=d4sKn1i z{%dHfG7Hq0Mq8UMhd*couZyp6Y;KX?8my=mNpSgvveJP**Q^ynS(z*%Ms-&SWlTfR zkr~u_$XQY4=Mmu0%R;zl1)4))0aahjS8m%Z$iHcqpwWS9p+1NT)JKpBRaEL2wBM-J zAEy=xeTx5~{;qlFSyaOh%bWlH~L)#@2*qr)l+~zKY&cJ|f;NE^M$xd!#b~LI5h84*=&A=$IO~j4x z-$cL%w+asY4gI)GVYipPgdMgkxp*-1-Ps#KU$-FURNnaNFHhBr zqeDNVWDIM6MP8k~f{=xB4s-=J08gfg#5ozUiV0-{f3Ux$TXbP_?J z+wf;H5{36n>MQQQ0Q}a=9v1M+0D6(|`?KI-rtt%zR`4LqK3$C(Y7Yd5Vm!-_^K$K~ zNUA|_XVVGP(GpT}>a3ePc3U~sn3jENDZSySAaaX|;fA64y zQY&3r5#Sie4BcL*J&z4v=U@nUOV;*Tg5fV&%gOEf{Z`Pov>uI$p+Dsi2Ksx-1Ksxb z&UL|K8A*L{k^Sv~NJF5`=u zSx*h1e$b^@Pt`4VTX$s-nq$?2MXqLiY=YFcx@g}lb8QO~otM!HyQl^u)@7BY};^u?n5D}Nsb<-d-zfB`9w??=bPG15UUUoo`MWSJSo-w^;mzG0ymd!kU%~Wso!SNg zh+`9jngP_49(Mi{mAxb7qFXT}Tq|D1pH60$g9e*RG9EeKOy%@yaD z7p#AbvNus1!BDZ2+n8+>q~?qki|f8b3(iP=;Bg-V_QstcIAb)s=jh2QNbND`A{|g2 zhuYQQyUdQW(aG}-8G5oZYEWfXMw}Vc0apG4Y8#Yz5A|T^I@|-R06QKTTQ8?wV6e|0gvAw(U&v9tCkY`Z!Cxb(wZWV{)C{LlXZ8y}AAyaP| zddfb2kWQ%_4XPjP!k);a3BlW;gzUKjep~~nsVOCfx#MBnvc@|)=)70W*pCXRj$PQ# z?k^ivsGQhds4VCcQK)#4%i2B>Wa!)3lzF;QZ`}hXlv3K8A_Dc{8dYq5zll>EjRXwt zo8xd*mNpWHX#k8?coBfKXTrt3Uy!MQO}KSi@dZHk6VIq5R6M&Ck6hw-s?^IEcU;{G5t53#IHOx(&=dQWw!^rMrl;!LVSz20 zw*BhRX3~9ebevEs(hv^%p>Jl++9ze7ARYqM> znFV8f_I95&4};l5iR+7j>kH*!e35$j6dK4WaZ10*gbXKR@=P(352npz<5wo7WO52B zJS7FMYw=XpPEO%T=s5xO5+WbO0cQ8qR`XSTk|s4orTZGP^l?7Qr3XEzthhUnecauc zn)JKFh#aR7-1P9|Aw#pPPf{)zRf($G1kkQy?Br+xYQmvyM>He_Yc0cZ32YXQDmqy& z`d0B^Zfh5={IRRzYLe2NLl{tQgnEHAB^kYG!U-; z#O%ab4e;pP^U*z^2-tMBhJO7x2<1=i4&^7Jn>tZ`ZekG1pM=7~qWos&Q{X~0p!{l} zd=O=;n;7LU7bt&5Xa9u3_t}1Q@9twJbnlM84V=uTIGj}N1KgBEH4#eKN2pyLH%DXK zPSyj=5dO4}Ph`}N<>COif@UOc!o<}`prz&4;jnuWWA}jl(Tm3KlfvM4((gCBW<(40 zZoh%iJ6+h^DZlpv;P(cGWr5$P9P5hTX}EwC!S`RS0l`<}69~Qx`+Xqz8cf4%9Uizj z@;v;WOIRTr%Un40js+X&J&m(r^xkqI^xhlsfZm%z(fia_1bS~A3-tbZcj(>U1nBsC zhY?~kyQj38hi(TxA8o44>KA8rPu5$;*)+4WL8(}VY-r$t%u|5OAumOQ0+0Oord(&6 zx$V>7{=dcDXKgS{eDLB4UoP%jpi#?pHu+IG*4usiA@ND)f(##c7xz&Rd~7Em>_t&{ z{XfX~97y{spkvF>kL=?|3w%zw$h9BR+9xf;YhiHCGQ3_*-Dw%R6N_~KpLbTe&kjQ9 zqXk008+6c4G`liu03&pzVm9C$(!a{)ewBkN^|-PGzgzW{Gxboq##aQsA}VJ>1siWU zk@$+NoEceZ!dp%hzM?8;Mpc^dmSez|p>n37(uB919{B1}IkQKl32!;b{??wAGkaE= z@RkF60k=k1&Wx^veb>!7G5Ct9oEcMT!dp%+eD$iF*{jlox18Si>Rmarcclq$IeqZe zr*dYWN)z64V(}GQIWxA>gtr_RG`O{I<;=d7CcNb+_);ooDwQU@!JPc&TjMHc##Nf| zmeUVk{VHent2E&)r$4^>SI+ETX~J910DKLooH?Mbd{f^{RQ)kH22M z0E1(}w7ehL|6jRYy|_`VSA!t${eQ`N_1rW6Z@mHr|9|V%|M~T*-?VPmtG!b>D0scP zi(=D;=^3c6e~Os%u$}bW)kCM-6k&^Vbi^#Xi2@=x z0JN(u*|~Z=P%Ui%U?t?1=R!@ZII4+9p|jqjy4jc%-}mmmPY(-$ii3D%EK9uSu={48 zv)R$o)6vnhbeQ9CJ8=L4`=XLk6saQo;%zpAT zm|2^9+dCp{Y0Bo@ag|dc-H}5}3-|zY0fjlq{4UiK9V? zS%G^IRT_&Na4jEFpXy7SqUF(MSC8`5<7rfFB$c969LEl(lm2q`WY1@4s|CIth5;&! z)Jb$u<(|=M;wxiJzB{75dno3*+Ylw!wI({(wJZ@SQn;n=+!WY#ypANvl!|>+O;>J; zHWj*)CTBBroW^A*D^J$_*@wtV&fe7RS>v+DLx5Sy`KV&IlD)&;;j@B--%e^_kD+?3 zj;cN+s@57@UyJMdT~9?ps{XVsS{s56!lumPx+iW+CbBkNJ0($ha2J}A>pZGfC#<@8 z?Ut24eZivqzTLd%grg%iUXz z@QF;4As*7|${kNaBEXR(Dex7o79Ew7t6bGVaXc#D2%@wQ~{6^*#O`9V@Ky{ zJa&)bg~qO`H^#1Oyodv#PT|Rmyg6sipt^YcC z_G5KlCzE|3@*BSEzPYS#8>r!4uM*+`s58Rq;47%hRCg4qBt}=^NPN5Mnwe;J#@&*! zYC@d#cJzn!XVf|959=)1L$)uy*%)nTSmAmeD#C)fYVz%ceA;h=r`fufASn@u#67jw z`w-X{ql5-m4Ae%VTJ2Yz4eq#i0j*&Dv`3XH=s6p#U7ghKhc1bi_(9c}LO2nMz2r$y zwWo1tgJYUxYcEo0xYV_JJPzm}qKX$Qw(^nAlb**rAcDPFk5i=b_h_B!%4~JV?z)*Y zT@7Pzfg;~fgliiQ0-j!`8n|OW6M>ggU=js7D3C<?scLd`1~zaeS!*#9mb$FYjG=;uPFgY9nw_{#zMMev9DLLbNRrDWJ*DbN)g z_A)UMq4j#=EX)qPr@EG6{+?DUFff=vljCq~WHYfQU>&aGfoyrAe98lDrrn1tMc$wo&}2$>cGMn@gTzjHl>c{kDNQFW!2?P{%I0z%wo)}%^ z%LJkv-l2z%v2Xh`T#Q}a(i zvr-$CB$k~oP$NcNpWy|pXGnxD4QRy9@f-!wmR*6HdigwqZtRM0h}3%(dNUwL7@dp+ z%EzUDMvH41D26M*lEUa6v|gr4EnB(L1w!hrd?pLB|^#O(>F1Yzw{0&9mRZQ0P(r{0%9cG808 zbPWz)ntt0kr>&3joX%w@yGE$VPu)-R5;51idH+xYP-#NP(+b=S2Z+VseVBt<;H3$# zger>d&aKfV?iJ}{Z+Mz=H1m2T44#`nv)n^&lq~^d{oxM#jwqxVWc!Clq3oJBMGK;TA`$)3 z9OMFb+21WL!<`#KiETf@4~fRAXf(w0Djr$xB0=%@MMsCB_!B|#cO~(^+7KY}mm-a% z@C)#2cAj+^4nG!JZh*-D{YymTH-K!lI8WF{5$WFNMJDrc=$(y|$?Q!a^Q%GTH-XH@ z`Ezb7&Y)XVx2**UaEN3iW$^<~Qj!@)WpgikM;}{C!{ihI$Ye?G-vJtYGL1R>pe+E0 z$^D4|avyu5t~9#x;08ovN?(YS*J_hNYk60L%-;eX6wkwyi=WI-Rh>&OMCOaLkK$;v zV>R|xxOAiknrltPxUjshpO%L*{9pZ~{ETStelU|erXzJigy~s0+8(z5PhI^{R7!bD{MXRh0Mq4f|!d zT(Igv{xn?(q#&bY+Hqt4k{tFQALUE#Xl6?rQnOe&;1Y`)D+6iQPfPuxa0A`LeF+P{ zY{%K?e!3S3GW+$mqn<1P3y#}W$I)+Xw>r+oEVsZMuHyI&dV!~f)6;b;Jean^A-mte z)Ud9U{24Jjy!Wz@EV9BHXV~P5Vil#i-%BGC*Rk2+X9Y z&I8ycSzw(JCoF+1yci9CXLi*vW?*b!j?Uc`X(vmNmEl>L|Mgn-haZ*xJ0Ky%v$ zJ@Tfm`MIWHQcQwA-6WjQG$W4ncrH$AOwd-hjmCwAAWdp)lLGLU<=pK!j2R*dvW`YA zm|i~WeUkfwYjXH(AcBerp5^FFGcs*e9YfC|Pl$p*^}4T_1Z}ADxFmdD9B7t@5YVBw z#gjTHZX1H)d!1Y_eJ%BZWMg^3WY%v3*JBN#GG~J@^Lu@z6ZcbHPh1ek8gM8E0(anE z+jhvs42026GWh!Fu7uc}yq^n8!eWTQ zK~MrC2qnbWzroUpR{JjA(W9`o+5p8w5B!LeQ6;@$Gze;R2`%h>QhQo@!)|K>6E?yO z6YLaWYH2nRs#FZ*dDe)8r(}Nf{6XKKH$|SuIH*I*_6N0a5Y^&{47$c2^gah6!`%HO z&xBniZ7h9zh&l3G3UkfbPqPRjNzOSfusNgwS%PYft*NAW$X8%YxD4@#41Iz#!#Sjbp1yC1^nrBF{fGSG_Lc4AII*#oek zE&ek)(iJ-uS*GsB$v8~J)L%+%#WDc#1WXvdqziU|vKa}FggD05yHKIvwoo<1 z{mO5{VkEF~*Ym#4`?jV5s`fwEi9E&i(4G7`vEK=bPu-?%4owl@@0}l??(_Oha%({7 z<-Gg6!sF)w(5?r(1tn zdyw2IaArCYI$>o`wOe~!wBYG)>|w-bQ+Lovgv__^+rB$y@E@%FhiPA^z&5on^P<~# zpW&k0*RVEB`)=KS;r4wWdFQd6^3DeTphqRYdw)EaDyeI zhDpy|V5-%WU3b1b=pRAGG?A^JzX)kiiYBB%_rPX9q(Nz|C23IYssa| zI62YWi0rXU?)lQ}gILbJNV!Fn(8^hr!2{x#W=Er9b{uUryVqcOJg4fqeg<+^*kz}^oE#5 z1SB%oMn!~cFT_U4@SJx9>&r=bQS;CPBrgj2q8RWpZA~OFYR+h5fl=akN)aQ-i~f_C za&5aTiw>om^{}J{;n9C|;#c#ckJ5peKnT<|fdH@3T+_};0i)bt0V5XmBw5jewyWNy z+T)$;qdx};B9bp6c~Qt0HO67PPV%CVFWRH(Pg#cUu#fL|p1kNDzr5%kmM{7$M80UB z+O|(!)RqnbC1vwv{e*PU{g5vDxH76AiHq95@mcdB85Wf!#6|lg33*XST}pvb5(PKtQHa(Y$NCk#za>v0E}Bd7HBwwu zBt{y_Jt~QwEN&bJhEG?C(Rs*E2;LMVsuvKvX)$9%=^>_$ki^x01ET*ApyJxKp)dyy z@B?$T75xxtc6{D$9*$Mp7Y!?sZMnlgry2N}3Nu`gUT+4i&;q(a!+}~W1gF~}@dM#$ z%$SO=FlvA$@1o%LiJrX~g18-(Q3EQoE`j`dHt1vm8FkV2bdqOWv^yR0jM3;M>i~$? zmUIzh?>Bt_iLVbJ0okYl1ctd13>_%X4~CF&p26^T>6CK-?^={kIfGDO5%wWc z$WaIR_28iM<=54rXYAt#bjq(scFM2``E|$D`z=Tw~FDU=p ztld@r@0?FJYdh=0r4LXQKh*j3^f2es^Ms0bpe%ancE6DScc$O`^}!_1q`o1&8j>hg zNzQwkF+{J1^lQ87)qHS22wZGvjh%Wm*thQW(sE7Z2hmTD(HN0_KkjsKN5w!h1&e4i z^!q#ML!z2UY46yLOkcR;C+6yF2*j`(4OR8WGSLkoI4cSZ{ugVX;J>Fo^-SbN*ZxymnA+cqeU!iUQs35X7`BS$3~l*AL#K#Y17cdt5yL&W z7lJ~Am3tE+C<$0G$y#+#f4B_09|+|KI;z=>1`ZhO!`w$g!*?_0=HQhe1Tyrt8`;2v z>sBz`Ohn8xNsvMZSuOWR>0r;}zTg6GcJ;w-+(`vsUxhnNw82XR9*8|OlJ{2EK*~+9 zn_Ib_#7^j3zr}pVEC7JwzLV#{DpY!B3Wa*tv4?Xrk*jfzXBQ4sKM3+RYVX2l0|(Z3 z28hVUxBQW@Kpq1}#1+RhFQ$(X=UT(xU_-x4T{9MfVwk7p!J+`=gvwJ+oFZ*|c{(W1 z8s!(Yu3Gq0Kl~_vkA9WN`A(N^rgTS0cc667oD%UqlkP{-eWq3TS4g*1y7Q&0O7{lo zUMb!F(v6Vru@fSn-O{a=?#t5MAl+5cohRMN(j6h)cPP$dneNMWM zNVinF_eyuRbf-vnl5|s~nx~g>3rE8RKFX{fvEBj5lA4oS{wzEopKPBDu(k+$l zZ0TC0n=W0WbmOHPA>HG0y#6ZP2I>Ay)@P$E_ciI?NA|~=qq3gT{Xn`k(*1*UUzTo_ z?4J$t+YhhCyW{P&jQQQA^ThXt5Z52Sb*>2af7hgR{r3BZx-AnfE}lOf425yMcx8j9 z-))utcIj@EZg=sk28ew8;r{%o&GeUfScESwnqQh%x^i~j;>9KTv&%~JXP4$JpS!Fe ze`IM{e%{i&{37d0W683D(!#|h%aQKeH${8;aV|Pti~J@MoBm*ei;YvApKoDd(d_)W zW!6&8dGX?c1;%+(ms*QTipP$%6)!K%Tbew4p0T9Zm}i_frKGrEo^e^;Vq1a8R+qk6 zlzWBz9)X5X9YXy_OaEfqV&nXk)&gT;iLH2nv8dR%w6tVd(E^mZAkUg-%(EJomXsB7 z@kZoRE%U!l=AVazrAzXx)}rEjjMmb;BCD}z$~U`bK&?2-9-rT3J~E-kpH2(Z19r_en}Wt%@TzhudEsRjA@H;m56 zSTH}MAU}O{+U&ft;?(O>My95XOyRiJl}iiC{7|CP_1PleWR9qw&Vqr2w!wM+FTg)N zjQ?KvLz^z}7u235=-{HIi&u7Sl#Cf#ZU+X4`X;XoEoOKx%qzl3jWv!=Nl7t|pI|I2 zSX?+VW#r0&ywc?1#;dOm@feSY*QcCU?|X5Q9*cZK%Uc5fQDOY=gMYYo(&ghBVdB5p zO?;A_pg9p*Zc#V>i=;oL3H}S}&;J?tSejRg5eNXth_?lNz-lr7nee3k^v|P<#i*hA zOhXHO(8nl3yKr!5Sh??PmG zHGWv782$S551q#?nIip+;zh+J%ZsxL3Sn(>!K9T_ixx~TDY29k-;-UOUxH--A$gb( z=3=1Pq6N1XSf`dQC@2NC&t3s^pM^ET40DzD6qHVti{I@FOG>TeKcl!{#nOU&Yr%r) zdH3YyTWxuZFVNmOqCT=5OF{8H)`ju|$>rM}ohzN}x6pRgOcD7kC@8~9RAwx(S&b!y z#`(0m(F(KS9RU}xGb;YN$*0vK@@batyeWA_%L>q}g6Ty|3XInpvuvf@!B~oA!wVMt zAc!x2VQEQm2~gjdkwAk)X%th@v#}f(SW))uB_#_2V6HR#S+s|`mi`uDw@dKpA9p`I z`Q43j+)Yd77u{nkv6UGKQWhI2=Ml!zyyAOkAuq(LWF*|KtCIO&MS0?HkNoz-_m9-% zlET8W0&7rH+hw^l)#&dX{D`iZQerH#K#q4DX2o95g1nEyX+(+Z1L6aUjCRd*N;w39i5(K;pWJ9axSzUQFv&2 zC@biQRt)tg2`^elLj7mKuU8oV`S7~|zmfRS-z?c*;tRBGov62e{TwYLs?t9~Mhq?Q zUicIJ5}N);>33`1J${T+DeFZ-7W7AOMtgy2rA5ntpl&L`Vz9z`JMcaRDBrYn>Ea@> z5X~qC5-TmsTMTR-ykY5P{aY&6xBQY~K%9+M5h|`*ycXw=zZjI*1>!BeTg-zWrJv>( zS!v@24C?PLT~)mpUxEYrQ}7#xUl2T&+2&&<&9{c7H^ZejBK`NkV~7oflaP8$fFG?B zKTbd7l@$PyP$yKsC)$q-HTW+LPe||txBr6lcDVGkb_e(0!lFW}v9#!(g&|$|qqJ># zX%TQ=V0^hzUF&lmy-RU^@xJH>KRr4HeuF7M{sp(^Lhulp58++f4}|llJ#?u5H2BfF z63(CUF%f@*2^%JE{Y0H{`9f^D0d)Dc#Rvt7P;6T=zo3+8tNeww;zeb|Iw&e$YP0?z zy*?ihbi)|2|4Zd{XF&mxqk(?!)hPON9_mbgHTY4x{Pbg9nf3OSOXipO>7wrFp_F9; zA9NSK!79T2`3BRald#t>0=_FP1_4z-+=H=3tV{)^*r?lzOAGSy7v{}hTwtV%k2Rul z_-mK-rgen=ex%+iVB#IUmEP2~pGZ#cC-~XQitaB6kgn$s@BACa5SIl+IMot+%aD9X zi0Kjz5sx7F4GaIF={F;p5x-FXx8O(XS~!2go1y+~@FO@6D*uA`G&KFUNK0@Z&YyU7 zq5dR7N9%bw|Dj?02Vh~RB|S7g`4j#P^^fnyzjrtJkbk&*V&SiZ$-hrG@fGQBUIKPs zSrLIPcx!pYS1Uy}x~3fhj&Qjg|Ap538l)$BDb#;~G^6+b}9dTVs>gcpF*Wd8d^o&f?q?@v`C+Dc1CH+F~rCt>61h0yAB;gF~o@ad>GTQaIKk$#%`zC(R_WS;CZ@-_tId*Ir2ygIC zyK+*05VG}rPG`yD1;mI1-FjlTpbPROy8-U#nO9W;-5#K{z#1xY}*i)?Y2Fl zU6QQUwShh&Iy%(<4E%||8S1YCLx||z@4NpGmk;r{Lj4EG{6qcYyYU|ke;Sw2_?LI% z9|QkEi0|izrTj(o2b$aE57EN@5v@yqM*N7kiDRrTf8C|)A!8ZgexG#y{Lf3*&AGwS zwPL?GO}hSlWq0QkvRc9Wm;YJ#q+%AQ^O1UqU#wqf&rZ(9AN8%Y_bkoH9DzYuZs(#W1?^Aq zT;C_%Uv_i-!<;$P;njRK;hlifyf@)=j!pB)gu{@fXFjlI>6y<>oDZWhW&DecS4;GG zd~kla09+YdIa~!?C0rF;HCzqc9Jsk~^Wf&gEr44HcNW~)a4&#c1a}VHxp3#fErvUv z-34$<;FiK&2)7LGA~+jvIo!o?m%y!nyA)=}8*2A^JZGa2HT?uy;+|_U!;jV$(1lI<)8Ey+)J6s3cR=90&+u=Id zy%6qNxE*ji;kw{LNI!QBkk4|faPt#Bc@0k|;S zEKL->c!_i?r+6{`T|K{JJg^-2G;d8fonw)_#)QWKqdA`*PeXx39Xmp$2RcNKjT3@{ zwv$XmhG(fxe5g2|y%$-+(?et82Lw2*8KjBQf|JlM{+Js7x-!s`h?mQ>X*bz~f zzMGgJVlpkqm~VgKnfx~~@xmR`|Bq1U>OPbICMLRjrvD$6-*^3){5LUi{hl-VZ(?H4 zP1FC6@bAC%O#YjgxOHIq|55pa(KGoEnuTZNrZw^@(HV{U?PoCNUwB62z3U7{d-oZQ z^u9A1=hvUXDBp8NBfS3%#`mpfG`0t^Vn6m6FWl_OIxhYfwm*03*2d>fU9{-AQ$Hzx z?$k$r^OsZI-~S64VaszTAS}XFT!K%y|FA!un2*1U=2{a!DYquTBTaPwpl0I4d;Js3 z9`#e$LfAssLfDdWMZcNYa<_BsSE4b}=Z2QIeY=w)Wv{8Kl-I9f?Kx5lb8xx~J--y` zia`|@hnjKH=O7JlF3X9M+88N7grM^qI~YQOpgjtGBYPx9$?eQAPL)(%12;FVw%4}Y zGGN!S<~iy|uKZ`3Pr6D6tpfccoIAVJ!mK?UpL7by^_OY>(ync^ue!=!dFZD)Km8T@ ze;mJ(0dMB8v}ZjFCZ1rp>{~Y{aK3v$or&w8JI69AWNFp&JUzppswpX{Lj%zsgbE_n zTt@_iivAzYUj-1p;O81o=}+Q8$(i_fb3DP<3@5|!SY#h*+h^>{FJEoH!HEAx4$D4U z^wV`|IX&@(uhepkg7()1WK~>_bAA_c2E^|Y?k3(U8I1v47#NF3l%{lM7)VCK@pvMG z^YQ~qk5=#@84Wq{5B*%@gR2ISVJE#DyRkx*>w(%42dW@i7i8r?jg2GhA&kL+fz>q{ zNWaxleEJu6kv^H#Lq^)*SOqn{Q8#GmJRD0);F$i zTEBLE^ZIq`X-nU_enV?RYh!Cu>)O`l)^)8dt+dJCumLD;K=B)p^#&x{01&ks-5KhpEiUw^^Q+g2HpCXZJ5u#wDq z3+E^E;UNxROdsKNYDZleqN_J2`MbzD&G@wOQwK!(LWhF zjjheiE$f?`8?Y!fY* zj^C4bG?(12pnb={UqYX;KM6JG45>HjCFnTgeJLldflvB#Cj0`xYAKE{K<5Vj%8IJW z>a%K=&N+YX1@jippSz%JVE~5%)#v)oD_iV4KX5_i65mq)Md#XqRe`!W^}dEcqrb`b zKL7ju50!nO>VN!yE`QGdeBe~|hYpUv>ecURxM9z$?mM*f2lE$PyW`J)sb6zV|1F`P z-1F+!y!LhPf8-ONeEbVv`tlEd`m47u|E-~@^Xk{WA4wkn(vN=n zvy=1A>gbF*6Zib{r~dV^r=C9f`h;6Qj5N@JI76iYJy{7`pStAAa~HpZwIh z=PkQv>$a|4dv5B#^~Epw=;Pmh>iZ{u|6i%}{h6^hT~@#5eGh;9u`fRP^pD=U>CJCx zxc{PWe*5dEx^~@kb7j?ng_p1S?eF4=^;ciBx#Q4ld-sk#@ug$mIR0a`Shc3SR zM`d@nS1l>4IP0zt&Y$=|`9;-tEeV`okMm;OxrXm4ULQHPwNt zKxKf=dFGat2j*1x=ATpERk@_{hDv|Md2_qVwgl<|zOu6_7R(KnU3h889xZ#(r4vt- z-+d%-LB-wA1#YZ7w|a5)qPdIazNn(6;)04BD_50otyx_*x6Bu4oU^*@f{HnTi4Wq_ zn#P@hiFa3B6<83ss;yV}2EuT15KJlZu|M~X7`s%y-7fn1`HSv}5n#ETJYARZ*wpPup$jn(5 zxT);M>WP;wURrZ*^^UTMS5|!B-E+??YkZ*WuJ2w}Ik&ug;=K#+`mai#eMJSnzPfDU zUjs`53+7oBJ|8OQFR!fh<4DV2Q$EK(zigrJEdSZ%FF0$F?;QVm{`2Q8Ex)kpBHs$% zi^}%-9}Ya?|CIkp|2O>Kp8Hhwcl=NLzw0|u{$u|y%AWE6);?MGtp9%kfA-D2{K}18 zyY7F-JKlN6t6u-6cm4Ax{_&BD%IcP@H(vjTW8WxSw76yc^*6lap$|X&>2)X0e)%h2 z^N#Fbq_Nnw3)AjnA78SxvZ`jzqVrld1Rs3=@qepcf9SOjR@Pj(aVU2G>&{Aq9{cU@ zZW{dKUrzP*z4dMNYc8+d{r1BT{L{PN_uz*<@u|;O%$a-6g~4k&u6ytMzWTMpmFHh@ z@ueHD`NeO3ck1ykl-ZYDdRgt-*5LLR?&$7?N+rhLK*Sl^mp*vsOJ4czhaNt1^w@_V zjwk;0^|xMpM|q&EE-)1Et*M{5`@%ruf~93Es+X0oDsL~Ff5pT@6)VbCl+{)>&*|E7 z*ZS&nYpNDs*|8xIsj6-`w|seENx5%RYuUBsYszXWt1CCzmzT}0ZV3d-FQ_b=TiM;Y ze(k)qmGxCMcU{(V?W(FP&b{EWrHjt1?m_|W^UklVsn}k1dG*+w%^R<%xU#&a;<^f7 z`NBZ?#H$9EZLg}Cc<-$jcg(4&nD>HUMNP}!*?2DWvs56rKssn}3ib63mxl~)FqUhiAbH1DNv8ycH4@%dNoh|IgEVd1&=KXmuD z2ma;m4V71v^;cY0v$dwS`~`O(x!Jk4Y(wQ)n`lV9`Pr&_zH>$OyMA%k+6BG~E9RF~ z-F4q9%J!Ad3shGweBHpd>daLWf2v7WCC}M(~`{|9s-VSMDgQDf7Sdto9vOPki>O3SZgo@+Hmw zyXLPhi_X2iX5z!G7tUKpD=Jr8b#d2SeRD4a zE-h8_5w*H<;wzWd+*47MrZa61K_?*0JBttgNv}tWW`k_sfWM9Sqg$^XxBxzW6J$z# zsd2i;-Wg|+2)-BFKP7n|ukO#f5)_MnxLk1F5AbZDNU1JT6B}18nLn`|rY$7JX*ZQe0-)x74STKnnAccjOmf6;8%LlAm zUU1kt`#k%ix%R+CzgvBH)fEl)>co3btoA=RP`B*Q1NGMPcFQ|X4YWMx`*Dk}X8HOR z^M1Vk1M|Zh)+~NtL&MVW_CG9pV8^EBaQDd&bAh^Nk03t?w@H zvrhbY_lJKHzTr1NU4G-SXCAoGwtjo#N#9Fu!X>Oqs}8#pKmPM=pVM&8LZ1UF?f3i2 zF7Yi}a`T*Eb+vDCnXei|t$bDBs;Vm%`|R~dP*w$MTv_A4&=;iSWmSk$v)Xg}=)8TWU5Rgtt(H04wJEiSLc&Rsgjus|}R--h=!te|KQ(oW`;{d@T!B zqMkK@M&w!PyE?F<+*h^HH`m`>4YJ@11*j>}D89G*0#)azmi770_06vglz*;@DsvvS zD*B4z`TV~IZWZvC_;*(kp2&wAeY-KPypH-qm7F4S?Jy7@^bp+t5{%Rp|*V2lwAk7RqsE~LK~EoS5^5d zmzBLCV687}s`AbEom=i(fLzZ~IhIF#hmq#$GPFSDXr(nUagrvNWtsEt8lHDM!s8IR z(j^vLOWzwWN^ef`{MGRlDadYyF}Q(nffJBl^U5a2ToB&?Of9|?VkD;EB%eHDPZI>P z<>{nwY$KGD@G7jd<&_xWo~f*A_1V>$h+pRW&bYq}`*ROhW9B1rCMl0}1j$L*b)He6 zpEV=uBIaFV!=A)HJg3(|xxYvEZdPg()xR$MWK-#RpVKQJ+7r!69b9)6H$9(^|NBrT z?V(NhLx9m9&I?X5I1~OMz#8%As{f?-Ila=;zS)&NUF<$5qIs52`)f1bhXGsf0e=KA zulf;O$rxAY^&UaxWc=BD9x@~SlpIg2rMr#m>YLQv#=Wr-5#&B|O83*vT>eu)fMjXm z5!@$?j^j9uHr?Y8?9h3X6E)+%4Dsnc+pORT=}{&ekIMU{>PS;W@q%M~0Bga99>|({Dc6&!p(6;`;r}3q+My{Yx zO0VyjhEzJJ#S*A3gZtb#!E?wYN*&p{vT++8@@gook(@1u6$Dm!3A9KoC-V{2i)*w5PXtH9>3!{E+T?%(`!o)uov5BbY#XKtG)x~l zBlEX$z9g?A_Z|+5e04YO@kSDR<6tzR*&GXhq_d#>QNguF#$_6Rkz0y27j4lkM3bAY zSJTnFGVOK)T{CxCc57w4WS#s9=36teO-1ABP+MW@An| z2^)54IY$tmcmi?~Z;1|O!-BW)aT1pJBKMZCYkdV-$Y)JvWUV+2|MaN}J^s&>fAY%b zufab}ROaI^{F4b6{>ckY>!S%Tm4EU|Ukd-^m7e$~6E6Id2^ap!3qBS8Nz{*~YR_ok zKQ%vQ^Z1wZr=x)vDE?zc#_lBi*uiSu?>BS#r{l-W_a%&yUR4ltg=j}`DaxdTGY z9x1}enp{X*#r5TEtoL)ibe2PI|JwbHxiyUCzzCxtF|Ayns{*5|``YaERu}o|v ztt5cZQ?fx96^8`Fh^ZLV%d};Y)i@rFMTQC8Xbfh9U{)1tBb3;%I@#-*S{v#$x0dBB zyAoJ3oE~Qgj1b5_S)kM)y>;c?yh5_1f2wjh*;r`n8|}0*NHhixP#aAp1+QJ5?Z7jd z(?Q@;h5nXQBqCRBNv3zFVx^SWoq*_byfaQqwNib^YjeelXBX16%)_bLK{WgIi&$`| zU$^9jfGR)c=<`!E{%5dI*@$n#t08D0IjRZo28`Oym48}v#lYGp$Oxemy~_2u7W-~8 z4|erx8)x_{Nk|R1f1aKfqVMr44p-{W`Qvgpd^=jM9!Hy_;6L`m-l!guvD*vVvoP*( z7_2?^1H{_Fnj$tIw>z-1S)q$?16;`|dNp|ihAS8Xrq8bsZEEG_B2`&>tee|Gt)+kq{TTSPpGc{73x5~5$^YHT--VBq zct;+aTKX8L7rjSGZ+&)Z>BoxT-~IW?(~G?EZcZ=yFNeS2osQbf?IisnPA`1Iu_sDP z{}xU!^uhW{Y3bi-l;7~R($c?=(~BO_z>}q=|1_tUd3ER;rKSH4r3s|H=}Px^OG|$Nr@}-^A%<{vG+@)Y6Y6!cmaC zR47O4wi6xc?c5du{p_V3^7gjg?cJJ8bthu!M4SW6f)VM34cgp_LVIio>_(I6>$Rpn zi%K|jy%LXgckEPJnDKtIh<837FjHLv6X zsV$hq9f4n`5l(t3Rx+*whTT}tz9|uMARHY z0e|`o7G9!p5lHSbPT^SXAjuV26-b}E74x!Haphbu4Wc#zx9*zqp*$ugq?s2jp-byt z(gBkj3FQEa8^OaA>S~S>mxbJ%2Fro4mBOeSa&VafV~-g+rUP-uJ`hf0HPne}iK&SksTEk(F|b&)4}W-3dT;DV{wyI$Qa> z(0}S*$O@8}3`(N6hrbJd(&Ow;;CR4A^>jH0IiAQ%T@fgWHgUNF!$C*44s(22H(l|G zuA(P(QqEBs++i;Ab-UP9pG0GA9 z5!|d<{7g#YUDb=TQG)oJN6O=m>DF7j!quM0n{ zB*pDboB4=5+Y3&6R1;pRJliY%CCJx}?_~M_Ug>FFHsK=AHsK=A_JULW3NIPLZb{N! zezo0j#-{X%k?s#i!%1SO`zc?@BUlmO4;ShFyb(B%qw%L`aFO{Z`UxV}o2q>M(A+Xb zmu)HpPWH_rUoVMYm=$3>y@#a@|5MYHFW2odsL{1PfC)(LGAl*9^fD&6+^L=enDJ-z z96)IS^g}$zg9q(Vd$De(Ft^kD(M~j;XQiE7dLa*TxdU9ToI~u6D^u8DI%(@?)2?#} z?O#LXR{+E5@&p0gOA+HFMX!XGoL?R9iJ~`5qX`_{W zNQfrWf@p$U1VBj37{!EB2Mv~a+Mv!*+wt zc<jF6RisRePLxWBg@b)tk?B_ zp{l)Qy&68zY?sWhP{d~nS6#wKSGs)pCIpPGZ~3xPonDI%3k9!P(V-!WG)0E02BK~$ zzVIJHKd0M%*>~#lH`fL9sC$iW&*{)39F;)7$alQpw5FNx4*}MQKNo$P^uC8z`cmXOUg>GvoA8GLqx&8v{1Lz|Edr&2KS>q@B1(P_o@^|5fT_juOu!p}3zv{+|A{^xJaz5t5C!dxxK5sGbam80Lo-oRj z^QUJRF8rfG^DS$guBX()JSVjIyVPS=#uKy%aGD)b>ZZJsZsKyIT>l>U#J|WR_v(#e zgw1#_1B})v5PIB&i57f+2foo5Fw-3cjPf_({~ItG$A5diR0}zs7TsU69-dCm*^ED{ z@=X^XShZf`Ie@k!Cw!pL*D1)sW@V_+jT1;VG?NUxKEQZ}6_&7v0H0{*tQ20rl|ug8 zs@qBU(yyWH%kgKzzs~RsE=f7{dmZSeDvo*<@zh1J1@>c%r_lE=1D{s>kt{N~FOpu* z^R|0~u8+_)(nqJ2*@TlFF-pH`e!wqxXb>#bm8XZpjRXI<9rxp52cgu|I({|ssB3C! zmTZKzEipHp&AJxJMkEi@$p*#`B^x2!)5&ICV-pl*pwpA7>%$E(m^+2gVGZ=GV)1!3 z^J*wdvtQ^P4C2E~IK6}6)sH+96JG#2B-O{^Wjym#Wyy?>lV%fo8r>BTv_Os}}x{@tCStfbTO% zNj$$<9=&%;XI3Ws62J+MJo)@-4)2I|nEAX3Fk07%B7|@anl<=v^}|wALh~bObqcl_ zmqJoX6;+*q;iQqSXe&J`Ty|H}zAMot7tYgNi7ikT9gK}2hLes%JFy)Ho3MTyrJaR3 zpg*n0cg^PV##C=1rO&l3oG1r|?JoXkL4$J41vQYZ!;1 zHxf_d6bG4)XKyfPB4qbFBg)1p2Ma;O8I46!34JBE8_J@ttG9bd8Q2xhE3&W)h^#wl zrkT(V-ir&#R<<-tNn+H+XV*;&^H42?ymi0<<%lstLq z)f!%m4$FKQ=Jax(U}8yW=@Xn@Evmrl$c3e)ALsP4&pL5YY3c9b^s--FcyVdz-_Ge} zf4pf$Y3Uy^>Yu!hVEiiJZ`T@r6!5&3wYQ*?*FK!B@tYOWsy+KJvGaN4~CI*Y8$cJ!`u1NHd>@kRS1h zUT~7vnebBOkzVO9LB6i`oz(t}S9+qGCj4Q*NFHgz9|6p(egvobiCj+!96+{nIE^tVj-7cuZ@j)b(rdnl5dyym~9wSrf%=qrtuf~(Fx#4XU~v}Wksguu}h(>l2&ckt(}~9x$A@LXTIca zR;uX|v7k1Pb8Xl498jU0oTY6P8dGD5u{89!AahX(ju6=4%%Y496naVP$JUSi+XL5&si_U5vjo zl_$`?QnudG22@@Nu3GOMF%Q9;wp)u|`e5!#3YZv!U5I;*5f=Roh``KjAhtR2E@l$yNqS@l*=T zjUwFZ3{WamG@)us|OW`lP(i6Qh;lf{-aN#e!;8ee< zo_9`H-$wM%g^%HG$evGb;&*05USH~YsF~l%*E2s<3Jr6knlURQnX8teS5bqcdF++X8ga_InPPPbAUTs&UyaU`aXAGm)~!q@8hCJ zFTzb=oU?-qiX>prO}}*|;#_;9ek~;!+}XBgMyR(Y-RpWZUfa37j=-mN(S*MNFtsN2 zlI|c5t^P&_KVM>eBaH8t;nTT|&<%6lJo>{*wQh=krtv0zr1KNMSbo+C9Z&on;oHa2 zx!Mk<4LK8;aoHaOLrg|F9X$D#R9EwHB{kSj*U;1bsj)bjvy8$@T|aJer}ifLQ_5mv zYMiZqW`@TG>tO(~uF;7^TADU&hz@RWBCSnp`?JT2b&d6n&Gii`0qnPD*Xy(UbiWT! z+rj+-%q7WcvV2n{rS$d=aXgW?(z!SBwL*udaBhm&(8DzHxel=9_{-z7NN(s-a0L$& zhP47rhbYd95E<^Q-5PI^D}}?@;*BP;e~6`V%^yz|j^HkaGFBN)g%7|A<0zhP38@zb zQ6;xfau|=glNmzAFNG3Ap+u5YPjM$7#(2;HFzXo&gZhrdbPPO<0~0dQh`U5mr_Pm+ zQzbB1WLzNM^o`3T4o*sU$M?k(2jXPjCYs2A9iyiYu?w=)mK8f!w*?#F`Jd-{6Hi6% z&}GaI@_Z=9)l2%h*XwqZcXy7~PA$D(7iXQ#>6NSjFe&fk72f#-I2{?G2z9mX?C8D9 z+}_s0nhlID5!-yVm|e1d#pMtUQtk$gx5z;&IV^aYnR-ao)R8c*AFHt+;q)TcBp!+C z5~jP!FwZ0T-plcX|Cah5ZaGbTAwb8qJQyDe;r=|X|G)|nIm!1J=PPnq$=3=_p0A|; zE~lrn19FnS`>M&))BOP4VX?z(-4=Q^w#ddQp9i~iJJWA+!+Ugii4H=3&UQvOggd!U}^)T@#y+Oy7MZ;ZNQ(I&#M9J z^MJn*Fd8FybgI~4L;ij+FG=o}h>O7G#KCgV;5rvp)~~3`a>lQKN5xILUu2zI#$ln) zd4)2bgJcOAkLA^az{S()k$MJDH^Np#?qZd;ROPmml|8V|oq#AKR5^IDBa^JjEHX{T zCnR}yN%RdB3!Bmmp4FgJAq#4S+25|~2%wp`s-WyOxGw=A(|o%ZxZ`5hB|Gn4&+R07 zATn=`-8%Wa5&HQ~quuSWcRF-Yo|E(+=k&5}bw^4|e~i-$-I#DnOaEO?Pr7L2j_jRU zdZ9c2!RbxvU^#^1?|cRtd7l71(rm;RIVn+{B#o&-__#-KnfyT(Qbom9Is zk_jW!8OPNGvc=NrWxc1kxgClQ&ZWRSj>|d@)0zT$iwY1gW5VAGIMvOB2hb1%*E%0^ zPmQR8HpxN<yh#FSri}Zug4;jZo&xGHT_Lg+Au1S1J|HeO_I`wk=-HyM#_`3yv+wiv@fAp;u zf9K$@3V-encI~$d38m=N;gM#zb?USI~B2iW9qXP!^gCPBViXl zy2v8!W7-Xpu({7DR=#N$NW!MRezEkXogE1ik#^h5Dwf`~>my<6R4;p1#qu%jElJqC zH&86SX>Uiu?(}&TD$*at>?dUpO=SQTNnc=xi0u>oy~%ao9EKBcc}jmH`;@kp#3WL) zQaO>rDS5!My}Et_-0mVTmT@iXk;nm#cIk70oLxnAB!*?!#>?t>3x)4XL4X8BVujOD5Yd{g`C`;es588iXd_)Ibn?A@hiUCe1Y^@#EcniNG(?EQizstJW z%3-0i5>LVphIM`d7e6OA$0DyxP15CYNJ^rpFTLJ@$7S~7&Ni$~<}92b@t3(gS;uMr zPGx!8^N_+^{z*2{EId9@LCp{RZyA?PNnl!>5Gg%8dZ=JX64rQK3$IRjl@@K{BlxLyel)B-~#$Mela|^ z^Y`q&lEbCqxmhZ7-hv5AdnJbtpRL0rry@78M2AIpJzwP=R?8!#n5#x7NCWmrBf7pq zf6g_+I@jF4&ocfp4s4Du<4W{YkXp0+ zTE~2b=e6v^4skiz`WKnEQz%DC$FX6IC@pODL+s81G{`w5bUR3S)f|@j{{jw6y-j?d zWqf7+*c@NRh2SaSV(nt~#|9*%{*ZZpY*GHaUpO~^-X|~C;bCr1>DQT}KQw+wG~ZA3 zgH;5HXl_sH_B)R&x}Wi8Q7q1{^am5KgPcy|BoT-*u)KIx(VED(m9FY%Ccl|c| zdp8g#+>7D^p2y1#r*u0AA39U`gx&afT5I>h#-x(&Onw%x+=B?&bJBdQU`K${pDA7@9#=3PTev{m*6W%j>Bee$FZ-#e)5 zBlCHt=)H;GI~hN-yqTi&Q$3s1{66d)9>^cZCSFJRdo8!;Ne;`nWw7dd`dw@sdmM$T zq~^MAmcQqq#!u#FlEbBzPx{s58lKVpEn(~DI(&`(Yz_QMhwC_ebcG&2dGy$0R4*RS zpAXZepXPXZG0%6|zipbI&&M6RP=}8jKaxe5j4wpJ^3_c~Dk_I#Ug?DKD&y=2- z?XVaK6V3*`FN()oW(kiKj7Q!)@W8_Uhx~q=?mRHtq19}MJPVRWXCQc(@sR#M<8@EU zW696LJ9Rxp&PG1mPh+MDv$d^u*<&eVe*5Bn&*x%{vwZkg2R-j+ex?> z{V?(RI1*Eu?099&+;4Tb^WXkr-HyU1%~U_h1@T8Ye;EgKUQ75LiRyA?Uo}(xWR`b5 zmzUp9x;3DDn;GN%&d5tNKEh9${Z}eKzlZaa@p+KL!UstoNVr)4n)PYt^{CkTdFVHK z{gm}8xiEjdYB-v)uhjTF!Tx92{}lTlWB&;I?xvPJ z2`1x3zDxNMZjvADHyGi|j4<6DcDorlK{M}KDZ5dcFb~|V+u;^2^iEVj)?@;?&Myn} zEoKEbbzr7g9EH(e7(clOHV1=lCF5$~=Y77)alP^(*@#*HYp~Iw`e#);b5B$SgQPm? z{Q+2qN7KEqeUDgKL75KId0%@vXI(rPOyvjDZ8$)5DL4d!@?2@b2bX>%N}1$y-(Wf~ zM|l~n0IoMSg<#QWrP`QZwd$t{Rj0tvX)@vH7kZ!91gn<<3V=H6&{#Z%Ybvn#Q4oQ1 znK&9vOzJKUp*D%70Bf7)+%0 zhU0wfU+@vbiZax152w2zpL0DV2xJ{-5a=5s^!AeD6h^`E$;cQks)W#vD#kX4E)9*K zGbk7g4u;c?34>ZfY;|1gA$O7Va?eRtHnbV8bC0rZI z?O1c-Sl$ePzFzPOz3zzobT{}U;w=hFr$!A1w~mdB(61t&=~It`H)CM-voArqR$33% z^C8S&kZ%3t#$r}Imd1Q`ga(6QNv+OwS$lGdk@f86fnh#K84O0kg)r9xo_a**4JFpv zfH;*0Yf?=qkC-w2945M%o`nyM!fpnb#Ws-%kUbmFz%eu`u>oiYuTP-mW6rM3u#?j5 z&rw|v6(iTjA|-zQmAYR={`Al2Q|eb9t2(!AHhIvVty@}~nkYsdxV@{l0Jf*Op`k4r zP2u)eeq1QFM4$#eUXY@ry|X)<$`pRs9gmTs5AGu;#!?X{HvsZ7?(Fs|u18(xqX+3b z+x7dUuIE)TfYght&}JY#o8sTe6b0Q{Ymg`6drMbR3z1N`O856=dhxjQ^bBq~>W6khS)|ibO+mLN(M_j|pHASsiWidN zS;uf)fhH&q62&m!w79v!c}o#m z#HER?Cpy0-V}8<#J}^+2ZBk4QZZr2x&4M@|JpC014R=T8FN&IY!CjL~_X)O0&TVSI zWpgJ@%Mlt|`so{25~-3F5>Zn93)gZ1g{vdsY(L}!Q>spMIvBYOe6=uvjf&q^wZtNYK_>2+V^hCjkSoz{I_3Q*~c27{Z`@@s&=b>%-$j7g1p z0(DQY?o{)Wi}stJT;$*Ul-6S^HlqOlP8c)ICwnRcbJS}0@vLGP7BhFtm ziE%9fWX7`CH%&d!bjpElIHSo7+14tMzYKJR!S7-mIDN>^US+qB>HF^kHtx))hDH(x zX2={tp~0;<72S`K?oeHFhPNJggB}MXjQ>8!Q%FY$?>AzzWNVhl4%>ahsAtsPfkkn| z?gh(+=i&J!wCEV!rl&^{)$Sb6I^ur)m@=m^UBemaOjKD=$f#2LmGLjV7m2@7;EjNbbG6T;@6~wa za|tqWrJvM3q*r>9BbxAs0V5f&34a7Iulf_5>M!>hlJpx+#^dSUg^~Mhl3u6QvU~b{ zYwEAGcpg%YdHC)pH$IL{4YI^2N@B$Cp0VN=dJ-+ z+uy3oo$5UxGrrsdniV`HZqxb|5-{r4$B7hm}Rt@W_Gic$#ckMjR&!dmkKM&T+0oWRk&{@Ye#q%me;7 z&V6W|HsgN@aFONUvvtaFS#FTHTUt`zuJM+6Naw{DQbGex`fm;R^RjmVrA>%U!C$27 z!|Cx+$WL(f40(sA9ywY{WEfvcjVCjSy`X5rdfFpR!)41C{reL0 zsK)yaU%$F6fZ07QSHcV*%E>2-!6bySkn#lFiZn7Sf*jZV;h5k%8eK9@$ z)8v;eU-96liR|&2<{7=)<2%#z)1^~V{&eY-q@Qm45e+}h66RW6S+fbRFLmoB5DO=! zVJ^@yG7`fD#K@Mh)PBbW)=Q`xfORw%q!lkaf=N25-g%;jS{nB^t|NIJ1S|=A1jdbt zp>Zibed9fP{S!J(x+nB!!b#toWM5hzs!&hMjK2wSz49Tw>RdhrdR25qOv~Nyd=#Q) z?d1qvD1nvDvTix~mzIf8zMp)ruD9qJt^=f8T<4ftp-kbUNaz3jneC6ep{%Mjwt17F8kmIo8une%Jv$IZHf2^n471ocG zriX`# z#eRcqAW-da^D36a!8e78GUNd$LU=C}GbFYWRcj)rPf8?th>ou?FjZbEzz6bK&6G52 zk3by-Tslnh=4+F<6;HCK{-mP9`Q&$x%1)6v$us|$hMJ41cDLR)sVcjzW&8z9iNQii;!E45OLI>d9I3cAn!COzTH z|0w5=`Ow@sW}GbMVm|bBZtdLC*4JV8bX>Q)qqncC;|AOH=2YHqiXszI___(hi5{)^ zlhL;skqwwK3PT@q?guqpHs!!VOI-L(Di8L`=dU3TK4#!A@?aA#@?bAG@lq!ILx45n z&sG0P<-K0%OOf|_rKfewgp0h_gg=6~mwLn}IMr{uCmoSQ%27uhn0~oa8N8G6VLfiH zMc(8-i;W0GL1$i03?hbpEFG~-x}ScN6+*BPN4dN`sx-WVg>_KbJS`#@7CE)>FJ~%e zPX0>U`8nPD>>@W7dAj%w`*r$a-;=}o`xbq!mBRXhmbb%LIUxyWBSLW~7Rj6IvCNE+ z2UElhOGNH&wpJa{{T$+Ue`DqxCIEjNXMa0t-3JGBr1DaLXnlw@s_N5 zx?3`^qGi6j*ZY0b%b-T4=oF;o*)0atbadM+``^0#Wj}v4>Q4Iy6W#?FG3#4;dpzty zd+iF6IjON`aan2CD=sPW=fS6G-dV{x^6WeRjO!=&qMtYF>*ht6Mae@%y;)Dn2A#Z*E`j71nLcfC`{-(P&30Jg55m=Q>2ca#%VC#yMq1ztIpKt#+7$=^{`xRt31PY zG?u0j4m|=kgGOpGLEEyv{Sn=d!Z#h~Fr6)sGyC)FfKfl^RhD+MZ8FNc5}B=uv3L|O zbmfM=eE=^l3=x1dAsTEka&gsm<;F8v^&zjv9MJJ`Cq|7m?mtS+gl#*9Y zmB{rWEQ+Mc)OVf*70>ZQOq0GaoX$Opn)b5BowI{~^~ZESh+K*0D!GZTFI4Br+xfe| zrN3mnbnK$^>!P`%y?o92e|k_+H3T{LzoDS`E&vQ&#}x^XYb^S>8 zMUM8t^m_*QH(Zx+vJBk4T+R!r5V++SxKxe_UjsO;c_zFSaGKjD{3^g{pKZc715R_< zgl_?yU=UjHm3EN&Ou=3a#;OYo%;irj$}rIt?klolanJ zbD+>d7NIlaYFB}^Q*qGwS;*ZK+wSe`+P0&krvn9O#a}FuSTSwf*|Z_iXC-@DM`>51 zWlpq)rV?X&vC$cJMuF>}bcShB_+f z?CR_2>1xASM^DeL9y;#WfrEse-p*|*MrTh)JKk?X9^}mY{{b+fW4pWBcK2=H)zf)X zM}E(9AJdl1>jkO2uBK5!g}|BRG@)`-kG%MW<0nr7(uWyH=O!2~dMl5^CdH|-!aX=o z;oa)NyvY^QaUYDd)cBpShWex7Or=|bkN2! zk+kuO3}1-KB4J*TvIsTTPT7@Ju4dWmd32uvRm+C;0hi^vVUqXeRqpT{6;@Z2To5-; zt-3RNUjMDn>v>tD!B)dwz3v?RoQ4;hXJ7}F`vIpHy_$hoY3cui(+eFqw6C=Ef8q4m z{nuz|=@&e%>nVCICljTmU&85S|7@p9OTU)W3tjBal$L%6rp8u&-;sl* zrH^rX#XDg{oVdNT^!qtI(GqeC?<_6-J)B;~-=>$8mi`T#o_I)d$-7ER{~k^+}92;|DsWU>)z7RKh5byx4Pk#rKSHRr%cBtw$x&%hqRsq)O0{&<|I@*i>t7o{ZpCsmKsT2g<#KO=Pdcn3hmms`@kJj(=G{feY%~cOez>(F4N zXVU|HU0bDeA7@7rNxU^q@oK-BKMu@%%8(EB#f-E&B&_?eR|$a%m zIllBS*8Gz0tqNb89sjS?_he_J{nOA28rkne>Kf}C8|oWW!?Vu$$#3a#Ao@FR1c4DA z*=Rpc{c1D)RJ;-12a;zU_w*QICG<`nG{*3g4Yp|bg9vpm1_lw{!^We1Yfi`nH zcl^o5zgeD)e^>f~{-2RHnPhBIJ?{IC_xLp7-Q1tMpq6XP{az*hg@(%hSYj+q!fgk) z{9uj_YTYwrsKV8wwDvXDH>t@*3ymhxFML|#AK^~<0em&Tii|sXFEE^p)s3d>K=bPq z#Y{unYq)Ntv91Z_L`O!f8=wKXE8eD!k|DIu**wl6ZFYnUEK-i$q{p|#K6KAVMpVXK znJwy|^;&1#iFWLVR=U*%<1I?NCl5v#;K|~)ljutPKosG$9211%heG5(ODml zt{y$z93|A2{#*V$T?3fNi0Hcu-%0g@&3qb=mSjb)^pmc4W_qHjtMF&S-v$_+)tm4? z0d|Q8yph*Cd{WMh&t&w(%%9}QN_J3WJj%FtHNVaG4AM4Up}_?SyQ|MugUwc>GHuu1 z5R32WUbT`g>E=y`HF?uP#cN$PGl$16{LJB@jlIAmb!s0}l!;qQ97qcbeC7A_I3c+` zxjOvO`XhEGY6%D@>;J+_Sl>6;k8`M0x<54*Cqtc4*y8N3kKhr_M8Cdq4GxiX7jV~P zcx1pm)3Mv>w8YGZ=EY=mulp8F zj~dvYDL%^ng>J{1c{O$9v=)u}35;-V_XFK8B3IhVVdA05J@bA2U55(8GqMKaJsx7s z@;NhI;vvT8Mn+&C=FwI7t!mKG)owGq>W3On+N+Q=>!ISiDxXb13l+c&z>wAJ-*t>v zzeda2318ZpKyG^C$=`80uY4}XHxu3k7||IoIE`Zyz8f&wmznVE0b5Z7PWeE4ba!S5 zQ-qv0g!m-RbXx#=!OR-tM82|XS~-+

      !Hj^jK#r_M259c182iQ+p@mT`I8O* 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 z
      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 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi deleted file mode 100644 index d7642fcc4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/__init__.pyi +++ /dev/null @@ -1,34 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import types -import typing - -def check_pkcs7_padding(data: bytes) -> bool: ... -def check_ansix923_padding(data: bytes) -> bool: ... - -class ObjectIdentifier: - def __init__(self, val: str) -> None: ... - @property - def dotted_string(self) -> str: ... - @property - def _name(self) -> str: ... - -T = typing.TypeVar("T") - -class FixedPool(typing.Generic[T]): - def __init__( - self, - create: typing.Callable[[], T], - ) -> None: ... - def acquire(self) -> "PoolAcquisition[T]": ... - -class PoolAcquisition(typing.Generic[T]): - def __enter__(self) -> T: ... - def __exit__( - self, - exc_type: typing.Optional[typing.Type[BaseException]], - exc_value: typing.Optional[BaseException], - exc_tb: typing.Optional[types.TracebackType], - ) -> None: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi deleted file mode 100644 index a8369ba83..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/asn1.pyi +++ /dev/null @@ -1,16 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -class TestCertificate: - not_after_tag: int - not_before_tag: int - issuer_value_tags: typing.List[int] - subject_value_tags: typing.List[int] - -def decode_dss_signature(signature: bytes) -> typing.Tuple[int, int]: ... -def encode_dss_signature(r: int, s: int) -> bytes: ... -def parse_spki_for_data(data: bytes) -> bytes: ... -def test_parse_certificate(data: bytes) -> TestCertificate: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi deleted file mode 100644 index 47a037ade..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/ocsp.pyi +++ /dev/null @@ -1,25 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES -from cryptography.x509.ocsp import ( - OCSPRequest, - OCSPRequestBuilder, - OCSPResponse, - OCSPResponseBuilder, - OCSPResponseStatus, -) - -def load_der_ocsp_request(data: bytes) -> OCSPRequest: ... -def load_der_ocsp_response(data: bytes) -> OCSPResponse: ... -def create_ocsp_request(builder: OCSPRequestBuilder) -> OCSPRequest: ... -def create_ocsp_response( - status: OCSPResponseStatus, - builder: typing.Optional[OCSPResponseBuilder], - private_key: typing.Optional[PRIVATE_KEY_TYPES], - hash_algorithm: typing.Optional[hashes.HashAlgorithm], -) -> OCSPResponse: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi deleted file mode 100644 index 66bd85098..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/pkcs7.pyi +++ /dev/null @@ -1,15 +0,0 @@ -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.serialization import pkcs7 - -def serialize_certificates( - certs: typing.List[x509.Certificate], - encoding: serialization.Encoding, -) -> bytes: ... -def sign_and_serialize( - builder: pkcs7.PKCS7SignatureBuilder, - encoding: serialization.Encoding, - options: typing.Iterable[pkcs7.PKCS7Options], -) -> bytes: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi deleted file mode 100644 index 1bbde8005..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi +++ /dev/null @@ -1,42 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES - -def load_pem_x509_certificate(data: bytes) -> x509.Certificate: ... -def load_pem_x509_certificates( - data: bytes, -) -> typing.List[x509.Certificate]: ... -def load_der_x509_certificate(data: bytes) -> x509.Certificate: ... -def load_pem_x509_crl(data: bytes) -> x509.CertificateRevocationList: ... -def load_der_x509_crl(data: bytes) -> x509.CertificateRevocationList: ... -def load_pem_x509_csr(data: bytes) -> x509.CertificateSigningRequest: ... -def load_der_x509_csr(data: bytes) -> x509.CertificateSigningRequest: ... -def encode_name_bytes(name: x509.Name) -> bytes: ... -def encode_extension_value(extension: x509.ExtensionType) -> bytes: ... -def create_x509_certificate( - builder: x509.CertificateBuilder, - private_key: PRIVATE_KEY_TYPES, - hash_algorithm: typing.Optional[hashes.HashAlgorithm], -) -> x509.Certificate: ... -def create_x509_csr( - builder: x509.CertificateSigningRequestBuilder, - private_key: PRIVATE_KEY_TYPES, - hash_algorithm: typing.Optional[hashes.HashAlgorithm], -) -> x509.CertificateSigningRequest: ... -def create_x509_crl( - builder: x509.CertificateRevocationListBuilder, - private_key: PRIVATE_KEY_TYPES, - hash_algorithm: typing.Optional[hashes.HashAlgorithm], -) -> x509.CertificateRevocationList: ... - -class Sct: ... -class Certificate: ... -class RevokedCertificate: ... -class CertificateRevocationList: ... -class CertificateSigningRequest: ... diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py deleted file mode 100644 index b50933623..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py deleted file mode 100644 index 7903a9bb4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py +++ /dev/null @@ -1,360 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - - -def cryptography_has_ec2m() -> typing.List[str]: - return [ - "EC_POINT_get_affine_coordinates_GF2m", - ] - - -def cryptography_has_ssl3_method() -> typing.List[str]: - return [ - "SSLv3_method", - "SSLv3_client_method", - "SSLv3_server_method", - ] - - -def cryptography_has_set_cert_cb() -> typing.List[str]: - return [ - "SSL_CTX_set_cert_cb", - "SSL_set_cert_cb", - ] - - -def cryptography_has_ssl_st() -> typing.List[str]: - return [ - "SSL_ST_BEFORE", - "SSL_ST_OK", - "SSL_ST_INIT", - "SSL_ST_RENEGOTIATE", - ] - - -def cryptography_has_tls_st() -> typing.List[str]: - return [ - "TLS_ST_BEFORE", - "TLS_ST_OK", - ] - - -def cryptography_has_evp_pkey_set_alias_type() -> typing.List[str]: - return [ - "EVP_PKEY_set_alias_type", - ] - - -def cryptography_has_scrypt() -> typing.List[str]: - return [ - "EVP_PBE_scrypt", - ] - - -def cryptography_has_evp_pkey_dhx() -> typing.List[str]: - return [ - "EVP_PKEY_DHX", - "d2i_DHxparams_bio", - "i2d_DHxparams_bio", - ] - - -def cryptography_has_mem_functions() -> typing.List[str]: - return [ - "Cryptography_CRYPTO_set_mem_functions", - ] - - -def cryptography_has_x509_store_ctx_get_issuer() -> typing.List[str]: - return [ - "X509_STORE_set_get_issuer", - ] - - -def cryptography_has_ed448() -> typing.List[str]: - return [ - "EVP_PKEY_ED448", - "NID_ED448", - ] - - -def cryptography_has_ed25519() -> typing.List[str]: - return [ - "NID_ED25519", - "EVP_PKEY_ED25519", - ] - - -def cryptography_has_poly1305() -> typing.List[str]: - return [ - "NID_poly1305", - "EVP_PKEY_POLY1305", - ] - - -def cryptography_has_evp_digestfinal_xof() -> typing.List[str]: - return [ - "EVP_DigestFinalXOF", - ] - - -def cryptography_has_evp_pkey_get_set_tls_encodedpoint() -> typing.List[str]: - return [ - "EVP_PKEY_get1_tls_encodedpoint", - "EVP_PKEY_set1_tls_encodedpoint", - ] - - -def cryptography_has_fips() -> typing.List[str]: - return [ - "FIPS_mode_set", - "FIPS_mode", - ] - - -def cryptography_has_ssl_sigalgs() -> typing.List[str]: - return [ - "SSL_CTX_set1_sigalgs_list", - ] - - -def cryptography_has_psk() -> typing.List[str]: - return [ - "SSL_CTX_use_psk_identity_hint", - "SSL_CTX_set_psk_server_callback", - "SSL_CTX_set_psk_client_callback", - ] - - -def cryptography_has_psk_tlsv13() -> typing.List[str]: - return [ - "SSL_CTX_set_psk_find_session_callback", - "SSL_CTX_set_psk_use_session_callback", - "Cryptography_SSL_SESSION_new", - "SSL_CIPHER_find", - "SSL_SESSION_set1_master_key", - "SSL_SESSION_set_cipher", - "SSL_SESSION_set_protocol_version", - ] - - -def cryptography_has_custom_ext() -> typing.List[str]: - return [ - "SSL_CTX_add_client_custom_ext", - "SSL_CTX_add_server_custom_ext", - "SSL_extension_supported", - ] - - -def cryptography_has_openssl_cleanup() -> typing.List[str]: - return [ - "OPENSSL_cleanup", - ] - - -def cryptography_has_tlsv13_functions() -> typing.List[str]: - return [ - "SSL_VERIFY_POST_HANDSHAKE", - "SSL_CTX_set_ciphersuites", - "SSL_verify_client_post_handshake", - "SSL_CTX_set_post_handshake_auth", - "SSL_set_post_handshake_auth", - "SSL_SESSION_get_max_early_data", - "SSL_write_early_data", - "SSL_read_early_data", - "SSL_CTX_set_max_early_data", - ] - - -def cryptography_has_raw_key() -> typing.List[str]: - return [ - "EVP_PKEY_new_raw_private_key", - "EVP_PKEY_new_raw_public_key", - "EVP_PKEY_get_raw_private_key", - "EVP_PKEY_get_raw_public_key", - ] - - -def cryptography_has_engine() -> typing.List[str]: - return [ - "ENGINE_by_id", - "ENGINE_init", - "ENGINE_finish", - "ENGINE_get_default_RAND", - "ENGINE_set_default_RAND", - "ENGINE_unregister_RAND", - "ENGINE_ctrl_cmd", - "ENGINE_free", - "ENGINE_get_name", - "Cryptography_add_osrandom_engine", - "ENGINE_ctrl_cmd_string", - "ENGINE_load_builtin_engines", - "ENGINE_load_private_key", - "ENGINE_load_public_key", - "SSL_CTX_set_client_cert_engine", - ] - - -def cryptography_has_verified_chain() -> typing.List[str]: - return [ - "SSL_get0_verified_chain", - ] - - -def cryptography_has_srtp() -> typing.List[str]: - return [ - "SSL_CTX_set_tlsext_use_srtp", - "SSL_set_tlsext_use_srtp", - "SSL_get_selected_srtp_profile", - ] - - -def cryptography_has_providers() -> typing.List[str]: - return [ - "OSSL_PROVIDER_load", - "OSSL_PROVIDER_unload", - "ERR_LIB_PROV", - "PROV_R_WRONG_FINAL_BLOCK_LENGTH", - "PROV_R_BAD_DECRYPT", - ] - - -def cryptography_has_op_no_renegotiation() -> typing.List[str]: - return [ - "SSL_OP_NO_RENEGOTIATION", - ] - - -def cryptography_has_dtls_get_data_mtu() -> typing.List[str]: - return [ - "DTLS_get_data_mtu", - ] - - -def cryptography_has_300_fips() -> typing.List[str]: - return [ - "EVP_default_properties_is_fips_enabled", - "EVP_default_properties_enable_fips", - ] - - -def cryptography_has_ssl_cookie() -> typing.List[str]: - return [ - "SSL_OP_COOKIE_EXCHANGE", - "DTLSv1_listen", - "SSL_CTX_set_cookie_generate_cb", - "SSL_CTX_set_cookie_verify_cb", - ] - - -def cryptography_has_pkcs7_funcs() -> typing.List[str]: - return [ - "SMIME_write_PKCS7", - "PEM_write_bio_PKCS7_stream", - "PKCS7_sign_add_signer", - "PKCS7_final", - "PKCS7_verify", - "SMIME_read_PKCS7", - "PKCS7_get0_signers", - ] - - -def cryptography_has_bn_flags() -> typing.List[str]: - return [ - "BN_FLG_CONSTTIME", - "BN_set_flags", - "BN_prime_checks_for_size", - ] - - -def cryptography_has_evp_pkey_dh() -> typing.List[str]: - return [ - "EVP_PKEY_set1_DH", - ] - - -def cryptography_has_300_evp_cipher() -> typing.List[str]: - return ["EVP_CIPHER_fetch", "EVP_CIPHER_free"] - - -def cryptography_has_unexpected_eof_while_reading() -> typing.List[str]: - return ["SSL_R_UNEXPECTED_EOF_WHILE_READING"] - - -def cryptography_has_pkcs12_set_mac() -> typing.List[str]: - return ["PKCS12_set_mac"] - - -def cryptography_has_ssl_op_ignore_unexpected_eof() -> typing.List[str]: - return [ - "SSL_OP_IGNORE_UNEXPECTED_EOF", - ] - - -def cryptography_has_get_extms_support() -> typing.List[str]: - return ["SSL_get_extms_support"] - - -# This is a mapping of -# {condition: function-returning-names-dependent-on-that-condition} so we can -# loop over them and delete unsupported names at runtime. It will be removed -# when cffi supports #if in cdef. We use functions instead of just a dict of -# lists so we can use coverage to measure which are used. -CONDITIONAL_NAMES = { - "Cryptography_HAS_EC2M": cryptography_has_ec2m, - "Cryptography_HAS_SSL3_METHOD": cryptography_has_ssl3_method, - "Cryptography_HAS_SET_CERT_CB": cryptography_has_set_cert_cb, - "Cryptography_HAS_SSL_ST": cryptography_has_ssl_st, - "Cryptography_HAS_TLS_ST": cryptography_has_tls_st, - "Cryptography_HAS_EVP_PKEY_set_alias_type": ( - cryptography_has_evp_pkey_set_alias_type - ), - "Cryptography_HAS_SCRYPT": cryptography_has_scrypt, - "Cryptography_HAS_EVP_PKEY_DHX": cryptography_has_evp_pkey_dhx, - "Cryptography_HAS_MEM_FUNCTIONS": cryptography_has_mem_functions, - "Cryptography_HAS_X509_STORE_CTX_GET_ISSUER": ( - cryptography_has_x509_store_ctx_get_issuer - ), - "Cryptography_HAS_ED448": cryptography_has_ed448, - "Cryptography_HAS_ED25519": cryptography_has_ed25519, - "Cryptography_HAS_POLY1305": cryptography_has_poly1305, - "Cryptography_HAS_EVP_PKEY_get_set_tls_encodedpoint": ( - cryptography_has_evp_pkey_get_set_tls_encodedpoint - ), - "Cryptography_HAS_FIPS": cryptography_has_fips, - "Cryptography_HAS_SIGALGS": cryptography_has_ssl_sigalgs, - "Cryptography_HAS_PSK": cryptography_has_psk, - "Cryptography_HAS_PSK_TLSv1_3": cryptography_has_psk_tlsv13, - "Cryptography_HAS_CUSTOM_EXT": cryptography_has_custom_ext, - "Cryptography_HAS_OPENSSL_CLEANUP": cryptography_has_openssl_cleanup, - "Cryptography_HAS_TLSv1_3_FUNCTIONS": cryptography_has_tlsv13_functions, - "Cryptography_HAS_RAW_KEY": cryptography_has_raw_key, - "Cryptography_HAS_EVP_DIGESTFINAL_XOF": ( - cryptography_has_evp_digestfinal_xof - ), - "Cryptography_HAS_ENGINE": cryptography_has_engine, - "Cryptography_HAS_VERIFIED_CHAIN": cryptography_has_verified_chain, - "Cryptography_HAS_SRTP": cryptography_has_srtp, - "Cryptography_HAS_PROVIDERS": cryptography_has_providers, - "Cryptography_HAS_OP_NO_RENEGOTIATION": ( - cryptography_has_op_no_renegotiation - ), - "Cryptography_HAS_DTLS_GET_DATA_MTU": cryptography_has_dtls_get_data_mtu, - "Cryptography_HAS_300_FIPS": cryptography_has_300_fips, - "Cryptography_HAS_SSL_COOKIE": cryptography_has_ssl_cookie, - "Cryptography_HAS_PKCS7_FUNCS": cryptography_has_pkcs7_funcs, - "Cryptography_HAS_BN_FLAGS": cryptography_has_bn_flags, - "Cryptography_HAS_EVP_PKEY_DH": cryptography_has_evp_pkey_dh, - "Cryptography_HAS_300_EVP_CIPHER": cryptography_has_300_evp_cipher, - "Cryptography_HAS_UNEXPECTED_EOF_WHILE_READING": ( - cryptography_has_unexpected_eof_while_reading - ), - "Cryptography_HAS_PKCS12_SET_MAC": cryptography_has_pkcs12_set_mac, - "Cryptography_HAS_SSL_OP_IGNORE_UNEXPECTED_EOF": ( - cryptography_has_ssl_op_ignore_unexpected_eof - ), - "Cryptography_HAS_GET_EXTMS_SUPPORT": cryptography_has_get_extms_support, -} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py deleted file mode 100644 index a1602164d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py +++ /dev/null @@ -1,244 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import os -import sys -import threading -import types -import typing -import warnings - -import cryptography -from cryptography.exceptions import InternalError -from cryptography.hazmat.bindings._openssl import ffi, lib -from cryptography.hazmat.bindings.openssl._conditional import CONDITIONAL_NAMES - -_OpenSSLErrorWithText = typing.NamedTuple( - "_OpenSSLErrorWithText", - [("code", int), ("lib", int), ("reason", int), ("reason_text", bytes)], -) - - -class _OpenSSLError: - def __init__(self, code: int, lib: int, reason: int): - self._code = code - self._lib = lib - self._reason = reason - - def _lib_reason_match(self, lib: int, reason: int) -> bool: - return lib == self.lib and reason == self.reason - - @property - def code(self) -> int: - return self._code - - @property - def lib(self) -> int: - return self._lib - - @property - def reason(self) -> int: - return self._reason - - -def _consume_errors(lib) -> typing.List[_OpenSSLError]: - errors = [] - while True: - code: int = lib.ERR_get_error() - if code == 0: - break - - err_lib: int = lib.ERR_GET_LIB(code) - err_reason: int = lib.ERR_GET_REASON(code) - - errors.append(_OpenSSLError(code, err_lib, err_reason)) - - return errors - - -def _errors_with_text( - errors: typing.List[_OpenSSLError], -) -> typing.List[_OpenSSLErrorWithText]: - errors_with_text = [] - for err in errors: - buf = ffi.new("char[]", 256) - lib.ERR_error_string_n(err.code, buf, len(buf)) - err_text_reason: bytes = ffi.string(buf) - - errors_with_text.append( - _OpenSSLErrorWithText( - err.code, err.lib, err.reason, err_text_reason - ) - ) - - return errors_with_text - - -def _consume_errors_with_text(lib): - return _errors_with_text(_consume_errors(lib)) - - -def _openssl_assert( - lib, ok: bool, errors: typing.Optional[typing.List[_OpenSSLError]] = None -) -> None: - if not ok: - if errors is None: - errors = _consume_errors(lib) - errors_with_text = _errors_with_text(errors) - - raise InternalError( - "Unknown OpenSSL error. This error is commonly encountered when " - "another library is not cleaning up the OpenSSL error stack. If " - "you are using cryptography with another library that uses " - "OpenSSL try disabling it before reporting a bug. Otherwise " - "please file an issue at https://github.com/pyca/cryptography/" - "issues with information on how to reproduce " - "this. ({0!r})".format(errors_with_text), - errors_with_text, - ) - - -def _legacy_provider_error(loaded: bool) -> None: - if not loaded: - raise RuntimeError( - "OpenSSL 3.0's legacy provider failed to load. This is a fatal " - "error by default, but cryptography supports running without " - "legacy algorithms by setting the environment variable " - "CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this error," - " you have likely made a mistake with your OpenSSL configuration." - ) - - -def build_conditional_library( - lib: typing.Any, - conditional_names: typing.Dict[str, typing.Callable[[], typing.List[str]]], -) -> typing.Any: - conditional_lib = types.ModuleType("lib") - conditional_lib._original_lib = lib # type: ignore[attr-defined] - excluded_names = set() - for condition, names_cb in conditional_names.items(): - if not getattr(lib, condition): - excluded_names.update(names_cb()) - - for attr in dir(lib): - if attr not in excluded_names: - setattr(conditional_lib, attr, getattr(lib, attr)) - - return conditional_lib - - -class Binding: - """ - OpenSSL API wrapper. - """ - - lib: typing.ClassVar = None - ffi = ffi - _lib_loaded = False - _init_lock = threading.Lock() - _legacy_provider: typing.Any = ffi.NULL - _legacy_provider_loaded = False - _default_provider: typing.Any = ffi.NULL - - def __init__(self) -> None: - self._ensure_ffi_initialized() - - def _enable_fips(self) -> None: - # This function enables FIPS mode for OpenSSL 3.0.0 on installs that - # have the FIPS provider installed properly. - _openssl_assert(self.lib, self.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER) - self._base_provider = self.lib.OSSL_PROVIDER_load( - self.ffi.NULL, b"base" - ) - _openssl_assert(self.lib, self._base_provider != self.ffi.NULL) - self.lib._fips_provider = self.lib.OSSL_PROVIDER_load( - self.ffi.NULL, b"fips" - ) - _openssl_assert(self.lib, self.lib._fips_provider != self.ffi.NULL) - - res = self.lib.EVP_default_properties_enable_fips(self.ffi.NULL, 1) - _openssl_assert(self.lib, res == 1) - - @classmethod - def _register_osrandom_engine(cls) -> None: - # Clear any errors extant in the queue before we start. In many - # scenarios other things may be interacting with OpenSSL in the same - # process space and it has proven untenable to assume that they will - # reliably clear the error queue. Once we clear it here we will - # error on any subsequent unexpected item in the stack. - cls.lib.ERR_clear_error() - if cls.lib.CRYPTOGRAPHY_NEEDS_OSRANDOM_ENGINE: - result = cls.lib.Cryptography_add_osrandom_engine() - _openssl_assert(cls.lib, result in (1, 2)) - - @classmethod - def _ensure_ffi_initialized(cls) -> None: - with cls._init_lock: - if not cls._lib_loaded: - cls.lib = build_conditional_library(lib, CONDITIONAL_NAMES) - cls._lib_loaded = True - cls._register_osrandom_engine() - # As of OpenSSL 3.0.0 we must register a legacy cipher provider - # to get RC2 (needed for junk asymmetric private key - # serialization), RC4, Blowfish, IDEA, SEED, etc. These things - # are ugly legacy, but we aren't going to get rid of them - # any time soon. - if cls.lib.CRYPTOGRAPHY_OPENSSL_300_OR_GREATER: - if not os.environ.get("CRYPTOGRAPHY_OPENSSL_NO_LEGACY"): - cls._legacy_provider = cls.lib.OSSL_PROVIDER_load( - cls.ffi.NULL, b"legacy" - ) - cls._legacy_provider_loaded = ( - cls._legacy_provider != cls.ffi.NULL - ) - _legacy_provider_error(cls._legacy_provider_loaded) - - cls._default_provider = cls.lib.OSSL_PROVIDER_load( - cls.ffi.NULL, b"default" - ) - _openssl_assert( - cls.lib, cls._default_provider != cls.ffi.NULL - ) - - @classmethod - def init_static_locks(cls) -> None: - cls._ensure_ffi_initialized() - - -def _verify_package_version(version: str) -> None: - # Occasionally we run into situations where the version of the Python - # package does not match the version of the shared object that is loaded. - # This may occur in environments where multiple versions of cryptography - # are installed and available in the python path. To avoid errors cropping - # up later this code checks that the currently imported package and the - # shared object that were loaded have the same version and raise an - # ImportError if they do not - so_package_version = ffi.string(lib.CRYPTOGRAPHY_PACKAGE_VERSION) - if version.encode("ascii") != so_package_version: - raise ImportError( - "The version of cryptography does not match the loaded " - "shared object. This can happen if you have multiple copies of " - "cryptography installed in your Python path. Please try creating " - "a new virtual environment to resolve this issue. " - "Loaded python version: {}, shared object version: {}".format( - version, so_package_version - ) - ) - - -_verify_package_version(cryptography.__version__) - -Binding.init_static_locks() - -if ( - sys.platform == "win32" - and os.environ.get("PROCESSOR_ARCHITEW6432") is not None -): - warnings.warn( - "You are using cryptography on a 32-bit Python on a 64-bit Windows " - "Operating System. Cryptography will be significantly faster if you " - "switch to using a 64-bit Python.", - UserWarning, - stacklevel=2, - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py deleted file mode 100644 index b50933623..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py deleted file mode 100644 index fb815a0e9..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_asymmetric.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import abc - -# This exists to break an import cycle. It is normally accessible from the -# asymmetric padding module. - - -class AsymmetricPadding(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this padding (e.g. "PSS", "PKCS1"). - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py deleted file mode 100644 index 138a104e2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_cipheralgorithm.py +++ /dev/null @@ -1,43 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import abc -import typing - -# This exists to break an import cycle. It is normally accessible from the -# ciphers module. - - -class CipherAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this mode (e.g. "AES", "Camellia"). - """ - - @property - @abc.abstractmethod - def key_sizes(self) -> typing.FrozenSet[int]: - """ - Valid key sizes for this algorithm in bits - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The size of the key being used as an integer in bits (e.g. 128, 256). - """ - - -class BlockCipherAlgorithm(metaclass=abc.ABCMeta): - key: bytes - - @property - @abc.abstractmethod - def block_size(self) -> int: - """ - The size of a block as an integer in bits (e.g. 64, 128). - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py deleted file mode 100644 index fddb4c85e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/_serialization.py +++ /dev/null @@ -1,168 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import abc -import typing - -from cryptography import utils -from cryptography.hazmat.primitives.hashes import HashAlgorithm - -# This exists to break an import cycle. These classes are normally accessible -# from the serialization module. - - -class PBES(utils.Enum): - PBESv1SHA1And3KeyTripleDESCBC = "PBESv1 using SHA1 and 3-Key TripleDES" - PBESv2SHA256AndAES256CBC = "PBESv2 using SHA256 PBKDF2 and AES256 CBC" - - -class Encoding(utils.Enum): - PEM = "PEM" - DER = "DER" - OpenSSH = "OpenSSH" - Raw = "Raw" - X962 = "ANSI X9.62" - SMIME = "S/MIME" - - -class PrivateFormat(utils.Enum): - PKCS8 = "PKCS8" - TraditionalOpenSSL = "TraditionalOpenSSL" - Raw = "Raw" - OpenSSH = "OpenSSH" - PKCS12 = "PKCS12" - - def encryption_builder(self) -> "KeySerializationEncryptionBuilder": - if self not in (PrivateFormat.OpenSSH, PrivateFormat.PKCS12): - raise ValueError( - "encryption_builder only supported with PrivateFormat.OpenSSH" - " and PrivateFormat.PKCS12" - ) - return KeySerializationEncryptionBuilder(self) - - -class PublicFormat(utils.Enum): - SubjectPublicKeyInfo = "X.509 subjectPublicKeyInfo with PKCS#1" - PKCS1 = "Raw PKCS#1" - OpenSSH = "OpenSSH" - Raw = "Raw" - CompressedPoint = "X9.62 Compressed Point" - UncompressedPoint = "X9.62 Uncompressed Point" - - -class ParameterFormat(utils.Enum): - PKCS3 = "PKCS3" - - -class KeySerializationEncryption(metaclass=abc.ABCMeta): - pass - - -class BestAvailableEncryption(KeySerializationEncryption): - def __init__(self, password: bytes): - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - self.password = password - - -class NoEncryption(KeySerializationEncryption): - pass - - -class KeySerializationEncryptionBuilder(object): - def __init__( - self, - format: PrivateFormat, - *, - _kdf_rounds: typing.Optional[int] = None, - _hmac_hash: typing.Optional[HashAlgorithm] = None, - _key_cert_algorithm: typing.Optional[PBES] = None, - ) -> None: - self._format = format - - self._kdf_rounds = _kdf_rounds - self._hmac_hash = _hmac_hash - self._key_cert_algorithm = _key_cert_algorithm - - def kdf_rounds(self, rounds: int) -> "KeySerializationEncryptionBuilder": - if self._kdf_rounds is not None: - raise ValueError("kdf_rounds already set") - - if not isinstance(rounds, int): - raise TypeError("kdf_rounds must be an integer") - - if rounds < 1: - raise ValueError("kdf_rounds must be a positive integer") - - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=rounds, - _hmac_hash=self._hmac_hash, - _key_cert_algorithm=self._key_cert_algorithm, - ) - - def hmac_hash( - self, algorithm: HashAlgorithm - ) -> "KeySerializationEncryptionBuilder": - if self._format is not PrivateFormat.PKCS12: - raise TypeError( - "hmac_hash only supported with PrivateFormat.PKCS12" - ) - - if self._hmac_hash is not None: - raise ValueError("hmac_hash already set") - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=self._kdf_rounds, - _hmac_hash=algorithm, - _key_cert_algorithm=self._key_cert_algorithm, - ) - - def key_cert_algorithm( - self, algorithm: PBES - ) -> "KeySerializationEncryptionBuilder": - if self._format is not PrivateFormat.PKCS12: - raise TypeError( - "key_cert_algorithm only supported with " - "PrivateFormat.PKCS12" - ) - if self._key_cert_algorithm is not None: - raise ValueError("key_cert_algorithm already set") - return KeySerializationEncryptionBuilder( - self._format, - _kdf_rounds=self._kdf_rounds, - _hmac_hash=self._hmac_hash, - _key_cert_algorithm=algorithm, - ) - - def build(self, password: bytes) -> KeySerializationEncryption: - if not isinstance(password, bytes) or len(password) == 0: - raise ValueError("Password must be 1 or more bytes.") - - return _KeySerializationEncryption( - self._format, - password, - kdf_rounds=self._kdf_rounds, - hmac_hash=self._hmac_hash, - key_cert_algorithm=self._key_cert_algorithm, - ) - - -class _KeySerializationEncryption(KeySerializationEncryption): - def __init__( - self, - format: PrivateFormat, - password: bytes, - *, - kdf_rounds: typing.Optional[int], - hmac_hash: typing.Optional[HashAlgorithm], - key_cert_algorithm: typing.Optional[PBES], - ): - self._format = format - self.password = password - - self._kdf_rounds = kdf_rounds - self._hmac_hash = hmac_hash - self._key_cert_algorithm = key_cert_algorithm diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py deleted file mode 100644 index b50933623..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py deleted file mode 100644 index 33de0e551..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dh.py +++ /dev/null @@ -1,251 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography.hazmat.primitives import _serialization - -_MIN_MODULUS_SIZE = 512 - - -def generate_parameters( - generator: int, key_size: int, backend: typing.Any = None -) -> "DHParameters": - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.generate_dh_parameters(generator, key_size) - - -class DHParameterNumbers: - def __init__(self, p: int, g: int, q: typing.Optional[int] = None) -> None: - if not isinstance(p, int) or not isinstance(g, int): - raise TypeError("p and g must be integers") - if q is not None and not isinstance(q, int): - raise TypeError("q must be integer or None") - - if g < 2: - raise ValueError("DH generator must be 2 or greater") - - if p.bit_length() < _MIN_MODULUS_SIZE: - raise ValueError( - "p (modulus) must be at least {}-bit".format(_MIN_MODULUS_SIZE) - ) - - self._p = p - self._g = g - self._q = q - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DHParameterNumbers): - return NotImplemented - - return ( - self._p == other._p and self._g == other._g and self._q == other._q - ) - - def parameters(self, backend: typing.Any = None) -> "DHParameters": - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_dh_parameter_numbers(self) - - @property - def p(self) -> int: - return self._p - - @property - def g(self) -> int: - return self._g - - @property - def q(self) -> typing.Optional[int]: - return self._q - - -class DHPublicNumbers: - def __init__(self, y: int, parameter_numbers: DHParameterNumbers) -> None: - if not isinstance(y, int): - raise TypeError("y must be an integer.") - - if not isinstance(parameter_numbers, DHParameterNumbers): - raise TypeError( - "parameters must be an instance of DHParameterNumbers." - ) - - self._y = y - self._parameter_numbers = parameter_numbers - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DHPublicNumbers): - return NotImplemented - - return ( - self._y == other._y - and self._parameter_numbers == other._parameter_numbers - ) - - def public_key(self, backend: typing.Any = None) -> "DHPublicKey": - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_dh_public_numbers(self) - - @property - def y(self) -> int: - return self._y - - @property - def parameter_numbers(self) -> DHParameterNumbers: - return self._parameter_numbers - - -class DHPrivateNumbers: - def __init__(self, x: int, public_numbers: DHPublicNumbers) -> None: - if not isinstance(x, int): - raise TypeError("x must be an integer.") - - if not isinstance(public_numbers, DHPublicNumbers): - raise TypeError( - "public_numbers must be an instance of " "DHPublicNumbers." - ) - - self._x = x - self._public_numbers = public_numbers - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DHPrivateNumbers): - return NotImplemented - - return ( - self._x == other._x - and self._public_numbers == other._public_numbers - ) - - def private_key(self, backend: typing.Any = None) -> "DHPrivateKey": - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_dh_private_numbers(self) - - @property - def public_numbers(self) -> DHPublicNumbers: - return self._public_numbers - - @property - def x(self) -> int: - return self._x - - -class DHParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> "DHPrivateKey": - """ - Generates and returns a DHPrivateKey. - """ - - @abc.abstractmethod - def parameter_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.ParameterFormat, - ) -> bytes: - """ - Returns the parameters serialized as bytes. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> DHParameterNumbers: - """ - Returns a DHParameterNumbers. - """ - - -DHParametersWithSerialization = DHParameters - - -class DHPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> DHPublicNumbers: - """ - Returns a DHPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -DHPublicKeyWithSerialization = DHPublicKey - - -class DHPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> DHPublicKey: - """ - The DHPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DHParameters: - """ - The DHParameters object associated with this private key. - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: DHPublicKey) -> bytes: - """ - Given peer's DHPublicKey, carry out the key exchange and - return shared key as bytes. - """ - - @abc.abstractmethod - def private_numbers(self) -> DHPrivateNumbers: - """ - Returns a DHPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -DHPrivateKeyWithSerialization = DHPrivateKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py deleted file mode 100644 index 6103d8093..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/dsa.py +++ /dev/null @@ -1,288 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class DSAParameters(metaclass=abc.ABCMeta): - @abc.abstractmethod - def generate_private_key(self) -> "DSAPrivateKey": - """ - Generates and returns a DSAPrivateKey. - """ - - @abc.abstractmethod - def parameter_numbers(self) -> "DSAParameterNumbers": - """ - Returns a DSAParameterNumbers. - """ - - -DSAParametersWithNumbers = DSAParameters - - -class DSAPrivateKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def public_key(self) -> "DSAPublicKey": - """ - The DSAPublicKey associated with this private key. - """ - - @abc.abstractmethod - def parameters(self) -> DSAParameters: - """ - The DSAParameters object associated with this private key. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> bytes: - """ - Signs the data - """ - - @abc.abstractmethod - def private_numbers(self) -> "DSAPrivateNumbers": - """ - Returns a DSAPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -DSAPrivateKeyWithSerialization = DSAPrivateKey - - -class DSAPublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the prime modulus. - """ - - @abc.abstractmethod - def parameters(self) -> DSAParameters: - """ - The DSAParameters object associated with this public key. - """ - - @abc.abstractmethod - def public_numbers(self) -> "DSAPublicNumbers": - """ - Returns a DSAPublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> None: - """ - Verifies the signature of the data. - """ - - -DSAPublicKeyWithSerialization = DSAPublicKey - - -class DSAParameterNumbers: - def __init__(self, p: int, q: int, g: int): - if ( - not isinstance(p, int) - or not isinstance(q, int) - or not isinstance(g, int) - ): - raise TypeError( - "DSAParameterNumbers p, q, and g arguments must be integers." - ) - - self._p = p - self._q = q - self._g = g - - @property - def p(self) -> int: - return self._p - - @property - def q(self) -> int: - return self._q - - @property - def g(self) -> int: - return self._g - - def parameters(self, backend: typing.Any = None) -> DSAParameters: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_dsa_parameter_numbers(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DSAParameterNumbers): - return NotImplemented - - return self.p == other.p and self.q == other.q and self.g == other.g - - def __repr__(self) -> str: - return ( - "".format(self=self) - ) - - -class DSAPublicNumbers: - def __init__(self, y: int, parameter_numbers: DSAParameterNumbers): - if not isinstance(y, int): - raise TypeError("DSAPublicNumbers y argument must be an integer.") - - if not isinstance(parameter_numbers, DSAParameterNumbers): - raise TypeError( - "parameter_numbers must be a DSAParameterNumbers instance." - ) - - self._y = y - self._parameter_numbers = parameter_numbers - - @property - def y(self) -> int: - return self._y - - @property - def parameter_numbers(self) -> DSAParameterNumbers: - return self._parameter_numbers - - def public_key(self, backend: typing.Any = None) -> DSAPublicKey: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_dsa_public_numbers(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DSAPublicNumbers): - return NotImplemented - - return ( - self.y == other.y - and self.parameter_numbers == other.parameter_numbers - ) - - def __repr__(self) -> str: - return ( - "".format(self=self) - ) - - -class DSAPrivateNumbers: - def __init__(self, x: int, public_numbers: DSAPublicNumbers): - if not isinstance(x, int): - raise TypeError("DSAPrivateNumbers x argument must be an integer.") - - if not isinstance(public_numbers, DSAPublicNumbers): - raise TypeError( - "public_numbers must be a DSAPublicNumbers instance." - ) - self._public_numbers = public_numbers - self._x = x - - @property - def x(self) -> int: - return self._x - - @property - def public_numbers(self) -> DSAPublicNumbers: - return self._public_numbers - - def private_key(self, backend: typing.Any = None) -> DSAPrivateKey: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_dsa_private_numbers(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DSAPrivateNumbers): - return NotImplemented - - return ( - self.x == other.x and self.public_numbers == other.public_numbers - ) - - -def generate_parameters( - key_size: int, backend: typing.Any = None -) -> DSAParameters: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.generate_dsa_parameters(key_size) - - -def generate_private_key( - key_size: int, backend: typing.Any = None -) -> DSAPrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.generate_dsa_private_key_and_parameters(key_size) - - -def _check_dsa_parameters(parameters: DSAParameterNumbers) -> None: - if parameters.p.bit_length() not in [1024, 2048, 3072, 4096]: - raise ValueError( - "p must be exactly 1024, 2048, 3072, or 4096 bits long" - ) - if parameters.q.bit_length() not in [160, 224, 256]: - raise ValueError("q must be exactly 160, 224, or 256 bits long") - - if not (1 < parameters.g < parameters.p): - raise ValueError("g, p don't satisfy 1 < g < p.") - - -def _check_dsa_private_numbers(numbers: DSAPrivateNumbers) -> None: - parameters = numbers.public_numbers.parameter_numbers - _check_dsa_parameters(parameters) - if numbers.x <= 0 or numbers.x >= parameters.q: - raise ValueError("x must be > 0 and < q.") - - if numbers.public_numbers.y != pow(parameters.g, numbers.x, parameters.p): - raise ValueError("y must be equal to (g ** x % p).") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py deleted file mode 100644 index c5df2c27a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ec.py +++ /dev/null @@ -1,483 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography import utils -from cryptography.hazmat._oid import ObjectIdentifier -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class EllipticCurveOID: - SECP192R1 = ObjectIdentifier("1.2.840.10045.3.1.1") - SECP224R1 = ObjectIdentifier("1.3.132.0.33") - SECP256K1 = ObjectIdentifier("1.3.132.0.10") - SECP256R1 = ObjectIdentifier("1.2.840.10045.3.1.7") - SECP384R1 = ObjectIdentifier("1.3.132.0.34") - SECP521R1 = ObjectIdentifier("1.3.132.0.35") - BRAINPOOLP256R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.7") - BRAINPOOLP384R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.11") - BRAINPOOLP512R1 = ObjectIdentifier("1.3.36.3.3.2.8.1.1.13") - SECT163K1 = ObjectIdentifier("1.3.132.0.1") - SECT163R2 = ObjectIdentifier("1.3.132.0.15") - SECT233K1 = ObjectIdentifier("1.3.132.0.26") - SECT233R1 = ObjectIdentifier("1.3.132.0.27") - SECT283K1 = ObjectIdentifier("1.3.132.0.16") - SECT283R1 = ObjectIdentifier("1.3.132.0.17") - SECT409K1 = ObjectIdentifier("1.3.132.0.36") - SECT409R1 = ObjectIdentifier("1.3.132.0.37") - SECT571K1 = ObjectIdentifier("1.3.132.0.38") - SECT571R1 = ObjectIdentifier("1.3.132.0.39") - - -class EllipticCurve(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - The name of the curve. e.g. secp256r1. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - -class EllipticCurveSignatureAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def algorithm( - self, - ) -> typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm]: - """ - The digest algorithm used with this signature. - """ - - -class EllipticCurvePrivateKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def exchange( - self, algorithm: "ECDH", peer_public_key: "EllipticCurvePublicKey" - ) -> bytes: - """ - Performs a key exchange operation using the provided algorithm with the - provided peer's public key. - """ - - @abc.abstractmethod - def public_key(self) -> "EllipticCurvePublicKey": - """ - The EllipticCurvePublicKey for this private key. - """ - - @property - @abc.abstractmethod - def curve(self) -> EllipticCurve: - """ - The EllipticCurve that this key is on. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - signature_algorithm: EllipticCurveSignatureAlgorithm, - ) -> bytes: - """ - Signs the data - """ - - @abc.abstractmethod - def private_numbers(self) -> "EllipticCurvePrivateNumbers": - """ - Returns an EllipticCurvePrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -EllipticCurvePrivateKeyWithSerialization = EllipticCurvePrivateKey - - -class EllipticCurvePublicKey(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def curve(self) -> EllipticCurve: - """ - The EllipticCurve that this key is on. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - Bit size of a secret scalar for the curve. - """ - - @abc.abstractmethod - def public_numbers(self) -> "EllipticCurvePublicNumbers": - """ - Returns an EllipticCurvePublicNumbers. - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - signature_algorithm: EllipticCurveSignatureAlgorithm, - ) -> None: - """ - Verifies the signature of the data. - """ - - @classmethod - def from_encoded_point( - cls, curve: EllipticCurve, data: bytes - ) -> "EllipticCurvePublicKey": - utils._check_bytes("data", data) - - if not isinstance(curve, EllipticCurve): - raise TypeError("curve must be an EllipticCurve instance") - - if len(data) == 0: - raise ValueError("data must not be an empty byte string") - - if data[0] not in [0x02, 0x03, 0x04]: - raise ValueError("Unsupported elliptic curve point type") - - from cryptography.hazmat.backends.openssl.backend import backend - - return backend.load_elliptic_curve_public_bytes(curve, data) - - -EllipticCurvePublicKeyWithSerialization = EllipticCurvePublicKey - - -class SECT571R1(EllipticCurve): - name = "sect571r1" - key_size = 570 - - -class SECT409R1(EllipticCurve): - name = "sect409r1" - key_size = 409 - - -class SECT283R1(EllipticCurve): - name = "sect283r1" - key_size = 283 - - -class SECT233R1(EllipticCurve): - name = "sect233r1" - key_size = 233 - - -class SECT163R2(EllipticCurve): - name = "sect163r2" - key_size = 163 - - -class SECT571K1(EllipticCurve): - name = "sect571k1" - key_size = 571 - - -class SECT409K1(EllipticCurve): - name = "sect409k1" - key_size = 409 - - -class SECT283K1(EllipticCurve): - name = "sect283k1" - key_size = 283 - - -class SECT233K1(EllipticCurve): - name = "sect233k1" - key_size = 233 - - -class SECT163K1(EllipticCurve): - name = "sect163k1" - key_size = 163 - - -class SECP521R1(EllipticCurve): - name = "secp521r1" - key_size = 521 - - -class SECP384R1(EllipticCurve): - name = "secp384r1" - key_size = 384 - - -class SECP256R1(EllipticCurve): - name = "secp256r1" - key_size = 256 - - -class SECP256K1(EllipticCurve): - name = "secp256k1" - key_size = 256 - - -class SECP224R1(EllipticCurve): - name = "secp224r1" - key_size = 224 - - -class SECP192R1(EllipticCurve): - name = "secp192r1" - key_size = 192 - - -class BrainpoolP256R1(EllipticCurve): - name = "brainpoolP256r1" - key_size = 256 - - -class BrainpoolP384R1(EllipticCurve): - name = "brainpoolP384r1" - key_size = 384 - - -class BrainpoolP512R1(EllipticCurve): - name = "brainpoolP512r1" - key_size = 512 - - -_CURVE_TYPES: typing.Dict[str, typing.Type[EllipticCurve]] = { - "prime192v1": SECP192R1, - "prime256v1": SECP256R1, - "secp192r1": SECP192R1, - "secp224r1": SECP224R1, - "secp256r1": SECP256R1, - "secp384r1": SECP384R1, - "secp521r1": SECP521R1, - "secp256k1": SECP256K1, - "sect163k1": SECT163K1, - "sect233k1": SECT233K1, - "sect283k1": SECT283K1, - "sect409k1": SECT409K1, - "sect571k1": SECT571K1, - "sect163r2": SECT163R2, - "sect233r1": SECT233R1, - "sect283r1": SECT283R1, - "sect409r1": SECT409R1, - "sect571r1": SECT571R1, - "brainpoolP256r1": BrainpoolP256R1, - "brainpoolP384r1": BrainpoolP384R1, - "brainpoolP512r1": BrainpoolP512R1, -} - - -class ECDSA(EllipticCurveSignatureAlgorithm): - def __init__( - self, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ): - self._algorithm = algorithm - - @property - def algorithm( - self, - ) -> typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm]: - return self._algorithm - - -def generate_private_key( - curve: EllipticCurve, backend: typing.Any = None -) -> EllipticCurvePrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.generate_elliptic_curve_private_key(curve) - - -def derive_private_key( - private_value: int, - curve: EllipticCurve, - backend: typing.Any = None, -) -> EllipticCurvePrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - if not isinstance(private_value, int): - raise TypeError("private_value must be an integer type.") - - if private_value <= 0: - raise ValueError("private_value must be a positive integer.") - - if not isinstance(curve, EllipticCurve): - raise TypeError("curve must provide the EllipticCurve interface.") - - return ossl.derive_elliptic_curve_private_key(private_value, curve) - - -class EllipticCurvePublicNumbers: - def __init__(self, x: int, y: int, curve: EllipticCurve): - if not isinstance(x, int) or not isinstance(y, int): - raise TypeError("x and y must be integers.") - - if not isinstance(curve, EllipticCurve): - raise TypeError("curve must provide the EllipticCurve interface.") - - self._y = y - self._x = x - self._curve = curve - - def public_key(self, backend: typing.Any = None) -> EllipticCurvePublicKey: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_elliptic_curve_public_numbers(self) - - @property - def curve(self) -> EllipticCurve: - return self._curve - - @property - def x(self) -> int: - return self._x - - @property - def y(self) -> int: - return self._y - - def __eq__(self, other: object) -> bool: - if not isinstance(other, EllipticCurvePublicNumbers): - return NotImplemented - - return ( - self.x == other.x - and self.y == other.y - and self.curve.name == other.curve.name - and self.curve.key_size == other.curve.key_size - ) - - def __hash__(self) -> int: - return hash((self.x, self.y, self.curve.name, self.curve.key_size)) - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - -class EllipticCurvePrivateNumbers: - def __init__( - self, private_value: int, public_numbers: EllipticCurvePublicNumbers - ): - if not isinstance(private_value, int): - raise TypeError("private_value must be an integer.") - - if not isinstance(public_numbers, EllipticCurvePublicNumbers): - raise TypeError( - "public_numbers must be an EllipticCurvePublicNumbers " - "instance." - ) - - self._private_value = private_value - self._public_numbers = public_numbers - - def private_key( - self, backend: typing.Any = None - ) -> EllipticCurvePrivateKey: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_elliptic_curve_private_numbers(self) - - @property - def private_value(self) -> int: - return self._private_value - - @property - def public_numbers(self) -> EllipticCurvePublicNumbers: - return self._public_numbers - - def __eq__(self, other: object) -> bool: - if not isinstance(other, EllipticCurvePrivateNumbers): - return NotImplemented - - return ( - self.private_value == other.private_value - and self.public_numbers == other.public_numbers - ) - - def __hash__(self) -> int: - return hash((self.private_value, self.public_numbers)) - - -class ECDH: - pass - - -_OID_TO_CURVE = { - EllipticCurveOID.SECP192R1: SECP192R1, - EllipticCurveOID.SECP224R1: SECP224R1, - EllipticCurveOID.SECP256K1: SECP256K1, - EllipticCurveOID.SECP256R1: SECP256R1, - EllipticCurveOID.SECP384R1: SECP384R1, - EllipticCurveOID.SECP521R1: SECP521R1, - EllipticCurveOID.BRAINPOOLP256R1: BrainpoolP256R1, - EllipticCurveOID.BRAINPOOLP384R1: BrainpoolP384R1, - EllipticCurveOID.BRAINPOOLP512R1: BrainpoolP512R1, - EllipticCurveOID.SECT163K1: SECT163K1, - EllipticCurveOID.SECT163R2: SECT163R2, - EllipticCurveOID.SECT233K1: SECT233K1, - EllipticCurveOID.SECT233R1: SECT233R1, - EllipticCurveOID.SECT283K1: SECT283K1, - EllipticCurveOID.SECT283R1: SECT283R1, - EllipticCurveOID.SECT409K1: SECT409K1, - EllipticCurveOID.SECT409R1: SECT409R1, - EllipticCurveOID.SECT571K1: SECT571K1, - EllipticCurveOID.SECT571R1: SECT571R1, -} - - -def get_curve_for_oid(oid: ObjectIdentifier) -> typing.Type[EllipticCurve]: - try: - return _OID_TO_CURVE[oid] - except KeyError: - raise LookupError( - "The provided object identifier has no matching elliptic " - "curve class" - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py deleted file mode 100644 index 220bf592c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed25519.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import _serialization - -_ED25519_KEY_SIZE = 32 -_ED25519_SIG_SIZE = 64 - - -class Ed25519PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> "Ed25519PublicKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed25519_supported(): - raise UnsupportedAlgorithm( - "ed25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return backend.ed25519_load_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def verify(self, signature: bytes, data: bytes) -> None: - """ - Verify the signature. - """ - - -class Ed25519PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> "Ed25519PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed25519_supported(): - raise UnsupportedAlgorithm( - "ed25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return backend.ed25519_generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> "Ed25519PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed25519_supported(): - raise UnsupportedAlgorithm( - "ed25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return backend.ed25519_load_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> Ed25519PublicKey: - """ - The Ed25519PublicKey derived from the private key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def sign(self, data: bytes) -> bytes: - """ - Signs the data. - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py deleted file mode 100644 index 27bc27c69..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/ed448.py +++ /dev/null @@ -1,87 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import _serialization - - -class Ed448PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> "Ed448PublicKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return backend.ed448_load_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def verify(self, signature: bytes, data: bytes) -> None: - """ - Verify the signature. - """ - - -class Ed448PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> "Ed448PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - return backend.ed448_generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> "Ed448PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.ed448_supported(): - raise UnsupportedAlgorithm( - "ed448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM, - ) - - return backend.ed448_load_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> Ed448PublicKey: - """ - The Ed448PublicKey derived from the private key. - """ - - @abc.abstractmethod - def sign(self, data: bytes) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py deleted file mode 100644 index dd3c648f1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py +++ /dev/null @@ -1,101 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives._asymmetric import ( - AsymmetricPadding as AsymmetricPadding, -) -from cryptography.hazmat.primitives.asymmetric import rsa - - -class PKCS1v15(AsymmetricPadding): - name = "EMSA-PKCS1-v1_5" - - -class _MaxLength: - "Sentinel value for `MAX_LENGTH`." - - -class _Auto: - "Sentinel value for `AUTO`." - - -class _DigestLength: - "Sentinel value for `DIGEST_LENGTH`." - - -class PSS(AsymmetricPadding): - MAX_LENGTH = _MaxLength() - AUTO = _Auto() - DIGEST_LENGTH = _DigestLength() - name = "EMSA-PSS" - _salt_length: typing.Union[int, _MaxLength, _Auto, _DigestLength] - - def __init__( - self, - mgf: "MGF", - salt_length: typing.Union[int, _MaxLength, _Auto, _DigestLength], - ) -> None: - self._mgf = mgf - - if not isinstance( - salt_length, (int, _MaxLength, _Auto, _DigestLength) - ): - raise TypeError( - "salt_length must be an integer, MAX_LENGTH, " - "DIGEST_LENGTH, or AUTO" - ) - - if isinstance(salt_length, int) and salt_length < 0: - raise ValueError("salt_length must be zero or greater.") - - self._salt_length = salt_length - - -class OAEP(AsymmetricPadding): - name = "EME-OAEP" - - def __init__( - self, - mgf: "MGF", - algorithm: hashes.HashAlgorithm, - label: typing.Optional[bytes], - ): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - self._mgf = mgf - self._algorithm = algorithm - self._label = label - - -class MGF(metaclass=abc.ABCMeta): - _algorithm: hashes.HashAlgorithm - - -class MGF1(MGF): - MAX_LENGTH = _MaxLength() - - def __init__(self, algorithm: hashes.HashAlgorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - - self._algorithm = algorithm - - -def calculate_max_pss_salt_length( - key: typing.Union["rsa.RSAPrivateKey", "rsa.RSAPublicKey"], - hash_algorithm: hashes.HashAlgorithm, -) -> int: - if not isinstance(key, (rsa.RSAPrivateKey, rsa.RSAPublicKey)): - raise TypeError("key must be an RSA public or private key") - # bit length - 1 per RFC 3447 - emlen = (key.key_size + 6) // 8 - salt_length = emlen - hash_algorithm.digest_size - 2 - assert salt_length >= 0 - return salt_length diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py deleted file mode 100644 index 81f5a0ec6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py +++ /dev/null @@ -1,432 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing -from math import gcd - -from cryptography.hazmat.primitives import _serialization, hashes -from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding -from cryptography.hazmat.primitives.asymmetric import utils as asym_utils - - -class RSAPrivateKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes: - """ - Decrypts the provided ciphertext. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the public modulus. - """ - - @abc.abstractmethod - def public_key(self) -> "RSAPublicKey": - """ - The RSAPublicKey associated with this private key. - """ - - @abc.abstractmethod - def sign( - self, - data: bytes, - padding: AsymmetricPadding, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> bytes: - """ - Signs the data. - """ - - @abc.abstractmethod - def private_numbers(self) -> "RSAPrivateNumbers": - """ - Returns an RSAPrivateNumbers. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - -RSAPrivateKeyWithSerialization = RSAPrivateKey - - -class RSAPublicKey(metaclass=abc.ABCMeta): - @abc.abstractmethod - def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes: - """ - Encrypts the given plaintext. - """ - - @property - @abc.abstractmethod - def key_size(self) -> int: - """ - The bit length of the public modulus. - """ - - @abc.abstractmethod - def public_numbers(self) -> "RSAPublicNumbers": - """ - Returns an RSAPublicNumbers - """ - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - Returns the key serialized as bytes. - """ - - @abc.abstractmethod - def verify( - self, - signature: bytes, - data: bytes, - padding: AsymmetricPadding, - algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm], - ) -> None: - """ - Verifies the signature of the data. - """ - - @abc.abstractmethod - def recover_data_from_signature( - self, - signature: bytes, - padding: AsymmetricPadding, - algorithm: typing.Optional[hashes.HashAlgorithm], - ) -> bytes: - """ - Recovers the original data from the signature. - """ - - -RSAPublicKeyWithSerialization = RSAPublicKey - - -def generate_private_key( - public_exponent: int, - key_size: int, - backend: typing.Any = None, -) -> RSAPrivateKey: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - _verify_rsa_parameters(public_exponent, key_size) - return ossl.generate_rsa_private_key(public_exponent, key_size) - - -def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None: - if public_exponent not in (3, 65537): - raise ValueError( - "public_exponent must be either 3 (for legacy compatibility) or " - "65537. Almost everyone should choose 65537 here!" - ) - - if key_size < 512: - raise ValueError("key_size must be at least 512-bits.") - - -def _check_private_key_components( - p: int, - q: int, - private_exponent: int, - dmp1: int, - dmq1: int, - iqmp: int, - public_exponent: int, - modulus: int, -) -> None: - if modulus < 3: - raise ValueError("modulus must be >= 3.") - - if p >= modulus: - raise ValueError("p must be < modulus.") - - if q >= modulus: - raise ValueError("q must be < modulus.") - - if dmp1 >= modulus: - raise ValueError("dmp1 must be < modulus.") - - if dmq1 >= modulus: - raise ValueError("dmq1 must be < modulus.") - - if iqmp >= modulus: - raise ValueError("iqmp must be < modulus.") - - if private_exponent >= modulus: - raise ValueError("private_exponent must be < modulus.") - - if public_exponent < 3 or public_exponent >= modulus: - raise ValueError("public_exponent must be >= 3 and < modulus.") - - if public_exponent & 1 == 0: - raise ValueError("public_exponent must be odd.") - - if dmp1 & 1 == 0: - raise ValueError("dmp1 must be odd.") - - if dmq1 & 1 == 0: - raise ValueError("dmq1 must be odd.") - - if p * q != modulus: - raise ValueError("p*q must equal modulus.") - - -def _check_public_key_components(e: int, n: int) -> None: - if n < 3: - raise ValueError("n must be >= 3.") - - if e < 3 or e >= n: - raise ValueError("e must be >= 3 and < n.") - - if e & 1 == 0: - raise ValueError("e must be odd.") - - -def _modinv(e: int, m: int) -> int: - """ - Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1 - """ - x1, x2 = 1, 0 - a, b = e, m - while b > 0: - q, r = divmod(a, b) - xn = x1 - q * x2 - a, b, x1, x2 = b, r, x2, xn - return x1 % m - - -def rsa_crt_iqmp(p: int, q: int) -> int: - """ - Compute the CRT (q ** -1) % p value from RSA primes p and q. - """ - return _modinv(q, p) - - -def rsa_crt_dmp1(private_exponent: int, p: int) -> int: - """ - Compute the CRT private_exponent % (p - 1) value from the RSA - private_exponent (d) and p. - """ - return private_exponent % (p - 1) - - -def rsa_crt_dmq1(private_exponent: int, q: int) -> int: - """ - Compute the CRT private_exponent % (q - 1) value from the RSA - private_exponent (d) and q. - """ - return private_exponent % (q - 1) - - -# Controls the number of iterations rsa_recover_prime_factors will perform -# to obtain the prime factors. Each iteration increments by 2 so the actual -# maximum attempts is half this number. -_MAX_RECOVERY_ATTEMPTS = 1000 - - -def rsa_recover_prime_factors( - n: int, e: int, d: int -) -> typing.Tuple[int, int]: - """ - Compute factors p and q from the private exponent d. We assume that n has - no more than two factors. This function is adapted from code in PyCrypto. - """ - # See 8.2.2(i) in Handbook of Applied Cryptography. - ktot = d * e - 1 - # The quantity d*e-1 is a multiple of phi(n), even, - # and can be represented as t*2^s. - t = ktot - while t % 2 == 0: - t = t // 2 - # Cycle through all multiplicative inverses in Zn. - # The algorithm is non-deterministic, but there is a 50% chance - # any candidate a leads to successful factoring. - # See "Digitalized Signatures and Public Key Functions as Intractable - # as Factorization", M. Rabin, 1979 - spotted = False - a = 2 - while not spotted and a < _MAX_RECOVERY_ATTEMPTS: - k = t - # Cycle through all values a^{t*2^i}=a^k - while k < ktot: - cand = pow(a, k, n) - # Check if a^k is a non-trivial root of unity (mod n) - if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: - # We have found a number such that (cand-1)(cand+1)=0 (mod n). - # Either of the terms divides n. - p = gcd(cand + 1, n) - spotted = True - break - k *= 2 - # This value was not any good... let's try another! - a += 2 - if not spotted: - raise ValueError("Unable to compute factors p and q from exponent d.") - # Found ! - q, r = divmod(n, p) - assert r == 0 - p, q = sorted((p, q), reverse=True) - return (p, q) - - -class RSAPrivateNumbers: - def __init__( - self, - p: int, - q: int, - d: int, - dmp1: int, - dmq1: int, - iqmp: int, - public_numbers: "RSAPublicNumbers", - ): - if ( - not isinstance(p, int) - or not isinstance(q, int) - or not isinstance(d, int) - or not isinstance(dmp1, int) - or not isinstance(dmq1, int) - or not isinstance(iqmp, int) - ): - raise TypeError( - "RSAPrivateNumbers p, q, d, dmp1, dmq1, iqmp arguments must" - " all be an integers." - ) - - if not isinstance(public_numbers, RSAPublicNumbers): - raise TypeError( - "RSAPrivateNumbers public_numbers must be an RSAPublicNumbers" - " instance." - ) - - self._p = p - self._q = q - self._d = d - self._dmp1 = dmp1 - self._dmq1 = dmq1 - self._iqmp = iqmp - self._public_numbers = public_numbers - - @property - def p(self) -> int: - return self._p - - @property - def q(self) -> int: - return self._q - - @property - def d(self) -> int: - return self._d - - @property - def dmp1(self) -> int: - return self._dmp1 - - @property - def dmq1(self) -> int: - return self._dmq1 - - @property - def iqmp(self) -> int: - return self._iqmp - - @property - def public_numbers(self) -> "RSAPublicNumbers": - return self._public_numbers - - def private_key( - self, - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, - ) -> RSAPrivateKey: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_rsa_private_numbers( - self, unsafe_skip_rsa_key_validation - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RSAPrivateNumbers): - return NotImplemented - - return ( - self.p == other.p - and self.q == other.q - and self.d == other.d - and self.dmp1 == other.dmp1 - and self.dmq1 == other.dmq1 - and self.iqmp == other.iqmp - and self.public_numbers == other.public_numbers - ) - - def __hash__(self) -> int: - return hash( - ( - self.p, - self.q, - self.d, - self.dmp1, - self.dmq1, - self.iqmp, - self.public_numbers, - ) - ) - - -class RSAPublicNumbers: - def __init__(self, e: int, n: int): - if not isinstance(e, int) or not isinstance(n, int): - raise TypeError("RSAPublicNumbers arguments must be integers.") - - self._e = e - self._n = n - - @property - def e(self) -> int: - return self._e - - @property - def n(self) -> int: - return self._n - - def public_key(self, backend: typing.Any = None) -> RSAPublicKey: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - return ossl.load_rsa_public_numbers(self) - - def __repr__(self) -> str: - return "".format(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RSAPublicNumbers): - return NotImplemented - - return self.e == other.e and self.n == other.n - - def __hash__(self) -> int: - return hash((self.e, self.n)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py deleted file mode 100644 index 6b5ff0801..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/types.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives.asymmetric import ( - dh, - dsa, - ec, - ed448, - ed25519, - rsa, - x448, - x25519, -) - -# Every asymmetric key type -PUBLIC_KEY_TYPES = typing.Union[ - dh.DHPublicKey, - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] -# Every asymmetric key type -PRIVATE_KEY_TYPES = typing.Union[ - dh.DHPrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - x25519.X25519PrivateKey, - x448.X448PrivateKey, -] -# Just the key types we allow to be used for x509 signing. This mirrors -# the certificate public key types -CERTIFICATE_PRIVATE_KEY_TYPES = typing.Union[ - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, -] -# Just the key types we allow to be used for x509 signing. This mirrors -# the certificate private key types -CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES = typing.Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, -] -# This type removes DHPublicKey. x448/x25519 can be a public key -# but cannot be used in signing so they are allowed here. -CERTIFICATE_PUBLIC_KEY_TYPES = typing.Union[ - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py deleted file mode 100644 index 140ca1960..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/utils.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography.hazmat.bindings._rust import asn1 -from cryptography.hazmat.primitives import hashes - -decode_dss_signature = asn1.decode_dss_signature -encode_dss_signature = asn1.encode_dss_signature - - -class Prehashed: - def __init__(self, algorithm: hashes.HashAlgorithm): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of HashAlgorithm.") - - self._algorithm = algorithm - self._digest_size = algorithm.digest_size - - @property - def digest_size(self) -> int: - return self._digest_size diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py deleted file mode 100644 index 690af78c2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x25519.py +++ /dev/null @@ -1,81 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import _serialization - - -class X25519PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> "X25519PublicKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return backend.x25519_load_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - -class X25519PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> "X25519PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - return backend.x25519_generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> "X25519PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x25519_supported(): - raise UnsupportedAlgorithm( - "X25519 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return backend.x25519_load_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> X25519PublicKey: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: X25519PublicKey) -> bytes: - """ - Performs a key exchange operation using the provided peer's public key. - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py deleted file mode 100644 index 7f71c2722..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/x448.py +++ /dev/null @@ -1,81 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc - -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives import _serialization - - -class X448PublicKey(metaclass=abc.ABCMeta): - @classmethod - def from_public_bytes(cls, data: bytes) -> "X448PublicKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return backend.x448_load_public_bytes(data) - - @abc.abstractmethod - def public_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PublicFormat, - ) -> bytes: - """ - The serialized bytes of the public key. - """ - - -class X448PrivateKey(metaclass=abc.ABCMeta): - @classmethod - def generate(cls) -> "X448PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - return backend.x448_generate_key() - - @classmethod - def from_private_bytes(cls, data: bytes) -> "X448PrivateKey": - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.x448_supported(): - raise UnsupportedAlgorithm( - "X448 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_EXCHANGE_ALGORITHM, - ) - - return backend.x448_load_private_bytes(data) - - @abc.abstractmethod - def public_key(self) -> X448PublicKey: - """ - The serialized bytes of the public key. - """ - - @abc.abstractmethod - def private_bytes( - self, - encoding: _serialization.Encoding, - format: _serialization.PrivateFormat, - encryption_algorithm: _serialization.KeySerializationEncryption, - ) -> bytes: - """ - The serialized bytes of the private key. - """ - - @abc.abstractmethod - def exchange(self, peer_public_key: X448PublicKey) -> bytes: - """ - Performs a key exchange operation using the provided peer's public key. - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py deleted file mode 100644 index 95f02842a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers.base import ( - AEADCipherContext, - AEADDecryptionContext, - AEADEncryptionContext, - Cipher, - CipherContext, -) - -__all__ = [ - "Cipher", - "CipherAlgorithm", - "BlockCipherAlgorithm", - "CipherContext", - "AEADCipherContext", - "AEADDecryptionContext", - "AEADEncryptionContext", -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py deleted file mode 100644 index 567301acc..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/aead.py +++ /dev/null @@ -1,374 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import os -import typing - -from cryptography import exceptions, utils -from cryptography.hazmat.backends.openssl import aead -from cryptography.hazmat.backends.openssl.backend import backend -from cryptography.hazmat.bindings._rust import FixedPool - - -class ChaCha20Poly1305: - _MAX_SIZE = 2**31 - 1 - - def __init__(self, key: bytes): - if not backend.aead_cipher_supported(self): - raise exceptions.UnsupportedAlgorithm( - "ChaCha20Poly1305 is not supported by this version of OpenSSL", - exceptions._Reasons.UNSUPPORTED_CIPHER, - ) - utils._check_byteslike("key", key) - - if len(key) != 32: - raise ValueError("ChaCha20Poly1305 key must be 32 bytes.") - - self._key = key - self._pool = FixedPool(self._create_fn) - - @classmethod - def generate_key(cls) -> bytes: - return os.urandom(32) - - def _create_fn(self): - return aead._aead_create_ctx(backend, self, self._key) - - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: - # This is OverflowError to match what cffi would raise - raise OverflowError( - "Data or associated data too long. Max 2**31 - 1 bytes" - ) - - self._check_params(nonce, data, associated_data) - with self._pool.acquire() as ctx: - return aead._encrypt( - backend, self, nonce, data, [associated_data], 16, ctx - ) - - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - self._check_params(nonce, data, associated_data) - with self._pool.acquire() as ctx: - return aead._decrypt( - backend, self, nonce, data, [associated_data], 16, ctx - ) - - def _check_params( - self, - nonce: bytes, - data: bytes, - associated_data: bytes, - ) -> None: - utils._check_byteslike("nonce", nonce) - utils._check_bytes("data", data) - utils._check_bytes("associated_data", associated_data) - if len(nonce) != 12: - raise ValueError("Nonce must be 12 bytes") - - -class AESCCM: - _MAX_SIZE = 2**31 - 1 - - def __init__(self, key: bytes, tag_length: int = 16): - utils._check_byteslike("key", key) - if len(key) not in (16, 24, 32): - raise ValueError("AESCCM key must be 128, 192, or 256 bits.") - - self._key = key - if not isinstance(tag_length, int): - raise TypeError("tag_length must be an integer") - - if tag_length not in (4, 6, 8, 10, 12, 14, 16): - raise ValueError("Invalid tag_length") - - self._tag_length = tag_length - - if not backend.aead_cipher_supported(self): - raise exceptions.UnsupportedAlgorithm( - "AESCCM is not supported by this version of OpenSSL", - exceptions._Reasons.UNSUPPORTED_CIPHER, - ) - - @classmethod - def generate_key(cls, bit_length: int) -> bytes: - if not isinstance(bit_length, int): - raise TypeError("bit_length must be an integer") - - if bit_length not in (128, 192, 256): - raise ValueError("bit_length must be 128, 192, or 256") - - return os.urandom(bit_length // 8) - - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: - # This is OverflowError to match what cffi would raise - raise OverflowError( - "Data or associated data too long. Max 2**31 - 1 bytes" - ) - - self._check_params(nonce, data, associated_data) - self._validate_lengths(nonce, len(data)) - return aead._encrypt( - backend, self, nonce, data, [associated_data], self._tag_length - ) - - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - self._check_params(nonce, data, associated_data) - return aead._decrypt( - backend, self, nonce, data, [associated_data], self._tag_length - ) - - def _validate_lengths(self, nonce: bytes, data_len: int) -> None: - # For information about computing this, see - # https://tools.ietf.org/html/rfc3610#section-2.1 - l_val = 15 - len(nonce) - if 2 ** (8 * l_val) < data_len: - raise ValueError("Data too long for nonce") - - def _check_params( - self, nonce: bytes, data: bytes, associated_data: bytes - ) -> None: - utils._check_byteslike("nonce", nonce) - utils._check_bytes("data", data) - utils._check_bytes("associated_data", associated_data) - if not 7 <= len(nonce) <= 13: - raise ValueError("Nonce must be between 7 and 13 bytes") - - -class AESGCM: - _MAX_SIZE = 2**31 - 1 - - def __init__(self, key: bytes): - utils._check_byteslike("key", key) - if len(key) not in (16, 24, 32): - raise ValueError("AESGCM key must be 128, 192, or 256 bits.") - - self._key = key - - @classmethod - def generate_key(cls, bit_length: int) -> bytes: - if not isinstance(bit_length, int): - raise TypeError("bit_length must be an integer") - - if bit_length not in (128, 192, 256): - raise ValueError("bit_length must be 128, 192, or 256") - - return os.urandom(bit_length // 8) - - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: - # This is OverflowError to match what cffi would raise - raise OverflowError( - "Data or associated data too long. Max 2**31 - 1 bytes" - ) - - self._check_params(nonce, data, associated_data) - return aead._encrypt(backend, self, nonce, data, [associated_data], 16) - - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - self._check_params(nonce, data, associated_data) - return aead._decrypt(backend, self, nonce, data, [associated_data], 16) - - def _check_params( - self, - nonce: bytes, - data: bytes, - associated_data: bytes, - ) -> None: - utils._check_byteslike("nonce", nonce) - utils._check_bytes("data", data) - utils._check_bytes("associated_data", associated_data) - if len(nonce) < 8 or len(nonce) > 128: - raise ValueError("Nonce must be between 8 and 128 bytes") - - -class AESOCB3: - _MAX_SIZE = 2**31 - 1 - - def __init__(self, key: bytes): - utils._check_byteslike("key", key) - if len(key) not in (16, 24, 32): - raise ValueError("AESOCB3 key must be 128, 192, or 256 bits.") - - self._key = key - - if not backend.aead_cipher_supported(self): - raise exceptions.UnsupportedAlgorithm( - "OCB3 is not supported by this version of OpenSSL", - exceptions._Reasons.UNSUPPORTED_CIPHER, - ) - - @classmethod - def generate_key(cls, bit_length: int) -> bytes: - if not isinstance(bit_length, int): - raise TypeError("bit_length must be an integer") - - if bit_length not in (128, 192, 256): - raise ValueError("bit_length must be 128, 192, or 256") - - return os.urandom(bit_length // 8) - - def encrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: - # This is OverflowError to match what cffi would raise - raise OverflowError( - "Data or associated data too long. Max 2**31 - 1 bytes" - ) - - self._check_params(nonce, data, associated_data) - return aead._encrypt(backend, self, nonce, data, [associated_data], 16) - - def decrypt( - self, - nonce: bytes, - data: bytes, - associated_data: typing.Optional[bytes], - ) -> bytes: - if associated_data is None: - associated_data = b"" - - self._check_params(nonce, data, associated_data) - return aead._decrypt(backend, self, nonce, data, [associated_data], 16) - - def _check_params( - self, - nonce: bytes, - data: bytes, - associated_data: bytes, - ) -> None: - utils._check_byteslike("nonce", nonce) - utils._check_bytes("data", data) - utils._check_bytes("associated_data", associated_data) - if len(nonce) < 12 or len(nonce) > 15: - raise ValueError("Nonce must be between 12 and 15 bytes") - - -class AESSIV(object): - _MAX_SIZE = 2**31 - 1 - - def __init__(self, key: bytes): - utils._check_byteslike("key", key) - if len(key) not in (32, 48, 64): - raise ValueError("AESSIV key must be 256, 384, or 512 bits.") - - self._key = key - - if not backend.aead_cipher_supported(self): - raise exceptions.UnsupportedAlgorithm( - "AES-SIV is not supported by this version of OpenSSL", - exceptions._Reasons.UNSUPPORTED_CIPHER, - ) - - @classmethod - def generate_key(cls, bit_length: int) -> bytes: - if not isinstance(bit_length, int): - raise TypeError("bit_length must be an integer") - - if bit_length not in (256, 384, 512): - raise ValueError("bit_length must be 256, 384, or 512") - - return os.urandom(bit_length // 8) - - def encrypt( - self, - data: bytes, - associated_data: typing.Optional[typing.List[bytes]], - ) -> bytes: - if associated_data is None: - associated_data = [] - - self._check_params(data, associated_data) - - if len(data) > self._MAX_SIZE or any( - len(ad) > self._MAX_SIZE for ad in associated_data - ): - # This is OverflowError to match what cffi would raise - raise OverflowError( - "Data or associated data too long. Max 2**31 - 1 bytes" - ) - - return aead._encrypt(backend, self, b"", data, associated_data, 16) - - def decrypt( - self, - data: bytes, - associated_data: typing.Optional[typing.List[bytes]], - ) -> bytes: - if associated_data is None: - associated_data = [] - - self._check_params(data, associated_data) - - return aead._decrypt(backend, self, b"", data, associated_data, 16) - - def _check_params( - self, - data: bytes, - associated_data: typing.List[bytes], - ) -> None: - utils._check_bytes("data", data) - if len(data) == 0: - raise ValueError("data must not be zero length") - if not isinstance(associated_data, list) or not all( - isinstance(x, bytes) for x in associated_data - ): - raise TypeError("associated_data must be a list of bytes or None") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py deleted file mode 100644 index 613854261..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py +++ /dev/null @@ -1,227 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography import utils -from cryptography.hazmat.primitives.ciphers import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) - - -def _verify_key_size(algorithm: CipherAlgorithm, key: bytes) -> bytes: - # Verify that the key is instance of bytes - utils._check_byteslike("key", key) - - # Verify that the key size matches the expected key size - if len(key) * 8 not in algorithm.key_sizes: - raise ValueError( - "Invalid key size ({}) for {}.".format( - len(key) * 8, algorithm.name - ) - ) - return key - - -class AES(CipherAlgorithm, BlockCipherAlgorithm): - name = "AES" - block_size = 128 - # 512 added to support AES-256-XTS, which uses 512-bit keys - key_sizes = frozenset([128, 192, 256, 512]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class AES128(CipherAlgorithm, BlockCipherAlgorithm): - name = "AES" - block_size = 128 - key_sizes = frozenset([128]) - key_size = 128 - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - -class AES256(CipherAlgorithm, BlockCipherAlgorithm): - name = "AES" - block_size = 128 - key_sizes = frozenset([256]) - key_size = 256 - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - -class Camellia(CipherAlgorithm, BlockCipherAlgorithm): - name = "camellia" - block_size = 128 - key_sizes = frozenset([128, 192, 256]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class TripleDES(CipherAlgorithm, BlockCipherAlgorithm): - name = "3DES" - block_size = 64 - key_sizes = frozenset([64, 128, 192]) - - def __init__(self, key: bytes): - if len(key) == 8: - key += key + key - elif len(key) == 16: - key += key[:8] - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class Blowfish(CipherAlgorithm, BlockCipherAlgorithm): - name = "Blowfish" - block_size = 64 - key_sizes = frozenset(range(32, 449, 8)) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -_BlowfishInternal = Blowfish -utils.deprecated( - Blowfish, - __name__, - "Blowfish has been deprecated", - utils.DeprecatedIn37, - name="Blowfish", -) - - -class CAST5(CipherAlgorithm, BlockCipherAlgorithm): - name = "CAST5" - block_size = 64 - key_sizes = frozenset(range(40, 129, 8)) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -_CAST5Internal = CAST5 -utils.deprecated( - CAST5, - __name__, - "CAST5 has been deprecated", - utils.DeprecatedIn37, - name="CAST5", -) - - -class ARC4(CipherAlgorithm): - name = "RC4" - key_sizes = frozenset([40, 56, 64, 80, 128, 160, 192, 256]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class IDEA(CipherAlgorithm, BlockCipherAlgorithm): - name = "IDEA" - block_size = 64 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -_IDEAInternal = IDEA -utils.deprecated( - IDEA, - __name__, - "IDEA has been deprecated", - utils.DeprecatedIn37, - name="IDEA", -) - - -class SEED(CipherAlgorithm, BlockCipherAlgorithm): - name = "SEED" - block_size = 128 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -_SEEDInternal = SEED -utils.deprecated( - SEED, - __name__, - "SEED has been deprecated", - utils.DeprecatedIn37, - name="SEED", -) - - -class ChaCha20(CipherAlgorithm): - name = "ChaCha20" - key_sizes = frozenset([256]) - - def __init__(self, key: bytes, nonce: bytes): - self.key = _verify_key_size(self, key) - utils._check_byteslike("nonce", nonce) - - if len(nonce) != 16: - raise ValueError("nonce must be 128-bits (16 bytes)") - - self._nonce = nonce - - @property - def nonce(self) -> bytes: - return self._nonce - - @property - def key_size(self) -> int: - return len(self.key) * 8 - - -class SM4(CipherAlgorithm, BlockCipherAlgorithm): - name = "SM4" - block_size = 128 - key_sizes = frozenset([128]) - - def __init__(self, key: bytes): - self.key = _verify_key_size(self, key) - - @property - def key_size(self) -> int: - return len(self.key) * 8 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py deleted file mode 100644 index d7c4f096d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/base.py +++ /dev/null @@ -1,269 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography.exceptions import ( - AlreadyFinalized, - AlreadyUpdated, - NotYetFinalized, -) -from cryptography.hazmat.primitives._cipheralgorithm import CipherAlgorithm -from cryptography.hazmat.primitives.ciphers import modes - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.ciphers import ( - _CipherContext as _BackendCipherContext, - ) - - -class CipherContext(metaclass=abc.ABCMeta): - @abc.abstractmethod - def update(self, data: bytes) -> bytes: - """ - Processes the provided bytes through the cipher and returns the results - as bytes. - """ - - @abc.abstractmethod - def update_into(self, data: bytes, buf: bytes) -> int: - """ - Processes the provided bytes and writes the resulting data into the - provided buffer. Returns the number of bytes written. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Returns the results of processing the final block as bytes. - """ - - -class AEADCipherContext(CipherContext, metaclass=abc.ABCMeta): - @abc.abstractmethod - def authenticate_additional_data(self, data: bytes) -> None: - """ - Authenticates the provided bytes. - """ - - -class AEADDecryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): - @abc.abstractmethod - def finalize_with_tag(self, tag: bytes) -> bytes: - """ - Returns the results of processing the final block as bytes and allows - delayed passing of the authentication tag. - """ - - -class AEADEncryptionContext(AEADCipherContext, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tag(self) -> bytes: - """ - Returns tag bytes. This is only available after encryption is - finalized. - """ - - -Mode = typing.TypeVar( - "Mode", bound=typing.Optional[modes.Mode], covariant=True -) - - -class Cipher(typing.Generic[Mode]): - def __init__( - self, - algorithm: CipherAlgorithm, - mode: Mode, - backend: typing.Any = None, - ) -> None: - - if not isinstance(algorithm, CipherAlgorithm): - raise TypeError("Expected interface of CipherAlgorithm.") - - if mode is not None: - # mypy needs this assert to narrow the type from our generic - # type. Maybe it won't some time in the future. - assert isinstance(mode, modes.Mode) - mode.validate_for_algorithm(algorithm) - - self.algorithm = algorithm - self.mode = mode - - @typing.overload - def encryptor( - self: "Cipher[modes.ModeWithAuthenticationTag]", - ) -> AEADEncryptionContext: - ... - - @typing.overload - def encryptor( - self: "_CIPHER_TYPE", - ) -> CipherContext: - ... - - def encryptor(self): - if isinstance(self.mode, modes.ModeWithAuthenticationTag): - if self.mode.tag is not None: - raise ValueError( - "Authentication tag must be None when encrypting." - ) - from cryptography.hazmat.backends.openssl.backend import backend - - ctx = backend.create_symmetric_encryption_ctx( - self.algorithm, self.mode - ) - return self._wrap_ctx(ctx, encrypt=True) - - @typing.overload - def decryptor( - self: "Cipher[modes.ModeWithAuthenticationTag]", - ) -> AEADDecryptionContext: - ... - - @typing.overload - def decryptor( - self: "_CIPHER_TYPE", - ) -> CipherContext: - ... - - def decryptor(self): - from cryptography.hazmat.backends.openssl.backend import backend - - ctx = backend.create_symmetric_decryption_ctx( - self.algorithm, self.mode - ) - return self._wrap_ctx(ctx, encrypt=False) - - def _wrap_ctx( - self, ctx: "_BackendCipherContext", encrypt: bool - ) -> typing.Union[ - AEADEncryptionContext, AEADDecryptionContext, CipherContext - ]: - if isinstance(self.mode, modes.ModeWithAuthenticationTag): - if encrypt: - return _AEADEncryptionContext(ctx) - else: - return _AEADDecryptionContext(ctx) - else: - return _CipherContext(ctx) - - -_CIPHER_TYPE = Cipher[ - typing.Union[ - modes.ModeWithNonce, - modes.ModeWithTweak, - None, - modes.ECB, - modes.ModeWithInitializationVector, - ] -] - - -class _CipherContext(CipherContext): - _ctx: typing.Optional["_BackendCipherContext"] - - def __init__(self, ctx: "_BackendCipherContext") -> None: - self._ctx = ctx - - def update(self, data: bytes) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - return self._ctx.update(data) - - def update_into(self, data: bytes, buf: bytes) -> int: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - return self._ctx.update_into(data, buf) - - def finalize(self) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - data = self._ctx.finalize() - self._ctx = None - return data - - -class _AEADCipherContext(AEADCipherContext): - _ctx: typing.Optional["_BackendCipherContext"] - _tag: typing.Optional[bytes] - - def __init__(self, ctx: "_BackendCipherContext") -> None: - self._ctx = ctx - self._bytes_processed = 0 - self._aad_bytes_processed = 0 - self._tag = None - self._updated = False - - def _check_limit(self, data_size: int) -> None: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - self._updated = True - self._bytes_processed += data_size - if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES: - raise ValueError( - "{} has a maximum encrypted byte limit of {}".format( - self._ctx._mode.name, self._ctx._mode._MAX_ENCRYPTED_BYTES - ) - ) - - def update(self, data: bytes) -> bytes: - self._check_limit(len(data)) - # mypy needs this assert even though _check_limit already checked - assert self._ctx is not None - return self._ctx.update(data) - - def update_into(self, data: bytes, buf: bytes) -> int: - self._check_limit(len(data)) - # mypy needs this assert even though _check_limit already checked - assert self._ctx is not None - return self._ctx.update_into(data, buf) - - def finalize(self) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - data = self._ctx.finalize() - self._tag = self._ctx.tag - self._ctx = None - return data - - def authenticate_additional_data(self, data: bytes) -> None: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - if self._updated: - raise AlreadyUpdated("Update has been called on this context.") - - self._aad_bytes_processed += len(data) - if self._aad_bytes_processed > self._ctx._mode._MAX_AAD_BYTES: - raise ValueError( - "{} has a maximum AAD byte limit of {}".format( - self._ctx._mode.name, self._ctx._mode._MAX_AAD_BYTES - ) - ) - - self._ctx.authenticate_additional_data(data) - - -class _AEADDecryptionContext(_AEADCipherContext, AEADDecryptionContext): - def finalize_with_tag(self, tag: bytes) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - data = self._ctx.finalize_with_tag(tag) - self._tag = self._ctx.tag - self._ctx = None - return data - - -class _AEADEncryptionContext(_AEADCipherContext, AEADEncryptionContext): - @property - def tag(self) -> bytes: - if self._ctx is not None: - raise NotYetFinalized( - "You must finalize encryption before " "getting the tag." - ) - assert self._tag is not None - return self._tag diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py deleted file mode 100644 index b7468b1bd..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/ciphers/modes.py +++ /dev/null @@ -1,275 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm, _Reasons -from cryptography.hazmat.primitives._cipheralgorithm import ( - BlockCipherAlgorithm, - CipherAlgorithm, -) -from cryptography.hazmat.primitives.ciphers import algorithms - - -class Mode(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this mode (e.g. "ECB", "CBC"). - """ - - @abc.abstractmethod - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - """ - Checks that all the necessary invariants of this (mode, algorithm) - combination are met. - """ - - -class ModeWithInitializationVector(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def initialization_vector(self) -> bytes: - """ - The value of the initialization vector for this mode as bytes. - """ - - -class ModeWithTweak(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tweak(self) -> bytes: - """ - The value of the tweak for this mode as bytes. - """ - - -class ModeWithNonce(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def nonce(self) -> bytes: - """ - The value of the nonce for this mode as bytes. - """ - - -class ModeWithAuthenticationTag(Mode, metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def tag(self) -> typing.Optional[bytes]: - """ - The value of the tag supplied to the constructor of this mode. - """ - - -def _check_aes_key_length(self: Mode, algorithm: CipherAlgorithm) -> None: - if algorithm.key_size > 256 and algorithm.name == "AES": - raise ValueError( - "Only 128, 192, and 256 bit keys are allowed for this AES mode" - ) - - -def _check_iv_length( - self: ModeWithInitializationVector, algorithm: BlockCipherAlgorithm -) -> None: - if len(self.initialization_vector) * 8 != algorithm.block_size: - raise ValueError( - "Invalid IV size ({}) for {}.".format( - len(self.initialization_vector), self.name - ) - ) - - -def _check_nonce_length( - nonce: bytes, name: str, algorithm: CipherAlgorithm -) -> None: - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - f"{name} requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - if len(nonce) * 8 != algorithm.block_size: - raise ValueError( - "Invalid nonce size ({}) for {}.".format(len(nonce), name) - ) - - -def _check_iv_and_key_length( - self: ModeWithInitializationVector, algorithm: CipherAlgorithm -) -> None: - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - f"{self} requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - _check_aes_key_length(self, algorithm) - _check_iv_length(self, algorithm) - - -class CBC(ModeWithInitializationVector): - name = "CBC" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class XTS(ModeWithTweak): - name = "XTS" - - def __init__(self, tweak: bytes): - utils._check_byteslike("tweak", tweak) - - if len(tweak) != 16: - raise ValueError("tweak must be 128-bits (16 bytes)") - - self._tweak = tweak - - @property - def tweak(self) -> bytes: - return self._tweak - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - if isinstance(algorithm, (algorithms.AES128, algorithms.AES256)): - raise TypeError( - "The AES128 and AES256 classes do not support XTS, please use " - "the standard AES class instead." - ) - - if algorithm.key_size not in (256, 512): - raise ValueError( - "The XTS specification requires a 256-bit key for AES-128-XTS" - " and 512-bit key for AES-256-XTS" - ) - - -class ECB(Mode): - name = "ECB" - - validate_for_algorithm = _check_aes_key_length - - -class OFB(ModeWithInitializationVector): - name = "OFB" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CFB(ModeWithInitializationVector): - name = "CFB" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CFB8(ModeWithInitializationVector): - name = "CFB8" - - def __init__(self, initialization_vector: bytes): - utils._check_byteslike("initialization_vector", initialization_vector) - self._initialization_vector = initialization_vector - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - validate_for_algorithm = _check_iv_and_key_length - - -class CTR(ModeWithNonce): - name = "CTR" - - def __init__(self, nonce: bytes): - utils._check_byteslike("nonce", nonce) - self._nonce = nonce - - @property - def nonce(self) -> bytes: - return self._nonce - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - _check_aes_key_length(self, algorithm) - _check_nonce_length(self.nonce, self.name, algorithm) - - -class GCM(ModeWithInitializationVector, ModeWithAuthenticationTag): - name = "GCM" - _MAX_ENCRYPTED_BYTES = (2**39 - 256) // 8 - _MAX_AAD_BYTES = (2**64) // 8 - - def __init__( - self, - initialization_vector: bytes, - tag: typing.Optional[bytes] = None, - min_tag_length: int = 16, - ): - # OpenSSL 3.0.0 constrains GCM IVs to [64, 1024] bits inclusive - # This is a sane limit anyway so we'll enforce it here. - utils._check_byteslike("initialization_vector", initialization_vector) - if len(initialization_vector) < 8 or len(initialization_vector) > 128: - raise ValueError( - "initialization_vector must be between 8 and 128 bytes (64 " - "and 1024 bits)." - ) - self._initialization_vector = initialization_vector - if tag is not None: - utils._check_bytes("tag", tag) - if min_tag_length < 4: - raise ValueError("min_tag_length must be >= 4") - if len(tag) < min_tag_length: - raise ValueError( - "Authentication tag must be {} bytes or longer.".format( - min_tag_length - ) - ) - self._tag = tag - self._min_tag_length = min_tag_length - - @property - def tag(self) -> typing.Optional[bytes]: - return self._tag - - @property - def initialization_vector(self) -> bytes: - return self._initialization_vector - - def validate_for_algorithm(self, algorithm: CipherAlgorithm) -> None: - _check_aes_key_length(self, algorithm) - if not isinstance(algorithm, BlockCipherAlgorithm): - raise UnsupportedAlgorithm( - "GCM requires a block cipher algorithm", - _Reasons.UNSUPPORTED_CIPHER, - ) - block_size_bytes = algorithm.block_size // 8 - if self._tag is not None and len(self._tag) > block_size_bytes: - raise ValueError( - "Authentication tag cannot be more than {} bytes.".format( - block_size_bytes - ) - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py deleted file mode 100644 index 00c4bd11d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/cmac.py +++ /dev/null @@ -1,64 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized -from cryptography.hazmat.primitives import ciphers - -if typing.TYPE_CHECKING: - from cryptography.hazmat.backends.openssl.cmac import _CMACContext - - -class CMAC: - _ctx: typing.Optional["_CMACContext"] - _algorithm: ciphers.BlockCipherAlgorithm - - def __init__( - self, - algorithm: ciphers.BlockCipherAlgorithm, - backend: typing.Any = None, - ctx: typing.Optional["_CMACContext"] = None, - ) -> None: - if not isinstance(algorithm, ciphers.BlockCipherAlgorithm): - raise TypeError("Expected instance of BlockCipherAlgorithm.") - self._algorithm = algorithm - - if ctx is None: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - self._ctx = ossl.create_cmac_ctx(self._algorithm) - else: - self._ctx = ctx - - def update(self, data: bytes) -> None: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - - utils._check_bytes("data", data) - self._ctx.update(data) - - def finalize(self) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - digest = self._ctx.finalize() - self._ctx = None - return digest - - def verify(self, signature: bytes) -> None: - utils._check_bytes("signature", signature) - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - - ctx, self._ctx = self._ctx, None - ctx.verify(signature) - - def copy(self) -> "CMAC": - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - return CMAC(self._algorithm, ctx=self._ctx.copy()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py deleted file mode 100644 index a02fa9c45..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/constant_time.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import hmac - - -def bytes_eq(a: bytes, b: bytes) -> bool: - if not isinstance(a, bytes) or not isinstance(b, bytes): - raise TypeError("a and b must be bytes.") - - return hmac.compare_digest(a, b) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py deleted file mode 100644 index 330c08dfa..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hashes.py +++ /dev/null @@ -1,261 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import abc -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized - - -class HashAlgorithm(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def name(self) -> str: - """ - A string naming this algorithm (e.g. "sha256", "md5"). - """ - - @property - @abc.abstractmethod - def digest_size(self) -> int: - """ - The size of the resulting digest in bytes. - """ - - @property - @abc.abstractmethod - def block_size(self) -> typing.Optional[int]: - """ - The internal block size of the hash function, or None if the hash - function does not use blocks internally (e.g. SHA3). - """ - - -class HashContext(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def algorithm(self) -> HashAlgorithm: - """ - A HashAlgorithm that will be used by this context. - """ - - @abc.abstractmethod - def update(self, data: bytes) -> None: - """ - Processes the provided bytes through the hash. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Finalizes the hash context and returns the hash digest as bytes. - """ - - @abc.abstractmethod - def copy(self) -> "HashContext": - """ - Return a HashContext that is a copy of the current context. - """ - - -class ExtendableOutputFunction(metaclass=abc.ABCMeta): - """ - An interface for extendable output functions. - """ - - -class Hash(HashContext): - _ctx: typing.Optional[HashContext] - - def __init__( - self, - algorithm: HashAlgorithm, - backend: typing.Any = None, - ctx: typing.Optional["HashContext"] = None, - ) -> None: - if not isinstance(algorithm, HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - self._algorithm = algorithm - - if ctx is None: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - self._ctx = ossl.create_hash_ctx(self.algorithm) - else: - self._ctx = ctx - - @property - def algorithm(self) -> HashAlgorithm: - return self._algorithm - - def update(self, data: bytes) -> None: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - utils._check_byteslike("data", data) - self._ctx.update(data) - - def copy(self) -> "Hash": - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - return Hash(self.algorithm, ctx=self._ctx.copy()) - - def finalize(self) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - digest = self._ctx.finalize() - self._ctx = None - return digest - - -class SHA1(HashAlgorithm): - name = "sha1" - digest_size = 20 - block_size = 64 - - -class SHA512_224(HashAlgorithm): # noqa: N801 - name = "sha512-224" - digest_size = 28 - block_size = 128 - - -class SHA512_256(HashAlgorithm): # noqa: N801 - name = "sha512-256" - digest_size = 32 - block_size = 128 - - -class SHA224(HashAlgorithm): - name = "sha224" - digest_size = 28 - block_size = 64 - - -class SHA256(HashAlgorithm): - name = "sha256" - digest_size = 32 - block_size = 64 - - -class SHA384(HashAlgorithm): - name = "sha384" - digest_size = 48 - block_size = 128 - - -class SHA512(HashAlgorithm): - name = "sha512" - digest_size = 64 - block_size = 128 - - -class SHA3_224(HashAlgorithm): # noqa: N801 - name = "sha3-224" - digest_size = 28 - block_size = None - - -class SHA3_256(HashAlgorithm): # noqa: N801 - name = "sha3-256" - digest_size = 32 - block_size = None - - -class SHA3_384(HashAlgorithm): # noqa: N801 - name = "sha3-384" - digest_size = 48 - block_size = None - - -class SHA3_512(HashAlgorithm): # noqa: N801 - name = "sha3-512" - digest_size = 64 - block_size = None - - -class SHAKE128(HashAlgorithm, ExtendableOutputFunction): - name = "shake128" - block_size = None - - def __init__(self, digest_size: int): - if not isinstance(digest_size, int): - raise TypeError("digest_size must be an integer") - - if digest_size < 1: - raise ValueError("digest_size must be a positive integer") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class SHAKE256(HashAlgorithm, ExtendableOutputFunction): - name = "shake256" - block_size = None - - def __init__(self, digest_size: int): - if not isinstance(digest_size, int): - raise TypeError("digest_size must be an integer") - - if digest_size < 1: - raise ValueError("digest_size must be a positive integer") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class MD5(HashAlgorithm): - name = "md5" - digest_size = 16 - block_size = 64 - - -class BLAKE2b(HashAlgorithm): - name = "blake2b" - _max_digest_size = 64 - _min_digest_size = 1 - block_size = 128 - - def __init__(self, digest_size: int): - - if digest_size != 64: - raise ValueError("Digest size must be 64") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class BLAKE2s(HashAlgorithm): - name = "blake2s" - block_size = 64 - _max_digest_size = 32 - _min_digest_size = 1 - - def __init__(self, digest_size: int): - - if digest_size != 32: - raise ValueError("Digest size must be 32") - - self._digest_size = digest_size - - @property - def digest_size(self) -> int: - return self._digest_size - - -class SM3(HashAlgorithm): - name = "sm3" - digest_size = 32 - block_size = 64 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py deleted file mode 100644 index 8f1c0eae6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/hmac.py +++ /dev/null @@ -1,70 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized -from cryptography.hazmat.backends.openssl.hmac import _HMACContext -from cryptography.hazmat.primitives import hashes - - -class HMAC(hashes.HashContext): - _ctx: typing.Optional[_HMACContext] - - def __init__( - self, - key: bytes, - algorithm: hashes.HashAlgorithm, - backend: typing.Any = None, - ctx=None, - ): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise TypeError("Expected instance of hashes.HashAlgorithm.") - self._algorithm = algorithm - - self._key = key - if ctx is None: - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - self._ctx = ossl.create_hmac_ctx(key, self.algorithm) - else: - self._ctx = ctx - - @property - def algorithm(self) -> hashes.HashAlgorithm: - return self._algorithm - - def update(self, data: bytes) -> None: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - utils._check_byteslike("data", data) - self._ctx.update(data) - - def copy(self) -> "HMAC": - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - return HMAC( - self._key, - self.algorithm, - ctx=self._ctx.copy(), - ) - - def finalize(self) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - digest = self._ctx.finalize() - self._ctx = None - return digest - - def verify(self, signature: bytes) -> None: - utils._check_bytes("signature", signature) - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - - ctx, self._ctx = self._ctx, None - ctx.verify(signature) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py deleted file mode 100644 index 38e2f8bc4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc - - -class KeyDerivationFunction(metaclass=abc.ABCMeta): - @abc.abstractmethod - def derive(self, key_material: bytes) -> bytes: - """ - Deterministically generates and returns a new key based on the existing - key material. - """ - - @abc.abstractmethod - def verify(self, key_material: bytes, expected_key: bytes) -> None: - """ - Checks whether the key generated by the key material matches the - expected derived key. Raises an exception if they do not match. - """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py deleted file mode 100644 index 94312fec3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/concatkdf.py +++ /dev/null @@ -1,127 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized, InvalidKey -from cryptography.hazmat.primitives import constant_time, hashes, hmac -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -def _int_to_u32be(n: int) -> bytes: - return n.to_bytes(length=4, byteorder="big") - - -def _common_args_checks( - algorithm: hashes.HashAlgorithm, - length: int, - otherinfo: typing.Optional[bytes], -) -> None: - max_length = algorithm.digest_size * (2**32 - 1) - if length > max_length: - raise ValueError( - "Cannot derive keys larger than {} bits.".format(max_length) - ) - if otherinfo is not None: - utils._check_bytes("otherinfo", otherinfo) - - -def _concatkdf_derive( - key_material: bytes, - length: int, - auxfn: typing.Callable[[], hashes.HashContext], - otherinfo: bytes, -) -> bytes: - utils._check_byteslike("key_material", key_material) - output = [b""] - outlen = 0 - counter = 1 - - while length > outlen: - h = auxfn() - h.update(_int_to_u32be(counter)) - h.update(key_material) - h.update(otherinfo) - output.append(h.finalize()) - outlen += len(output[-1]) - counter += 1 - - return b"".join(output)[:length] - - -class ConcatKDFHash(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - otherinfo: typing.Optional[bytes], - backend: typing.Any = None, - ): - _common_args_checks(algorithm, length, otherinfo) - self._algorithm = algorithm - self._length = length - self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" - - self._used = False - - def _hash(self) -> hashes.Hash: - return hashes.Hash(self._algorithm) - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized - self._used = True - return _concatkdf_derive( - key_material, self._length, self._hash, self._otherinfo - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey - - -class ConcatKDFHMAC(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: typing.Optional[bytes], - otherinfo: typing.Optional[bytes], - backend: typing.Any = None, - ): - _common_args_checks(algorithm, length, otherinfo) - self._algorithm = algorithm - self._length = length - self._otherinfo: bytes = otherinfo if otherinfo is not None else b"" - - if algorithm.block_size is None: - raise TypeError( - "{} is unsupported for ConcatKDF".format(algorithm.name) - ) - - if salt is None: - salt = b"\x00" * algorithm.block_size - else: - utils._check_bytes("salt", salt) - - self._salt = salt - - self._used = False - - def _hmac(self) -> hmac.HMAC: - return hmac.HMAC(self._salt, self._algorithm) - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized - self._used = True - return _concatkdf_derive( - key_material, self._length, self._hmac, self._otherinfo - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py deleted file mode 100644 index 2152ae220..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +++ /dev/null @@ -1,100 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized, InvalidKey -from cryptography.hazmat.primitives import constant_time, hashes, hmac -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class HKDF(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: typing.Optional[bytes], - info: typing.Optional[bytes], - backend: typing.Any = None, - ): - self._algorithm = algorithm - - if salt is None: - salt = b"\x00" * self._algorithm.digest_size - else: - utils._check_bytes("salt", salt) - - self._salt = salt - - self._hkdf_expand = HKDFExpand(self._algorithm, length, info) - - def _extract(self, key_material: bytes) -> bytes: - h = hmac.HMAC(self._salt, self._algorithm) - h.update(key_material) - return h.finalize() - - def derive(self, key_material: bytes) -> bytes: - utils._check_byteslike("key_material", key_material) - return self._hkdf_expand.derive(self._extract(key_material)) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey - - -class HKDFExpand(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - info: typing.Optional[bytes], - backend: typing.Any = None, - ): - self._algorithm = algorithm - - max_length = 255 * algorithm.digest_size - - if length > max_length: - raise ValueError( - "Cannot derive keys larger than {} octets.".format(max_length) - ) - - self._length = length - - if info is None: - info = b"" - else: - utils._check_bytes("info", info) - - self._info = info - - self._used = False - - def _expand(self, key_material: bytes) -> bytes: - output = [b""] - counter = 1 - - while self._algorithm.digest_size * (len(output) - 1) < self._length: - h = hmac.HMAC(key_material, self._algorithm) - h.update(output[-1]) - h.update(self._info) - h.update(bytes([counter])) - output.append(h.finalize()) - counter += 1 - - return b"".join(output)[: self._length] - - def derive(self, key_material: bytes) -> bytes: - utils._check_byteslike("key_material", key_material) - if self._used: - raise AlreadyFinalized - - self._used = True - return self._expand(key_material) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py deleted file mode 100644 index 7f185a9af..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +++ /dev/null @@ -1,297 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import utils -from cryptography.exceptions import ( - AlreadyFinalized, - InvalidKey, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.primitives import ( - ciphers, - cmac, - constant_time, - hashes, - hmac, -) -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class Mode(utils.Enum): - CounterMode = "ctr" - - -class CounterLocation(utils.Enum): - BeforeFixed = "before_fixed" - AfterFixed = "after_fixed" - MiddleFixed = "middle_fixed" - - -class _KBKDFDeriver: - def __init__( - self, - prf: typing.Callable, - mode: Mode, - length: int, - rlen: int, - llen: typing.Optional[int], - location: CounterLocation, - break_location: typing.Optional[int], - label: typing.Optional[bytes], - context: typing.Optional[bytes], - fixed: typing.Optional[bytes], - ): - assert callable(prf) - - if not isinstance(mode, Mode): - raise TypeError("mode must be of type Mode") - - if not isinstance(location, CounterLocation): - raise TypeError("location must be of type CounterLocation") - - if break_location is None and location is CounterLocation.MiddleFixed: - raise ValueError("Please specify a break_location") - - if ( - break_location is not None - and location != CounterLocation.MiddleFixed - ): - raise ValueError( - "break_location is ignored when location is not" - " CounterLocation.MiddleFixed" - ) - - if break_location is not None and not isinstance(break_location, int): - raise TypeError("break_location must be an integer") - - if break_location is not None and break_location < 0: - raise ValueError("break_location must be a positive integer") - - if (label or context) and fixed: - raise ValueError( - "When supplying fixed data, " "label and context are ignored." - ) - - if rlen is None or not self._valid_byte_length(rlen): - raise ValueError("rlen must be between 1 and 4") - - if llen is None and fixed is None: - raise ValueError("Please specify an llen") - - if llen is not None and not isinstance(llen, int): - raise TypeError("llen must be an integer") - - if label is None: - label = b"" - - if context is None: - context = b"" - - utils._check_bytes("label", label) - utils._check_bytes("context", context) - self._prf = prf - self._mode = mode - self._length = length - self._rlen = rlen - self._llen = llen - self._location = location - self._break_location = break_location - self._label = label - self._context = context - self._used = False - self._fixed_data = fixed - - @staticmethod - def _valid_byte_length(value: int) -> bool: - if not isinstance(value, int): - raise TypeError("value must be of type int") - - value_bin = utils.int_to_bytes(1, value) - if not 1 <= len(value_bin) <= 4: - return False - return True - - def derive(self, key_material: bytes, prf_output_size: int) -> bytes: - if self._used: - raise AlreadyFinalized - - utils._check_byteslike("key_material", key_material) - self._used = True - - # inverse floor division (equivalent to ceiling) - rounds = -(-self._length // prf_output_size) - - output = [b""] - - # For counter mode, the number of iterations shall not be - # larger than 2^r-1, where r <= 32 is the binary length of the counter - # This ensures that the counter values used as an input to the - # PRF will not repeat during a particular call to the KDF function. - r_bin = utils.int_to_bytes(1, self._rlen) - if rounds > pow(2, len(r_bin) * 8) - 1: - raise ValueError("There are too many iterations.") - - fixed = self._generate_fixed_input() - - if self._location == CounterLocation.BeforeFixed: - data_before_ctr = b"" - data_after_ctr = fixed - elif self._location == CounterLocation.AfterFixed: - data_before_ctr = fixed - data_after_ctr = b"" - else: - if isinstance( - self._break_location, int - ) and self._break_location > len(fixed): - raise ValueError("break_location offset > len(fixed)") - data_before_ctr = fixed[: self._break_location] - data_after_ctr = fixed[self._break_location :] - - for i in range(1, rounds + 1): - h = self._prf(key_material) - - counter = utils.int_to_bytes(i, self._rlen) - input_data = data_before_ctr + counter + data_after_ctr - - h.update(input_data) - - output.append(h.finalize()) - - return b"".join(output)[: self._length] - - def _generate_fixed_input(self) -> bytes: - if self._fixed_data and isinstance(self._fixed_data, bytes): - return self._fixed_data - - l_val = utils.int_to_bytes(self._length * 8, self._llen) - - return b"".join([self._label, b"\x00", self._context, l_val]) - - -class KBKDFHMAC(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - mode: Mode, - length: int, - rlen: int, - llen: typing.Optional[int], - location: CounterLocation, - label: typing.Optional[bytes], - context: typing.Optional[bytes], - fixed: typing.Optional[bytes], - backend: typing.Any = None, - *, - break_location: typing.Optional[int] = None, - ): - if not isinstance(algorithm, hashes.HashAlgorithm): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported hash algorithm.", - _Reasons.UNSUPPORTED_HASH, - ) - - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.hmac_supported(algorithm): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported hmac algorithm.", - _Reasons.UNSUPPORTED_HASH, - ) - - self._algorithm = algorithm - - self._deriver = _KBKDFDeriver( - self._prf, - mode, - length, - rlen, - llen, - location, - break_location, - label, - context, - fixed, - ) - - def _prf(self, key_material: bytes) -> hmac.HMAC: - return hmac.HMAC(key_material, self._algorithm) - - def derive(self, key_material: bytes) -> bytes: - return self._deriver.derive(key_material, self._algorithm.digest_size) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey - - -class KBKDFCMAC(KeyDerivationFunction): - def __init__( - self, - algorithm, - mode: Mode, - length: int, - rlen: int, - llen: typing.Optional[int], - location: CounterLocation, - label: typing.Optional[bytes], - context: typing.Optional[bytes], - fixed: typing.Optional[bytes], - backend: typing.Any = None, - *, - break_location: typing.Optional[int] = None, - ): - if not issubclass( - algorithm, ciphers.BlockCipherAlgorithm - ) or not issubclass(algorithm, ciphers.CipherAlgorithm): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported cipher algorithm.", - _Reasons.UNSUPPORTED_CIPHER, - ) - - self._algorithm = algorithm - self._cipher: typing.Optional[ciphers.BlockCipherAlgorithm] = None - - self._deriver = _KBKDFDeriver( - self._prf, - mode, - length, - rlen, - llen, - location, - break_location, - label, - context, - fixed, - ) - - def _prf(self, _: bytes) -> cmac.CMAC: - assert self._cipher is not None - - return cmac.CMAC(self._cipher) - - def derive(self, key_material: bytes) -> bytes: - self._cipher = self._algorithm(key_material) - - assert self._cipher is not None - - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.cmac_algorithm_supported(self._cipher): - raise UnsupportedAlgorithm( - "Algorithm supplied is not a supported cipher algorithm.", - _Reasons.UNSUPPORTED_CIPHER, - ) - - return self._deriver.derive(key_material, self._cipher.block_size // 8) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py deleted file mode 100644 index 8d23f8c25..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/pbkdf2.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils -from cryptography.exceptions import ( - AlreadyFinalized, - InvalidKey, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.primitives import constant_time, hashes -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -class PBKDF2HMAC(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - salt: bytes, - iterations: int, - backend: typing.Any = None, - ): - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.pbkdf2_hmac_supported(algorithm): - raise UnsupportedAlgorithm( - "{} is not supported for PBKDF2 by this backend.".format( - algorithm.name - ), - _Reasons.UNSUPPORTED_HASH, - ) - self._used = False - self._algorithm = algorithm - self._length = length - utils._check_bytes("salt", salt) - self._salt = salt - self._iterations = iterations - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized("PBKDF2 instances can only be used once.") - self._used = True - - utils._check_byteslike("key_material", key_material) - from cryptography.hazmat.backends.openssl.backend import backend - - return backend.derive_pbkdf2_hmac( - self._algorithm, - self._length, - self._salt, - self._iterations, - key_material, - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - derived_key = self.derive(key_material) - if not constant_time.bytes_eq(derived_key, expected_key): - raise InvalidKey("Keys do not match.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py deleted file mode 100644 index 286f4388c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/scrypt.py +++ /dev/null @@ -1,73 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import sys -import typing - -from cryptography import utils -from cryptography.exceptions import ( - AlreadyFinalized, - InvalidKey, - UnsupportedAlgorithm, -) -from cryptography.hazmat.primitives import constant_time -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - -# This is used by the scrypt tests to skip tests that require more memory -# than the MEM_LIMIT -_MEM_LIMIT = sys.maxsize // 2 - - -class Scrypt(KeyDerivationFunction): - def __init__( - self, - salt: bytes, - length: int, - n: int, - r: int, - p: int, - backend: typing.Any = None, - ): - from cryptography.hazmat.backends.openssl.backend import ( - backend as ossl, - ) - - if not ossl.scrypt_supported(): - raise UnsupportedAlgorithm( - "This version of OpenSSL does not support scrypt" - ) - self._length = length - utils._check_bytes("salt", salt) - if n < 2 or (n & (n - 1)) != 0: - raise ValueError("n must be greater than 1 and be a power of 2.") - - if r < 1: - raise ValueError("r must be greater than or equal to 1.") - - if p < 1: - raise ValueError("p must be greater than or equal to 1.") - - self._used = False - self._salt = salt - self._n = n - self._r = r - self._p = p - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized("Scrypt instances can only be used once.") - self._used = True - - utils._check_byteslike("key_material", key_material) - from cryptography.hazmat.backends.openssl.backend import backend - - return backend.derive_scrypt( - key_material, self._salt, self._length, self._n, self._r, self._p - ) - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - derived_key = self.derive(key_material) - if not constant_time.bytes_eq(derived_key, expected_key): - raise InvalidKey("Keys do not match.") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py deleted file mode 100644 index 651e691aa..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/kdf/x963kdf.py +++ /dev/null @@ -1,62 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized, InvalidKey -from cryptography.hazmat.primitives import constant_time, hashes -from cryptography.hazmat.primitives.kdf import KeyDerivationFunction - - -def _int_to_u32be(n: int) -> bytes: - return n.to_bytes(length=4, byteorder="big") - - -class X963KDF(KeyDerivationFunction): - def __init__( - self, - algorithm: hashes.HashAlgorithm, - length: int, - sharedinfo: typing.Optional[bytes], - backend: typing.Any = None, - ): - max_len = algorithm.digest_size * (2**32 - 1) - if length > max_len: - raise ValueError( - "Cannot derive keys larger than {} bits.".format(max_len) - ) - if sharedinfo is not None: - utils._check_bytes("sharedinfo", sharedinfo) - - self._algorithm = algorithm - self._length = length - self._sharedinfo = sharedinfo - self._used = False - - def derive(self, key_material: bytes) -> bytes: - if self._used: - raise AlreadyFinalized - self._used = True - utils._check_byteslike("key_material", key_material) - output = [b""] - outlen = 0 - counter = 1 - - while self._length > outlen: - h = hashes.Hash(self._algorithm) - h.update(key_material) - h.update(_int_to_u32be(counter)) - if self._sharedinfo is not None: - h.update(self._sharedinfo) - output.append(h.finalize()) - outlen += len(output[-1]) - counter += 1 - - return b"".join(output)[: self._length] - - def verify(self, key_material: bytes, expected_key: bytes) -> None: - if not constant_time.bytes_eq(self.derive(key_material), expected_key): - raise InvalidKey diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py deleted file mode 100644 index 64771ca3c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/keywrap.py +++ /dev/null @@ -1,176 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography.hazmat.primitives.ciphers import Cipher -from cryptography.hazmat.primitives.ciphers.algorithms import AES -from cryptography.hazmat.primitives.ciphers.modes import ECB -from cryptography.hazmat.primitives.constant_time import bytes_eq - - -def _wrap_core( - wrapping_key: bytes, - a: bytes, - r: typing.List[bytes], -) -> bytes: - # RFC 3394 Key Wrap - 2.2.1 (index method) - encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() - n = len(r) - for j in range(6): - for i in range(n): - # every encryption operation is a discrete 16 byte chunk (because - # AES has a 128-bit block size) and since we're using ECB it is - # safe to reuse the encryptor for the entire operation - b = encryptor.update(a + r[i]) - a = ( - int.from_bytes(b[:8], byteorder="big") ^ ((n * j) + i + 1) - ).to_bytes(length=8, byteorder="big") - r[i] = b[-8:] - - assert encryptor.finalize() == b"" - - return a + b"".join(r) - - -def aes_key_wrap( - wrapping_key: bytes, - key_to_wrap: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if len(key_to_wrap) < 16: - raise ValueError("The key to wrap must be at least 16 bytes") - - if len(key_to_wrap) % 8 != 0: - raise ValueError("The key to wrap must be a multiple of 8 bytes") - - a = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" - r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] - return _wrap_core(wrapping_key, a, r) - - -def _unwrap_core( - wrapping_key: bytes, - a: bytes, - r: typing.List[bytes], -) -> typing.Tuple[bytes, typing.List[bytes]]: - # Implement RFC 3394 Key Unwrap - 2.2.2 (index method) - decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() - n = len(r) - for j in reversed(range(6)): - for i in reversed(range(n)): - atr = ( - int.from_bytes(a, byteorder="big") ^ ((n * j) + i + 1) - ).to_bytes(length=8, byteorder="big") + r[i] - # every decryption operation is a discrete 16 byte chunk so - # it is safe to reuse the decryptor for the entire operation - b = decryptor.update(atr) - a = b[:8] - r[i] = b[-8:] - - assert decryptor.finalize() == b"" - return a, r - - -def aes_key_wrap_with_padding( - wrapping_key: bytes, - key_to_wrap: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - aiv = b"\xA6\x59\x59\xA6" + len(key_to_wrap).to_bytes( - length=4, byteorder="big" - ) - # pad the key to wrap if necessary - pad = (8 - (len(key_to_wrap) % 8)) % 8 - key_to_wrap = key_to_wrap + b"\x00" * pad - if len(key_to_wrap) == 8: - # RFC 5649 - 4.1 - exactly 8 octets after padding - encryptor = Cipher(AES(wrapping_key), ECB()).encryptor() - b = encryptor.update(aiv + key_to_wrap) - assert encryptor.finalize() == b"" - return b - else: - r = [key_to_wrap[i : i + 8] for i in range(0, len(key_to_wrap), 8)] - return _wrap_core(wrapping_key, aiv, r) - - -def aes_key_unwrap_with_padding( - wrapping_key: bytes, - wrapped_key: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapped_key) < 16: - raise InvalidUnwrap("Must be at least 16 bytes") - - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - if len(wrapped_key) == 16: - # RFC 5649 - 4.2 - exactly two 64-bit blocks - decryptor = Cipher(AES(wrapping_key), ECB()).decryptor() - out = decryptor.update(wrapped_key) - assert decryptor.finalize() == b"" - a = out[:8] - data = out[8:] - n = 1 - else: - r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] - encrypted_aiv = r.pop(0) - n = len(r) - a, r = _unwrap_core(wrapping_key, encrypted_aiv, r) - data = b"".join(r) - - # 1) Check that MSB(32,A) = A65959A6. - # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n. If so, let - # MLI = LSB(32,A). - # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of - # the output data are zero. - mli = int.from_bytes(a[4:], byteorder="big") - b = (8 * n) - mli - if ( - not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") - or not 8 * (n - 1) < mli <= 8 * n - or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b)) - ): - raise InvalidUnwrap() - - if b == 0: - return data - else: - return data[:-b] - - -def aes_key_unwrap( - wrapping_key: bytes, - wrapped_key: bytes, - backend: typing.Any = None, -) -> bytes: - if len(wrapped_key) < 24: - raise InvalidUnwrap("Must be at least 24 bytes") - - if len(wrapped_key) % 8 != 0: - raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes") - - if len(wrapping_key) not in [16, 24, 32]: - raise ValueError("The wrapping key must be a valid AES key length") - - aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6" - r = [wrapped_key[i : i + 8] for i in range(0, len(wrapped_key), 8)] - a = r.pop(0) - a, r = _unwrap_core(wrapping_key, a, r) - if not bytes_eq(a, aiv): - raise InvalidUnwrap() - - return b"".join(r) - - -class InvalidUnwrap(Exception): - pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py deleted file mode 100644 index d6c1d9152..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/padding.py +++ /dev/null @@ -1,224 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import typing - -from cryptography import utils -from cryptography.exceptions import AlreadyFinalized -from cryptography.hazmat.bindings._rust import ( - check_ansix923_padding, - check_pkcs7_padding, -) - - -class PaddingContext(metaclass=abc.ABCMeta): - @abc.abstractmethod - def update(self, data: bytes) -> bytes: - """ - Pads the provided bytes and returns any available data as bytes. - """ - - @abc.abstractmethod - def finalize(self) -> bytes: - """ - Finalize the padding, returns bytes. - """ - - -def _byte_padding_check(block_size: int) -> None: - if not (0 <= block_size <= 2040): - raise ValueError("block_size must be in range(0, 2041).") - - if block_size % 8 != 0: - raise ValueError("block_size must be a multiple of 8.") - - -def _byte_padding_update( - buffer_: typing.Optional[bytes], data: bytes, block_size: int -) -> typing.Tuple[bytes, bytes]: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - utils._check_byteslike("data", data) - - buffer_ += bytes(data) - - finished_blocks = len(buffer_) // (block_size // 8) - - result = buffer_[: finished_blocks * (block_size // 8)] - buffer_ = buffer_[finished_blocks * (block_size // 8) :] - - return buffer_, result - - -def _byte_padding_pad( - buffer_: typing.Optional[bytes], - block_size: int, - paddingfn: typing.Callable[[int], bytes], -) -> bytes: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - pad_size = block_size // 8 - len(buffer_) - return buffer_ + paddingfn(pad_size) - - -def _byte_unpadding_update( - buffer_: typing.Optional[bytes], data: bytes, block_size: int -) -> typing.Tuple[bytes, bytes]: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - utils._check_byteslike("data", data) - - buffer_ += bytes(data) - - finished_blocks = max(len(buffer_) // (block_size // 8) - 1, 0) - - result = buffer_[: finished_blocks * (block_size // 8)] - buffer_ = buffer_[finished_blocks * (block_size // 8) :] - - return buffer_, result - - -def _byte_unpadding_check( - buffer_: typing.Optional[bytes], - block_size: int, - checkfn: typing.Callable[[bytes], int], -) -> bytes: - if buffer_ is None: - raise AlreadyFinalized("Context was already finalized.") - - if len(buffer_) != block_size // 8: - raise ValueError("Invalid padding bytes.") - - valid = checkfn(buffer_) - - if not valid: - raise ValueError("Invalid padding bytes.") - - pad_size = buffer_[-1] - return buffer_[:-pad_size] - - -class PKCS7: - def __init__(self, block_size: int): - _byte_padding_check(block_size) - self.block_size = block_size - - def padder(self) -> PaddingContext: - return _PKCS7PaddingContext(self.block_size) - - def unpadder(self) -> PaddingContext: - return _PKCS7UnpaddingContext(self.block_size) - - -class _PKCS7PaddingContext(PaddingContext): - _buffer: typing.Optional[bytes] - - def __init__(self, block_size: int): - self.block_size = block_size - # TODO: more copies than necessary, we should use zero-buffer (#193) - self._buffer = b"" - - def update(self, data: bytes) -> bytes: - self._buffer, result = _byte_padding_update( - self._buffer, data, self.block_size - ) - return result - - def _padding(self, size: int) -> bytes: - return bytes([size]) * size - - def finalize(self) -> bytes: - result = _byte_padding_pad( - self._buffer, self.block_size, self._padding - ) - self._buffer = None - return result - - -class _PKCS7UnpaddingContext(PaddingContext): - _buffer: typing.Optional[bytes] - - def __init__(self, block_size: int): - self.block_size = block_size - # TODO: more copies than necessary, we should use zero-buffer (#193) - self._buffer = b"" - - def update(self, data: bytes) -> bytes: - self._buffer, result = _byte_unpadding_update( - self._buffer, data, self.block_size - ) - return result - - def finalize(self) -> bytes: - result = _byte_unpadding_check( - self._buffer, self.block_size, check_pkcs7_padding - ) - self._buffer = None - return result - - -class ANSIX923: - def __init__(self, block_size: int): - _byte_padding_check(block_size) - self.block_size = block_size - - def padder(self) -> PaddingContext: - return _ANSIX923PaddingContext(self.block_size) - - def unpadder(self) -> PaddingContext: - return _ANSIX923UnpaddingContext(self.block_size) - - -class _ANSIX923PaddingContext(PaddingContext): - _buffer: typing.Optional[bytes] - - def __init__(self, block_size: int): - self.block_size = block_size - # TODO: more copies than necessary, we should use zero-buffer (#193) - self._buffer = b"" - - def update(self, data: bytes) -> bytes: - self._buffer, result = _byte_padding_update( - self._buffer, data, self.block_size - ) - return result - - def _padding(self, size: int) -> bytes: - return bytes([0]) * (size - 1) + bytes([size]) - - def finalize(self) -> bytes: - result = _byte_padding_pad( - self._buffer, self.block_size, self._padding - ) - self._buffer = None - return result - - -class _ANSIX923UnpaddingContext(PaddingContext): - _buffer: typing.Optional[bytes] - - def __init__(self, block_size: int): - self.block_size = block_size - # TODO: more copies than necessary, we should use zero-buffer (#193) - self._buffer = b"" - - def update(self, data: bytes) -> bytes: - self._buffer, result = _byte_unpadding_update( - self._buffer, data, self.block_size - ) - return result - - def finalize(self) -> bytes: - result = _byte_unpadding_check( - self._buffer, - self.block_size, - check_ansix923_padding, - ) - self._buffer = None - return result diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py deleted file mode 100644 index 7fcf4a50f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/poly1305.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import utils -from cryptography.exceptions import ( - AlreadyFinalized, - UnsupportedAlgorithm, - _Reasons, -) -from cryptography.hazmat.backends.openssl.poly1305 import _Poly1305Context - - -class Poly1305: - _ctx: typing.Optional[_Poly1305Context] - - def __init__(self, key: bytes): - from cryptography.hazmat.backends.openssl.backend import backend - - if not backend.poly1305_supported(): - raise UnsupportedAlgorithm( - "poly1305 is not supported by this version of OpenSSL.", - _Reasons.UNSUPPORTED_MAC, - ) - self._ctx = backend.create_poly1305_ctx(key) - - def update(self, data: bytes) -> None: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - utils._check_byteslike("data", data) - self._ctx.update(data) - - def finalize(self) -> bytes: - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - mac = self._ctx.finalize() - self._ctx = None - return mac - - def verify(self, tag: bytes) -> None: - utils._check_bytes("tag", tag) - if self._ctx is None: - raise AlreadyFinalized("Context was already finalized.") - - ctx, self._ctx = self._ctx, None - ctx.verify(tag) - - @classmethod - def generate_tag(cls, key: bytes, data: bytes) -> bytes: - p = Poly1305(key) - p.update(data) - return p.finalize() - - @classmethod - def verify_tag(cls, key: bytes, data: bytes, tag: bytes) -> None: - p = Poly1305(key) - p.update(data) - p.verify(tag) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py deleted file mode 100644 index af4112f39..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/__init__.py +++ /dev/null @@ -1,46 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography.hazmat.primitives._serialization import ( - BestAvailableEncryption, - Encoding, - KeySerializationEncryption, - NoEncryption, - ParameterFormat, - PrivateFormat, - PublicFormat, - _KeySerializationEncryption, -) -from cryptography.hazmat.primitives.serialization.base import ( - load_der_parameters, - load_der_private_key, - load_der_public_key, - load_pem_parameters, - load_pem_private_key, - load_pem_public_key, -) -from cryptography.hazmat.primitives.serialization.ssh import ( - load_ssh_private_key, - load_ssh_public_key, -) - -__all__ = [ - "load_der_parameters", - "load_der_private_key", - "load_der_public_key", - "load_pem_parameters", - "load_pem_private_key", - "load_pem_public_key", - "load_ssh_private_key", - "load_ssh_public_key", - "Encoding", - "PrivateFormat", - "PublicFormat", - "ParameterFormat", - "KeySerializationEncryption", - "BestAvailableEncryption", - "NoEncryption", - "_KeySerializationEncryption", -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py deleted file mode 100644 index 8a8417664..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/base.py +++ /dev/null @@ -1,72 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import typing - -from cryptography.hazmat.primitives.asymmetric import dh -from cryptography.hazmat.primitives.asymmetric.types import ( - PRIVATE_KEY_TYPES, - PUBLIC_KEY_TYPES, -) - - -def load_pem_private_key( - data: bytes, - password: typing.Optional[bytes], - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> PRIVATE_KEY_TYPES: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_pem_private_key( - data, password, unsafe_skip_rsa_key_validation - ) - - -def load_pem_public_key( - data: bytes, backend: typing.Any = None -) -> PUBLIC_KEY_TYPES: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_pem_public_key(data) - - -def load_pem_parameters( - data: bytes, backend: typing.Any = None -) -> "dh.DHParameters": - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_pem_parameters(data) - - -def load_der_private_key( - data: bytes, - password: typing.Optional[bytes], - backend: typing.Any = None, - *, - unsafe_skip_rsa_key_validation: bool = False, -) -> PRIVATE_KEY_TYPES: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_der_private_key( - data, password, unsafe_skip_rsa_key_validation - ) - - -def load_der_public_key( - data: bytes, backend: typing.Any = None -) -> PUBLIC_KEY_TYPES: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_der_public_key(data) - - -def load_der_parameters( - data: bytes, backend: typing.Any = None -) -> "dh.DHParameters": - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_der_parameters(data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py deleted file mode 100644 index 05212257d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs12.py +++ /dev/null @@ -1,226 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography import x509 -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives._serialization import PBES as PBES -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed448, - ed25519, - rsa, -) -from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES - -__all__ = [ - "PBES", - "PKCS12Certificate", - "PKCS12KeyAndCertificates", - "load_key_and_certificates", - "load_pkcs12", - "serialize_key_and_certificates", -] - -_ALLOWED_PKCS12_TYPES = typing.Union[ - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, -] - - -class PKCS12Certificate: - def __init__( - self, - cert: x509.Certificate, - friendly_name: typing.Optional[bytes], - ): - if not isinstance(cert, x509.Certificate): - raise TypeError("Expecting x509.Certificate object") - if friendly_name is not None and not isinstance(friendly_name, bytes): - raise TypeError("friendly_name must be bytes or None") - self._cert = cert - self._friendly_name = friendly_name - - @property - def friendly_name(self) -> typing.Optional[bytes]: - return self._friendly_name - - @property - def certificate(self) -> x509.Certificate: - return self._cert - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PKCS12Certificate): - return NotImplemented - - return ( - self.certificate == other.certificate - and self.friendly_name == other.friendly_name - ) - - def __hash__(self) -> int: - return hash((self.certificate, self.friendly_name)) - - def __repr__(self) -> str: - return "".format( - self.certificate, self.friendly_name - ) - - -class PKCS12KeyAndCertificates: - def __init__( - self, - key: typing.Optional[PRIVATE_KEY_TYPES], - cert: typing.Optional[PKCS12Certificate], - additional_certs: typing.List[PKCS12Certificate], - ): - if key is not None and not isinstance( - key, - ( - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - ), - ): - raise TypeError( - "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" - " private key, or None." - ) - if cert is not None and not isinstance(cert, PKCS12Certificate): - raise TypeError("cert must be a PKCS12Certificate object or None") - if not all( - isinstance(add_cert, PKCS12Certificate) - for add_cert in additional_certs - ): - raise TypeError( - "all values in additional_certs must be PKCS12Certificate" - " objects" - ) - self._key = key - self._cert = cert - self._additional_certs = additional_certs - - @property - def key(self) -> typing.Optional[PRIVATE_KEY_TYPES]: - return self._key - - @property - def cert(self) -> typing.Optional[PKCS12Certificate]: - return self._cert - - @property - def additional_certs(self) -> typing.List[PKCS12Certificate]: - return self._additional_certs - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PKCS12KeyAndCertificates): - return NotImplemented - - return ( - self.key == other.key - and self.cert == other.cert - and self.additional_certs == other.additional_certs - ) - - def __hash__(self) -> int: - return hash((self.key, self.cert, tuple(self.additional_certs))) - - def __repr__(self) -> str: - fmt = ( - "" - ) - return fmt.format(self.key, self.cert, self.additional_certs) - - -def load_key_and_certificates( - data: bytes, - password: typing.Optional[bytes], - backend: typing.Any = None, -) -> typing.Tuple[ - typing.Optional[PRIVATE_KEY_TYPES], - typing.Optional[x509.Certificate], - typing.List[x509.Certificate], -]: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_key_and_certificates_from_pkcs12(data, password) - - -def load_pkcs12( - data: bytes, - password: typing.Optional[bytes], - backend: typing.Any = None, -) -> PKCS12KeyAndCertificates: - from cryptography.hazmat.backends.openssl.backend import backend as ossl - - return ossl.load_pkcs12(data, password) - - -_PKCS12_CAS_TYPES = typing.Union[ - x509.Certificate, - PKCS12Certificate, -] - - -def serialize_key_and_certificates( - name: typing.Optional[bytes], - key: typing.Optional[_ALLOWED_PKCS12_TYPES], - cert: typing.Optional[x509.Certificate], - cas: typing.Optional[typing.Iterable[_PKCS12_CAS_TYPES]], - encryption_algorithm: serialization.KeySerializationEncryption, -) -> bytes: - if key is not None and not isinstance( - key, - ( - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ec.EllipticCurvePrivateKey, - ed25519.Ed25519PrivateKey, - ed448.Ed448PrivateKey, - ), - ): - raise TypeError( - "Key must be RSA, DSA, EllipticCurve, ED25519, or ED448" - " private key, or None." - ) - if cert is not None and not isinstance(cert, x509.Certificate): - raise TypeError("cert must be a certificate or None") - - if cas is not None: - cas = list(cas) - if not all( - isinstance( - val, - ( - x509.Certificate, - PKCS12Certificate, - ), - ) - for val in cas - ): - raise TypeError("all values in cas must be certificates") - - if not isinstance( - encryption_algorithm, serialization.KeySerializationEncryption - ): - raise TypeError( - "Key encryption algorithm must be a " - "KeySerializationEncryption instance" - ) - - if key is None and cert is None and not cas: - raise ValueError("You must supply at least one of key, cert, or cas") - - from cryptography.hazmat.backends.openssl.backend import backend - - return backend.serialize_key_and_certificates_to_pkcs12( - name, key, cert, cas, encryption_algorithm - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py deleted file mode 100644 index 7e593e719..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/pkcs7.py +++ /dev/null @@ -1,219 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import email.base64mime -import email.generator -import email.message -import io -import typing - -from cryptography import utils, x509 -from cryptography.hazmat.bindings._rust import pkcs7 as rust_pkcs7 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ec, rsa -from cryptography.utils import _check_byteslike - - -def load_pem_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]: - from cryptography.hazmat.backends.openssl.backend import backend - - return backend.load_pem_pkcs7_certificates(data) - - -def load_der_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]: - from cryptography.hazmat.backends.openssl.backend import backend - - return backend.load_der_pkcs7_certificates(data) - - -def serialize_certificates( - certs: typing.List[x509.Certificate], - encoding: serialization.Encoding, -) -> bytes: - return rust_pkcs7.serialize_certificates(certs, encoding) - - -_ALLOWED_PKCS7_HASH_TYPES = typing.Union[ - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, -] - -_ALLOWED_PRIVATE_KEY_TYPES = typing.Union[ - rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey -] - - -class PKCS7Options(utils.Enum): - Text = "Add text/plain MIME type" - Binary = "Don't translate input data into canonical MIME format" - DetachedSignature = "Don't embed data in the PKCS7 structure" - NoCapabilities = "Don't embed SMIME capabilities" - NoAttributes = "Don't embed authenticatedAttributes" - NoCerts = "Don't embed signer certificate" - - -class PKCS7SignatureBuilder: - def __init__( - self, - data: typing.Optional[bytes] = None, - signers: typing.List[ - typing.Tuple[ - x509.Certificate, - _ALLOWED_PRIVATE_KEY_TYPES, - _ALLOWED_PKCS7_HASH_TYPES, - ] - ] = [], - additional_certs: typing.List[x509.Certificate] = [], - ): - self._data = data - self._signers = signers - self._additional_certs = additional_certs - - def set_data(self, data: bytes) -> "PKCS7SignatureBuilder": - _check_byteslike("data", data) - if self._data is not None: - raise ValueError("data may only be set once") - - return PKCS7SignatureBuilder(bytes(data), self._signers) - - def add_signer( - self, - certificate: x509.Certificate, - private_key: _ALLOWED_PRIVATE_KEY_TYPES, - hash_algorithm: _ALLOWED_PKCS7_HASH_TYPES, - ) -> "PKCS7SignatureBuilder": - if not isinstance( - hash_algorithm, - ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, - ), - ): - raise TypeError( - "hash_algorithm must be one of hashes.SHA1, SHA224, " - "SHA256, SHA384, or SHA512" - ) - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - if not isinstance( - private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey) - ): - raise TypeError("Only RSA & EC keys are supported at this time.") - - return PKCS7SignatureBuilder( - self._data, - self._signers + [(certificate, private_key, hash_algorithm)], - ) - - def add_certificate( - self, certificate: x509.Certificate - ) -> "PKCS7SignatureBuilder": - if not isinstance(certificate, x509.Certificate): - raise TypeError("certificate must be a x509.Certificate") - - return PKCS7SignatureBuilder( - self._data, self._signers, self._additional_certs + [certificate] - ) - - def sign( - self, - encoding: serialization.Encoding, - options: typing.Iterable[PKCS7Options], - backend: typing.Any = None, - ) -> bytes: - if len(self._signers) == 0: - raise ValueError("Must have at least one signer") - if self._data is None: - raise ValueError("You must add data to sign") - options = list(options) - if not all(isinstance(x, PKCS7Options) for x in options): - raise ValueError("options must be from the PKCS7Options enum") - if encoding not in ( - serialization.Encoding.PEM, - serialization.Encoding.DER, - serialization.Encoding.SMIME, - ): - raise ValueError( - "Must be PEM, DER, or SMIME from the Encoding enum" - ) - - # Text is a meaningless option unless it is accompanied by - # DetachedSignature - if ( - PKCS7Options.Text in options - and PKCS7Options.DetachedSignature not in options - ): - raise ValueError( - "When passing the Text option you must also pass " - "DetachedSignature" - ) - - if PKCS7Options.Text in options and encoding in ( - serialization.Encoding.DER, - serialization.Encoding.PEM, - ): - raise ValueError( - "The Text option is only available for SMIME serialization" - ) - - # No attributes implies no capabilities so we'll error if you try to - # pass both. - if ( - PKCS7Options.NoAttributes in options - and PKCS7Options.NoCapabilities in options - ): - raise ValueError( - "NoAttributes is a superset of NoCapabilities. Do not pass " - "both values." - ) - - return rust_pkcs7.sign_and_serialize(self, encoding, options) - - -def _smime_encode(data: bytes, signature: bytes, micalg: str) -> bytes: - # This function works pretty hard to replicate what OpenSSL does - # precisely. For good and for ill. - - m = email.message.Message() - m.add_header("MIME-Version", "1.0") - m.add_header( - "Content-Type", - "multipart/signed", - protocol="application/x-pkcs7-signature", - micalg=micalg, - ) - - m.preamble = "This is an S/MIME signed message\n" - - msg_part = email.message.MIMEPart() - msg_part.set_payload(data) - msg_part.add_header("Content-Type", "text/plain") - m.attach(msg_part) - - sig_part = email.message.MIMEPart() - sig_part.add_header( - "Content-Type", "application/x-pkcs7-signature", name="smime.p7s" - ) - sig_part.add_header("Content-Transfer-Encoding", "base64") - sig_part.add_header( - "Content-Disposition", "attachment", filename="smime.p7s" - ) - sig_part.set_payload( - email.base64mime.body_encode(signature, maxlinelen=65) - ) - del sig_part["MIME-Version"] - m.attach(sig_part) - - fp = io.BytesIO() - g = email.generator.BytesGenerator( - fp, maxheaderlen=0, mangle_from_=False, policy=m.policy - ) - g.flatten(m) - return fp.getvalue() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py deleted file mode 100644 index 7125badb4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +++ /dev/null @@ -1,758 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import binascii -import os -import re -import typing -from base64 import encodebytes as _base64_encode - -from cryptography import utils -from cryptography.exceptions import UnsupportedAlgorithm -from cryptography.hazmat.primitives.asymmetric import dsa, ec, ed25519, rsa -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.primitives.serialization import ( - Encoding, - KeySerializationEncryption, - NoEncryption, - PrivateFormat, - PublicFormat, - _KeySerializationEncryption, -) - -try: - from bcrypt import kdf as _bcrypt_kdf - - _bcrypt_supported = True -except ImportError: - _bcrypt_supported = False - - def _bcrypt_kdf( - password: bytes, - salt: bytes, - desired_key_bytes: int, - rounds: int, - ignore_few_rounds: bool = False, - ) -> bytes: - raise UnsupportedAlgorithm("Need bcrypt module") - - -_SSH_ED25519 = b"ssh-ed25519" -_SSH_RSA = b"ssh-rsa" -_SSH_DSA = b"ssh-dss" -_ECDSA_NISTP256 = b"ecdsa-sha2-nistp256" -_ECDSA_NISTP384 = b"ecdsa-sha2-nistp384" -_ECDSA_NISTP521 = b"ecdsa-sha2-nistp521" -_CERT_SUFFIX = b"-cert-v01@openssh.com" - -_SSH_PUBKEY_RC = re.compile(rb"\A(\S+)[ \t]+(\S+)") -_SK_MAGIC = b"openssh-key-v1\0" -_SK_START = b"-----BEGIN OPENSSH PRIVATE KEY-----" -_SK_END = b"-----END OPENSSH PRIVATE KEY-----" -_BCRYPT = b"bcrypt" -_NONE = b"none" -_DEFAULT_CIPHER = b"aes256-ctr" -_DEFAULT_ROUNDS = 16 - -# re is only way to work on bytes-like data -_PEM_RC = re.compile(_SK_START + b"(.*?)" + _SK_END, re.DOTALL) - -# padding for max blocksize -_PADDING = memoryview(bytearray(range(1, 1 + 16))) - -# ciphers that are actually used in key wrapping -_SSH_CIPHERS: typing.Dict[ - bytes, - typing.Tuple[ - typing.Type[algorithms.AES], - int, - typing.Union[typing.Type[modes.CTR], typing.Type[modes.CBC]], - int, - ], -] = { - b"aes256-ctr": (algorithms.AES, 32, modes.CTR, 16), - b"aes256-cbc": (algorithms.AES, 32, modes.CBC, 16), -} - -# map local curve name to key type -_ECDSA_KEY_TYPE = { - "secp256r1": _ECDSA_NISTP256, - "secp384r1": _ECDSA_NISTP384, - "secp521r1": _ECDSA_NISTP521, -} - - -def _ecdsa_key_type(public_key: ec.EllipticCurvePublicKey) -> bytes: - """Return SSH key_type and curve_name for private key.""" - curve = public_key.curve - if curve.name not in _ECDSA_KEY_TYPE: - raise ValueError( - f"Unsupported curve for ssh private key: {curve.name!r}" - ) - return _ECDSA_KEY_TYPE[curve.name] - - -def _ssh_pem_encode( - data: bytes, - prefix: bytes = _SK_START + b"\n", - suffix: bytes = _SK_END + b"\n", -) -> bytes: - return b"".join([prefix, _base64_encode(data), suffix]) - - -def _check_block_size(data: bytes, block_len: int) -> None: - """Require data to be full blocks""" - if not data or len(data) % block_len != 0: - raise ValueError("Corrupt data: missing padding") - - -def _check_empty(data: bytes) -> None: - """All data should have been parsed.""" - if data: - raise ValueError("Corrupt data: unparsed data") - - -def _init_cipher( - ciphername: bytes, - password: typing.Optional[bytes], - salt: bytes, - rounds: int, -) -> Cipher[typing.Union[modes.CBC, modes.CTR]]: - """Generate key + iv and return cipher.""" - if not password: - raise ValueError("Key is password-protected.") - - algo, key_len, mode, iv_len = _SSH_CIPHERS[ciphername] - seed = _bcrypt_kdf(password, salt, key_len + iv_len, rounds, True) - return Cipher(algo(seed[:key_len]), mode(seed[key_len:])) - - -def _get_u32(data: memoryview) -> typing.Tuple[int, memoryview]: - """Uint32""" - if len(data) < 4: - raise ValueError("Invalid data") - return int.from_bytes(data[:4], byteorder="big"), data[4:] - - -def _get_u64(data: memoryview) -> typing.Tuple[int, memoryview]: - """Uint64""" - if len(data) < 8: - raise ValueError("Invalid data") - return int.from_bytes(data[:8], byteorder="big"), data[8:] - - -def _get_sshstr(data: memoryview) -> typing.Tuple[memoryview, memoryview]: - """Bytes with u32 length prefix""" - n, data = _get_u32(data) - if n > len(data): - raise ValueError("Invalid data") - return data[:n], data[n:] - - -def _get_mpint(data: memoryview) -> typing.Tuple[int, memoryview]: - """Big integer.""" - val, data = _get_sshstr(data) - if val and val[0] > 0x7F: - raise ValueError("Invalid data") - return int.from_bytes(val, "big"), data - - -def _to_mpint(val: int) -> bytes: - """Storage format for signed bigint.""" - if val < 0: - raise ValueError("negative mpint not allowed") - if not val: - return b"" - nbytes = (val.bit_length() + 8) // 8 - return utils.int_to_bytes(val, nbytes) - - -class _FragList: - """Build recursive structure without data copy.""" - - flist: typing.List[bytes] - - def __init__( - self, init: typing.Optional[typing.List[bytes]] = None - ) -> None: - self.flist = [] - if init: - self.flist.extend(init) - - def put_raw(self, val: bytes) -> None: - """Add plain bytes""" - self.flist.append(val) - - def put_u32(self, val: int) -> None: - """Big-endian uint32""" - self.flist.append(val.to_bytes(length=4, byteorder="big")) - - def put_sshstr(self, val: typing.Union[bytes, "_FragList"]) -> None: - """Bytes prefixed with u32 length""" - if isinstance(val, (bytes, memoryview, bytearray)): - self.put_u32(len(val)) - self.flist.append(val) - else: - self.put_u32(val.size()) - self.flist.extend(val.flist) - - def put_mpint(self, val: int) -> None: - """Big-endian bigint prefixed with u32 length""" - self.put_sshstr(_to_mpint(val)) - - def size(self) -> int: - """Current number of bytes""" - return sum(map(len, self.flist)) - - def render(self, dstbuf: memoryview, pos: int = 0) -> int: - """Write into bytearray""" - for frag in self.flist: - flen = len(frag) - start, pos = pos, pos + flen - dstbuf[start:pos] = frag - return pos - - def tobytes(self) -> bytes: - """Return as bytes""" - buf = memoryview(bytearray(self.size())) - self.render(buf) - return buf.tobytes() - - -class _SSHFormatRSA: - """Format for RSA keys. - - Public: - mpint e, n - Private: - mpint n, e, d, iqmp, p, q - """ - - def get_public(self, data: memoryview): - """RSA public fields""" - e, data = _get_mpint(data) - n, data = _get_mpint(data) - return (e, n), data - - def load_public( - self, data: memoryview - ) -> typing.Tuple[rsa.RSAPublicKey, memoryview]: - """Make RSA public key from data.""" - (e, n), data = self.get_public(data) - public_numbers = rsa.RSAPublicNumbers(e, n) - public_key = public_numbers.public_key() - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> typing.Tuple[rsa.RSAPrivateKey, memoryview]: - """Make RSA private key from data.""" - n, data = _get_mpint(data) - e, data = _get_mpint(data) - d, data = _get_mpint(data) - iqmp, data = _get_mpint(data) - p, data = _get_mpint(data) - q, data = _get_mpint(data) - - if (e, n) != pubfields: - raise ValueError("Corrupt data: rsa field mismatch") - dmp1 = rsa.rsa_crt_dmp1(d, p) - dmq1 = rsa.rsa_crt_dmq1(d, q) - public_numbers = rsa.RSAPublicNumbers(e, n) - private_numbers = rsa.RSAPrivateNumbers( - p, q, d, dmp1, dmq1, iqmp, public_numbers - ) - private_key = private_numbers.private_key() - return private_key, data - - def encode_public( - self, public_key: rsa.RSAPublicKey, f_pub: _FragList - ) -> None: - """Write RSA public key""" - pubn = public_key.public_numbers() - f_pub.put_mpint(pubn.e) - f_pub.put_mpint(pubn.n) - - def encode_private( - self, private_key: rsa.RSAPrivateKey, f_priv: _FragList - ) -> None: - """Write RSA private key""" - private_numbers = private_key.private_numbers() - public_numbers = private_numbers.public_numbers - - f_priv.put_mpint(public_numbers.n) - f_priv.put_mpint(public_numbers.e) - - f_priv.put_mpint(private_numbers.d) - f_priv.put_mpint(private_numbers.iqmp) - f_priv.put_mpint(private_numbers.p) - f_priv.put_mpint(private_numbers.q) - - -class _SSHFormatDSA: - """Format for DSA keys. - - Public: - mpint p, q, g, y - Private: - mpint p, q, g, y, x - """ - - def get_public( - self, data: memoryview - ) -> typing.Tuple[typing.Tuple, memoryview]: - """DSA public fields""" - p, data = _get_mpint(data) - q, data = _get_mpint(data) - g, data = _get_mpint(data) - y, data = _get_mpint(data) - return (p, q, g, y), data - - def load_public( - self, data: memoryview - ) -> typing.Tuple[dsa.DSAPublicKey, memoryview]: - """Make DSA public key from data.""" - (p, q, g, y), data = self.get_public(data) - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - self._validate(public_numbers) - public_key = public_numbers.public_key() - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> typing.Tuple[dsa.DSAPrivateKey, memoryview]: - """Make DSA private key from data.""" - (p, q, g, y), data = self.get_public(data) - x, data = _get_mpint(data) - - if (p, q, g, y) != pubfields: - raise ValueError("Corrupt data: dsa field mismatch") - parameter_numbers = dsa.DSAParameterNumbers(p, q, g) - public_numbers = dsa.DSAPublicNumbers(y, parameter_numbers) - self._validate(public_numbers) - private_numbers = dsa.DSAPrivateNumbers(x, public_numbers) - private_key = private_numbers.private_key() - return private_key, data - - def encode_public( - self, public_key: dsa.DSAPublicKey, f_pub: _FragList - ) -> None: - """Write DSA public key""" - public_numbers = public_key.public_numbers() - parameter_numbers = public_numbers.parameter_numbers - self._validate(public_numbers) - - f_pub.put_mpint(parameter_numbers.p) - f_pub.put_mpint(parameter_numbers.q) - f_pub.put_mpint(parameter_numbers.g) - f_pub.put_mpint(public_numbers.y) - - def encode_private( - self, private_key: dsa.DSAPrivateKey, f_priv: _FragList - ) -> None: - """Write DSA private key""" - self.encode_public(private_key.public_key(), f_priv) - f_priv.put_mpint(private_key.private_numbers().x) - - def _validate(self, public_numbers: dsa.DSAPublicNumbers) -> None: - parameter_numbers = public_numbers.parameter_numbers - if parameter_numbers.p.bit_length() != 1024: - raise ValueError("SSH supports only 1024 bit DSA keys") - - -class _SSHFormatECDSA: - """Format for ECDSA keys. - - Public: - str curve - bytes point - Private: - str curve - bytes point - mpint secret - """ - - def __init__(self, ssh_curve_name: bytes, curve: ec.EllipticCurve): - self.ssh_curve_name = ssh_curve_name - self.curve = curve - - def get_public( - self, data: memoryview - ) -> typing.Tuple[typing.Tuple, memoryview]: - """ECDSA public fields""" - curve, data = _get_sshstr(data) - point, data = _get_sshstr(data) - if curve != self.ssh_curve_name: - raise ValueError("Curve name mismatch") - if point[0] != 4: - raise NotImplementedError("Need uncompressed point") - return (curve, point), data - - def load_public( - self, data: memoryview - ) -> typing.Tuple[ec.EllipticCurvePublicKey, memoryview]: - """Make ECDSA public key from data.""" - (curve_name, point), data = self.get_public(data) - public_key = ec.EllipticCurvePublicKey.from_encoded_point( - self.curve, point.tobytes() - ) - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> typing.Tuple[ec.EllipticCurvePrivateKey, memoryview]: - """Make ECDSA private key from data.""" - (curve_name, point), data = self.get_public(data) - secret, data = _get_mpint(data) - - if (curve_name, point) != pubfields: - raise ValueError("Corrupt data: ecdsa field mismatch") - private_key = ec.derive_private_key(secret, self.curve) - return private_key, data - - def encode_public( - self, public_key: ec.EllipticCurvePublicKey, f_pub: _FragList - ) -> None: - """Write ECDSA public key""" - point = public_key.public_bytes( - Encoding.X962, PublicFormat.UncompressedPoint - ) - f_pub.put_sshstr(self.ssh_curve_name) - f_pub.put_sshstr(point) - - def encode_private( - self, private_key: ec.EllipticCurvePrivateKey, f_priv: _FragList - ) -> None: - """Write ECDSA private key""" - public_key = private_key.public_key() - private_numbers = private_key.private_numbers() - - self.encode_public(public_key, f_priv) - f_priv.put_mpint(private_numbers.private_value) - - -class _SSHFormatEd25519: - """Format for Ed25519 keys. - - Public: - bytes point - Private: - bytes point - bytes secret_and_point - """ - - def get_public( - self, data: memoryview - ) -> typing.Tuple[typing.Tuple, memoryview]: - """Ed25519 public fields""" - point, data = _get_sshstr(data) - return (point,), data - - def load_public( - self, data: memoryview - ) -> typing.Tuple[ed25519.Ed25519PublicKey, memoryview]: - """Make Ed25519 public key from data.""" - (point,), data = self.get_public(data) - public_key = ed25519.Ed25519PublicKey.from_public_bytes( - point.tobytes() - ) - return public_key, data - - def load_private( - self, data: memoryview, pubfields - ) -> typing.Tuple[ed25519.Ed25519PrivateKey, memoryview]: - """Make Ed25519 private key from data.""" - (point,), data = self.get_public(data) - keypair, data = _get_sshstr(data) - - secret = keypair[:32] - point2 = keypair[32:] - if point != point2 or (point,) != pubfields: - raise ValueError("Corrupt data: ed25519 field mismatch") - private_key = ed25519.Ed25519PrivateKey.from_private_bytes(secret) - return private_key, data - - def encode_public( - self, public_key: ed25519.Ed25519PublicKey, f_pub: _FragList - ) -> None: - """Write Ed25519 public key""" - raw_public_key = public_key.public_bytes( - Encoding.Raw, PublicFormat.Raw - ) - f_pub.put_sshstr(raw_public_key) - - def encode_private( - self, private_key: ed25519.Ed25519PrivateKey, f_priv: _FragList - ) -> None: - """Write Ed25519 private key""" - public_key = private_key.public_key() - raw_private_key = private_key.private_bytes( - Encoding.Raw, PrivateFormat.Raw, NoEncryption() - ) - raw_public_key = public_key.public_bytes( - Encoding.Raw, PublicFormat.Raw - ) - f_keypair = _FragList([raw_private_key, raw_public_key]) - - self.encode_public(public_key, f_priv) - f_priv.put_sshstr(f_keypair) - - -_KEY_FORMATS = { - _SSH_RSA: _SSHFormatRSA(), - _SSH_DSA: _SSHFormatDSA(), - _SSH_ED25519: _SSHFormatEd25519(), - _ECDSA_NISTP256: _SSHFormatECDSA(b"nistp256", ec.SECP256R1()), - _ECDSA_NISTP384: _SSHFormatECDSA(b"nistp384", ec.SECP384R1()), - _ECDSA_NISTP521: _SSHFormatECDSA(b"nistp521", ec.SECP521R1()), -} - - -def _lookup_kformat(key_type: bytes): - """Return valid format or throw error""" - if not isinstance(key_type, bytes): - key_type = memoryview(key_type).tobytes() - if key_type in _KEY_FORMATS: - return _KEY_FORMATS[key_type] - raise UnsupportedAlgorithm(f"Unsupported key type: {key_type!r}") - - -_SSH_PRIVATE_KEY_TYPES = typing.Union[ - ec.EllipticCurvePrivateKey, - rsa.RSAPrivateKey, - dsa.DSAPrivateKey, - ed25519.Ed25519PrivateKey, -] - - -def load_ssh_private_key( - data: bytes, - password: typing.Optional[bytes], - backend: typing.Any = None, -) -> _SSH_PRIVATE_KEY_TYPES: - """Load private key from OpenSSH custom encoding.""" - utils._check_byteslike("data", data) - if password is not None: - utils._check_bytes("password", password) - - m = _PEM_RC.search(data) - if not m: - raise ValueError("Not OpenSSH private key format") - p1 = m.start(1) - p2 = m.end(1) - data = binascii.a2b_base64(memoryview(data)[p1:p2]) - if not data.startswith(_SK_MAGIC): - raise ValueError("Not OpenSSH private key format") - data = memoryview(data)[len(_SK_MAGIC) :] - - # parse header - ciphername, data = _get_sshstr(data) - kdfname, data = _get_sshstr(data) - kdfoptions, data = _get_sshstr(data) - nkeys, data = _get_u32(data) - if nkeys != 1: - raise ValueError("Only one key supported") - - # load public key data - pubdata, data = _get_sshstr(data) - pub_key_type, pubdata = _get_sshstr(pubdata) - kformat = _lookup_kformat(pub_key_type) - pubfields, pubdata = kformat.get_public(pubdata) - _check_empty(pubdata) - - # load secret data - edata, data = _get_sshstr(data) - _check_empty(data) - - if (ciphername, kdfname) != (_NONE, _NONE): - ciphername_bytes = ciphername.tobytes() - if ciphername_bytes not in _SSH_CIPHERS: - raise UnsupportedAlgorithm( - f"Unsupported cipher: {ciphername_bytes!r}" - ) - if kdfname != _BCRYPT: - raise UnsupportedAlgorithm(f"Unsupported KDF: {kdfname!r}") - blklen = _SSH_CIPHERS[ciphername_bytes][3] - _check_block_size(edata, blklen) - salt, kbuf = _get_sshstr(kdfoptions) - rounds, kbuf = _get_u32(kbuf) - _check_empty(kbuf) - ciph = _init_cipher(ciphername_bytes, password, salt.tobytes(), rounds) - edata = memoryview(ciph.decryptor().update(edata)) - else: - blklen = 8 - _check_block_size(edata, blklen) - ck1, edata = _get_u32(edata) - ck2, edata = _get_u32(edata) - if ck1 != ck2: - raise ValueError("Corrupt data: broken checksum") - - # load per-key struct - key_type, edata = _get_sshstr(edata) - if key_type != pub_key_type: - raise ValueError("Corrupt data: key type mismatch") - private_key, edata = kformat.load_private(edata, pubfields) - comment, edata = _get_sshstr(edata) - - # yes, SSH does padding check *after* all other parsing is done. - # need to follow as it writes zero-byte padding too. - if edata != _PADDING[: len(edata)]: - raise ValueError("Corrupt data: invalid padding") - - return private_key - - -def _serialize_ssh_private_key( - private_key: _SSH_PRIVATE_KEY_TYPES, - password: bytes, - encryption_algorithm: KeySerializationEncryption, -) -> bytes: - """Serialize private key with OpenSSH custom encoding.""" - utils._check_bytes("password", password) - - if isinstance(private_key, ec.EllipticCurvePrivateKey): - key_type = _ecdsa_key_type(private_key.public_key()) - elif isinstance(private_key, rsa.RSAPrivateKey): - key_type = _SSH_RSA - elif isinstance(private_key, dsa.DSAPrivateKey): - key_type = _SSH_DSA - elif isinstance(private_key, ed25519.Ed25519PrivateKey): - key_type = _SSH_ED25519 - else: - raise ValueError("Unsupported key type") - kformat = _lookup_kformat(key_type) - - # setup parameters - f_kdfoptions = _FragList() - if password: - ciphername = _DEFAULT_CIPHER - blklen = _SSH_CIPHERS[ciphername][3] - kdfname = _BCRYPT - rounds = _DEFAULT_ROUNDS - if ( - isinstance(encryption_algorithm, _KeySerializationEncryption) - and encryption_algorithm._kdf_rounds is not None - ): - rounds = encryption_algorithm._kdf_rounds - salt = os.urandom(16) - f_kdfoptions.put_sshstr(salt) - f_kdfoptions.put_u32(rounds) - ciph = _init_cipher(ciphername, password, salt, rounds) - else: - ciphername = kdfname = _NONE - blklen = 8 - ciph = None - nkeys = 1 - checkval = os.urandom(4) - comment = b"" - - # encode public and private parts together - f_public_key = _FragList() - f_public_key.put_sshstr(key_type) - kformat.encode_public(private_key.public_key(), f_public_key) - - f_secrets = _FragList([checkval, checkval]) - f_secrets.put_sshstr(key_type) - kformat.encode_private(private_key, f_secrets) - f_secrets.put_sshstr(comment) - f_secrets.put_raw(_PADDING[: blklen - (f_secrets.size() % blklen)]) - - # top-level structure - f_main = _FragList() - f_main.put_raw(_SK_MAGIC) - f_main.put_sshstr(ciphername) - f_main.put_sshstr(kdfname) - f_main.put_sshstr(f_kdfoptions) - f_main.put_u32(nkeys) - f_main.put_sshstr(f_public_key) - f_main.put_sshstr(f_secrets) - - # copy result info bytearray - slen = f_secrets.size() - mlen = f_main.size() - buf = memoryview(bytearray(mlen + blklen)) - f_main.render(buf) - ofs = mlen - slen - - # encrypt in-place - if ciph is not None: - ciph.encryptor().update_into(buf[ofs:mlen], buf[ofs:]) - - return _ssh_pem_encode(buf[:mlen]) - - -_SSH_PUBLIC_KEY_TYPES = typing.Union[ - ec.EllipticCurvePublicKey, - rsa.RSAPublicKey, - dsa.DSAPublicKey, - ed25519.Ed25519PublicKey, -] - - -def load_ssh_public_key( - data: bytes, backend: typing.Any = None -) -> _SSH_PUBLIC_KEY_TYPES: - """Load public key from OpenSSH one-line format.""" - utils._check_byteslike("data", data) - - m = _SSH_PUBKEY_RC.match(data) - if not m: - raise ValueError("Invalid line format") - key_type = orig_key_type = m.group(1) - key_body = m.group(2) - with_cert = False - if _CERT_SUFFIX == key_type[-len(_CERT_SUFFIX) :]: - with_cert = True - key_type = key_type[: -len(_CERT_SUFFIX)] - kformat = _lookup_kformat(key_type) - - try: - rest = memoryview(binascii.a2b_base64(key_body)) - except (TypeError, binascii.Error): - raise ValueError("Invalid key format") - - inner_key_type, rest = _get_sshstr(rest) - if inner_key_type != orig_key_type: - raise ValueError("Invalid key format") - if with_cert: - nonce, rest = _get_sshstr(rest) - public_key, rest = kformat.load_public(rest) - if with_cert: - serial, rest = _get_u64(rest) - cctype, rest = _get_u32(rest) - key_id, rest = _get_sshstr(rest) - principals, rest = _get_sshstr(rest) - valid_after, rest = _get_u64(rest) - valid_before, rest = _get_u64(rest) - crit_options, rest = _get_sshstr(rest) - extensions, rest = _get_sshstr(rest) - reserved, rest = _get_sshstr(rest) - sig_key, rest = _get_sshstr(rest) - signature, rest = _get_sshstr(rest) - _check_empty(rest) - return public_key - - -def serialize_ssh_public_key(public_key: _SSH_PUBLIC_KEY_TYPES) -> bytes: - """One-line public key format for OpenSSH""" - if isinstance(public_key, ec.EllipticCurvePublicKey): - key_type = _ecdsa_key_type(public_key) - elif isinstance(public_key, rsa.RSAPublicKey): - key_type = _SSH_RSA - elif isinstance(public_key, dsa.DSAPublicKey): - key_type = _SSH_DSA - elif isinstance(public_key, ed25519.Ed25519PublicKey): - key_type = _SSH_ED25519 - else: - raise ValueError("Unsupported key type") - kformat = _lookup_kformat(key_type) - - f_pub = _FragList() - f_pub.put_sshstr(key_type) - kformat.encode_public(public_key, f_pub) - - pub = binascii.b2a_base64(f_pub.tobytes()).strip() - return b"".join([key_type, b" ", pub]) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py deleted file mode 100644 index 8a8b30f2a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -class InvalidToken(Exception): - pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py deleted file mode 100644 index cbb22704b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/hotp.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import base64 -import typing -from urllib.parse import quote, urlencode - -from cryptography.hazmat.primitives import constant_time, hmac -from cryptography.hazmat.primitives.hashes import SHA1, SHA256, SHA512 -from cryptography.hazmat.primitives.twofactor import InvalidToken - -_ALLOWED_HASH_TYPES = typing.Union[SHA1, SHA256, SHA512] - - -def _generate_uri( - hotp: "HOTP", - type_name: str, - account_name: str, - issuer: typing.Optional[str], - extra_parameters: typing.List[typing.Tuple[str, int]], -) -> str: - parameters = [ - ("digits", hotp._length), - ("secret", base64.b32encode(hotp._key)), - ("algorithm", hotp._algorithm.name.upper()), - ] - - if issuer is not None: - parameters.append(("issuer", issuer)) - - parameters.extend(extra_parameters) - - label = ( - f"{quote(issuer)}:{quote(account_name)}" - if issuer - else quote(account_name) - ) - return f"otpauth://{type_name}/{label}?{urlencode(parameters)}" - - -class HOTP: - def __init__( - self, - key: bytes, - length: int, - algorithm: _ALLOWED_HASH_TYPES, - backend: typing.Any = None, - enforce_key_length: bool = True, - ) -> None: - if len(key) < 16 and enforce_key_length is True: - raise ValueError("Key length has to be at least 128 bits.") - - if not isinstance(length, int): - raise TypeError("Length parameter must be an integer type.") - - if length < 6 or length > 8: - raise ValueError("Length of HOTP has to be between 6 and 8.") - - if not isinstance(algorithm, (SHA1, SHA256, SHA512)): - raise TypeError("Algorithm must be SHA1, SHA256 or SHA512.") - - self._key = key - self._length = length - self._algorithm = algorithm - - def generate(self, counter: int) -> bytes: - truncated_value = self._dynamic_truncate(counter) - hotp = truncated_value % (10**self._length) - return "{0:0{1}}".format(hotp, self._length).encode() - - def verify(self, hotp: bytes, counter: int) -> None: - if not constant_time.bytes_eq(self.generate(counter), hotp): - raise InvalidToken("Supplied HOTP value does not match.") - - def _dynamic_truncate(self, counter: int) -> int: - ctx = hmac.HMAC(self._key, self._algorithm) - ctx.update(counter.to_bytes(length=8, byteorder="big")) - hmac_value = ctx.finalize() - - offset = hmac_value[len(hmac_value) - 1] & 0b1111 - p = hmac_value[offset : offset + 4] - return int.from_bytes(p, byteorder="big") & 0x7FFFFFFF - - def get_provisioning_uri( - self, account_name: str, counter: int, issuer: typing.Optional[str] - ) -> str: - return _generate_uri( - self, "hotp", account_name, issuer, [("counter", int(counter))] - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py deleted file mode 100644 index 314dbef71..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/hazmat/primitives/twofactor/totp.py +++ /dev/null @@ -1,48 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import typing - -from cryptography.hazmat.primitives import constant_time -from cryptography.hazmat.primitives.twofactor import InvalidToken -from cryptography.hazmat.primitives.twofactor.hotp import ( - _ALLOWED_HASH_TYPES, - HOTP, - _generate_uri, -) - - -class TOTP: - def __init__( - self, - key: bytes, - length: int, - algorithm: _ALLOWED_HASH_TYPES, - time_step: int, - backend: typing.Any = None, - enforce_key_length: bool = True, - ): - self._time_step = time_step - self._hotp = HOTP( - key, length, algorithm, enforce_key_length=enforce_key_length - ) - - def generate(self, time: typing.Union[int, float]) -> bytes: - counter = int(time / self._time_step) - return self._hotp.generate(counter) - - def verify(self, totp: bytes, time: int) -> None: - if not constant_time.bytes_eq(self.generate(time), totp): - raise InvalidToken("Supplied TOTP value does not match.") - - def get_provisioning_uri( - self, account_name: str, issuer: typing.Optional[str] - ) -> str: - return _generate_uri( - self._hotp, - "totp", - account_name, - issuer, - [("period", int(self._time_step))], - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/py.typed b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/py.typed deleted file mode 100644 index e69de29bb..000000000 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py deleted file mode 100644 index 7f4a4799b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/utils.py +++ /dev/null @@ -1,132 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import enum -import sys -import types -import typing -import warnings - - -# We use a UserWarning subclass, instead of DeprecationWarning, because CPython -# decided deprecation warnings should be invisble by default. -class CryptographyDeprecationWarning(UserWarning): - pass - - -# Several APIs were deprecated with no specific end-of-life date because of the -# ubiquity of their use. They should not be removed until we agree on when that -# cycle ends. -DeprecatedIn36 = CryptographyDeprecationWarning -DeprecatedIn37 = CryptographyDeprecationWarning -DeprecatedIn39 = CryptographyDeprecationWarning - - -def _check_bytes(name: str, value: bytes) -> None: - if not isinstance(value, bytes): - raise TypeError("{} must be bytes".format(name)) - - -def _check_byteslike(name: str, value: bytes) -> None: - try: - memoryview(value) - except TypeError: - raise TypeError("{} must be bytes-like".format(name)) - - -def int_to_bytes(integer: int, length: typing.Optional[int] = None) -> bytes: - return integer.to_bytes( - length or (integer.bit_length() + 7) // 8 or 1, "big" - ) - - -class InterfaceNotImplemented(Exception): - pass - - -# DeprecatedIn39 -- Our only known consumer is aws-encryption-sdk, but we've -# made this a no-op to avoid breaking old versions. -def verify_interface( - iface: abc.ABCMeta, klass: object, *, check_annotations: bool = False -): - # Exists exclusively for `aws-encryption-sdk` which relies on it existing, - # even though it was never a public API. - pass - - -class _DeprecatedValue: - def __init__(self, value: object, message: str, warning_class): - self.value = value - self.message = message - self.warning_class = warning_class - - -class _ModuleWithDeprecations(types.ModuleType): - def __init__(self, module: types.ModuleType): - super().__init__(module.__name__) - self.__dict__["_module"] = module - - def __getattr__(self, attr: str) -> object: - obj = getattr(self._module, attr) - if isinstance(obj, _DeprecatedValue): - warnings.warn(obj.message, obj.warning_class, stacklevel=2) - obj = obj.value - return obj - - def __setattr__(self, attr: str, value: object) -> None: - setattr(self._module, attr, value) - - def __delattr__(self, attr: str) -> None: - obj = getattr(self._module, attr) - if isinstance(obj, _DeprecatedValue): - warnings.warn(obj.message, obj.warning_class, stacklevel=2) - - delattr(self._module, attr) - - def __dir__(self) -> typing.Sequence[str]: - return ["_module"] + dir(self._module) - - -def deprecated( - value: object, - module_name: str, - message: str, - warning_class: typing.Type[Warning], - name: typing.Optional[str] = None, -) -> _DeprecatedValue: - module = sys.modules[module_name] - if not isinstance(module, _ModuleWithDeprecations): - sys.modules[module_name] = module = _ModuleWithDeprecations(module) - dv = _DeprecatedValue(value, message, warning_class) - # Maintain backwards compatibility with `name is None` for pyOpenSSL. - if name is not None: - setattr(module, name, dv) - return dv - - -def cached_property(func: typing.Callable) -> property: - cached_name = "_cached_{}".format(func) - sentinel = object() - - def inner(instance: object): - cache = getattr(instance, cached_name, sentinel) - if cache is not sentinel: - return cache - result = func(instance) - setattr(instance, cached_name, result) - return result - - return property(inner) - - -# Python 3.10 changed representation of enums. We use well-defined object -# representation and string representation from Python 3.9. -class Enum(enum.Enum): - def __repr__(self) -> str: - return f"<{self.__class__.__name__}.{self._name_}: {self._value_!r}>" - - def __str__(self) -> str: - return f"{self.__class__.__name__}.{self._name_}" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py deleted file mode 100644 index ad924ad42..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/__init__.py +++ /dev/null @@ -1,250 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -from cryptography.x509 import certificate_transparency -from cryptography.x509.base import ( - Attribute, - AttributeNotFound, - Attributes, - Certificate, - CertificateBuilder, - CertificateRevocationList, - CertificateRevocationListBuilder, - CertificateSigningRequest, - CertificateSigningRequestBuilder, - InvalidVersion, - RevokedCertificate, - RevokedCertificateBuilder, - Version, - load_der_x509_certificate, - load_der_x509_crl, - load_der_x509_csr, - load_pem_x509_certificate, - load_pem_x509_certificates, - load_pem_x509_crl, - load_pem_x509_csr, - random_serial_number, -) -from cryptography.x509.extensions import ( - AccessDescription, - AuthorityInformationAccess, - AuthorityKeyIdentifier, - BasicConstraints, - CertificateIssuer, - CertificatePolicies, - CRLDistributionPoints, - CRLNumber, - CRLReason, - DeltaCRLIndicator, - DistributionPoint, - DuplicateExtension, - ExtendedKeyUsage, - Extension, - ExtensionNotFound, - Extensions, - ExtensionType, - FreshestCRL, - GeneralNames, - InhibitAnyPolicy, - InvalidityDate, - IssuerAlternativeName, - IssuingDistributionPoint, - KeyUsage, - NameConstraints, - NoticeReference, - OCSPNoCheck, - OCSPNonce, - PolicyConstraints, - PolicyInformation, - PrecertificateSignedCertificateTimestamps, - PrecertPoison, - ReasonFlags, - SignedCertificateTimestamps, - SubjectAlternativeName, - SubjectInformationAccess, - SubjectKeyIdentifier, - TLSFeature, - TLSFeatureType, - UnrecognizedExtension, - UserNotice, -) -from cryptography.x509.general_name import ( - DirectoryName, - DNSName, - GeneralName, - IPAddress, - OtherName, - RegisteredID, - RFC822Name, - UniformResourceIdentifier, - UnsupportedGeneralNameType, -) -from cryptography.x509.name import ( - Name, - NameAttribute, - RelativeDistinguishedName, -) -from cryptography.x509.oid import ( - AuthorityInformationAccessOID, - CertificatePoliciesOID, - CRLEntryExtensionOID, - ExtendedKeyUsageOID, - ExtensionOID, - NameOID, - ObjectIdentifier, - SignatureAlgorithmOID, -) - -OID_AUTHORITY_INFORMATION_ACCESS = ExtensionOID.AUTHORITY_INFORMATION_ACCESS -OID_AUTHORITY_KEY_IDENTIFIER = ExtensionOID.AUTHORITY_KEY_IDENTIFIER -OID_BASIC_CONSTRAINTS = ExtensionOID.BASIC_CONSTRAINTS -OID_CERTIFICATE_POLICIES = ExtensionOID.CERTIFICATE_POLICIES -OID_CRL_DISTRIBUTION_POINTS = ExtensionOID.CRL_DISTRIBUTION_POINTS -OID_EXTENDED_KEY_USAGE = ExtensionOID.EXTENDED_KEY_USAGE -OID_FRESHEST_CRL = ExtensionOID.FRESHEST_CRL -OID_INHIBIT_ANY_POLICY = ExtensionOID.INHIBIT_ANY_POLICY -OID_ISSUER_ALTERNATIVE_NAME = ExtensionOID.ISSUER_ALTERNATIVE_NAME -OID_KEY_USAGE = ExtensionOID.KEY_USAGE -OID_NAME_CONSTRAINTS = ExtensionOID.NAME_CONSTRAINTS -OID_OCSP_NO_CHECK = ExtensionOID.OCSP_NO_CHECK -OID_POLICY_CONSTRAINTS = ExtensionOID.POLICY_CONSTRAINTS -OID_POLICY_MAPPINGS = ExtensionOID.POLICY_MAPPINGS -OID_SUBJECT_ALTERNATIVE_NAME = ExtensionOID.SUBJECT_ALTERNATIVE_NAME -OID_SUBJECT_DIRECTORY_ATTRIBUTES = ExtensionOID.SUBJECT_DIRECTORY_ATTRIBUTES -OID_SUBJECT_INFORMATION_ACCESS = ExtensionOID.SUBJECT_INFORMATION_ACCESS -OID_SUBJECT_KEY_IDENTIFIER = ExtensionOID.SUBJECT_KEY_IDENTIFIER - -OID_DSA_WITH_SHA1 = SignatureAlgorithmOID.DSA_WITH_SHA1 -OID_DSA_WITH_SHA224 = SignatureAlgorithmOID.DSA_WITH_SHA224 -OID_DSA_WITH_SHA256 = SignatureAlgorithmOID.DSA_WITH_SHA256 -OID_ECDSA_WITH_SHA1 = SignatureAlgorithmOID.ECDSA_WITH_SHA1 -OID_ECDSA_WITH_SHA224 = SignatureAlgorithmOID.ECDSA_WITH_SHA224 -OID_ECDSA_WITH_SHA256 = SignatureAlgorithmOID.ECDSA_WITH_SHA256 -OID_ECDSA_WITH_SHA384 = SignatureAlgorithmOID.ECDSA_WITH_SHA384 -OID_ECDSA_WITH_SHA512 = SignatureAlgorithmOID.ECDSA_WITH_SHA512 -OID_RSA_WITH_MD5 = SignatureAlgorithmOID.RSA_WITH_MD5 -OID_RSA_WITH_SHA1 = SignatureAlgorithmOID.RSA_WITH_SHA1 -OID_RSA_WITH_SHA224 = SignatureAlgorithmOID.RSA_WITH_SHA224 -OID_RSA_WITH_SHA256 = SignatureAlgorithmOID.RSA_WITH_SHA256 -OID_RSA_WITH_SHA384 = SignatureAlgorithmOID.RSA_WITH_SHA384 -OID_RSA_WITH_SHA512 = SignatureAlgorithmOID.RSA_WITH_SHA512 -OID_RSASSA_PSS = SignatureAlgorithmOID.RSASSA_PSS - -OID_COMMON_NAME = NameOID.COMMON_NAME -OID_COUNTRY_NAME = NameOID.COUNTRY_NAME -OID_DOMAIN_COMPONENT = NameOID.DOMAIN_COMPONENT -OID_DN_QUALIFIER = NameOID.DN_QUALIFIER -OID_EMAIL_ADDRESS = NameOID.EMAIL_ADDRESS -OID_GENERATION_QUALIFIER = NameOID.GENERATION_QUALIFIER -OID_GIVEN_NAME = NameOID.GIVEN_NAME -OID_LOCALITY_NAME = NameOID.LOCALITY_NAME -OID_ORGANIZATIONAL_UNIT_NAME = NameOID.ORGANIZATIONAL_UNIT_NAME -OID_ORGANIZATION_NAME = NameOID.ORGANIZATION_NAME -OID_PSEUDONYM = NameOID.PSEUDONYM -OID_SERIAL_NUMBER = NameOID.SERIAL_NUMBER -OID_STATE_OR_PROVINCE_NAME = NameOID.STATE_OR_PROVINCE_NAME -OID_SURNAME = NameOID.SURNAME -OID_TITLE = NameOID.TITLE - -OID_CLIENT_AUTH = ExtendedKeyUsageOID.CLIENT_AUTH -OID_CODE_SIGNING = ExtendedKeyUsageOID.CODE_SIGNING -OID_EMAIL_PROTECTION = ExtendedKeyUsageOID.EMAIL_PROTECTION -OID_OCSP_SIGNING = ExtendedKeyUsageOID.OCSP_SIGNING -OID_SERVER_AUTH = ExtendedKeyUsageOID.SERVER_AUTH -OID_TIME_STAMPING = ExtendedKeyUsageOID.TIME_STAMPING - -OID_ANY_POLICY = CertificatePoliciesOID.ANY_POLICY -OID_CPS_QUALIFIER = CertificatePoliciesOID.CPS_QUALIFIER -OID_CPS_USER_NOTICE = CertificatePoliciesOID.CPS_USER_NOTICE - -OID_CERTIFICATE_ISSUER = CRLEntryExtensionOID.CERTIFICATE_ISSUER -OID_CRL_REASON = CRLEntryExtensionOID.CRL_REASON -OID_INVALIDITY_DATE = CRLEntryExtensionOID.INVALIDITY_DATE - -OID_CA_ISSUERS = AuthorityInformationAccessOID.CA_ISSUERS -OID_OCSP = AuthorityInformationAccessOID.OCSP - -__all__ = [ - "certificate_transparency", - "load_pem_x509_certificate", - "load_pem_x509_certificates", - "load_der_x509_certificate", - "load_pem_x509_csr", - "load_der_x509_csr", - "load_pem_x509_crl", - "load_der_x509_crl", - "random_serial_number", - "Attribute", - "AttributeNotFound", - "Attributes", - "InvalidVersion", - "DeltaCRLIndicator", - "DuplicateExtension", - "ExtensionNotFound", - "UnsupportedGeneralNameType", - "NameAttribute", - "Name", - "RelativeDistinguishedName", - "ObjectIdentifier", - "ExtensionType", - "Extensions", - "Extension", - "ExtendedKeyUsage", - "FreshestCRL", - "IssuingDistributionPoint", - "TLSFeature", - "TLSFeatureType", - "OCSPNoCheck", - "BasicConstraints", - "CRLNumber", - "KeyUsage", - "AuthorityInformationAccess", - "SubjectInformationAccess", - "AccessDescription", - "CertificatePolicies", - "PolicyInformation", - "UserNotice", - "NoticeReference", - "SubjectKeyIdentifier", - "NameConstraints", - "CRLDistributionPoints", - "DistributionPoint", - "ReasonFlags", - "InhibitAnyPolicy", - "SubjectAlternativeName", - "IssuerAlternativeName", - "AuthorityKeyIdentifier", - "GeneralNames", - "GeneralName", - "RFC822Name", - "DNSName", - "UniformResourceIdentifier", - "RegisteredID", - "DirectoryName", - "IPAddress", - "OtherName", - "Certificate", - "CertificateRevocationList", - "CertificateRevocationListBuilder", - "CertificateSigningRequest", - "RevokedCertificate", - "RevokedCertificateBuilder", - "CertificateSigningRequestBuilder", - "CertificateBuilder", - "Version", - "OID_CA_ISSUERS", - "OID_OCSP", - "CertificateIssuer", - "CRLReason", - "InvalidityDate", - "UnrecognizedExtension", - "PolicyConstraints", - "PrecertificateSignedCertificateTimestamps", - "PrecertPoison", - "OCSPNonce", - "SignedCertificateTimestamps", - "SignatureAlgorithmOID", - "NameOID", -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py deleted file mode 100644 index 6eae41cbe..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/base.py +++ /dev/null @@ -1,1131 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import datetime -import os -import typing - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import ( - dsa, - ec, - ed448, - ed25519, - rsa, - x448, - x25519, -) -from cryptography.hazmat.primitives.asymmetric.types import ( - CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, - CERTIFICATE_PRIVATE_KEY_TYPES, - CERTIFICATE_PUBLIC_KEY_TYPES, -) -from cryptography.x509.extensions import ( - Extension, - Extensions, - ExtensionType, - _make_sequence_methods, -) -from cryptography.x509.name import Name, _ASN1Type -from cryptography.x509.oid import ObjectIdentifier - -_EARLIEST_UTC_TIME = datetime.datetime(1950, 1, 1) - - -class AttributeNotFound(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super(AttributeNotFound, self).__init__(msg) - self.oid = oid - - -def _reject_duplicate_extension( - extension: Extension[ExtensionType], - extensions: typing.List[Extension[ExtensionType]], -) -> None: - # This is quadratic in the number of extensions - for e in extensions: - if e.oid == extension.oid: - raise ValueError("This extension has already been set.") - - -def _reject_duplicate_attribute( - oid: ObjectIdentifier, - attributes: typing.List[ - typing.Tuple[ObjectIdentifier, bytes, typing.Optional[int]] - ], -) -> None: - # This is quadratic in the number of attributes - for attr_oid, _, _ in attributes: - if attr_oid == oid: - raise ValueError("This attribute has already been set.") - - -def _convert_to_naive_utc_time(time: datetime.datetime) -> datetime.datetime: - """Normalizes a datetime to a naive datetime in UTC. - - time -- datetime to normalize. Assumed to be in UTC if not timezone - aware. - """ - if time.tzinfo is not None: - offset = time.utcoffset() - offset = offset if offset else datetime.timedelta() - return time.replace(tzinfo=None) - offset - else: - return time - - -class Attribute: - def __init__( - self, - oid: ObjectIdentifier, - value: bytes, - _type: int = _ASN1Type.UTF8String.value, - ) -> None: - self._oid = oid - self._value = value - self._type = _type - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return "".format(self.oid, self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Attribute): - return NotImplemented - - return ( - self.oid == other.oid - and self.value == other.value - and self._type == other._type - ) - - def __hash__(self) -> int: - return hash((self.oid, self.value, self._type)) - - -class Attributes: - def __init__( - self, - attributes: typing.Iterable[Attribute], - ) -> None: - self._attributes = list(attributes) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_attributes") - - def __repr__(self) -> str: - return "".format(self._attributes) - - def get_attribute_for_oid(self, oid: ObjectIdentifier) -> Attribute: - for attr in self: - if attr.oid == oid: - return attr - - raise AttributeNotFound("No {} attribute was found".format(oid), oid) - - -class Version(utils.Enum): - v1 = 0 - v3 = 2 - - -class InvalidVersion(Exception): - def __init__(self, msg: str, parsed_version: int) -> None: - super(InvalidVersion, self).__init__(msg) - self.parsed_version = parsed_version - - -class Certificate(metaclass=abc.ABCMeta): - @abc.abstractmethod - def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: - """ - Returns bytes using digest passed. - """ - - @property - @abc.abstractmethod - def serial_number(self) -> int: - """ - Returns certificate serial number - """ - - @property - @abc.abstractmethod - def version(self) -> Version: - """ - Returns the certificate version - """ - - @abc.abstractmethod - def public_key(self) -> CERTIFICATE_PUBLIC_KEY_TYPES: - """ - Returns the public key - """ - - @property - @abc.abstractmethod - def not_valid_before(self) -> datetime.datetime: - """ - Not before time (represented as UTC datetime) - """ - - @property - @abc.abstractmethod - def not_valid_after(self) -> datetime.datetime: - """ - Not after time (represented as UTC datetime) - """ - - @property - @abc.abstractmethod - def issuer(self) -> Name: - """ - Returns the issuer name object. - """ - - @property - @abc.abstractmethod - def subject(self) -> Name: - """ - Returns the subject name object. - """ - - @property - @abc.abstractmethod - def signature_hash_algorithm( - self, - ) -> typing.Optional[hashes.HashAlgorithm]: - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - in the certificate. - """ - - @property - @abc.abstractmethod - def signature_algorithm_oid(self) -> ObjectIdentifier: - """ - Returns the ObjectIdentifier of the signature algorithm. - """ - - @property - @abc.abstractmethod - def extensions(self) -> Extensions: - """ - Returns an Extensions object. - """ - - @property - @abc.abstractmethod - def signature(self) -> bytes: - """ - Returns the signature bytes. - """ - - @property - @abc.abstractmethod - def tbs_certificate_bytes(self) -> bytes: - """ - Returns the tbsCertificate payload bytes as defined in RFC 5280. - """ - - @property - @abc.abstractmethod - def tbs_precertificate_bytes(self) -> bytes: - """ - Returns the tbsCertificate payload bytes with the SCT list extension - stripped. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __hash__(self) -> int: - """ - Computes a hash. - """ - - @abc.abstractmethod - def public_bytes(self, encoding: serialization.Encoding) -> bytes: - """ - Serializes the certificate to PEM or DER format. - """ - - -# Runtime isinstance checks need this since the rust class is not a subclass. -Certificate.register(rust_x509.Certificate) - - -class RevokedCertificate(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def serial_number(self) -> int: - """ - Returns the serial number of the revoked certificate. - """ - - @property - @abc.abstractmethod - def revocation_date(self) -> datetime.datetime: - """ - Returns the date of when this certificate was revoked. - """ - - @property - @abc.abstractmethod - def extensions(self) -> Extensions: - """ - Returns an Extensions object containing a list of Revoked extensions. - """ - - -# Runtime isinstance checks need this since the rust class is not a subclass. -RevokedCertificate.register(rust_x509.RevokedCertificate) - - -class _RawRevokedCertificate(RevokedCertificate): - def __init__( - self, - serial_number: int, - revocation_date: datetime.datetime, - extensions: Extensions, - ): - self._serial_number = serial_number - self._revocation_date = revocation_date - self._extensions = extensions - - @property - def serial_number(self) -> int: - return self._serial_number - - @property - def revocation_date(self) -> datetime.datetime: - return self._revocation_date - - @property - def extensions(self) -> Extensions: - return self._extensions - - -class CertificateRevocationList(metaclass=abc.ABCMeta): - @abc.abstractmethod - def public_bytes(self, encoding: serialization.Encoding) -> bytes: - """ - Serializes the CRL to PEM or DER format. - """ - - @abc.abstractmethod - def fingerprint(self, algorithm: hashes.HashAlgorithm) -> bytes: - """ - Returns bytes using digest passed. - """ - - @abc.abstractmethod - def get_revoked_certificate_by_serial_number( - self, serial_number: int - ) -> typing.Optional[RevokedCertificate]: - """ - Returns an instance of RevokedCertificate or None if the serial_number - is not in the CRL. - """ - - @property - @abc.abstractmethod - def signature_hash_algorithm( - self, - ) -> typing.Optional[hashes.HashAlgorithm]: - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - in the certificate. - """ - - @property - @abc.abstractmethod - def signature_algorithm_oid(self) -> ObjectIdentifier: - """ - Returns the ObjectIdentifier of the signature algorithm. - """ - - @property - @abc.abstractmethod - def issuer(self) -> Name: - """ - Returns the X509Name with the issuer of this CRL. - """ - - @property - @abc.abstractmethod - def next_update(self) -> typing.Optional[datetime.datetime]: - """ - Returns the date of next update for this CRL. - """ - - @property - @abc.abstractmethod - def last_update(self) -> datetime.datetime: - """ - Returns the date of last update for this CRL. - """ - - @property - @abc.abstractmethod - def extensions(self) -> Extensions: - """ - Returns an Extensions object containing a list of CRL extensions. - """ - - @property - @abc.abstractmethod - def signature(self) -> bytes: - """ - Returns the signature bytes. - """ - - @property - @abc.abstractmethod - def tbs_certlist_bytes(self) -> bytes: - """ - Returns the tbsCertList payload bytes as defined in RFC 5280. - """ - - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __len__(self) -> int: - """ - Number of revoked certificates in the CRL. - """ - - @typing.overload - def __getitem__(self, idx: int) -> RevokedCertificate: - ... - - @typing.overload - def __getitem__(self, idx: slice) -> typing.List[RevokedCertificate]: - ... - - @abc.abstractmethod - def __getitem__( - self, idx: typing.Union[int, slice] - ) -> typing.Union[RevokedCertificate, typing.List[RevokedCertificate]]: - """ - Returns a revoked certificate (or slice of revoked certificates). - """ - - @abc.abstractmethod - def __iter__(self) -> typing.Iterator[RevokedCertificate]: - """ - Iterator over the revoked certificates - """ - - @abc.abstractmethod - def is_signature_valid( - self, public_key: CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES - ) -> bool: - """ - Verifies signature of revocation list against given public key. - """ - - -CertificateRevocationList.register(rust_x509.CertificateRevocationList) - - -class CertificateSigningRequest(metaclass=abc.ABCMeta): - @abc.abstractmethod - def __eq__(self, other: object) -> bool: - """ - Checks equality. - """ - - @abc.abstractmethod - def __hash__(self) -> int: - """ - Computes a hash. - """ - - @abc.abstractmethod - def public_key(self) -> CERTIFICATE_PUBLIC_KEY_TYPES: - """ - Returns the public key - """ - - @property - @abc.abstractmethod - def subject(self) -> Name: - """ - Returns the subject name object. - """ - - @property - @abc.abstractmethod - def signature_hash_algorithm( - self, - ) -> typing.Optional[hashes.HashAlgorithm]: - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - in the certificate. - """ - - @property - @abc.abstractmethod - def signature_algorithm_oid(self) -> ObjectIdentifier: - """ - Returns the ObjectIdentifier of the signature algorithm. - """ - - @property - @abc.abstractmethod - def extensions(self) -> Extensions: - """ - Returns the extensions in the signing request. - """ - - @property - @abc.abstractmethod - def attributes(self) -> Attributes: - """ - Returns an Attributes object. - """ - - @abc.abstractmethod - def public_bytes(self, encoding: serialization.Encoding) -> bytes: - """ - Encodes the request to PEM or DER format. - """ - - @property - @abc.abstractmethod - def signature(self) -> bytes: - """ - Returns the signature bytes. - """ - - @property - @abc.abstractmethod - def tbs_certrequest_bytes(self) -> bytes: - """ - Returns the PKCS#10 CertificationRequestInfo bytes as defined in RFC - 2986. - """ - - @property - @abc.abstractmethod - def is_signature_valid(self) -> bool: - """ - Verifies signature of signing request. - """ - - @abc.abstractmethod - def get_attribute_for_oid(self, oid: ObjectIdentifier) -> bytes: - """ - Get the attribute value for a given OID. - """ - - -# Runtime isinstance checks need this since the rust class is not a subclass. -CertificateSigningRequest.register(rust_x509.CertificateSigningRequest) - - -# Backend argument preserved for API compatibility, but ignored. -def load_pem_x509_certificate( - data: bytes, backend: typing.Any = None -) -> Certificate: - return rust_x509.load_pem_x509_certificate(data) - - -def load_pem_x509_certificates(data: bytes) -> typing.List[Certificate]: - return rust_x509.load_pem_x509_certificates(data) - - -# Backend argument preserved for API compatibility, but ignored. -def load_der_x509_certificate( - data: bytes, backend: typing.Any = None -) -> Certificate: - return rust_x509.load_der_x509_certificate(data) - - -# Backend argument preserved for API compatibility, but ignored. -def load_pem_x509_csr( - data: bytes, backend: typing.Any = None -) -> CertificateSigningRequest: - return rust_x509.load_pem_x509_csr(data) - - -# Backend argument preserved for API compatibility, but ignored. -def load_der_x509_csr( - data: bytes, backend: typing.Any = None -) -> CertificateSigningRequest: - return rust_x509.load_der_x509_csr(data) - - -# Backend argument preserved for API compatibility, but ignored. -def load_pem_x509_crl( - data: bytes, backend: typing.Any = None -) -> CertificateRevocationList: - return rust_x509.load_pem_x509_crl(data) - - -# Backend argument preserved for API compatibility, but ignored. -def load_der_x509_crl( - data: bytes, backend: typing.Any = None -) -> CertificateRevocationList: - return rust_x509.load_der_x509_crl(data) - - -class CertificateSigningRequestBuilder: - def __init__( - self, - subject_name: typing.Optional[Name] = None, - extensions: typing.List[Extension[ExtensionType]] = [], - attributes: typing.List[ - typing.Tuple[ObjectIdentifier, bytes, typing.Optional[int]] - ] = [], - ): - """ - Creates an empty X.509 certificate request (v1). - """ - self._subject_name = subject_name - self._extensions = extensions - self._attributes = attributes - - def subject_name(self, name: Name) -> "CertificateSigningRequestBuilder": - """ - Sets the certificate requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._subject_name is not None: - raise ValueError("The subject name may only be set once.") - return CertificateSigningRequestBuilder( - name, self._extensions, self._attributes - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> "CertificateSigningRequestBuilder": - """ - Adds an X.509 extension to the certificate request. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return CertificateSigningRequestBuilder( - self._subject_name, - self._extensions + [extension], - self._attributes, - ) - - def add_attribute( - self, - oid: ObjectIdentifier, - value: bytes, - *, - _tag: typing.Optional[_ASN1Type] = None, - ) -> "CertificateSigningRequestBuilder": - """ - Adds an X.509 attribute with an OID and associated value. - """ - if not isinstance(oid, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - - if not isinstance(value, bytes): - raise TypeError("value must be bytes") - - if _tag is not None and not isinstance(_tag, _ASN1Type): - raise TypeError("tag must be _ASN1Type") - - _reject_duplicate_attribute(oid, self._attributes) - - if _tag is not None: - tag = _tag.value - else: - tag = None - - return CertificateSigningRequestBuilder( - self._subject_name, - self._extensions, - self._attributes + [(oid, value, tag)], - ) - - def sign( - self, - private_key: CERTIFICATE_PRIVATE_KEY_TYPES, - algorithm: typing.Optional[hashes.HashAlgorithm], - backend: typing.Any = None, - ) -> CertificateSigningRequest: - """ - Signs the request using the requestor's private key. - """ - if self._subject_name is None: - raise ValueError("A CertificateSigningRequest must have a subject") - return rust_x509.create_x509_csr(self, private_key, algorithm) - - -class CertificateBuilder: - _extensions: typing.List[Extension[ExtensionType]] - - def __init__( - self, - issuer_name: typing.Optional[Name] = None, - subject_name: typing.Optional[Name] = None, - public_key: typing.Optional[CERTIFICATE_PUBLIC_KEY_TYPES] = None, - serial_number: typing.Optional[int] = None, - not_valid_before: typing.Optional[datetime.datetime] = None, - not_valid_after: typing.Optional[datetime.datetime] = None, - extensions: typing.List[Extension[ExtensionType]] = [], - ) -> None: - self._version = Version.v3 - self._issuer_name = issuer_name - self._subject_name = subject_name - self._public_key = public_key - self._serial_number = serial_number - self._not_valid_before = not_valid_before - self._not_valid_after = not_valid_after - self._extensions = extensions - - def issuer_name(self, name: Name) -> "CertificateBuilder": - """ - Sets the CA's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._issuer_name is not None: - raise ValueError("The issuer name may only be set once.") - return CertificateBuilder( - name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def subject_name(self, name: Name) -> "CertificateBuilder": - """ - Sets the requestor's distinguished name. - """ - if not isinstance(name, Name): - raise TypeError("Expecting x509.Name object.") - if self._subject_name is not None: - raise ValueError("The subject name may only be set once.") - return CertificateBuilder( - self._issuer_name, - name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def public_key( - self, - key: CERTIFICATE_PUBLIC_KEY_TYPES, - ) -> "CertificateBuilder": - """ - Sets the requestor's public key (as found in the signing request). - """ - if not isinstance( - key, - ( - dsa.DSAPublicKey, - rsa.RSAPublicKey, - ec.EllipticCurvePublicKey, - ed25519.Ed25519PublicKey, - ed448.Ed448PublicKey, - x25519.X25519PublicKey, - x448.X448PublicKey, - ), - ): - raise TypeError( - "Expecting one of DSAPublicKey, RSAPublicKey," - " EllipticCurvePublicKey, Ed25519PublicKey," - " Ed448PublicKey, X25519PublicKey, or " - "X448PublicKey." - ) - if self._public_key is not None: - raise ValueError("The public key may only be set once.") - return CertificateBuilder( - self._issuer_name, - self._subject_name, - key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def serial_number(self, number: int) -> "CertificateBuilder": - """ - Sets the certificate serial number. - """ - if not isinstance(number, int): - raise TypeError("Serial number must be of integral type.") - if self._serial_number is not None: - raise ValueError("The serial number may only be set once.") - if number <= 0: - raise ValueError("The serial number should be positive.") - - # ASN.1 integers are always signed, so most significant bit must be - # zero. - if number.bit_length() >= 160: # As defined in RFC 5280 - raise ValueError( - "The serial number should not be more than 159 " "bits." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - number, - self._not_valid_before, - self._not_valid_after, - self._extensions, - ) - - def not_valid_before( - self, time: datetime.datetime - ) -> "CertificateBuilder": - """ - Sets the certificate activation time. - """ - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._not_valid_before is not None: - raise ValueError("The not valid before may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The not valid before date must be on or after" - " 1950 January 1)." - ) - if self._not_valid_after is not None and time > self._not_valid_after: - raise ValueError( - "The not valid before date must be before the not valid after " - "date." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - time, - self._not_valid_after, - self._extensions, - ) - - def not_valid_after(self, time: datetime.datetime) -> "CertificateBuilder": - """ - Sets the certificate expiration time. - """ - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._not_valid_after is not None: - raise ValueError("The not valid after may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The not valid after date must be on or after" - " 1950 January 1." - ) - if ( - self._not_valid_before is not None - and time < self._not_valid_before - ): - raise ValueError( - "The not valid after date must be after the not valid before " - "date." - ) - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - time, - self._extensions, - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> "CertificateBuilder": - """ - Adds an X.509 extension to the certificate. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return CertificateBuilder( - self._issuer_name, - self._subject_name, - self._public_key, - self._serial_number, - self._not_valid_before, - self._not_valid_after, - self._extensions + [extension], - ) - - def sign( - self, - private_key: CERTIFICATE_PRIVATE_KEY_TYPES, - algorithm: typing.Optional[hashes.HashAlgorithm], - backend: typing.Any = None, - ) -> Certificate: - """ - Signs the certificate using the CA's private key. - """ - if self._subject_name is None: - raise ValueError("A certificate must have a subject name") - - if self._issuer_name is None: - raise ValueError("A certificate must have an issuer name") - - if self._serial_number is None: - raise ValueError("A certificate must have a serial number") - - if self._not_valid_before is None: - raise ValueError("A certificate must have a not valid before time") - - if self._not_valid_after is None: - raise ValueError("A certificate must have a not valid after time") - - if self._public_key is None: - raise ValueError("A certificate must have a public key") - - return rust_x509.create_x509_certificate(self, private_key, algorithm) - - -class CertificateRevocationListBuilder: - _extensions: typing.List[Extension[ExtensionType]] - _revoked_certificates: typing.List[RevokedCertificate] - - def __init__( - self, - issuer_name: typing.Optional[Name] = None, - last_update: typing.Optional[datetime.datetime] = None, - next_update: typing.Optional[datetime.datetime] = None, - extensions: typing.List[Extension[ExtensionType]] = [], - revoked_certificates: typing.List[RevokedCertificate] = [], - ): - self._issuer_name = issuer_name - self._last_update = last_update - self._next_update = next_update - self._extensions = extensions - self._revoked_certificates = revoked_certificates - - def issuer_name( - self, issuer_name: Name - ) -> "CertificateRevocationListBuilder": - if not isinstance(issuer_name, Name): - raise TypeError("Expecting x509.Name object.") - if self._issuer_name is not None: - raise ValueError("The issuer name may only be set once.") - return CertificateRevocationListBuilder( - issuer_name, - self._last_update, - self._next_update, - self._extensions, - self._revoked_certificates, - ) - - def last_update( - self, last_update: datetime.datetime - ) -> "CertificateRevocationListBuilder": - if not isinstance(last_update, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._last_update is not None: - raise ValueError("Last update may only be set once.") - last_update = _convert_to_naive_utc_time(last_update) - if last_update < _EARLIEST_UTC_TIME: - raise ValueError( - "The last update date must be on or after" " 1950 January 1." - ) - if self._next_update is not None and last_update > self._next_update: - raise ValueError( - "The last update date must be before the next update date." - ) - return CertificateRevocationListBuilder( - self._issuer_name, - last_update, - self._next_update, - self._extensions, - self._revoked_certificates, - ) - - def next_update( - self, next_update: datetime.datetime - ) -> "CertificateRevocationListBuilder": - if not isinstance(next_update, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._next_update is not None: - raise ValueError("Last update may only be set once.") - next_update = _convert_to_naive_utc_time(next_update) - if next_update < _EARLIEST_UTC_TIME: - raise ValueError( - "The last update date must be on or after" " 1950 January 1." - ) - if self._last_update is not None and next_update < self._last_update: - raise ValueError( - "The next update date must be after the last update date." - ) - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - next_update, - self._extensions, - self._revoked_certificates, - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> "CertificateRevocationListBuilder": - """ - Adds an X.509 extension to the certificate revocation list. - """ - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - self._next_update, - self._extensions + [extension], - self._revoked_certificates, - ) - - def add_revoked_certificate( - self, revoked_certificate: RevokedCertificate - ) -> "CertificateRevocationListBuilder": - """ - Adds a revoked certificate to the CRL. - """ - if not isinstance(revoked_certificate, RevokedCertificate): - raise TypeError("Must be an instance of RevokedCertificate") - - return CertificateRevocationListBuilder( - self._issuer_name, - self._last_update, - self._next_update, - self._extensions, - self._revoked_certificates + [revoked_certificate], - ) - - def sign( - self, - private_key: CERTIFICATE_PRIVATE_KEY_TYPES, - algorithm: typing.Optional[hashes.HashAlgorithm], - backend: typing.Any = None, - ) -> CertificateRevocationList: - if self._issuer_name is None: - raise ValueError("A CRL must have an issuer name") - - if self._last_update is None: - raise ValueError("A CRL must have a last update time") - - if self._next_update is None: - raise ValueError("A CRL must have a next update time") - - return rust_x509.create_x509_crl(self, private_key, algorithm) - - -class RevokedCertificateBuilder: - def __init__( - self, - serial_number: typing.Optional[int] = None, - revocation_date: typing.Optional[datetime.datetime] = None, - extensions: typing.List[Extension[ExtensionType]] = [], - ): - self._serial_number = serial_number - self._revocation_date = revocation_date - self._extensions = extensions - - def serial_number(self, number: int) -> "RevokedCertificateBuilder": - if not isinstance(number, int): - raise TypeError("Serial number must be of integral type.") - if self._serial_number is not None: - raise ValueError("The serial number may only be set once.") - if number <= 0: - raise ValueError("The serial number should be positive") - - # ASN.1 integers are always signed, so most significant bit must be - # zero. - if number.bit_length() >= 160: # As defined in RFC 5280 - raise ValueError( - "The serial number should not be more than 159 " "bits." - ) - return RevokedCertificateBuilder( - number, self._revocation_date, self._extensions - ) - - def revocation_date( - self, time: datetime.datetime - ) -> "RevokedCertificateBuilder": - if not isinstance(time, datetime.datetime): - raise TypeError("Expecting datetime object.") - if self._revocation_date is not None: - raise ValueError("The revocation date may only be set once.") - time = _convert_to_naive_utc_time(time) - if time < _EARLIEST_UTC_TIME: - raise ValueError( - "The revocation date must be on or after" " 1950 January 1." - ) - return RevokedCertificateBuilder( - self._serial_number, time, self._extensions - ) - - def add_extension( - self, extval: ExtensionType, critical: bool - ) -> "RevokedCertificateBuilder": - if not isinstance(extval, ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - return RevokedCertificateBuilder( - self._serial_number, - self._revocation_date, - self._extensions + [extension], - ) - - def build(self, backend: typing.Any = None) -> RevokedCertificate: - if self._serial_number is None: - raise ValueError("A revoked certificate must have a serial number") - if self._revocation_date is None: - raise ValueError( - "A revoked certificate must have a revocation date" - ) - return _RawRevokedCertificate( - self._serial_number, - self._revocation_date, - Extensions(self._extensions), - ) - - -def random_serial_number() -> int: - return int.from_bytes(os.urandom(20), "big") >> 1 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py deleted file mode 100644 index a67709865..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/certificate_transparency.py +++ /dev/null @@ -1,96 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import datetime - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives.hashes import HashAlgorithm - - -class LogEntryType(utils.Enum): - X509_CERTIFICATE = 0 - PRE_CERTIFICATE = 1 - - -class Version(utils.Enum): - v1 = 0 - - -class SignatureAlgorithm(utils.Enum): - """ - Signature algorithms that are valid for SCTs. - - These are exactly the same as SignatureAlgorithm in RFC 5246 (TLS 1.2). - - See: - """ - - ANONYMOUS = 0 - RSA = 1 - DSA = 2 - ECDSA = 3 - - -class SignedCertificateTimestamp(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def version(self) -> Version: - """ - Returns the SCT version. - """ - - @property - @abc.abstractmethod - def log_id(self) -> bytes: - """ - Returns an identifier indicating which log this SCT is for. - """ - - @property - @abc.abstractmethod - def timestamp(self) -> datetime.datetime: - """ - Returns the timestamp for this SCT. - """ - - @property - @abc.abstractmethod - def entry_type(self) -> LogEntryType: - """ - Returns whether this is an SCT for a certificate or pre-certificate. - """ - - @property - @abc.abstractmethod - def signature_hash_algorithm(self) -> HashAlgorithm: - """ - Returns the hash algorithm used for the SCT's signature. - """ - - @property - @abc.abstractmethod - def signature_algorithm(self) -> SignatureAlgorithm: - """ - Returns the signing algorithm used for the SCT's signature. - """ - - @property - @abc.abstractmethod - def signature(self) -> bytes: - """ - Returns the signature for this SCT. - """ - - @property - @abc.abstractmethod - def extension_bytes(self) -> bytes: - """ - Returns the raw bytes of any extensions for this SCT. - """ - - -SignedCertificateTimestamp.register(rust_x509.Sct) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py deleted file mode 100644 index 2012515f2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/extensions.py +++ /dev/null @@ -1,2113 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import datetime -import hashlib -import ipaddress -import typing - -from cryptography import utils -from cryptography.hazmat.bindings._rust import asn1 -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.hazmat.primitives import constant_time, serialization -from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey -from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey -from cryptography.hazmat.primitives.asymmetric.types import ( - CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES, - CERTIFICATE_PUBLIC_KEY_TYPES, -) -from cryptography.x509.certificate_transparency import ( - SignedCertificateTimestamp, -) -from cryptography.x509.general_name import ( - _IPADDRESS_TYPES, - DirectoryName, - DNSName, - GeneralName, - IPAddress, - OtherName, - RegisteredID, - RFC822Name, - UniformResourceIdentifier, -) -from cryptography.x509.name import Name, RelativeDistinguishedName -from cryptography.x509.oid import ( - CRLEntryExtensionOID, - ExtensionOID, - ObjectIdentifier, - OCSPExtensionOID, -) - -ExtensionTypeVar = typing.TypeVar( - "ExtensionTypeVar", bound="ExtensionType", covariant=True -) - - -def _key_identifier_from_public_key( - public_key: CERTIFICATE_PUBLIC_KEY_TYPES, -) -> bytes: - if isinstance(public_key, RSAPublicKey): - data = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.PKCS1, - ) - elif isinstance(public_key, EllipticCurvePublicKey): - data = public_key.public_bytes( - serialization.Encoding.X962, - serialization.PublicFormat.UncompressedPoint, - ) - else: - # This is a very slow way to do this. - serialized = public_key.public_bytes( - serialization.Encoding.DER, - serialization.PublicFormat.SubjectPublicKeyInfo, - ) - data = asn1.parse_spki_for_data(serialized) - - return hashlib.sha1(data).digest() - - -def _make_sequence_methods(field_name: str): - def len_method(self) -> int: - return len(getattr(self, field_name)) - - def iter_method(self): - return iter(getattr(self, field_name)) - - def getitem_method(self, idx): - return getattr(self, field_name)[idx] - - return len_method, iter_method, getitem_method - - -class DuplicateExtension(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super(DuplicateExtension, self).__init__(msg) - self.oid = oid - - -class ExtensionNotFound(Exception): - def __init__(self, msg: str, oid: ObjectIdentifier) -> None: - super(ExtensionNotFound, self).__init__(msg) - self.oid = oid - - -class ExtensionType(metaclass=abc.ABCMeta): - oid: typing.ClassVar[ObjectIdentifier] - - def public_bytes(self) -> bytes: - """ - Serializes the extension type to DER. - """ - raise NotImplementedError( - "public_bytes is not implemented for extension type {0!r}".format( - self - ) - ) - - -class Extensions: - def __init__( - self, extensions: typing.Iterable["Extension[ExtensionType]"] - ) -> None: - self._extensions = list(extensions) - - def get_extension_for_oid( - self, oid: ObjectIdentifier - ) -> "Extension[ExtensionType]": - for ext in self: - if ext.oid == oid: - return ext - - raise ExtensionNotFound("No {} extension was found".format(oid), oid) - - def get_extension_for_class( - self, extclass: typing.Type[ExtensionTypeVar] - ) -> "Extension[ExtensionTypeVar]": - if extclass is UnrecognizedExtension: - raise TypeError( - "UnrecognizedExtension can't be used with " - "get_extension_for_class because more than one instance of the" - " class may be present." - ) - - for ext in self: - if isinstance(ext.value, extclass): - return ext - - raise ExtensionNotFound( - "No {} extension was found".format(extclass), extclass.oid - ) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_extensions") - - def __repr__(self) -> str: - return "".format(self._extensions) - - -class CRLNumber(ExtensionType): - oid = ExtensionOID.CRL_NUMBER - - def __init__(self, crl_number: int) -> None: - if not isinstance(crl_number, int): - raise TypeError("crl_number must be an integer") - - self._crl_number = crl_number - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLNumber): - return NotImplemented - - return self.crl_number == other.crl_number - - def __hash__(self) -> int: - return hash(self.crl_number) - - def __repr__(self) -> str: - return "".format(self.crl_number) - - @property - def crl_number(self) -> int: - return self._crl_number - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AuthorityKeyIdentifier(ExtensionType): - oid = ExtensionOID.AUTHORITY_KEY_IDENTIFIER - - def __init__( - self, - key_identifier: typing.Optional[bytes], - authority_cert_issuer: typing.Optional[typing.Iterable[GeneralName]], - authority_cert_serial_number: typing.Optional[int], - ) -> None: - if (authority_cert_issuer is None) != ( - authority_cert_serial_number is None - ): - raise ValueError( - "authority_cert_issuer and authority_cert_serial_number " - "must both be present or both None" - ) - - if authority_cert_issuer is not None: - authority_cert_issuer = list(authority_cert_issuer) - if not all( - isinstance(x, GeneralName) for x in authority_cert_issuer - ): - raise TypeError( - "authority_cert_issuer must be a list of GeneralName " - "objects" - ) - - if authority_cert_serial_number is not None and not isinstance( - authority_cert_serial_number, int - ): - raise TypeError("authority_cert_serial_number must be an integer") - - self._key_identifier = key_identifier - self._authority_cert_issuer = authority_cert_issuer - self._authority_cert_serial_number = authority_cert_serial_number - - # This takes a subset of CERTIFICATE_PUBLIC_KEY_TYPES because an issuer - # cannot have an X25519/X448 key. This introduces some unfortunate - # asymmetry that requires typing users to explicitly - # narrow their type, but we should make this accurate and not just - # convenient. - @classmethod - def from_issuer_public_key( - cls, public_key: CERTIFICATE_ISSUER_PUBLIC_KEY_TYPES - ) -> "AuthorityKeyIdentifier": - digest = _key_identifier_from_public_key(public_key) - return cls( - key_identifier=digest, - authority_cert_issuer=None, - authority_cert_serial_number=None, - ) - - @classmethod - def from_issuer_subject_key_identifier( - cls, ski: "SubjectKeyIdentifier" - ) -> "AuthorityKeyIdentifier": - return cls( - key_identifier=ski.digest, - authority_cert_issuer=None, - authority_cert_serial_number=None, - ) - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AuthorityKeyIdentifier): - return NotImplemented - - return ( - self.key_identifier == other.key_identifier - and self.authority_cert_issuer == other.authority_cert_issuer - and self.authority_cert_serial_number - == other.authority_cert_serial_number - ) - - def __hash__(self) -> int: - if self.authority_cert_issuer is None: - aci = None - else: - aci = tuple(self.authority_cert_issuer) - return hash( - (self.key_identifier, aci, self.authority_cert_serial_number) - ) - - @property - def key_identifier(self) -> typing.Optional[bytes]: - return self._key_identifier - - @property - def authority_cert_issuer( - self, - ) -> typing.Optional[typing.List[GeneralName]]: - return self._authority_cert_issuer - - @property - def authority_cert_serial_number(self) -> typing.Optional[int]: - return self._authority_cert_serial_number - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SubjectKeyIdentifier(ExtensionType): - oid = ExtensionOID.SUBJECT_KEY_IDENTIFIER - - def __init__(self, digest: bytes) -> None: - self._digest = digest - - @classmethod - def from_public_key( - cls, public_key: CERTIFICATE_PUBLIC_KEY_TYPES - ) -> "SubjectKeyIdentifier": - return cls(_key_identifier_from_public_key(public_key)) - - @property - def digest(self) -> bytes: - return self._digest - - @property - def key_identifier(self) -> bytes: - return self._digest - - def __repr__(self) -> str: - return "".format(self.digest) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectKeyIdentifier): - return NotImplemented - - return constant_time.bytes_eq(self.digest, other.digest) - - def __hash__(self) -> int: - return hash(self.digest) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AuthorityInformationAccess(ExtensionType): - oid = ExtensionOID.AUTHORITY_INFORMATION_ACCESS - - def __init__( - self, descriptions: typing.Iterable["AccessDescription"] - ) -> None: - descriptions = list(descriptions) - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") - - def __repr__(self) -> str: - return "".format(self._descriptions) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AuthorityInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __hash__(self) -> int: - return hash(tuple(self._descriptions)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SubjectInformationAccess(ExtensionType): - oid = ExtensionOID.SUBJECT_INFORMATION_ACCESS - - def __init__( - self, descriptions: typing.Iterable["AccessDescription"] - ) -> None: - descriptions = list(descriptions) - if not all(isinstance(x, AccessDescription) for x in descriptions): - raise TypeError( - "Every item in the descriptions list must be an " - "AccessDescription" - ) - - self._descriptions = descriptions - - __len__, __iter__, __getitem__ = _make_sequence_methods("_descriptions") - - def __repr__(self) -> str: - return "".format(self._descriptions) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectInformationAccess): - return NotImplemented - - return self._descriptions == other._descriptions - - def __hash__(self) -> int: - return hash(tuple(self._descriptions)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class AccessDescription: - def __init__( - self, access_method: ObjectIdentifier, access_location: GeneralName - ) -> None: - if not isinstance(access_method, ObjectIdentifier): - raise TypeError("access_method must be an ObjectIdentifier") - - if not isinstance(access_location, GeneralName): - raise TypeError("access_location must be a GeneralName") - - self._access_method = access_method - self._access_location = access_location - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, AccessDescription): - return NotImplemented - - return ( - self.access_method == other.access_method - and self.access_location == other.access_location - ) - - def __hash__(self) -> int: - return hash((self.access_method, self.access_location)) - - @property - def access_method(self) -> ObjectIdentifier: - return self._access_method - - @property - def access_location(self) -> GeneralName: - return self._access_location - - -class BasicConstraints(ExtensionType): - oid = ExtensionOID.BASIC_CONSTRAINTS - - def __init__(self, ca: bool, path_length: typing.Optional[int]) -> None: - if not isinstance(ca, bool): - raise TypeError("ca must be a boolean value") - - if path_length is not None and not ca: - raise ValueError("path_length must be None when ca is False") - - if path_length is not None and ( - not isinstance(path_length, int) or path_length < 0 - ): - raise TypeError( - "path_length must be a non-negative integer or None" - ) - - self._ca = ca - self._path_length = path_length - - @property - def ca(self) -> bool: - return self._ca - - @property - def path_length(self) -> typing.Optional[int]: - return self._path_length - - def __repr__(self) -> str: - return ( - "" - ).format(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, BasicConstraints): - return NotImplemented - - return self.ca == other.ca and self.path_length == other.path_length - - def __hash__(self) -> int: - return hash((self.ca, self.path_length)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class DeltaCRLIndicator(ExtensionType): - oid = ExtensionOID.DELTA_CRL_INDICATOR - - def __init__(self, crl_number: int) -> None: - if not isinstance(crl_number, int): - raise TypeError("crl_number must be an integer") - - self._crl_number = crl_number - - @property - def crl_number(self) -> int: - return self._crl_number - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DeltaCRLIndicator): - return NotImplemented - - return self.crl_number == other.crl_number - - def __hash__(self) -> int: - return hash(self.crl_number) - - def __repr__(self) -> str: - return "".format(self) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CRLDistributionPoints(ExtensionType): - oid = ExtensionOID.CRL_DISTRIBUTION_POINTS - - def __init__( - self, distribution_points: typing.Iterable["DistributionPoint"] - ) -> None: - distribution_points = list(distribution_points) - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_distribution_points" - ) - - def __repr__(self) -> str: - return "".format(self._distribution_points) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLDistributionPoints): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __hash__(self) -> int: - return hash(tuple(self._distribution_points)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class FreshestCRL(ExtensionType): - oid = ExtensionOID.FRESHEST_CRL - - def __init__( - self, distribution_points: typing.Iterable["DistributionPoint"] - ) -> None: - distribution_points = list(distribution_points) - if not all( - isinstance(x, DistributionPoint) for x in distribution_points - ): - raise TypeError( - "distribution_points must be a list of DistributionPoint " - "objects" - ) - - self._distribution_points = distribution_points - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_distribution_points" - ) - - def __repr__(self) -> str: - return "".format(self._distribution_points) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, FreshestCRL): - return NotImplemented - - return self._distribution_points == other._distribution_points - - def __hash__(self) -> int: - return hash(tuple(self._distribution_points)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class DistributionPoint: - def __init__( - self, - full_name: typing.Optional[typing.Iterable[GeneralName]], - relative_name: typing.Optional[RelativeDistinguishedName], - reasons: typing.Optional[typing.FrozenSet["ReasonFlags"]], - crl_issuer: typing.Optional[typing.Iterable[GeneralName]], - ) -> None: - if full_name and relative_name: - raise ValueError( - "You cannot provide both full_name and relative_name, at " - "least one must be None." - ) - if not full_name and not relative_name and not crl_issuer: - raise ValueError( - "Either full_name, relative_name or crl_issuer must be " - "provided." - ) - - if full_name is not None: - full_name = list(full_name) - if not all(isinstance(x, GeneralName) for x in full_name): - raise TypeError( - "full_name must be a list of GeneralName objects" - ) - - if relative_name: - if not isinstance(relative_name, RelativeDistinguishedName): - raise TypeError( - "relative_name must be a RelativeDistinguishedName" - ) - - if crl_issuer is not None: - crl_issuer = list(crl_issuer) - if not all(isinstance(x, GeneralName) for x in crl_issuer): - raise TypeError( - "crl_issuer must be None or a list of general names" - ) - - if reasons and ( - not isinstance(reasons, frozenset) - or not all(isinstance(x, ReasonFlags) for x in reasons) - ): - raise TypeError("reasons must be None or frozenset of ReasonFlags") - - if reasons and ( - ReasonFlags.unspecified in reasons - or ReasonFlags.remove_from_crl in reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in a " - "DistributionPoint" - ) - - self._full_name = full_name - self._relative_name = relative_name - self._reasons = reasons - self._crl_issuer = crl_issuer - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name - and self.relative_name == other.relative_name - and self.reasons == other.reasons - and self.crl_issuer == other.crl_issuer - ) - - def __hash__(self) -> int: - if self.full_name is not None: - fn: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple( - self.full_name - ) - else: - fn = None - - if self.crl_issuer is not None: - crl_issuer: typing.Optional[ - typing.Tuple[GeneralName, ...] - ] = tuple(self.crl_issuer) - else: - crl_issuer = None - - return hash((fn, self.relative_name, self.reasons, crl_issuer)) - - @property - def full_name(self) -> typing.Optional[typing.List[GeneralName]]: - return self._full_name - - @property - def relative_name(self) -> typing.Optional[RelativeDistinguishedName]: - return self._relative_name - - @property - def reasons(self) -> typing.Optional[typing.FrozenSet["ReasonFlags"]]: - return self._reasons - - @property - def crl_issuer(self) -> typing.Optional[typing.List[GeneralName]]: - return self._crl_issuer - - -class ReasonFlags(utils.Enum): - unspecified = "unspecified" - key_compromise = "keyCompromise" - ca_compromise = "cACompromise" - affiliation_changed = "affiliationChanged" - superseded = "superseded" - cessation_of_operation = "cessationOfOperation" - certificate_hold = "certificateHold" - privilege_withdrawn = "privilegeWithdrawn" - aa_compromise = "aACompromise" - remove_from_crl = "removeFromCRL" - - -# These are distribution point bit string mappings. Not to be confused with -# CRLReason reason flags bit string mappings. -# ReasonFlags ::= BIT STRING { -# unused (0), -# keyCompromise (1), -# cACompromise (2), -# affiliationChanged (3), -# superseded (4), -# cessationOfOperation (5), -# certificateHold (6), -# privilegeWithdrawn (7), -# aACompromise (8) } -_REASON_BIT_MAPPING = { - 1: ReasonFlags.key_compromise, - 2: ReasonFlags.ca_compromise, - 3: ReasonFlags.affiliation_changed, - 4: ReasonFlags.superseded, - 5: ReasonFlags.cessation_of_operation, - 6: ReasonFlags.certificate_hold, - 7: ReasonFlags.privilege_withdrawn, - 8: ReasonFlags.aa_compromise, -} - -_CRLREASONFLAGS = { - ReasonFlags.key_compromise: 1, - ReasonFlags.ca_compromise: 2, - ReasonFlags.affiliation_changed: 3, - ReasonFlags.superseded: 4, - ReasonFlags.cessation_of_operation: 5, - ReasonFlags.certificate_hold: 6, - ReasonFlags.privilege_withdrawn: 7, - ReasonFlags.aa_compromise: 8, -} - - -class PolicyConstraints(ExtensionType): - oid = ExtensionOID.POLICY_CONSTRAINTS - - def __init__( - self, - require_explicit_policy: typing.Optional[int], - inhibit_policy_mapping: typing.Optional[int], - ) -> None: - if require_explicit_policy is not None and not isinstance( - require_explicit_policy, int - ): - raise TypeError( - "require_explicit_policy must be a non-negative integer or " - "None" - ) - - if inhibit_policy_mapping is not None and not isinstance( - inhibit_policy_mapping, int - ): - raise TypeError( - "inhibit_policy_mapping must be a non-negative integer or None" - ) - - if inhibit_policy_mapping is None and require_explicit_policy is None: - raise ValueError( - "At least one of require_explicit_policy and " - "inhibit_policy_mapping must not be None" - ) - - self._require_explicit_policy = require_explicit_policy - self._inhibit_policy_mapping = inhibit_policy_mapping - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PolicyConstraints): - return NotImplemented - - return ( - self.require_explicit_policy == other.require_explicit_policy - and self.inhibit_policy_mapping == other.inhibit_policy_mapping - ) - - def __hash__(self) -> int: - return hash( - (self.require_explicit_policy, self.inhibit_policy_mapping) - ) - - @property - def require_explicit_policy(self) -> typing.Optional[int]: - return self._require_explicit_policy - - @property - def inhibit_policy_mapping(self) -> typing.Optional[int]: - return self._inhibit_policy_mapping - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CertificatePolicies(ExtensionType): - oid = ExtensionOID.CERTIFICATE_POLICIES - - def __init__(self, policies: typing.Iterable["PolicyInformation"]) -> None: - policies = list(policies) - if not all(isinstance(x, PolicyInformation) for x in policies): - raise TypeError( - "Every item in the policies list must be a " - "PolicyInformation" - ) - - self._policies = policies - - __len__, __iter__, __getitem__ = _make_sequence_methods("_policies") - - def __repr__(self) -> str: - return "".format(self._policies) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CertificatePolicies): - return NotImplemented - - return self._policies == other._policies - - def __hash__(self) -> int: - return hash(tuple(self._policies)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PolicyInformation: - def __init__( - self, - policy_identifier: ObjectIdentifier, - policy_qualifiers: typing.Optional[ - typing.Iterable[typing.Union[str, "UserNotice"]] - ], - ) -> None: - if not isinstance(policy_identifier, ObjectIdentifier): - raise TypeError("policy_identifier must be an ObjectIdentifier") - - self._policy_identifier = policy_identifier - - if policy_qualifiers is not None: - policy_qualifiers = list(policy_qualifiers) - if not all( - isinstance(x, (str, UserNotice)) for x in policy_qualifiers - ): - raise TypeError( - "policy_qualifiers must be a list of strings and/or " - "UserNotice objects or None" - ) - - self._policy_qualifiers = policy_qualifiers - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PolicyInformation): - return NotImplemented - - return ( - self.policy_identifier == other.policy_identifier - and self.policy_qualifiers == other.policy_qualifiers - ) - - def __hash__(self) -> int: - if self.policy_qualifiers is not None: - pq: typing.Optional[ - typing.Tuple[typing.Union[str, "UserNotice"], ...] - ] = tuple(self.policy_qualifiers) - else: - pq = None - - return hash((self.policy_identifier, pq)) - - @property - def policy_identifier(self) -> ObjectIdentifier: - return self._policy_identifier - - @property - def policy_qualifiers( - self, - ) -> typing.Optional[typing.List[typing.Union[str, "UserNotice"]]]: - return self._policy_qualifiers - - -class UserNotice: - def __init__( - self, - notice_reference: typing.Optional["NoticeReference"], - explicit_text: typing.Optional[str], - ) -> None: - if notice_reference and not isinstance( - notice_reference, NoticeReference - ): - raise TypeError( - "notice_reference must be None or a NoticeReference" - ) - - self._notice_reference = notice_reference - self._explicit_text = explicit_text - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UserNotice): - return NotImplemented - - return ( - self.notice_reference == other.notice_reference - and self.explicit_text == other.explicit_text - ) - - def __hash__(self) -> int: - return hash((self.notice_reference, self.explicit_text)) - - @property - def notice_reference(self) -> typing.Optional["NoticeReference"]: - return self._notice_reference - - @property - def explicit_text(self) -> typing.Optional[str]: - return self._explicit_text - - -class NoticeReference: - def __init__( - self, - organization: typing.Optional[str], - notice_numbers: typing.Iterable[int], - ) -> None: - self._organization = organization - notice_numbers = list(notice_numbers) - if not all(isinstance(x, int) for x in notice_numbers): - raise TypeError("notice_numbers must be a list of integers") - - self._notice_numbers = notice_numbers - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NoticeReference): - return NotImplemented - - return ( - self.organization == other.organization - and self.notice_numbers == other.notice_numbers - ) - - def __hash__(self) -> int: - return hash((self.organization, tuple(self.notice_numbers))) - - @property - def organization(self) -> typing.Optional[str]: - return self._organization - - @property - def notice_numbers(self) -> typing.List[int]: - return self._notice_numbers - - -class ExtendedKeyUsage(ExtensionType): - oid = ExtensionOID.EXTENDED_KEY_USAGE - - def __init__(self, usages: typing.Iterable[ObjectIdentifier]) -> None: - usages = list(usages) - if not all(isinstance(x, ObjectIdentifier) for x in usages): - raise TypeError( - "Every item in the usages list must be an ObjectIdentifier" - ) - - self._usages = usages - - __len__, __iter__, __getitem__ = _make_sequence_methods("_usages") - - def __repr__(self) -> str: - return "".format(self._usages) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, ExtendedKeyUsage): - return NotImplemented - - return self._usages == other._usages - - def __hash__(self) -> int: - return hash(tuple(self._usages)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPNoCheck(ExtensionType): - oid = ExtensionOID.OCSP_NO_CHECK - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPNoCheck): - return NotImplemented - - return True - - def __hash__(self) -> int: - return hash(OCSPNoCheck) - - def __repr__(self) -> str: - return "" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrecertPoison(ExtensionType): - oid = ExtensionOID.PRECERT_POISON - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrecertPoison): - return NotImplemented - - return True - - def __hash__(self) -> int: - return hash(PrecertPoison) - - def __repr__(self) -> str: - return "" - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class TLSFeature(ExtensionType): - oid = ExtensionOID.TLS_FEATURE - - def __init__(self, features: typing.Iterable["TLSFeatureType"]) -> None: - features = list(features) - if ( - not all(isinstance(x, TLSFeatureType) for x in features) - or len(features) == 0 - ): - raise TypeError( - "features must be a list of elements from the TLSFeatureType " - "enum" - ) - - self._features = features - - __len__, __iter__, __getitem__ = _make_sequence_methods("_features") - - def __repr__(self) -> str: - return "".format(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, TLSFeature): - return NotImplemented - - return self._features == other._features - - def __hash__(self) -> int: - return hash(tuple(self._features)) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class TLSFeatureType(utils.Enum): - # status_request is defined in RFC 6066 and is used for what is commonly - # called OCSP Must-Staple when present in the TLS Feature extension in an - # X.509 certificate. - status_request = 5 - # status_request_v2 is defined in RFC 6961 and allows multiple OCSP - # responses to be provided. It is not currently in use by clients or - # servers. - status_request_v2 = 17 - - -_TLS_FEATURE_TYPE_TO_ENUM = {x.value: x for x in TLSFeatureType} - - -class InhibitAnyPolicy(ExtensionType): - oid = ExtensionOID.INHIBIT_ANY_POLICY - - def __init__(self, skip_certs: int) -> None: - if not isinstance(skip_certs, int): - raise TypeError("skip_certs must be an integer") - - if skip_certs < 0: - raise ValueError("skip_certs must be a non-negative integer") - - self._skip_certs = skip_certs - - def __repr__(self) -> str: - return "".format(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, InhibitAnyPolicy): - return NotImplemented - - return self.skip_certs == other.skip_certs - - def __hash__(self) -> int: - return hash(self.skip_certs) - - @property - def skip_certs(self) -> int: - return self._skip_certs - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class KeyUsage(ExtensionType): - oid = ExtensionOID.KEY_USAGE - - def __init__( - self, - digital_signature: bool, - content_commitment: bool, - key_encipherment: bool, - data_encipherment: bool, - key_agreement: bool, - key_cert_sign: bool, - crl_sign: bool, - encipher_only: bool, - decipher_only: bool, - ) -> None: - if not key_agreement and (encipher_only or decipher_only): - raise ValueError( - "encipher_only and decipher_only can only be true when " - "key_agreement is true" - ) - - self._digital_signature = digital_signature - self._content_commitment = content_commitment - self._key_encipherment = key_encipherment - self._data_encipherment = data_encipherment - self._key_agreement = key_agreement - self._key_cert_sign = key_cert_sign - self._crl_sign = crl_sign - self._encipher_only = encipher_only - self._decipher_only = decipher_only - - @property - def digital_signature(self) -> bool: - return self._digital_signature - - @property - def content_commitment(self) -> bool: - return self._content_commitment - - @property - def key_encipherment(self) -> bool: - return self._key_encipherment - - @property - def data_encipherment(self) -> bool: - return self._data_encipherment - - @property - def key_agreement(self) -> bool: - return self._key_agreement - - @property - def key_cert_sign(self) -> bool: - return self._key_cert_sign - - @property - def crl_sign(self) -> bool: - return self._crl_sign - - @property - def encipher_only(self) -> bool: - if not self.key_agreement: - raise ValueError( - "encipher_only is undefined unless key_agreement is true" - ) - else: - return self._encipher_only - - @property - def decipher_only(self) -> bool: - if not self.key_agreement: - raise ValueError( - "decipher_only is undefined unless key_agreement is true" - ) - else: - return self._decipher_only - - def __repr__(self) -> str: - try: - encipher_only = self.encipher_only - decipher_only = self.decipher_only - except ValueError: - # Users found None confusing because even though encipher/decipher - # have no meaning unless key_agreement is true, to construct an - # instance of the class you still need to pass False. - encipher_only = False - decipher_only = False - - return ( - "" - ).format(self, encipher_only, decipher_only) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, KeyUsage): - return NotImplemented - - return ( - self.digital_signature == other.digital_signature - and self.content_commitment == other.content_commitment - and self.key_encipherment == other.key_encipherment - and self.data_encipherment == other.data_encipherment - and self.key_agreement == other.key_agreement - and self.key_cert_sign == other.key_cert_sign - and self.crl_sign == other.crl_sign - and self._encipher_only == other._encipher_only - and self._decipher_only == other._decipher_only - ) - - def __hash__(self) -> int: - return hash( - ( - self.digital_signature, - self.content_commitment, - self.key_encipherment, - self.data_encipherment, - self.key_agreement, - self.key_cert_sign, - self.crl_sign, - self._encipher_only, - self._decipher_only, - ) - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class NameConstraints(ExtensionType): - oid = ExtensionOID.NAME_CONSTRAINTS - - def __init__( - self, - permitted_subtrees: typing.Optional[typing.Iterable[GeneralName]], - excluded_subtrees: typing.Optional[typing.Iterable[GeneralName]], - ) -> None: - if permitted_subtrees is not None: - permitted_subtrees = list(permitted_subtrees) - if not permitted_subtrees: - raise ValueError( - "permitted_subtrees must be a non-empty list or None" - ) - if not all(isinstance(x, GeneralName) for x in permitted_subtrees): - raise TypeError( - "permitted_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_ip_name(permitted_subtrees) - - if excluded_subtrees is not None: - excluded_subtrees = list(excluded_subtrees) - if not excluded_subtrees: - raise ValueError( - "excluded_subtrees must be a non-empty list or None" - ) - if not all(isinstance(x, GeneralName) for x in excluded_subtrees): - raise TypeError( - "excluded_subtrees must be a list of GeneralName objects " - "or None" - ) - - self._validate_ip_name(excluded_subtrees) - - if permitted_subtrees is None and excluded_subtrees is None: - raise ValueError( - "At least one of permitted_subtrees and excluded_subtrees " - "must not be None" - ) - - self._permitted_subtrees = permitted_subtrees - self._excluded_subtrees = excluded_subtrees - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NameConstraints): - return NotImplemented - - return ( - self.excluded_subtrees == other.excluded_subtrees - and self.permitted_subtrees == other.permitted_subtrees - ) - - def _validate_ip_name(self, tree: typing.Iterable[GeneralName]) -> None: - if any( - isinstance(name, IPAddress) - and not isinstance( - name.value, (ipaddress.IPv4Network, ipaddress.IPv6Network) - ) - for name in tree - ): - raise TypeError( - "IPAddress name constraints must be an IPv4Network or" - " IPv6Network object" - ) - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __hash__(self) -> int: - if self.permitted_subtrees is not None: - ps: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple( - self.permitted_subtrees - ) - else: - ps = None - - if self.excluded_subtrees is not None: - es: typing.Optional[typing.Tuple[GeneralName, ...]] = tuple( - self.excluded_subtrees - ) - else: - es = None - - return hash((ps, es)) - - @property - def permitted_subtrees( - self, - ) -> typing.Optional[typing.List[GeneralName]]: - return self._permitted_subtrees - - @property - def excluded_subtrees( - self, - ) -> typing.Optional[typing.List[GeneralName]]: - return self._excluded_subtrees - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class Extension(typing.Generic[ExtensionTypeVar]): - def __init__( - self, oid: ObjectIdentifier, critical: bool, value: ExtensionTypeVar - ) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - - if not isinstance(critical, bool): - raise TypeError("critical must be a boolean value") - - self._oid = oid - self._critical = critical - self._value = value - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def critical(self) -> bool: - return self._critical - - @property - def value(self) -> ExtensionTypeVar: - return self._value - - def __repr__(self) -> str: - return ( - "" - ).format(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Extension): - return NotImplemented - - return ( - self.oid == other.oid - and self.critical == other.critical - and self.value == other.value - ) - - def __hash__(self) -> int: - return hash((self.oid, self.critical, self.value)) - - -class GeneralNames: - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - general_names = list(general_names) - if not all(isinstance(x, GeneralName) for x in general_names): - raise TypeError( - "Every item in the general_names list must be an " - "object conforming to the GeneralName interface" - ) - - self._general_names = general_names - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[UniformResourceIdentifier], - typing.Type[RFC822Name], - ], - ) -> typing.List[str]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[DirectoryName], - ) -> typing.List[Name]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[RegisteredID], - ) -> typing.List[ObjectIdentifier]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[IPAddress] - ) -> typing.List[_IPADDRESS_TYPES]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[OtherName] - ) -> typing.List[OtherName]: - ... - - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[DirectoryName], - typing.Type[IPAddress], - typing.Type[OtherName], - typing.Type[RFC822Name], - typing.Type[RegisteredID], - typing.Type[UniformResourceIdentifier], - ], - ) -> typing.Union[ - typing.List[_IPADDRESS_TYPES], - typing.List[str], - typing.List[OtherName], - typing.List[Name], - typing.List[ObjectIdentifier], - ]: - # Return the value of each GeneralName, except for OtherName instances - # which we return directly because it has two important properties not - # just one value. - objs = (i for i in self if isinstance(i, type)) - if type != OtherName: - return [i.value for i in objs] - return list(objs) - - def __repr__(self) -> str: - return "".format(self._general_names) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, GeneralNames): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(tuple(self._general_names)) - - -class SubjectAlternativeName(ExtensionType): - oid = ExtensionOID.SUBJECT_ALTERNATIVE_NAME - - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[UniformResourceIdentifier], - typing.Type[RFC822Name], - ], - ) -> typing.List[str]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[DirectoryName], - ) -> typing.List[Name]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[RegisteredID], - ) -> typing.List[ObjectIdentifier]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[IPAddress] - ) -> typing.List[_IPADDRESS_TYPES]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[OtherName] - ) -> typing.List[OtherName]: - ... - - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[DirectoryName], - typing.Type[IPAddress], - typing.Type[OtherName], - typing.Type[RFC822Name], - typing.Type[RegisteredID], - typing.Type[UniformResourceIdentifier], - ], - ) -> typing.Union[ - typing.List[_IPADDRESS_TYPES], - typing.List[str], - typing.List[OtherName], - typing.List[Name], - typing.List[ObjectIdentifier], - ]: - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return "".format(self._general_names) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SubjectAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class IssuerAlternativeName(ExtensionType): - oid = ExtensionOID.ISSUER_ALTERNATIVE_NAME - - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[UniformResourceIdentifier], - typing.Type[RFC822Name], - ], - ) -> typing.List[str]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[DirectoryName], - ) -> typing.List[Name]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[RegisteredID], - ) -> typing.List[ObjectIdentifier]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[IPAddress] - ) -> typing.List[_IPADDRESS_TYPES]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[OtherName] - ) -> typing.List[OtherName]: - ... - - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[DirectoryName], - typing.Type[IPAddress], - typing.Type[OtherName], - typing.Type[RFC822Name], - typing.Type[RegisteredID], - typing.Type[UniformResourceIdentifier], - ], - ) -> typing.Union[ - typing.List[_IPADDRESS_TYPES], - typing.List[str], - typing.List[OtherName], - typing.List[Name], - typing.List[ObjectIdentifier], - ]: - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return "".format(self._general_names) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IssuerAlternativeName): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CertificateIssuer(ExtensionType): - oid = CRLEntryExtensionOID.CERTIFICATE_ISSUER - - def __init__(self, general_names: typing.Iterable[GeneralName]) -> None: - self._general_names = GeneralNames(general_names) - - __len__, __iter__, __getitem__ = _make_sequence_methods("_general_names") - - @typing.overload - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[UniformResourceIdentifier], - typing.Type[RFC822Name], - ], - ) -> typing.List[str]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[DirectoryName], - ) -> typing.List[Name]: - ... - - @typing.overload - def get_values_for_type( - self, - type: typing.Type[RegisteredID], - ) -> typing.List[ObjectIdentifier]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[IPAddress] - ) -> typing.List[_IPADDRESS_TYPES]: - ... - - @typing.overload - def get_values_for_type( - self, type: typing.Type[OtherName] - ) -> typing.List[OtherName]: - ... - - def get_values_for_type( - self, - type: typing.Union[ - typing.Type[DNSName], - typing.Type[DirectoryName], - typing.Type[IPAddress], - typing.Type[OtherName], - typing.Type[RFC822Name], - typing.Type[RegisteredID], - typing.Type[UniformResourceIdentifier], - ], - ) -> typing.Union[ - typing.List[_IPADDRESS_TYPES], - typing.List[str], - typing.List[OtherName], - typing.List[Name], - typing.List[ObjectIdentifier], - ]: - return self._general_names.get_values_for_type(type) - - def __repr__(self) -> str: - return "".format(self._general_names) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CertificateIssuer): - return NotImplemented - - return self._general_names == other._general_names - - def __hash__(self) -> int: - return hash(self._general_names) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class CRLReason(ExtensionType): - oid = CRLEntryExtensionOID.CRL_REASON - - def __init__(self, reason: ReasonFlags) -> None: - if not isinstance(reason, ReasonFlags): - raise TypeError("reason must be an element from ReasonFlags") - - self._reason = reason - - def __repr__(self) -> str: - return "".format(self._reason) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, CRLReason): - return NotImplemented - - return self.reason == other.reason - - def __hash__(self) -> int: - return hash(self.reason) - - @property - def reason(self) -> ReasonFlags: - return self._reason - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class InvalidityDate(ExtensionType): - oid = CRLEntryExtensionOID.INVALIDITY_DATE - - def __init__(self, invalidity_date: datetime.datetime) -> None: - if not isinstance(invalidity_date, datetime.datetime): - raise TypeError("invalidity_date must be a datetime.datetime") - - self._invalidity_date = invalidity_date - - def __repr__(self) -> str: - return "".format( - self._invalidity_date - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, InvalidityDate): - return NotImplemented - - return self.invalidity_date == other.invalidity_date - - def __hash__(self) -> int: - return hash(self.invalidity_date) - - @property - def invalidity_date(self) -> datetime.datetime: - return self._invalidity_date - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class PrecertificateSignedCertificateTimestamps(ExtensionType): - oid = ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS - - def __init__( - self, - signed_certificate_timestamps: typing.Iterable[ - SignedCertificateTimestamp - ], - ) -> None: - signed_certificate_timestamps = list(signed_certificate_timestamps) - if not all( - isinstance(sct, SignedCertificateTimestamp) - for sct in signed_certificate_timestamps - ): - raise TypeError( - "Every item in the signed_certificate_timestamps list must be " - "a SignedCertificateTimestamp" - ) - self._signed_certificate_timestamps = signed_certificate_timestamps - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_signed_certificate_timestamps" - ) - - def __repr__(self) -> str: - return "".format( - list(self) - ) - - def __hash__(self) -> int: - return hash(tuple(self._signed_certificate_timestamps)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, PrecertificateSignedCertificateTimestamps): - return NotImplemented - - return ( - self._signed_certificate_timestamps - == other._signed_certificate_timestamps - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class SignedCertificateTimestamps(ExtensionType): - oid = ExtensionOID.SIGNED_CERTIFICATE_TIMESTAMPS - - def __init__( - self, - signed_certificate_timestamps: typing.Iterable[ - SignedCertificateTimestamp - ], - ) -> None: - signed_certificate_timestamps = list(signed_certificate_timestamps) - if not all( - isinstance(sct, SignedCertificateTimestamp) - for sct in signed_certificate_timestamps - ): - raise TypeError( - "Every item in the signed_certificate_timestamps list must be " - "a SignedCertificateTimestamp" - ) - self._signed_certificate_timestamps = signed_certificate_timestamps - - __len__, __iter__, __getitem__ = _make_sequence_methods( - "_signed_certificate_timestamps" - ) - - def __repr__(self) -> str: - return "".format(list(self)) - - def __hash__(self) -> int: - return hash(tuple(self._signed_certificate_timestamps)) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, SignedCertificateTimestamps): - return NotImplemented - - return ( - self._signed_certificate_timestamps - == other._signed_certificate_timestamps - ) - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class OCSPNonce(ExtensionType): - oid = OCSPExtensionOID.NONCE - - def __init__(self, nonce: bytes) -> None: - if not isinstance(nonce, bytes): - raise TypeError("nonce must be bytes") - - self._nonce = nonce - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OCSPNonce): - return NotImplemented - - return self.nonce == other.nonce - - def __hash__(self) -> int: - return hash(self.nonce) - - def __repr__(self) -> str: - return "".format(self) - - @property - def nonce(self) -> bytes: - return self._nonce - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class IssuingDistributionPoint(ExtensionType): - oid = ExtensionOID.ISSUING_DISTRIBUTION_POINT - - def __init__( - self, - full_name: typing.Optional[typing.Iterable[GeneralName]], - relative_name: typing.Optional[RelativeDistinguishedName], - only_contains_user_certs: bool, - only_contains_ca_certs: bool, - only_some_reasons: typing.Optional[typing.FrozenSet[ReasonFlags]], - indirect_crl: bool, - only_contains_attribute_certs: bool, - ) -> None: - if full_name is not None: - full_name = list(full_name) - - if only_some_reasons and ( - not isinstance(only_some_reasons, frozenset) - or not all(isinstance(x, ReasonFlags) for x in only_some_reasons) - ): - raise TypeError( - "only_some_reasons must be None or frozenset of ReasonFlags" - ) - - if only_some_reasons and ( - ReasonFlags.unspecified in only_some_reasons - or ReasonFlags.remove_from_crl in only_some_reasons - ): - raise ValueError( - "unspecified and remove_from_crl are not valid reasons in an " - "IssuingDistributionPoint" - ) - - if not ( - isinstance(only_contains_user_certs, bool) - and isinstance(only_contains_ca_certs, bool) - and isinstance(indirect_crl, bool) - and isinstance(only_contains_attribute_certs, bool) - ): - raise TypeError( - "only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl and only_contains_attribute_certs " - "must all be boolean." - ) - - crl_constraints = [ - only_contains_user_certs, - only_contains_ca_certs, - indirect_crl, - only_contains_attribute_certs, - ] - - if len([x for x in crl_constraints if x]) > 1: - raise ValueError( - "Only one of the following can be set to True: " - "only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl, only_contains_attribute_certs" - ) - - if not any( - [ - only_contains_user_certs, - only_contains_ca_certs, - indirect_crl, - only_contains_attribute_certs, - full_name, - relative_name, - only_some_reasons, - ] - ): - raise ValueError( - "Cannot create empty extension: " - "if only_contains_user_certs, only_contains_ca_certs, " - "indirect_crl, and only_contains_attribute_certs are all False" - ", then either full_name, relative_name, or only_some_reasons " - "must have a value." - ) - - self._only_contains_user_certs = only_contains_user_certs - self._only_contains_ca_certs = only_contains_ca_certs - self._indirect_crl = indirect_crl - self._only_contains_attribute_certs = only_contains_attribute_certs - self._only_some_reasons = only_some_reasons - self._full_name = full_name - self._relative_name = relative_name - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IssuingDistributionPoint): - return NotImplemented - - return ( - self.full_name == other.full_name - and self.relative_name == other.relative_name - and self.only_contains_user_certs == other.only_contains_user_certs - and self.only_contains_ca_certs == other.only_contains_ca_certs - and self.only_some_reasons == other.only_some_reasons - and self.indirect_crl == other.indirect_crl - and self.only_contains_attribute_certs - == other.only_contains_attribute_certs - ) - - def __hash__(self) -> int: - return hash( - ( - self.full_name, - self.relative_name, - self.only_contains_user_certs, - self.only_contains_ca_certs, - self.only_some_reasons, - self.indirect_crl, - self.only_contains_attribute_certs, - ) - ) - - @property - def full_name(self) -> typing.Optional[typing.List[GeneralName]]: - return self._full_name - - @property - def relative_name(self) -> typing.Optional[RelativeDistinguishedName]: - return self._relative_name - - @property - def only_contains_user_certs(self) -> bool: - return self._only_contains_user_certs - - @property - def only_contains_ca_certs(self) -> bool: - return self._only_contains_ca_certs - - @property - def only_some_reasons( - self, - ) -> typing.Optional[typing.FrozenSet[ReasonFlags]]: - return self._only_some_reasons - - @property - def indirect_crl(self) -> bool: - return self._indirect_crl - - @property - def only_contains_attribute_certs(self) -> bool: - return self._only_contains_attribute_certs - - def public_bytes(self) -> bytes: - return rust_x509.encode_extension_value(self) - - -class UnrecognizedExtension(ExtensionType): - def __init__(self, oid: ObjectIdentifier, value: bytes) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError("oid must be an ObjectIdentifier") - self._oid = oid - self._value = value - - @property - def oid(self) -> ObjectIdentifier: # type: ignore[override] - return self._oid - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return ( - "".format(self) - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UnrecognizedExtension): - return NotImplemented - - return self.oid == other.oid and self.value == other.value - - def __hash__(self) -> int: - return hash((self.oid, self.value)) - - def public_bytes(self) -> bytes: - return self.value diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py deleted file mode 100644 index b8b91ed94..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/general_name.py +++ /dev/null @@ -1,284 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import ipaddress -import typing -from email.utils import parseaddr - -from cryptography.x509.name import Name -from cryptography.x509.oid import ObjectIdentifier - -_IPADDRESS_TYPES = typing.Union[ - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network, -] - - -class UnsupportedGeneralNameType(Exception): - pass - - -class GeneralName(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def value(self) -> typing.Any: - """ - Return the value of the object - """ - - -class RFC822Name(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "RFC822Name values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - name, address = parseaddr(value) - if name or not address: - # parseaddr has found a name (e.g. Name ) or the entire - # value is an empty string. - raise ValueError("Invalid rfc822name value") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> "RFC822Name": - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return "".format(self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RFC822Name): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class DNSName(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "DNSName values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation(cls, value: str) -> "DNSName": - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return "".format(self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DNSName): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class UniformResourceIdentifier(GeneralName): - def __init__(self, value: str) -> None: - if isinstance(value, str): - try: - value.encode("ascii") - except UnicodeEncodeError: - raise ValueError( - "URI values should be passed as an A-label string. " - "This means unicode characters should be encoded via " - "a library like idna." - ) - else: - raise TypeError("value must be string") - - self._value = value - - @property - def value(self) -> str: - return self._value - - @classmethod - def _init_without_validation( - cls, value: str - ) -> "UniformResourceIdentifier": - instance = cls.__new__(cls) - instance._value = value - return instance - - def __repr__(self) -> str: - return "".format(self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, UniformResourceIdentifier): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class DirectoryName(GeneralName): - def __init__(self, value: Name) -> None: - if not isinstance(value, Name): - raise TypeError("value must be a Name") - - self._value = value - - @property - def value(self) -> Name: - return self._value - - def __repr__(self) -> str: - return "".format(self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, DirectoryName): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class RegisteredID(GeneralName): - def __init__(self, value: ObjectIdentifier) -> None: - if not isinstance(value, ObjectIdentifier): - raise TypeError("value must be an ObjectIdentifier") - - self._value = value - - @property - def value(self) -> ObjectIdentifier: - return self._value - - def __repr__(self) -> str: - return "".format(self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RegisteredID): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class IPAddress(GeneralName): - def __init__(self, value: _IPADDRESS_TYPES) -> None: - if not isinstance( - value, - ( - ipaddress.IPv4Address, - ipaddress.IPv6Address, - ipaddress.IPv4Network, - ipaddress.IPv6Network, - ), - ): - raise TypeError( - "value must be an instance of ipaddress.IPv4Address, " - "ipaddress.IPv6Address, ipaddress.IPv4Network, or " - "ipaddress.IPv6Network" - ) - - self._value = value - - @property - def value(self) -> _IPADDRESS_TYPES: - return self._value - - def _packed(self) -> bytes: - if isinstance( - self.value, (ipaddress.IPv4Address, ipaddress.IPv6Address) - ): - return self.value.packed - else: - return ( - self.value.network_address.packed + self.value.netmask.packed - ) - - def __repr__(self) -> str: - return "".format(self.value) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, IPAddress): - return NotImplemented - - return self.value == other.value - - def __hash__(self) -> int: - return hash(self.value) - - -class OtherName(GeneralName): - def __init__(self, type_id: ObjectIdentifier, value: bytes) -> None: - if not isinstance(type_id, ObjectIdentifier): - raise TypeError("type_id must be an ObjectIdentifier") - if not isinstance(value, bytes): - raise TypeError("value must be a binary string") - - self._type_id = type_id - self._value = value - - @property - def type_id(self) -> ObjectIdentifier: - return self._type_id - - @property - def value(self) -> bytes: - return self._value - - def __repr__(self) -> str: - return "".format( - self.type_id, self.value - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, OtherName): - return NotImplemented - - return self.type_id == other.type_id and self.value == other.value - - def __hash__(self) -> int: - return hash((self.type_id, self.value)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py deleted file mode 100644 index acd7c0f1e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/name.py +++ /dev/null @@ -1,460 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -import binascii -import re -import sys -import typing -import warnings - -from cryptography import utils -from cryptography.hazmat.bindings._rust import x509 as rust_x509 -from cryptography.x509.oid import NameOID, ObjectIdentifier - - -class _ASN1Type(utils.Enum): - BitString = 3 - OctetString = 4 - UTF8String = 12 - NumericString = 18 - PrintableString = 19 - T61String = 20 - IA5String = 22 - UTCTime = 23 - GeneralizedTime = 24 - VisibleString = 26 - UniversalString = 28 - BMPString = 30 - - -_ASN1_TYPE_TO_ENUM = {i.value: i for i in _ASN1Type} -_NAMEOID_DEFAULT_TYPE: typing.Dict[ObjectIdentifier, _ASN1Type] = { - NameOID.COUNTRY_NAME: _ASN1Type.PrintableString, - NameOID.JURISDICTION_COUNTRY_NAME: _ASN1Type.PrintableString, - NameOID.SERIAL_NUMBER: _ASN1Type.PrintableString, - NameOID.DN_QUALIFIER: _ASN1Type.PrintableString, - NameOID.EMAIL_ADDRESS: _ASN1Type.IA5String, - NameOID.DOMAIN_COMPONENT: _ASN1Type.IA5String, -} - -# Type alias -_OidNameMap = typing.Mapping[ObjectIdentifier, str] -_NameOidMap = typing.Mapping[str, ObjectIdentifier] - -#: Short attribute names from RFC 4514: -#: https://tools.ietf.org/html/rfc4514#page-7 -_NAMEOID_TO_NAME: _OidNameMap = { - NameOID.COMMON_NAME: "CN", - NameOID.LOCALITY_NAME: "L", - NameOID.STATE_OR_PROVINCE_NAME: "ST", - NameOID.ORGANIZATION_NAME: "O", - NameOID.ORGANIZATIONAL_UNIT_NAME: "OU", - NameOID.COUNTRY_NAME: "C", - NameOID.STREET_ADDRESS: "STREET", - NameOID.DOMAIN_COMPONENT: "DC", - NameOID.USER_ID: "UID", -} -_NAME_TO_NAMEOID = {v: k for k, v in _NAMEOID_TO_NAME.items()} - - -def _escape_dn_value(val: typing.Union[str, bytes]) -> str: - """Escape special characters in RFC4514 Distinguished Name value.""" - - if not val: - return "" - - # RFC 4514 Section 2.4 defines the value as being the # (U+0023) character - # followed by the hexadecimal encoding of the octets. - if isinstance(val, bytes): - return "#" + binascii.hexlify(val).decode("utf8") - - # See https://tools.ietf.org/html/rfc4514#section-2.4 - val = val.replace("\\", "\\\\") - val = val.replace('"', '\\"') - val = val.replace("+", "\\+") - val = val.replace(",", "\\,") - val = val.replace(";", "\\;") - val = val.replace("<", "\\<") - val = val.replace(">", "\\>") - val = val.replace("\0", "\\00") - - if val[0] in ("#", " "): - val = "\\" + val - if val[-1] == " ": - val = val[:-1] + "\\ " - - return val - - -def _unescape_dn_value(val: str) -> str: - if not val: - return "" - - # See https://tools.ietf.org/html/rfc4514#section-3 - - # special = escaped / SPACE / SHARP / EQUALS - # escaped = DQUOTE / PLUS / COMMA / SEMI / LANGLE / RANGLE - def sub(m): - val = m.group(1) - # Regular escape - if len(val) == 1: - return val - # Hex-value scape - return chr(int(val, 16)) - - return _RFC4514NameParser._PAIR_RE.sub(sub, val) - - -class NameAttribute: - def __init__( - self, - oid: ObjectIdentifier, - value: typing.Union[str, bytes], - _type: typing.Optional[_ASN1Type] = None, - *, - _validate: bool = True, - ) -> None: - if not isinstance(oid, ObjectIdentifier): - raise TypeError( - "oid argument must be an ObjectIdentifier instance." - ) - if _type == _ASN1Type.BitString: - if oid != NameOID.X500_UNIQUE_IDENTIFIER: - raise TypeError( - "oid must be X500_UNIQUE_IDENTIFIER for BitString type." - ) - if not isinstance(value, bytes): - raise TypeError("value must be bytes for BitString") - else: - if not isinstance(value, str): - raise TypeError("value argument must be a str") - - if ( - oid == NameOID.COUNTRY_NAME - or oid == NameOID.JURISDICTION_COUNTRY_NAME - ): - assert isinstance(value, str) - c_len = len(value.encode("utf8")) - if c_len != 2 and _validate is True: - raise ValueError( - "Country name must be a 2 character country code" - ) - elif c_len != 2: - warnings.warn( - "Country names should be two characters, but the " - "attribute is {} characters in length.".format(c_len), - stacklevel=2, - ) - - # The appropriate ASN1 string type varies by OID and is defined across - # multiple RFCs including 2459, 3280, and 5280. In general UTF8String - # is preferred (2459), but 3280 and 5280 specify several OIDs with - # alternate types. This means when we see the sentinel value we need - # to look up whether the OID has a non-UTF8 type. If it does, set it - # to that. Otherwise, UTF8! - if _type is None: - _type = _NAMEOID_DEFAULT_TYPE.get(oid, _ASN1Type.UTF8String) - - if not isinstance(_type, _ASN1Type): - raise TypeError("_type must be from the _ASN1Type enum") - - self._oid = oid - self._value = value - self._type = _type - - @property - def oid(self) -> ObjectIdentifier: - return self._oid - - @property - def value(self) -> typing.Union[str, bytes]: - return self._value - - @property - def rfc4514_attribute_name(self) -> str: - """ - The short attribute name (for example "CN") if available, - otherwise the OID dotted string. - """ - return _NAMEOID_TO_NAME.get(self.oid, self.oid.dotted_string) - - def rfc4514_string( - self, attr_name_overrides: typing.Optional[_OidNameMap] = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - - Use short attribute name if available, otherwise fall back to OID - dotted string. - """ - attr_name = ( - attr_name_overrides.get(self.oid) if attr_name_overrides else None - ) - if attr_name is None: - attr_name = self.rfc4514_attribute_name - - return f"{attr_name}={_escape_dn_value(self.value)}" - - def __eq__(self, other: object) -> bool: - if not isinstance(other, NameAttribute): - return NotImplemented - - return self.oid == other.oid and self.value == other.value - - def __hash__(self) -> int: - return hash((self.oid, self.value)) - - def __repr__(self) -> str: - return "".format(self) - - -class RelativeDistinguishedName: - def __init__(self, attributes: typing.Iterable[NameAttribute]): - attributes = list(attributes) - if not attributes: - raise ValueError("a relative distinguished name cannot be empty") - if not all(isinstance(x, NameAttribute) for x in attributes): - raise TypeError("attributes must be an iterable of NameAttribute") - - # Keep list and frozenset to preserve attribute order where it matters - self._attributes = attributes - self._attribute_set = frozenset(attributes) - - if len(self._attribute_set) != len(attributes): - raise ValueError("duplicate attributes are not allowed") - - def get_attributes_for_oid( - self, oid: ObjectIdentifier - ) -> typing.List[NameAttribute]: - return [i for i in self if i.oid == oid] - - def rfc4514_string( - self, attr_name_overrides: typing.Optional[_OidNameMap] = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - - Within each RDN, attributes are joined by '+', although that is rarely - used in certificates. - """ - return "+".join( - attr.rfc4514_string(attr_name_overrides) - for attr in self._attributes - ) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, RelativeDistinguishedName): - return NotImplemented - - return self._attribute_set == other._attribute_set - - def __hash__(self) -> int: - return hash(self._attribute_set) - - def __iter__(self) -> typing.Iterator[NameAttribute]: - return iter(self._attributes) - - def __len__(self) -> int: - return len(self._attributes) - - def __repr__(self) -> str: - return "".format(self.rfc4514_string()) - - -class Name: - @typing.overload - def __init__(self, attributes: typing.Iterable[NameAttribute]) -> None: - ... - - @typing.overload - def __init__( - self, attributes: typing.Iterable[RelativeDistinguishedName] - ) -> None: - ... - - def __init__( - self, - attributes: typing.Iterable[ - typing.Union[NameAttribute, RelativeDistinguishedName] - ], - ) -> None: - attributes = list(attributes) - if all(isinstance(x, NameAttribute) for x in attributes): - self._attributes = [ - RelativeDistinguishedName([typing.cast(NameAttribute, x)]) - for x in attributes - ] - elif all(isinstance(x, RelativeDistinguishedName) for x in attributes): - self._attributes = typing.cast( - typing.List[RelativeDistinguishedName], attributes - ) - else: - raise TypeError( - "attributes must be a list of NameAttribute" - " or a list RelativeDistinguishedName" - ) - - @classmethod - def from_rfc4514_string( - cls, - data: str, - attr_name_overrides: typing.Optional[_NameOidMap] = None, - ) -> "Name": - return _RFC4514NameParser(data, attr_name_overrides or {}).parse() - - def rfc4514_string( - self, attr_name_overrides: typing.Optional[_OidNameMap] = None - ) -> str: - """ - Format as RFC4514 Distinguished Name string. - For example 'CN=foobar.com,O=Foo Corp,C=US' - - An X.509 name is a two-level structure: a list of sets of attributes. - Each list element is separated by ',' and within each list element, set - elements are separated by '+'. The latter is almost never used in - real world certificates. According to RFC4514 section 2.1 the - RDNSequence must be reversed when converting to string representation. - """ - return ",".join( - attr.rfc4514_string(attr_name_overrides) - for attr in reversed(self._attributes) - ) - - def get_attributes_for_oid( - self, oid: ObjectIdentifier - ) -> typing.List[NameAttribute]: - return [i for i in self if i.oid == oid] - - @property - def rdns(self) -> typing.List[RelativeDistinguishedName]: - return self._attributes - - def public_bytes(self, backend: typing.Any = None) -> bytes: - return rust_x509.encode_name_bytes(self) - - def __eq__(self, other: object) -> bool: - if not isinstance(other, Name): - return NotImplemented - - return self._attributes == other._attributes - - def __hash__(self) -> int: - # TODO: this is relatively expensive, if this looks like a bottleneck - # for you, consider optimizing! - return hash(tuple(self._attributes)) - - def __iter__(self) -> typing.Iterator[NameAttribute]: - for rdn in self._attributes: - for ava in rdn: - yield ava - - def __len__(self) -> int: - return sum(len(rdn) for rdn in self._attributes) - - def __repr__(self) -> str: - rdns = ",".join(attr.rfc4514_string() for attr in self._attributes) - return "".format(rdns) - - -class _RFC4514NameParser: - _OID_RE = re.compile(r"(0|([1-9]\d*))(\.(0|([1-9]\d*)))+") - _DESCR_RE = re.compile(r"[a-zA-Z][a-zA-Z\d-]*") - - _PAIR = r"\\([\\ #=\"\+,;<>]|[\da-zA-Z]{2})" - _PAIR_RE = re.compile(_PAIR) - _LUTF1 = r"[\x01-\x1f\x21\x24-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _SUTF1 = r"[\x01-\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _TUTF1 = r"[\x01-\x1F\x21\x23-\x2A\x2D-\x3A\x3D\x3F-\x5B\x5D-\x7F]" - _UTFMB = rf"[\x80-{chr(sys.maxunicode)}]" - _LEADCHAR = rf"{_LUTF1}|{_UTFMB}" - _STRINGCHAR = rf"{_SUTF1}|{_UTFMB}" - _TRAILCHAR = rf"{_TUTF1}|{_UTFMB}" - _STRING_RE = re.compile( - rf""" - ( - ({_LEADCHAR}|{_PAIR}) - ( - ({_STRINGCHAR}|{_PAIR})* - ({_TRAILCHAR}|{_PAIR}) - )? - )? - """, - re.VERBOSE, - ) - _HEXSTRING_RE = re.compile(r"#([\da-zA-Z]{2})+") - - def __init__(self, data: str, attr_name_overrides: _NameOidMap) -> None: - self._data = data - self._idx = 0 - - self._attr_name_overrides = attr_name_overrides - - def _has_data(self) -> bool: - return self._idx < len(self._data) - - def _peek(self) -> typing.Optional[str]: - if self._has_data(): - return self._data[self._idx] - return None - - def _read_char(self, ch: str) -> None: - if self._peek() != ch: - raise ValueError - self._idx += 1 - - def _read_re(self, pat) -> str: - match = pat.match(self._data, pos=self._idx) - if match is None: - raise ValueError - val = match.group() - self._idx += len(val) - return val - - def parse(self) -> Name: - """ - Parses the `data` string and converts it to a Name. - - According to RFC4514 section 2.1 the RDNSequence must be - reversed when converting to string representation. So, when - we parse it, we need to reverse again to get the RDNs on the - correct order. - """ - rdns = [self._parse_rdn()] - - while self._has_data(): - self._read_char(",") - rdns.append(self._parse_rdn()) - - return Name(reversed(rdns)) - - def _parse_rdn(self) -> RelativeDistinguishedName: - nas = [self._parse_na()] - while self._peek() == "+": - self._read_char("+") - nas.append(self._parse_na()) - - return RelativeDistinguishedName(nas) - - def _parse_na(self) -> NameAttribute: - try: - oid_value = self._read_re(self._OID_RE) - except ValueError: - name = self._read_re(self._DESCR_RE) - oid = self._attr_name_overrides.get( - name, _NAME_TO_NAMEOID.get(name) - ) - if oid is None: - raise ValueError - else: - oid = ObjectIdentifier(oid_value) - - self._read_char("=") - if self._peek() == "#": - value = self._read_re(self._HEXSTRING_RE) - value = binascii.unhexlify(value[1:]).decode() - else: - raw_value = self._read_re(self._STRING_RE) - value = _unescape_dn_value(raw_value) - - return NameAttribute(oid, value) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py deleted file mode 100644 index 70aa3b361..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/ocsp.py +++ /dev/null @@ -1,621 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - - -import abc -import datetime -import typing - -from cryptography import utils, x509 -from cryptography.hazmat.bindings._rust import ocsp -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric.types import ( - CERTIFICATE_PRIVATE_KEY_TYPES, -) -from cryptography.x509.base import ( - _EARLIEST_UTC_TIME, - _convert_to_naive_utc_time, - _reject_duplicate_extension, -) - - -class OCSPResponderEncoding(utils.Enum): - HASH = "By Hash" - NAME = "By Name" - - -class OCSPResponseStatus(utils.Enum): - SUCCESSFUL = 0 - MALFORMED_REQUEST = 1 - INTERNAL_ERROR = 2 - TRY_LATER = 3 - SIG_REQUIRED = 5 - UNAUTHORIZED = 6 - - -_ALLOWED_HASHES = ( - hashes.SHA1, - hashes.SHA224, - hashes.SHA256, - hashes.SHA384, - hashes.SHA512, -) - - -def _verify_algorithm(algorithm: hashes.HashAlgorithm) -> None: - if not isinstance(algorithm, _ALLOWED_HASHES): - raise ValueError( - "Algorithm must be SHA1, SHA224, SHA256, SHA384, or SHA512" - ) - - -class OCSPCertStatus(utils.Enum): - GOOD = 0 - REVOKED = 1 - UNKNOWN = 2 - - -class _SingleResponse: - def __init__( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: typing.Optional[datetime.datetime], - revocation_time: typing.Optional[datetime.datetime], - revocation_reason: typing.Optional[x509.ReasonFlags], - ): - if not isinstance(cert, x509.Certificate) or not isinstance( - issuer, x509.Certificate - ): - raise TypeError("cert and issuer must be a Certificate") - - _verify_algorithm(algorithm) - if not isinstance(this_update, datetime.datetime): - raise TypeError("this_update must be a datetime object") - if next_update is not None and not isinstance( - next_update, datetime.datetime - ): - raise TypeError("next_update must be a datetime object or None") - - self._cert = cert - self._issuer = issuer - self._algorithm = algorithm - self._this_update = this_update - self._next_update = next_update - - if not isinstance(cert_status, OCSPCertStatus): - raise TypeError( - "cert_status must be an item from the OCSPCertStatus enum" - ) - if cert_status is not OCSPCertStatus.REVOKED: - if revocation_time is not None: - raise ValueError( - "revocation_time can only be provided if the certificate " - "is revoked" - ) - if revocation_reason is not None: - raise ValueError( - "revocation_reason can only be provided if the certificate" - " is revoked" - ) - else: - if not isinstance(revocation_time, datetime.datetime): - raise TypeError("revocation_time must be a datetime object") - - revocation_time = _convert_to_naive_utc_time(revocation_time) - if revocation_time < _EARLIEST_UTC_TIME: - raise ValueError( - "The revocation_time must be on or after" - " 1950 January 1." - ) - - if revocation_reason is not None and not isinstance( - revocation_reason, x509.ReasonFlags - ): - raise TypeError( - "revocation_reason must be an item from the ReasonFlags " - "enum or None" - ) - - self._cert_status = cert_status - self._revocation_time = revocation_time - self._revocation_reason = revocation_reason - - -class OCSPRequest(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def issuer_key_hash(self) -> bytes: - """ - The hash of the issuer public key - """ - - @property - @abc.abstractmethod - def issuer_name_hash(self) -> bytes: - """ - The hash of the issuer name - """ - - @property - @abc.abstractmethod - def hash_algorithm(self) -> hashes.HashAlgorithm: - """ - The hash algorithm used in the issuer name and key hashes - """ - - @property - @abc.abstractmethod - def serial_number(self) -> int: - """ - The serial number of the cert whose status is being checked - """ - - @abc.abstractmethod - def public_bytes(self, encoding: serialization.Encoding) -> bytes: - """ - Serializes the request to DER - """ - - @property - @abc.abstractmethod - def extensions(self) -> x509.Extensions: - """ - The list of request extensions. Not single request extensions. - """ - - -class OCSPSingleResponse(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def certificate_status(self) -> OCSPCertStatus: - """ - The status of the certificate (an element from the OCSPCertStatus enum) - """ - - @property - @abc.abstractmethod - def revocation_time(self) -> typing.Optional[datetime.datetime]: - """ - The date of when the certificate was revoked or None if not - revoked. - """ - - @property - @abc.abstractmethod - def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]: - """ - The reason the certificate was revoked or None if not specified or - not revoked. - """ - - @property - @abc.abstractmethod - def this_update(self) -> datetime.datetime: - """ - The most recent time at which the status being indicated is known by - the responder to have been correct - """ - - @property - @abc.abstractmethod - def next_update(self) -> typing.Optional[datetime.datetime]: - """ - The time when newer information will be available - """ - - @property - @abc.abstractmethod - def issuer_key_hash(self) -> bytes: - """ - The hash of the issuer public key - """ - - @property - @abc.abstractmethod - def issuer_name_hash(self) -> bytes: - """ - The hash of the issuer name - """ - - @property - @abc.abstractmethod - def hash_algorithm(self) -> hashes.HashAlgorithm: - """ - The hash algorithm used in the issuer name and key hashes - """ - - @property - @abc.abstractmethod - def serial_number(self) -> int: - """ - The serial number of the cert whose status is being checked - """ - - -class OCSPResponse(metaclass=abc.ABCMeta): - @property - @abc.abstractmethod - def responses(self) -> typing.Iterator[OCSPSingleResponse]: - """ - An iterator over the individual SINGLERESP structures in the - response - """ - - @property - @abc.abstractmethod - def response_status(self) -> OCSPResponseStatus: - """ - The status of the response. This is a value from the OCSPResponseStatus - enumeration - """ - - @property - @abc.abstractmethod - def signature_algorithm_oid(self) -> x509.ObjectIdentifier: - """ - The ObjectIdentifier of the signature algorithm - """ - - @property - @abc.abstractmethod - def signature_hash_algorithm( - self, - ) -> typing.Optional[hashes.HashAlgorithm]: - """ - Returns a HashAlgorithm corresponding to the type of the digest signed - """ - - @property - @abc.abstractmethod - def signature(self) -> bytes: - """ - The signature bytes - """ - - @property - @abc.abstractmethod - def tbs_response_bytes(self) -> bytes: - """ - The tbsResponseData bytes - """ - - @property - @abc.abstractmethod - def certificates(self) -> typing.List[x509.Certificate]: - """ - A list of certificates used to help build a chain to verify the OCSP - response. This situation occurs when the OCSP responder uses a delegate - certificate. - """ - - @property - @abc.abstractmethod - def responder_key_hash(self) -> typing.Optional[bytes]: - """ - The responder's key hash or None - """ - - @property - @abc.abstractmethod - def responder_name(self) -> typing.Optional[x509.Name]: - """ - The responder's Name or None - """ - - @property - @abc.abstractmethod - def produced_at(self) -> datetime.datetime: - """ - The time the response was produced - """ - - @property - @abc.abstractmethod - def certificate_status(self) -> OCSPCertStatus: - """ - The status of the certificate (an element from the OCSPCertStatus enum) - """ - - @property - @abc.abstractmethod - def revocation_time(self) -> typing.Optional[datetime.datetime]: - """ - The date of when the certificate was revoked or None if not - revoked. - """ - - @property - @abc.abstractmethod - def revocation_reason(self) -> typing.Optional[x509.ReasonFlags]: - """ - The reason the certificate was revoked or None if not specified or - not revoked. - """ - - @property - @abc.abstractmethod - def this_update(self) -> datetime.datetime: - """ - The most recent time at which the status being indicated is known by - the responder to have been correct - """ - - @property - @abc.abstractmethod - def next_update(self) -> typing.Optional[datetime.datetime]: - """ - The time when newer information will be available - """ - - @property - @abc.abstractmethod - def issuer_key_hash(self) -> bytes: - """ - The hash of the issuer public key - """ - - @property - @abc.abstractmethod - def issuer_name_hash(self) -> bytes: - """ - The hash of the issuer name - """ - - @property - @abc.abstractmethod - def hash_algorithm(self) -> hashes.HashAlgorithm: - """ - The hash algorithm used in the issuer name and key hashes - """ - - @property - @abc.abstractmethod - def serial_number(self) -> int: - """ - The serial number of the cert whose status is being checked - """ - - @property - @abc.abstractmethod - def extensions(self) -> x509.Extensions: - """ - The list of response extensions. Not single response extensions. - """ - - @property - @abc.abstractmethod - def single_extensions(self) -> x509.Extensions: - """ - The list of single response extensions. Not response extensions. - """ - - @abc.abstractmethod - def public_bytes(self, encoding: serialization.Encoding) -> bytes: - """ - Serializes the response to DER - """ - - -class OCSPRequestBuilder: - def __init__( - self, - request: typing.Optional[ - typing.Tuple[ - x509.Certificate, x509.Certificate, hashes.HashAlgorithm - ] - ] = None, - request_hash: typing.Optional[ - typing.Tuple[bytes, bytes, int, hashes.HashAlgorithm] - ] = None, - extensions: typing.List[x509.Extension[x509.ExtensionType]] = [], - ) -> None: - self._request = request - self._request_hash = request_hash - self._extensions = extensions - - def add_certificate( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - ) -> "OCSPRequestBuilder": - if self._request is not None or self._request_hash is not None: - raise ValueError("Only one certificate can be added to a request") - - _verify_algorithm(algorithm) - if not isinstance(cert, x509.Certificate) or not isinstance( - issuer, x509.Certificate - ): - raise TypeError("cert and issuer must be a Certificate") - - return OCSPRequestBuilder( - (cert, issuer, algorithm), self._request_hash, self._extensions - ) - - def add_certificate_by_hash( - self, - issuer_name_hash: bytes, - issuer_key_hash: bytes, - serial_number: int, - algorithm: hashes.HashAlgorithm, - ) -> "OCSPRequestBuilder": - if self._request is not None or self._request_hash is not None: - raise ValueError("Only one certificate can be added to a request") - - if not isinstance(serial_number, int): - raise TypeError("serial_number must be an integer") - - _verify_algorithm(algorithm) - utils._check_bytes("issuer_name_hash", issuer_name_hash) - utils._check_bytes("issuer_key_hash", issuer_key_hash) - if algorithm.digest_size != len( - issuer_name_hash - ) or algorithm.digest_size != len(issuer_key_hash): - raise ValueError( - "issuer_name_hash and issuer_key_hash must be the same length " - "as the digest size of the algorithm" - ) - - return OCSPRequestBuilder( - self._request, - (issuer_name_hash, issuer_key_hash, serial_number, algorithm), - self._extensions, - ) - - def add_extension( - self, extval: x509.ExtensionType, critical: bool - ) -> "OCSPRequestBuilder": - if not isinstance(extval, x509.ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = x509.Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return OCSPRequestBuilder( - self._request, self._request_hash, self._extensions + [extension] - ) - - def build(self) -> OCSPRequest: - if self._request is None and self._request_hash is None: - raise ValueError("You must add a certificate before building") - - return ocsp.create_ocsp_request(self) - - -class OCSPResponseBuilder: - def __init__( - self, - response: typing.Optional[_SingleResponse] = None, - responder_id: typing.Optional[ - typing.Tuple[x509.Certificate, OCSPResponderEncoding] - ] = None, - certs: typing.Optional[typing.List[x509.Certificate]] = None, - extensions: typing.List[x509.Extension[x509.ExtensionType]] = [], - ): - self._response = response - self._responder_id = responder_id - self._certs = certs - self._extensions = extensions - - def add_response( - self, - cert: x509.Certificate, - issuer: x509.Certificate, - algorithm: hashes.HashAlgorithm, - cert_status: OCSPCertStatus, - this_update: datetime.datetime, - next_update: typing.Optional[datetime.datetime], - revocation_time: typing.Optional[datetime.datetime], - revocation_reason: typing.Optional[x509.ReasonFlags], - ) -> "OCSPResponseBuilder": - if self._response is not None: - raise ValueError("Only one response per OCSPResponse.") - - singleresp = _SingleResponse( - cert, - issuer, - algorithm, - cert_status, - this_update, - next_update, - revocation_time, - revocation_reason, - ) - return OCSPResponseBuilder( - singleresp, - self._responder_id, - self._certs, - self._extensions, - ) - - def responder_id( - self, encoding: OCSPResponderEncoding, responder_cert: x509.Certificate - ) -> "OCSPResponseBuilder": - if self._responder_id is not None: - raise ValueError("responder_id can only be set once") - if not isinstance(responder_cert, x509.Certificate): - raise TypeError("responder_cert must be a Certificate") - if not isinstance(encoding, OCSPResponderEncoding): - raise TypeError( - "encoding must be an element from OCSPResponderEncoding" - ) - - return OCSPResponseBuilder( - self._response, - (responder_cert, encoding), - self._certs, - self._extensions, - ) - - def certificates( - self, certs: typing.Iterable[x509.Certificate] - ) -> "OCSPResponseBuilder": - if self._certs is not None: - raise ValueError("certificates may only be set once") - certs = list(certs) - if len(certs) == 0: - raise ValueError("certs must not be an empty list") - if not all(isinstance(x, x509.Certificate) for x in certs): - raise TypeError("certs must be a list of Certificates") - return OCSPResponseBuilder( - self._response, - self._responder_id, - certs, - self._extensions, - ) - - def add_extension( - self, extval: x509.ExtensionType, critical: bool - ) -> "OCSPResponseBuilder": - if not isinstance(extval, x509.ExtensionType): - raise TypeError("extension must be an ExtensionType") - - extension = x509.Extension(extval.oid, critical, extval) - _reject_duplicate_extension(extension, self._extensions) - - return OCSPResponseBuilder( - self._response, - self._responder_id, - self._certs, - self._extensions + [extension], - ) - - def sign( - self, - private_key: CERTIFICATE_PRIVATE_KEY_TYPES, - algorithm: typing.Optional[hashes.HashAlgorithm], - ) -> OCSPResponse: - if self._response is None: - raise ValueError("You must add a response before signing") - if self._responder_id is None: - raise ValueError("You must add a responder_id before signing") - - return ocsp.create_ocsp_response( - OCSPResponseStatus.SUCCESSFUL, self, private_key, algorithm - ) - - @classmethod - def build_unsuccessful( - cls, response_status: OCSPResponseStatus - ) -> OCSPResponse: - if not isinstance(response_status, OCSPResponseStatus): - raise TypeError( - "response_status must be an item from OCSPResponseStatus" - ) - if response_status is OCSPResponseStatus.SUCCESSFUL: - raise ValueError("response_status cannot be SUCCESSFUL") - - return ocsp.create_ocsp_response(response_status, None, None, None) - - -def load_der_ocsp_request(data: bytes) -> OCSPRequest: - return ocsp.load_der_ocsp_request(data) - - -def load_der_ocsp_response(data: bytes) -> OCSPResponse: - return ocsp.load_der_ocsp_response(data) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py b/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py deleted file mode 100644 index 0d91a5469..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/cryptography/x509/oid.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -from cryptography.hazmat._oid import ( - AttributeOID, - AuthorityInformationAccessOID, - CertificatePoliciesOID, - CRLEntryExtensionOID, - ExtendedKeyUsageOID, - ExtensionOID, - NameOID, - ObjectIdentifier, - OCSPExtensionOID, - SignatureAlgorithmOID, - SubjectInformationAccessOID, -) - -__all__ = [ - "AttributeOID", - "AuthorityInformationAccessOID", - "CRLEntryExtensionOID", - "CertificatePoliciesOID", - "ExtendedKeyUsageOID", - "ExtensionOID", - "NameOID", - "OCSPExtensionOID", - "ObjectIdentifier", - "SignatureAlgorithmOID", - "SubjectInformationAccessOID", -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE deleted file mode 100644 index 474479a2c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -"python-ecdsa" Copyright (c) 2010 Brian Warner - -Portions written in 2005 by Peter Pearson and placed in the public domain. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA deleted file mode 100644 index fa1c5feef..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/METADATA +++ /dev/null @@ -1,675 +0,0 @@ -Metadata-Version: 2.1 -Name: ecdsa -Version: 0.18.0 -Summary: ECDSA cryptographic signature library (pure python) -Home-page: http://github.com/tlsfuzzer/python-ecdsa -Author: Brian Warner -Author-email: warner@lothar.com -License: MIT -Platform: UNKNOWN -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.6 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.* -Description-Content-Type: text/markdown -License-File: LICENSE -Requires-Dist: six (>=1.9.0) -Provides-Extra: gmpy -Requires-Dist: gmpy ; extra == 'gmpy' -Provides-Extra: gmpy2 -Requires-Dist: gmpy2 ; extra == 'gmpy2' - -# Pure-Python ECDSA and ECDH - -[![Build Status](https://github.com/tlsfuzzer/python-ecdsa/workflows/GitHub%20CI/badge.svg?branch=master)](https://github.com/tlsfuzzer/python-ecdsa/actions?query=workflow%3A%22GitHub+CI%22+branch%3Amaster) -[![Documentation Status](https://readthedocs.org/projects/ecdsa/badge/?version=latest)](https://ecdsa.readthedocs.io/en/latest/?badge=latest) -[![Coverage Status](https://coveralls.io/repos/github/tlsfuzzer/python-ecdsa/badge.svg?branch=master)](https://coveralls.io/github/tlsfuzzer/python-ecdsa?branch=master) -![condition coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/tomato42/9b6ca1f3410207fbeca785a178781651/raw/python-ecdsa-condition-coverage.json) -[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/tlsfuzzer/python-ecdsa.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tlsfuzzer/python-ecdsa/context:python) -[![Total alerts](https://img.shields.io/lgtm/alerts/g/tlsfuzzer/python-ecdsa.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/tlsfuzzer/python-ecdsa/alerts/) -[![Latest Version](https://img.shields.io/pypi/v/ecdsa.svg?style=flat)](https://pypi.python.org/pypi/ecdsa/) -![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat) - - -This is an easy-to-use implementation of ECC (Elliptic Curve Cryptography) -with support for ECDSA (Elliptic Curve Digital Signature Algorithm), -EdDSA (Edwards-curve Digital Signature Algorithm) and ECDH -(Elliptic Curve Diffie-Hellman), implemented purely in Python, released under -the MIT license. With this library, you can quickly create key pairs (signing -key and verifying key), sign messages, and verify the signatures. You can -also agree on a shared secret key based on exchanged public keys. -The keys and signatures are very short, making them easy to handle and -incorporate into other protocols. - -**NOTE: This library should not be used in production settings, see [Security](#Security) for more details.** - -## Features - -This library provides key generation, signing, verifying, and shared secret -derivation for five -popular NIST "Suite B" GF(p) (_prime field_) curves, with key lengths of 192, -224, 256, 384, and 521 bits. The "short names" for these curves, as known by -the OpenSSL tool (`openssl ecparam -list_curves`), are: `prime192v1`, -`secp224r1`, `prime256v1`, `secp384r1`, and `secp521r1`. It includes the -256-bit curve `secp256k1` used by Bitcoin. There is also support for the -regular (non-twisted) variants of Brainpool curves from 160 to 512 bits. The -"short names" of those curves are: `brainpoolP160r1`, `brainpoolP192r1`, -`brainpoolP224r1`, `brainpoolP256r1`, `brainpoolP320r1`, `brainpoolP384r1`, -`brainpoolP512r1`. Few of the small curves from SEC standard are also -included (mainly to speed-up testing of the library), those are: -`secp112r1`, `secp112r2`, `secp128r1`, and `secp160r1`. -Key generation, siging and verifying is also supported for Ed25519 and -Ed448 curves. -No other curves are included, but it is not too hard to add support for more -curves over prime fields. - -## Dependencies - -This library uses only Python and the 'six' package. It is compatible with -Python 2.6, 2.7, and 3.3+. It also supports execution on alternative -implementations like pypy and pypy3. - -If `gmpy2` or `gmpy` is installed, they will be used for faster arithmetic. -Either of them can be installed after this library is installed, -`python-ecdsa` will detect their presence on start-up and use them -automatically. -You should prefer `gmpy2` on Python3 for optimal performance. - -To run the OpenSSL compatibility tests, the 'openssl' tool must be in your -`PATH`. This release has been tested successfully against OpenSSL 0.9.8o, -1.0.0a, 1.0.2f, 1.1.1d and 3.0.1 (among others). - - -## Installation - -This library is available on PyPI, it's recommended to install it using `pip`: - -``` -pip install ecdsa -``` - -In case higher performance is wanted and using native code is not a problem, -it's possible to specify installation together with `gmpy2`: - -``` -pip install ecdsa[gmpy2] -``` - -or (slower, legacy option): -``` -pip install ecdsa[gmpy] -``` - -## Speed - -The following table shows how long this library takes to generate key pairs -(`keygen`), to sign data (`sign`), to verify those signatures (`verify`), -to derive a shared secret (`ecdh`), and -to verify the signatures with no key-specific precomputation (`no PC verify`). -All those values are in seconds. -For convenience, the inverses of those values are also provided: -how many keys per second can be generated (`keygen/s`), how many signatures -can be made per second (`sign/s`), how many signatures can be verified -per second (`verify/s`), how many shared secrets can be derived per second -(`ecdh/s`), and how many signatures with no key specific -precomputation can be verified per second (`no PC verify/s`). The size of raw -signature (generally the smallest -the way a signature can be encoded) is also provided in the `siglen` column. -Use `tox -e speed` to generate this table on your own computer. -On an Intel Core i7 4790K @ 4.0GHz I'm getting the following performance: - -``` - siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s - NIST192p: 48 0.00032s 3134.06 0.00033s 2985.53 0.00063s 1598.36 0.00129s 774.43 - NIST224p: 56 0.00040s 2469.24 0.00042s 2367.88 0.00081s 1233.41 0.00170s 586.66 - NIST256p: 64 0.00051s 1952.73 0.00054s 1867.80 0.00098s 1021.86 0.00212s 471.27 - NIST384p: 96 0.00107s 935.92 0.00111s 904.23 0.00203s 491.77 0.00446s 224.00 - NIST521p: 132 0.00210s 475.52 0.00215s 464.16 0.00398s 251.28 0.00874s 114.39 - SECP256k1: 64 0.00052s 1921.54 0.00054s 1847.49 0.00105s 948.68 0.00210s 477.01 - BRAINPOOLP160r1: 40 0.00025s 4003.88 0.00026s 3845.12 0.00053s 1893.93 0.00105s 949.92 - BRAINPOOLP192r1: 48 0.00033s 3043.97 0.00034s 2975.98 0.00063s 1581.50 0.00135s 742.29 - BRAINPOOLP224r1: 56 0.00041s 2436.44 0.00043s 2315.51 0.00078s 1278.49 0.00180s 556.16 - BRAINPOOLP256r1: 64 0.00053s 1892.49 0.00054s 1846.24 0.00114s 875.64 0.00229s 437.25 - BRAINPOOLP320r1: 80 0.00073s 1361.26 0.00076s 1309.25 0.00143s 699.29 0.00322s 310.49 - BRAINPOOLP384r1: 96 0.00107s 931.29 0.00111s 901.80 0.00230s 434.19 0.00476s 210.20 - BRAINPOOLP512r1: 128 0.00207s 483.41 0.00212s 471.42 0.00425s 235.43 0.00912s 109.61 - SECP112r1: 28 0.00015s 6672.53 0.00016s 6440.34 0.00031s 3265.41 0.00056s 1774.20 - SECP112r2: 28 0.00015s 6697.11 0.00015s 6479.98 0.00028s 3524.72 0.00058s 1716.16 - SECP128r1: 32 0.00018s 5497.65 0.00019s 5272.89 0.00036s 2747.39 0.00072s 1396.16 - SECP160r1: 42 0.00025s 3949.32 0.00026s 3894.45 0.00046s 2153.85 0.00102s 985.07 - Ed25519: 64 0.00076s 1324.48 0.00042s 2405.01 0.00109s 918.05 0.00344s 290.50 - Ed448: 114 0.00176s 569.53 0.00115s 870.94 0.00282s 355.04 0.01024s 97.69 - - ecdh ecdh/s - NIST192p: 0.00104s 964.89 - NIST224p: 0.00134s 748.63 - NIST256p: 0.00170s 587.08 - NIST384p: 0.00352s 283.90 - NIST521p: 0.00717s 139.51 - SECP256k1: 0.00154s 648.40 - BRAINPOOLP160r1: 0.00082s 1220.70 - BRAINPOOLP192r1: 0.00105s 956.75 - BRAINPOOLP224r1: 0.00136s 734.52 - BRAINPOOLP256r1: 0.00178s 563.32 - BRAINPOOLP320r1: 0.00252s 397.23 - BRAINPOOLP384r1: 0.00376s 266.27 - BRAINPOOLP512r1: 0.00733s 136.35 - SECP112r1: 0.00046s 2180.40 - SECP112r2: 0.00045s 2229.14 - SECP128r1: 0.00054s 1868.15 - SECP160r1: 0.00080s 1243.98 -``` - -To test performance with `gmpy2` loaded, use `tox -e speedgmpy2`. -On the same machine I'm getting the following performance with `gmpy2`: -``` - siglen keygen keygen/s sign sign/s verify verify/s no PC verify no PC verify/s - NIST192p: 48 0.00017s 5933.40 0.00017s 5751.70 0.00032s 3125.28 0.00067s 1502.41 - NIST224p: 56 0.00021s 4782.87 0.00022s 4610.05 0.00040s 2487.04 0.00089s 1126.90 - NIST256p: 64 0.00023s 4263.98 0.00024s 4125.16 0.00045s 2200.88 0.00098s 1016.82 - NIST384p: 96 0.00041s 2449.54 0.00042s 2399.96 0.00083s 1210.57 0.00172s 581.43 - NIST521p: 132 0.00071s 1416.07 0.00072s 1389.81 0.00144s 692.93 0.00312s 320.40 - SECP256k1: 64 0.00024s 4245.05 0.00024s 4122.09 0.00045s 2206.40 0.00094s 1068.32 - BRAINPOOLP160r1: 40 0.00014s 6939.17 0.00015s 6681.55 0.00029s 3452.43 0.00057s 1769.81 - BRAINPOOLP192r1: 48 0.00017s 5920.05 0.00017s 5774.36 0.00034s 2979.00 0.00069s 1453.19 - BRAINPOOLP224r1: 56 0.00021s 4732.12 0.00022s 4622.65 0.00041s 2422.47 0.00087s 1149.87 - BRAINPOOLP256r1: 64 0.00024s 4233.02 0.00024s 4115.20 0.00047s 2143.27 0.00098s 1015.60 - BRAINPOOLP320r1: 80 0.00032s 3162.38 0.00032s 3077.62 0.00063s 1598.83 0.00136s 737.34 - BRAINPOOLP384r1: 96 0.00041s 2436.88 0.00042s 2395.62 0.00083s 1202.68 0.00178s 562.85 - BRAINPOOLP512r1: 128 0.00063s 1587.60 0.00064s 1558.83 0.00125s 799.96 0.00281s 355.83 - SECP112r1: 28 0.00009s 11118.66 0.00009s 10775.48 0.00018s 5456.00 0.00033s 3020.83 - SECP112r2: 28 0.00009s 11322.97 0.00009s 10857.71 0.00017s 5748.77 0.00032s 3094.28 - SECP128r1: 32 0.00010s 10078.39 0.00010s 9665.27 0.00019s 5200.58 0.00036s 2760.88 - SECP160r1: 42 0.00015s 6875.51 0.00015s 6647.35 0.00029s 3422.41 0.00057s 1768.35 - Ed25519: 64 0.00030s 3322.56 0.00018s 5568.63 0.00046s 2165.35 0.00153s 654.02 - Ed448: 114 0.00060s 1680.53 0.00039s 2567.40 0.00096s 1036.67 0.00350s 285.62 - - ecdh ecdh/s - NIST192p: 0.00050s 1985.70 - NIST224p: 0.00066s 1524.16 - NIST256p: 0.00071s 1413.07 - NIST384p: 0.00127s 788.89 - NIST521p: 0.00230s 434.85 - SECP256k1: 0.00071s 1409.95 - BRAINPOOLP160r1: 0.00042s 2374.65 - BRAINPOOLP192r1: 0.00051s 1960.01 - BRAINPOOLP224r1: 0.00066s 1518.37 - BRAINPOOLP256r1: 0.00071s 1399.90 - BRAINPOOLP320r1: 0.00100s 997.21 - BRAINPOOLP384r1: 0.00129s 777.51 - BRAINPOOLP512r1: 0.00210s 475.99 - SECP112r1: 0.00022s 4457.70 - SECP112r2: 0.00024s 4252.33 - SECP128r1: 0.00028s 3589.31 - SECP160r1: 0.00043s 2305.02 -``` - -(there's also `gmpy` version, execute it using `tox -e speedgmpy`) - -For comparison, a highly optimised implementation (including curve-specific -assembly for some curves), like the one in OpenSSL 1.1.1d, provides the -following performance numbers on the same machine. -Run `openssl speed ecdsa` and `openssl speed ecdh` to reproduce it: -``` - sign verify sign/s verify/s - 192 bits ecdsa (nistp192) 0.0002s 0.0002s 4785.6 5380.7 - 224 bits ecdsa (nistp224) 0.0000s 0.0001s 22475.6 9822.0 - 256 bits ecdsa (nistp256) 0.0000s 0.0001s 45069.6 14166.6 - 384 bits ecdsa (nistp384) 0.0008s 0.0006s 1265.6 1648.1 - 521 bits ecdsa (nistp521) 0.0003s 0.0005s 3753.1 1819.5 - 256 bits ecdsa (brainpoolP256r1) 0.0003s 0.0003s 2983.5 3333.2 - 384 bits ecdsa (brainpoolP384r1) 0.0008s 0.0007s 1258.8 1528.1 - 512 bits ecdsa (brainpoolP512r1) 0.0015s 0.0012s 675.1 860.1 - - sign verify sign/s verify/s - 253 bits EdDSA (Ed25519) 0.0000s 0.0001s 28217.9 10897.7 - 456 bits EdDSA (Ed448) 0.0003s 0.0005s 3926.5 2147.7 - - op op/s - 192 bits ecdh (nistp192) 0.0002s 4853.4 - 224 bits ecdh (nistp224) 0.0001s 15252.1 - 256 bits ecdh (nistp256) 0.0001s 18436.3 - 384 bits ecdh (nistp384) 0.0008s 1292.7 - 521 bits ecdh (nistp521) 0.0003s 2884.7 - 256 bits ecdh (brainpoolP256r1) 0.0003s 3066.5 - 384 bits ecdh (brainpoolP384r1) 0.0008s 1298.0 - 512 bits ecdh (brainpoolP512r1) 0.0014s 694.8 -``` - -Keys and signature can be serialized in different ways (see Usage, below). -For a NIST192p key, the three basic representations require strings of the -following lengths (in bytes): - - to_string: signkey= 24, verifykey= 48, signature=48 - compressed: signkey=n/a, verifykey= 25, signature=n/a - DER: signkey=106, verifykey= 80, signature=55 - PEM: signkey=278, verifykey=162, (no support for PEM signatures) - -## History - -In 2006, Peter Pearson announced his pure-python implementation of ECDSA in a -[message to sci.crypt][1], available from his [download site][2]. In 2010, -Brian Warner wrote a wrapper around this code, to make it a bit easier and -safer to use. In 2020, Hubert Kario included an implementation of elliptic -curve cryptography that uses Jacobian coordinates internally, improving -performance about 20-fold. You are looking at the README for this wrapper. - -[1]: http://www.derkeiler.com/Newsgroups/sci.crypt/2006-01/msg00651.html -[2]: http://webpages.charter.net/curryfans/peter/downloads.html - -## Testing - -To run the full test suite, do this: - - tox -e coverage - -On an Intel Core i7 4790K @ 4.0GHz, the tests take about 18 seconds to execute. -The test suite uses -[`hypothesis`](https://github.com/HypothesisWorks/hypothesis) so there is some -inherent variability in the test suite execution time. - -One part of `test_pyecdsa.py` and `test_ecdh.py` checks compatibility with -OpenSSL, by running the "openssl" CLI tool, make sure it's in your `PATH` if -you want to test compatibility with it (if OpenSSL is missing, too old, or -doesn't support all the curves supported in upstream releases you will see -skipped tests in the above `coverage` run). - -## Security - -This library was not designed with security in mind. If you are processing -data that needs to be protected we suggest you use a quality wrapper around -OpenSSL. [pyca/cryptography](https://cryptography.io) is one example of such -a wrapper. The primary use-case of this library is as a portable library for -interoperability testing and as a teaching tool. - -**This library does not protect against side-channel attacks.** - -Do not allow attackers to measure how long it takes you to generate a key pair -or sign a message. Do not allow attackers to run code on the same physical -machine when key pair generation or signing is taking place (this includes -virtual machines). Do not allow attackers to measure how much power your -computer uses while generating the key pair or signing a message. Do not allow -attackers to measure RF interference coming from your computer while generating -a key pair or signing a message. Note: just loading the private key will cause -key pair generation. Other operations or attack vectors may also be -vulnerable to attacks. **For a sophisticated attacker observing just one -operation with a private key will be sufficient to completely -reconstruct the private key**. - -Please also note that any Pure-python cryptographic library will be vulnerable -to the same side-channel attacks. This is because Python does not provide -side-channel secure primitives (with the exception of -[`hmac.compare_digest()`][3]), making side-channel secure programming -impossible. - -This library depends upon a strong source of random numbers. Do not use it on -a system where `os.urandom()` does not provide cryptographically secure -random numbers. - -[3]: https://docs.python.org/3/library/hmac.html#hmac.compare_digest - -## Usage - -You start by creating a `SigningKey`. You can use this to sign data, by passing -in data as a byte string and getting back the signature (also a byte string). -You can also ask a `SigningKey` to give you the corresponding `VerifyingKey`. -The `VerifyingKey` can be used to verify a signature, by passing it both the -data string and the signature byte string: it either returns True or raises -`BadSignatureError`. - -```python -from ecdsa import SigningKey -sk = SigningKey.generate() # uses NIST192p -vk = sk.verifying_key -signature = sk.sign(b"message") -assert vk.verify(signature, b"message") -``` - -Each `SigningKey`/`VerifyingKey` is associated with a specific curve, like -NIST192p (the default one). Longer curves are more secure, but take longer to -use, and result in longer keys and signatures. - -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -signature = sk.sign(b"message") -assert vk.verify(signature, b"message") -``` - -The `SigningKey` can be serialized into several different formats: the shortest -is to call `s=sk.to_string()`, and then re-create it with -`SigningKey.from_string(s, curve)` . This short form does not record the -curve, so you must be sure to pass to `from_string()` the same curve you used -for the original key. The short form of a NIST192p-based signing key is just 24 -bytes long. If a point encoding is invalid or it does not lie on the specified -curve, `from_string()` will raise `MalformedPointError`. - -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -sk_string = sk.to_string() -sk2 = SigningKey.from_string(sk_string, curve=NIST384p) -print(sk_string.hex()) -print(sk2.to_string().hex()) -``` - -Note: while the methods are called `to_string()` the type they return is -actually `bytes`, the "string" part is leftover from Python 2. - -`sk.to_pem()` and `sk.to_der()` will serialize the signing key into the same -formats that OpenSSL uses. The PEM file looks like the familiar ASCII-armored -`"-----BEGIN EC PRIVATE KEY-----"` base64-encoded format, and the DER format -is a shorter binary form of the same data. -`SigningKey.from_pem()/.from_der()` will undo this serialization. These -formats include the curve name, so you do not need to pass in a curve -identifier to the deserializer. In case the file is malformed `from_der()` -and `from_pem()` will raise `UnexpectedDER` or` MalformedPointError`. - -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -sk_pem = sk.to_pem() -sk2 = SigningKey.from_pem(sk_pem) -# sk and sk2 are the same key -``` - -Likewise, the `VerifyingKey` can be serialized in the same way: -`vk.to_string()/VerifyingKey.from_string()`, `to_pem()/from_pem()`, and -`to_der()/from_der()`. The same `curve=` argument is needed for -`VerifyingKey.from_string()`. - -```python -from ecdsa import SigningKey, VerifyingKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -vk_string = vk.to_string() -vk2 = VerifyingKey.from_string(vk_string, curve=NIST384p) -# vk and vk2 are the same key - -from ecdsa import SigningKey, VerifyingKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -vk_pem = vk.to_pem() -vk2 = VerifyingKey.from_pem(vk_pem) -# vk and vk2 are the same key -``` - -There are a couple of different ways to compute a signature. Fundamentally, -ECDSA takes a number that represents the data being signed, and returns a -pair of numbers that represent the signature. The `hashfunc=` argument to -`sk.sign()` and `vk.verify()` is used to turn an arbitrary string into a -fixed-length digest, which is then turned into a number that ECDSA can sign, -and both sign and verify must use the same approach. The default value is -`hashlib.sha1`, but if you use NIST256p or a longer curve, you can use -`hashlib.sha256` instead. - -There are also multiple ways to represent a signature. The default -`sk.sign()` and `vk.verify()` methods present it as a short string, for -simplicity and minimal overhead. To use a different scheme, use the -`sk.sign(sigencode=)` and `vk.verify(sigdecode=)` arguments. There are helper -functions in the `ecdsa.util` module that can be useful here. - -It is also possible to create a `SigningKey` from a "seed", which is -deterministic. This can be used in protocols where you want to derive -consistent signing keys from some other secret, for example when you want -three separate keys and only want to store a single master secret. You should -start with a uniformly-distributed unguessable seed with about `curve.baselen` -bytes of entropy, and then use one of the helper functions in `ecdsa.util` to -convert it into an integer in the correct range, and then finally pass it -into `SigningKey.from_secret_exponent()`, like this: - -```python -import os -from ecdsa import NIST384p, SigningKey -from ecdsa.util import randrange_from_seed__trytryagain - -def make_key(seed): - secexp = randrange_from_seed__trytryagain(seed, NIST384p.order) - return SigningKey.from_secret_exponent(secexp, curve=NIST384p) - -seed = os.urandom(NIST384p.baselen) # or other starting point -sk1a = make_key(seed) -sk1b = make_key(seed) -# note: sk1a and sk1b are the same key -assert sk1a.to_string() == sk1b.to_string() -sk2 = make_key(b"2-"+seed) # different key -assert sk1a.to_string() != sk2.to_string() -``` - -In case the application will verify a lot of signatures made with a single -key, it's possible to precompute some of the internal values to make -signature verification significantly faster. The break-even point occurs at -about 100 signatures verified. - -To perform precomputation, you can call the `precompute()` method -on `VerifyingKey` instance: -```python -from ecdsa import SigningKey, NIST384p -sk = SigningKey.generate(curve=NIST384p) -vk = sk.verifying_key -vk.precompute() -signature = sk.sign(b"message") -assert vk.verify(signature, b"message") -``` - -Once `precompute()` was called, all signature verifications with this key will -be faster to execute. - -## OpenSSL Compatibility - -To produce signatures that can be verified by OpenSSL tools, or to verify -signatures that were produced by those tools, use: - -```python -# openssl ecparam -name prime256v1 -genkey -out sk.pem -# openssl ec -in sk.pem -pubout -out vk.pem -# echo "data for signing" > data -# openssl dgst -sha256 -sign sk.pem -out data.sig data -# openssl dgst -sha256 -verify vk.pem -signature data.sig data -# openssl dgst -sha256 -prverify sk.pem -signature data.sig data - -import hashlib -from ecdsa import SigningKey, VerifyingKey -from ecdsa.util import sigencode_der, sigdecode_der - -with open("vk.pem") as f: - vk = VerifyingKey.from_pem(f.read()) - -with open("data", "rb") as f: - data = f.read() - -with open("data.sig", "rb") as f: - signature = f.read() - -assert vk.verify(signature, data, hashlib.sha256, sigdecode=sigdecode_der) - -with open("sk.pem") as f: - sk = SigningKey.from_pem(f.read(), hashlib.sha256) - -new_signature = sk.sign_deterministic(data, sigencode=sigencode_der) - -with open("data.sig2", "wb") as f: - f.write(new_signature) - -# openssl dgst -sha256 -verify vk.pem -signature data.sig2 data -``` - -Note: if compatibility with OpenSSL 1.0.0 or earlier is necessary, the -`sigencode_string` and `sigdecode_string` from `ecdsa.util` can be used for -respectively writing and reading the signatures. - -The keys also can be written in format that openssl can handle: - -```python -from ecdsa import SigningKey, VerifyingKey - -with open("sk.pem") as f: - sk = SigningKey.from_pem(f.read()) -with open("sk.pem", "wb") as f: - f.write(sk.to_pem()) - -with open("vk.pem") as f: - vk = VerifyingKey.from_pem(f.read()) -with open("vk.pem", "wb") as f: - f.write(vk.to_pem()) -``` - -## Entropy - -Creating a signing key with `SigningKey.generate()` requires some form of -entropy (as opposed to -`from_secret_exponent`/`from_string`/`from_der`/`from_pem`, -which are deterministic and do not require an entropy source). The default -source is `os.urandom()`, but you can pass any other function that behaves -like `os.urandom` as the `entropy=` argument to do something different. This -may be useful in unit tests, where you want to achieve repeatable results. The -`ecdsa.util.PRNG` utility is handy here: it takes a seed and produces a strong -pseudo-random stream from it: - -```python -from ecdsa.util import PRNG -from ecdsa import SigningKey -rng1 = PRNG(b"seed") -sk1 = SigningKey.generate(entropy=rng1) -rng2 = PRNG(b"seed") -sk2 = SigningKey.generate(entropy=rng2) -# sk1 and sk2 are the same key -``` - -Likewise, ECDSA signature generation requires a random number, and each -signature must use a different one (using the same number twice will -immediately reveal the private signing key). The `sk.sign()` method takes an -`entropy=` argument which behaves the same as `SigningKey.generate(entropy=)`. - -## Deterministic Signatures - -If you call `SigningKey.sign_deterministic(data)` instead of `.sign(data)`, -the code will generate a deterministic signature instead of a random one. -This uses the algorithm from RFC6979 to safely generate a unique `k` value, -derived from the private key and the message being signed. Each time you sign -the same message with the same key, you will get the same signature (using -the same `k`). - -This may become the default in a future version, as it is not vulnerable to -failures of the entropy source. - -## Examples - -Create a NIST192p key pair and immediately save both to disk: - -```python -from ecdsa import SigningKey -sk = SigningKey.generate() -vk = sk.verifying_key -with open("private.pem", "wb") as f: - f.write(sk.to_pem()) -with open("public.pem", "wb") as f: - f.write(vk.to_pem()) -``` - -Load a signing key from disk, use it to sign a message (using SHA-1), and write -the signature to disk: - -```python -from ecdsa import SigningKey -with open("private.pem") as f: - sk = SigningKey.from_pem(f.read()) -with open("message", "rb") as f: - message = f.read() -sig = sk.sign(message) -with open("signature", "wb") as f: - f.write(sig) -``` - -Load the verifying key, message, and signature from disk, and verify the -signature (assume SHA-1 hash): - -```python -from ecdsa import VerifyingKey, BadSignatureError -vk = VerifyingKey.from_pem(open("public.pem").read()) -with open("message", "rb") as f: - message = f.read() -with open("signature", "rb") as f: - sig = f.read() -try: - vk.verify(sig, message) - print "good signature" -except BadSignatureError: - print "BAD SIGNATURE" -``` - -Create a NIST521p key pair: - -```python -from ecdsa import SigningKey, NIST521p -sk = SigningKey.generate(curve=NIST521p) -vk = sk.verifying_key -``` - -Create three independent signing keys from a master seed: - -```python -from ecdsa import NIST192p, SigningKey -from ecdsa.util import randrange_from_seed__trytryagain - -def make_key_from_seed(seed, curve=NIST192p): - secexp = randrange_from_seed__trytryagain(seed, curve.order) - return SigningKey.from_secret_exponent(secexp, curve) - -sk1 = make_key_from_seed("1:%s" % seed) -sk2 = make_key_from_seed("2:%s" % seed) -sk3 = make_key_from_seed("3:%s" % seed) -``` - -Load a verifying key from disk and print it using hex encoding in -uncompressed and compressed format (defined in X9.62 and SEC1 standards): - -```python -from ecdsa import VerifyingKey - -with open("public.pem") as f: - vk = VerifyingKey.from_pem(f.read()) - -print("uncompressed: {0}".format(vk.to_string("uncompressed").hex())) -print("compressed: {0}".format(vk.to_string("compressed").hex())) -``` - -Load a verifying key from a hex string from compressed format, output -uncompressed: - -```python -from ecdsa import VerifyingKey, NIST256p - -comp_str = '022799c0d0ee09772fdd337d4f28dc155581951d07082fb19a38aa396b67e77759' -vk = VerifyingKey.from_string(bytearray.fromhex(comp_str), curve=NIST256p) -print(vk.to_string("uncompressed").hex()) -``` - -ECDH key exchange with remote party: - -```python -from ecdsa import ECDH, NIST256p - -ecdh = ECDH(curve=NIST256p) -ecdh.generate_private_key() -local_public_key = ecdh.get_public_key() -#send `local_public_key` to remote party and receive `remote_public_key` from remote party -with open("remote_public_key.pem") as e: - remote_public_key = e.read() -ecdh.load_received_public_key_pem(remote_public_key) -secret = ecdh.generate_sharedsecret_bytes() -``` - - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD deleted file mode 100644 index f4130d1c5..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/RECORD +++ /dev/null @@ -1,64 +0,0 @@ -ecdsa-0.18.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -ecdsa-0.18.0.dist-info/LICENSE,sha256=PsqYRXc9LluMydjBGdNF8ApIBuS9Zg1KPWzfnA6di7I,1147 -ecdsa-0.18.0.dist-info/METADATA,sha256=vesFVMWT6uSeOuNwGGxtBm8nm6GROqNNRO28jr8wWqM,29750 -ecdsa-0.18.0.dist-info/RECORD,, -ecdsa-0.18.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 -ecdsa-0.18.0.dist-info/top_level.txt,sha256=7ovPHfAPyTou19f8gOSbHm6B9dGjTibWolcCB7Zjovs,6 -ecdsa/__init__.py,sha256=m8jWFcZ9E-iiDqVaTy7ABtvEyTJlF58tZ45UAKj3UWo,1637 -ecdsa/__pycache__/__init__.cpython-310.pyc,, -ecdsa/__pycache__/_compat.cpython-310.pyc,, -ecdsa/__pycache__/_rwlock.cpython-310.pyc,, -ecdsa/__pycache__/_sha3.cpython-310.pyc,, -ecdsa/__pycache__/_version.cpython-310.pyc,, -ecdsa/__pycache__/curves.cpython-310.pyc,, -ecdsa/__pycache__/der.cpython-310.pyc,, -ecdsa/__pycache__/ecdh.cpython-310.pyc,, -ecdsa/__pycache__/ecdsa.cpython-310.pyc,, -ecdsa/__pycache__/eddsa.cpython-310.pyc,, -ecdsa/__pycache__/ellipticcurve.cpython-310.pyc,, -ecdsa/__pycache__/errors.cpython-310.pyc,, -ecdsa/__pycache__/keys.cpython-310.pyc,, -ecdsa/__pycache__/numbertheory.cpython-310.pyc,, -ecdsa/__pycache__/rfc6979.cpython-310.pyc,, -ecdsa/__pycache__/test_curves.cpython-310.pyc,, -ecdsa/__pycache__/test_der.cpython-310.pyc,, -ecdsa/__pycache__/test_ecdh.cpython-310.pyc,, -ecdsa/__pycache__/test_ecdsa.cpython-310.pyc,, -ecdsa/__pycache__/test_eddsa.cpython-310.pyc,, -ecdsa/__pycache__/test_ellipticcurve.cpython-310.pyc,, -ecdsa/__pycache__/test_jacobi.cpython-310.pyc,, -ecdsa/__pycache__/test_keys.cpython-310.pyc,, -ecdsa/__pycache__/test_malformed_sigs.cpython-310.pyc,, -ecdsa/__pycache__/test_numbertheory.cpython-310.pyc,, -ecdsa/__pycache__/test_pyecdsa.cpython-310.pyc,, -ecdsa/__pycache__/test_rw_lock.cpython-310.pyc,, -ecdsa/__pycache__/test_sha3.cpython-310.pyc,, -ecdsa/__pycache__/util.cpython-310.pyc,, -ecdsa/_compat.py,sha256=EhUF8-sFu1dKKGDibkmItbYm_nKoklSIBgkIburUoAg,4619 -ecdsa/_rwlock.py,sha256=CAwHp2V65ksI8B1UqY7EccK9LaUToiv6pDLVzm44eag,2849 -ecdsa/_sha3.py,sha256=DJs7QLmdkQMU35llyD8HQeAXNvf5sMcujO6oFdScIqI,4747 -ecdsa/_version.py,sha256=YZ3BGOHr1Ltse4LfX7F80J6qmKFA-NS-G2eYUuw2WnU,498 -ecdsa/curves.py,sha256=Na5rpnuADNvWkCTlUGbs9xwVogY6Vl2_3uNzpVGgxtE,14390 -ecdsa/der.py,sha256=OmfH8fojeqfJnasAt7I1P8j_qfwcwl-W4gDx1-cO8M0,14109 -ecdsa/ecdh.py,sha256=Tiirawt5xegVDrY9eS-ATvvfmTIznUyv5fy2k7VnzTk,11011 -ecdsa/ecdsa.py,sha256=LPRHHXNvGyZ67lyM6cWqUuhQceCwoktfPij20UTsdJo,24955 -ecdsa/eddsa.py,sha256=IzsGzoGAefcoYjF7DVjFkX5ZJqiK2LTOmAMe6wyf4UU,7170 -ecdsa/ellipticcurve.py,sha256=HwlFqrihf7Q2GQTLYQ0PeCwsgMhk_GlkZz3I2Xj4-eI,53625 -ecdsa/errors.py,sha256=b4mhnmIpRnEdHzbectHAA5F7O9MtSaI-fYoc13_vBxQ,130 -ecdsa/keys.py,sha256=plsomRHYrN3Z3iigUqKM5An8_6TDk1nJNpFv_cPGAvM,65124 -ecdsa/numbertheory.py,sha256=XWugBG59BxrpvjZm7Ytsnmkv770vK8IkClObThpbvAM,17479 -ecdsa/rfc6979.py,sha256=zwzo33lsZJA9r2dSf7HCliI_yIbw5cJ0Ek9tLdRRO40,2850 -ecdsa/test_curves.py,sha256=l5N-m4Yo5IAy4a8aJMsBamaSlLAfSoYjYqCj1HDEVpU,13081 -ecdsa/test_der.py,sha256=q2mr4HS_JyUxojWTSLJu-MQZiTwaCE7W_VG3rwwPEas,14956 -ecdsa/test_ecdh.py,sha256=hUJXTo_Cr9ji9-EpPvpQf7-TgB97WUj2tWcwU-LqCXc,15238 -ecdsa/test_ecdsa.py,sha256=1clBfDtA0zdF-13BoKXsJffL5K-iFutlMgov0kmGro0,23923 -ecdsa/test_eddsa.py,sha256=Vlv5J0C4zNJu5zzr756qpm0AJmARpzBKCWrhHaF_bR4,32615 -ecdsa/test_ellipticcurve.py,sha256=K6W_EQunOfE-RVSux6d1O7LXzSuAsVk9F1elwyY-rYA,6085 -ecdsa/test_jacobi.py,sha256=JDQeM_JKwPfwWBd4IgqtOp1rboeQNUIPPA334b-nmLQ,18388 -ecdsa/test_keys.py,sha256=n_IYLxG4JwD84dYLDmRjV2A-NqsSrrR1P0XBJOCZsEI,32833 -ecdsa/test_malformed_sigs.py,sha256=hiV2vwzFrIdNIC-inYUJIKboyAAw2TKAIVXtFadyojg,10857 -ecdsa/test_numbertheory.py,sha256=g6hi7NZFKuMSAxJSAYW5sWM7ivSCiw8g5-PeoDyowgY,11619 -ecdsa/test_pyecdsa.py,sha256=WeRujEKpkZzHvEeXNnnhM1QdMH0Lxou7Bl4u8RXY-jM,82757 -ecdsa/test_rw_lock.py,sha256=byv0_FTM90cbuHPCI6__LeQJkHL_zYEeVYIBO8e2LLc,7021 -ecdsa/test_sha3.py,sha256=oKULy5KOTaXjpLXSyuHrB1wjPiQDxB6INp7Tf1EU8Ko,3022 -ecdsa/util.py,sha256=cOEN3_c8p79Dc8a-LcUQP2ctIsYky35jhSWc9hLP1qc,14618 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL deleted file mode 100644 index 01b8fc7d4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt deleted file mode 100644 index aa5efdb54..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa-0.18.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -ecdsa diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py deleted file mode 100644 index ce8749aa0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/__init__.py +++ /dev/null @@ -1,90 +0,0 @@ -# while we don't use six in this file, we did bundle it for a long time, so -# keep as part of module in a virtual way (through __all__) -import six -from .keys import ( - SigningKey, - VerifyingKey, - BadSignatureError, - BadDigestError, - MalformedPointError, -) -from .curves import ( - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - Ed25519, - Ed448, -) -from .ecdh import ( - ECDH, - NoKeyError, - NoCurveError, - InvalidCurveError, - InvalidSharedSecretError, -) -from .der import UnexpectedDER -from . import _version - -# This code comes from http://github.com/tlsfuzzer/python-ecdsa -__all__ = [ - "curves", - "der", - "ecdsa", - "ellipticcurve", - "keys", - "numbertheory", - "test_pyecdsa", - "util", - "six", -] - -_hush_pyflakes = [ - SigningKey, - VerifyingKey, - BadSignatureError, - BadDigestError, - MalformedPointError, - UnexpectedDER, - InvalidCurveError, - NoKeyError, - InvalidSharedSecretError, - ECDH, - NoCurveError, - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - Ed25519, - Ed448, - six.b(""), -] -del _hush_pyflakes - -__version__ = _version.get_versions()["version"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py deleted file mode 100644 index 83d41a5f7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_compat.py +++ /dev/null @@ -1,153 +0,0 @@ -""" -Common functions for providing cross-python version compatibility. -""" -import sys -import re -import binascii -from six import integer_types - - -def str_idx_as_int(string, index): - """Take index'th byte from string, return as integer""" - val = string[index] - if isinstance(val, integer_types): - return val - return ord(val) - - -if sys.version_info < (3, 0): # pragma: no branch - import platform - - def normalise_bytes(buffer_object): - """Cast the input into array of bytes.""" - # flake8 runs on py3 where `buffer` indeed doesn't exist... - return buffer(buffer_object) # noqa: F821 - - def hmac_compat(ret): - return ret - - if ( - sys.version_info < (2, 7) - or sys.version_info < (2, 7, 4) - or platform.system() == "Java" - ): # pragma: no branch - - def remove_whitespace(text): - """Removes all whitespace from passed in string""" - return re.sub(r"\s+", "", text) - - def compat26_str(val): - return str(val) - - def bit_length(val): - if val == 0: - return 0 - return len(bin(val)) - 2 - - else: - - def remove_whitespace(text): - """Removes all whitespace from passed in string""" - return re.sub(r"\s+", "", text, flags=re.UNICODE) - - def compat26_str(val): - return val - - def bit_length(val): - """Return number of bits necessary to represent an integer.""" - return val.bit_length() - - def b2a_hex(val): - return binascii.b2a_hex(compat26_str(val)) - - def a2b_hex(val): - try: - return bytearray(binascii.a2b_hex(val)) - except Exception as e: - raise ValueError("base16 error: %s" % e) - - def bytes_to_int(val, byteorder): - """Convert bytes to an int.""" - if not val: - return 0 - if byteorder == "big": - return int(b2a_hex(val), 16) - if byteorder == "little": - return int(b2a_hex(val[::-1]), 16) - raise ValueError("Only 'big' and 'little' endian supported") - - def int_to_bytes(val, length=None, byteorder="big"): - """Return number converted to bytes""" - if length is None: - length = byte_length(val) - if byteorder == "big": - return bytearray( - (val >> i) & 0xFF for i in reversed(range(0, length * 8, 8)) - ) - if byteorder == "little": - return bytearray( - (val >> i) & 0xFF for i in range(0, length * 8, 8) - ) - raise ValueError("Only 'big' or 'little' endian supported") - -else: - if sys.version_info < (3, 4): # pragma: no branch - # on python 3.3 hmac.hmac.update() accepts only bytes, on newer - # versions it does accept memoryview() also - def hmac_compat(data): - if not isinstance(data, bytes): # pragma: no branch - return bytes(data) - return data - - def normalise_bytes(buffer_object): - """Cast the input into array of bytes.""" - if not buffer_object: - return b"" - return memoryview(buffer_object).cast("B") - - else: - - def hmac_compat(data): - return data - - def normalise_bytes(buffer_object): - """Cast the input into array of bytes.""" - return memoryview(buffer_object).cast("B") - - def compat26_str(val): - return val - - def remove_whitespace(text): - """Removes all whitespace from passed in string""" - return re.sub(r"\s+", "", text, flags=re.UNICODE) - - def a2b_hex(val): - try: - return bytearray(binascii.a2b_hex(bytearray(val, "ascii"))) - except Exception as e: - raise ValueError("base16 error: %s" % e) - - # pylint: disable=invalid-name - # pylint is stupid here and doesn't notice it's a function, not - # constant - bytes_to_int = int.from_bytes - # pylint: enable=invalid-name - - def bit_length(val): - """Return number of bits necessary to represent an integer.""" - return val.bit_length() - - def int_to_bytes(val, length=None, byteorder="big"): - """Convert integer to bytes.""" - if length is None: - length = byte_length(val) - # for gmpy we need to convert back to native int - if type(val) != int: - val = int(val) - return bytearray(val.to_bytes(length=length, byteorder=byteorder)) - - -def byte_length(val): - """Return number of bytes necessary to represent an integer.""" - length = bit_length(val) - return (length + 7) // 8 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py deleted file mode 100644 index 010e4981b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_rwlock.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright Mateusz Kobos, (c) 2011 -# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ -# released under the MIT licence - -import threading - - -__author__ = "Mateusz Kobos" - - -class RWLock: - """ - Read-Write locking primitive - - Synchronization object used in a solution of so-called second - readers-writers problem. In this problem, many readers can simultaneously - access a share, and a writer has an exclusive access to this share. - Additionally, the following constraints should be met: - 1) no reader should be kept waiting if the share is currently opened for - reading unless a writer is also waiting for the share, - 2) no writer should be kept waiting for the share longer than absolutely - necessary. - - The implementation is based on [1, secs. 4.2.2, 4.2.6, 4.2.7] - with a modification -- adding an additional lock (C{self.__readers_queue}) - -- in accordance with [2]. - - Sources: - [1] A.B. Downey: "The little book of semaphores", Version 2.1.5, 2008 - [2] P.J. Courtois, F. Heymans, D.L. Parnas: - "Concurrent Control with 'Readers' and 'Writers'", - Communications of the ACM, 1971 (via [3]) - [3] http://en.wikipedia.org/wiki/Readers-writers_problem - """ - - def __init__(self): - """ - A lock giving an even higher priority to the writer in certain - cases (see [2] for a discussion). - """ - self.__read_switch = _LightSwitch() - self.__write_switch = _LightSwitch() - self.__no_readers = threading.Lock() - self.__no_writers = threading.Lock() - self.__readers_queue = threading.Lock() - - def reader_acquire(self): - self.__readers_queue.acquire() - self.__no_readers.acquire() - self.__read_switch.acquire(self.__no_writers) - self.__no_readers.release() - self.__readers_queue.release() - - def reader_release(self): - self.__read_switch.release(self.__no_writers) - - def writer_acquire(self): - self.__write_switch.acquire(self.__no_readers) - self.__no_writers.acquire() - - def writer_release(self): - self.__no_writers.release() - self.__write_switch.release(self.__no_readers) - - -class _LightSwitch: - """An auxiliary "light switch"-like object. The first thread turns on the - "switch", the last one turns it off (see [1, sec. 4.2.2] for details).""" - - def __init__(self): - self.__counter = 0 - self.__mutex = threading.Lock() - - def acquire(self, lock): - self.__mutex.acquire() - self.__counter += 1 - if self.__counter == 1: - lock.acquire() - self.__mutex.release() - - def release(self, lock): - self.__mutex.acquire() - self.__counter -= 1 - if self.__counter == 0: - lock.release() - self.__mutex.release() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py deleted file mode 100644 index 2db005869..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_sha3.py +++ /dev/null @@ -1,181 +0,0 @@ -""" -Implementation of the SHAKE-256 algorithm for Ed448 -""" - -try: - import hashlib - - hashlib.new("shake256").digest(64) - - def shake_256(msg, outlen): - return hashlib.new("shake256", msg).digest(outlen) - -except (TypeError, ValueError): - - from ._compat import bytes_to_int, int_to_bytes - - # From little endian. - def _from_le(s): - return bytes_to_int(s, byteorder="little") - - # Rotate a word x by b places to the left. - def _rol(x, b): - return ((x << b) | (x >> (64 - b))) & (2**64 - 1) - - # Do the SHA-3 state transform on state s. - def _sha3_transform(s): - ROTATIONS = [ - 0, - 1, - 62, - 28, - 27, - 36, - 44, - 6, - 55, - 20, - 3, - 10, - 43, - 25, - 39, - 41, - 45, - 15, - 21, - 8, - 18, - 2, - 61, - 56, - 14, - ] - PERMUTATION = [ - 1, - 6, - 9, - 22, - 14, - 20, - 2, - 12, - 13, - 19, - 23, - 15, - 4, - 24, - 21, - 8, - 16, - 5, - 3, - 18, - 17, - 11, - 7, - 10, - ] - RC = [ - 0x0000000000000001, - 0x0000000000008082, - 0x800000000000808A, - 0x8000000080008000, - 0x000000000000808B, - 0x0000000080000001, - 0x8000000080008081, - 0x8000000000008009, - 0x000000000000008A, - 0x0000000000000088, - 0x0000000080008009, - 0x000000008000000A, - 0x000000008000808B, - 0x800000000000008B, - 0x8000000000008089, - 0x8000000000008003, - 0x8000000000008002, - 0x8000000000000080, - 0x000000000000800A, - 0x800000008000000A, - 0x8000000080008081, - 0x8000000000008080, - 0x0000000080000001, - 0x8000000080008008, - ] - - for rnd in range(0, 24): - # AddColumnParity (Theta) - c = [0] * 5 - d = [0] * 5 - for i in range(0, 25): - c[i % 5] ^= s[i] - for i in range(0, 5): - d[i] = c[(i + 4) % 5] ^ _rol(c[(i + 1) % 5], 1) - for i in range(0, 25): - s[i] ^= d[i % 5] - # RotateWords (Rho) - for i in range(0, 25): - s[i] = _rol(s[i], ROTATIONS[i]) - # PermuteWords (Pi) - t = s[PERMUTATION[0]] - for i in range(0, len(PERMUTATION) - 1): - s[PERMUTATION[i]] = s[PERMUTATION[i + 1]] - s[PERMUTATION[-1]] = t - # NonlinearMixRows (Chi) - for i in range(0, 25, 5): - t = [ - s[i], - s[i + 1], - s[i + 2], - s[i + 3], - s[i + 4], - s[i], - s[i + 1], - ] - for j in range(0, 5): - s[i + j] = t[j] ^ ((~t[j + 1]) & (t[j + 2])) - # AddRoundConstant (Iota) - s[0] ^= RC[rnd] - - # Reinterpret octet array b to word array and XOR it to state s. - def _reinterpret_to_words_and_xor(s, b): - for j in range(0, len(b) // 8): - s[j] ^= _from_le(b[8 * j : 8 * j + 8]) - - # Reinterpret word array w to octet array and return it. - def _reinterpret_to_octets(w): - mp = bytearray() - for j in range(0, len(w)): - mp += int_to_bytes(w[j], 8, byteorder="little") - return mp - - def _sha3_raw(msg, r_w, o_p, e_b): - """Semi-generic SHA-3 implementation""" - r_b = 8 * r_w - s = [0] * 25 - # Handle whole blocks. - idx = 0 - blocks = len(msg) // r_b - for i in range(0, blocks): - _reinterpret_to_words_and_xor(s, msg[idx : idx + r_b]) - idx += r_b - _sha3_transform(s) - # Handle last block padding. - m = bytearray(msg[idx:]) - m.append(o_p) - while len(m) < r_b: - m.append(0) - m[len(m) - 1] |= 128 - # Handle padded last block. - _reinterpret_to_words_and_xor(s, m) - _sha3_transform(s) - # Output. - out = bytearray() - while len(out) < e_b: - out += _reinterpret_to_octets(s[:r_w]) - _sha3_transform(s) - return out[:e_b] - - def shake_256(msg, outlen): - return _sha3_raw(msg, 17, 31, outlen) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py deleted file mode 100644 index 96aae17bb..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/_version.py +++ /dev/null @@ -1,21 +0,0 @@ - -# This file was generated by 'versioneer.py' (0.21) from -# revision-control system data, or from the parent directory name of an -# unpacked source archive. Distribution tarballs contain a pre-generated copy -# of this file. - -import json - -version_json = ''' -{ - "date": "2022-07-09T14:49:17+0200", - "dirty": false, - "error": null, - "full-revisionid": "341e0d8be9fedf66fbc9a95630b4ed2138343380", - "version": "0.18.0" -} -''' # END VERSION_JSON - - -def get_versions(): - return json.loads(version_json) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py deleted file mode 100644 index 1119ee5a3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/curves.py +++ /dev/null @@ -1,513 +0,0 @@ -from __future__ import division - -from six import PY2 -from . import der, ecdsa, ellipticcurve, eddsa -from .util import orderlen, number_to_string, string_to_number -from ._compat import normalise_bytes, bit_length - - -# orderlen was defined in this module previously, so keep it in __all__, -# will need to mark it as deprecated later -__all__ = [ - "UnknownCurveError", - "orderlen", - "Curve", - "SECP112r1", - "SECP112r2", - "SECP128r1", - "SECP160r1", - "NIST192p", - "NIST224p", - "NIST256p", - "NIST384p", - "NIST521p", - "curves", - "find_curve", - "curve_by_name", - "SECP256k1", - "BRAINPOOLP160r1", - "BRAINPOOLP192r1", - "BRAINPOOLP224r1", - "BRAINPOOLP256r1", - "BRAINPOOLP320r1", - "BRAINPOOLP384r1", - "BRAINPOOLP512r1", - "PRIME_FIELD_OID", - "CHARACTERISTIC_TWO_FIELD_OID", - "Ed25519", - "Ed448", -] - - -PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1) -CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2) - - -class UnknownCurveError(Exception): - pass - - -class Curve: - def __init__(self, name, curve, generator, oid, openssl_name=None): - self.name = name - self.openssl_name = openssl_name # maybe None - self.curve = curve - self.generator = generator - self.order = generator.order() - if isinstance(curve, ellipticcurve.CurveEdTw): - # EdDSA keys are special in that both private and public - # are the same size (as it's defined only with compressed points) - - # +1 for the sign bit and then round up - self.baselen = (bit_length(curve.p()) + 1 + 7) // 8 - self.verifying_key_length = self.baselen - else: - self.baselen = orderlen(self.order) - self.verifying_key_length = 2 * orderlen(curve.p()) - self.signature_length = 2 * self.baselen - self.oid = oid - if oid: - self.encoded_oid = der.encode_oid(*oid) - - def __eq__(self, other): - if isinstance(other, Curve): - return ( - self.curve == other.curve and self.generator == other.generator - ) - return NotImplemented - - def __ne__(self, other): - return not self == other - - def __repr__(self): - return self.name - - def to_der(self, encoding=None, point_encoding="uncompressed"): - """Serialise the curve parameters to binary string. - - :param str encoding: the format to save the curve parameters in. - Default is ``named_curve``, with fallback being the ``explicit`` - if the OID is not set for the curve. - :param str point_encoding: the point encoding of the generator when - explicit curve encoding is used. Ignored for ``named_curve`` - format. - - :return: DER encoded ECParameters structure - :rtype: bytes - """ - if encoding is None: - if self.oid: - encoding = "named_curve" - else: - encoding = "explicit" - - if encoding not in ("named_curve", "explicit"): - raise ValueError( - "Only 'named_curve' and 'explicit' encodings supported" - ) - - if encoding == "named_curve": - if not self.oid: - raise UnknownCurveError( - "Can't encode curve using named_curve encoding without " - "associated curve OID" - ) - return der.encode_oid(*self.oid) - elif isinstance(self.curve, ellipticcurve.CurveEdTw): - assert encoding == "explicit" - raise UnknownCurveError( - "Twisted Edwards curves don't support explicit encoding" - ) - - # encode the ECParameters sequence - curve_p = self.curve.p() - version = der.encode_integer(1) - field_id = der.encode_sequence( - der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p) - ) - curve = der.encode_sequence( - der.encode_octet_string( - number_to_string(self.curve.a() % curve_p, curve_p) - ), - der.encode_octet_string( - number_to_string(self.curve.b() % curve_p, curve_p) - ), - ) - base = der.encode_octet_string(self.generator.to_bytes(point_encoding)) - order = der.encode_integer(self.generator.order()) - seq_elements = [version, field_id, curve, base, order] - if self.curve.cofactor(): - cofactor = der.encode_integer(self.curve.cofactor()) - seq_elements.append(cofactor) - - return der.encode_sequence(*seq_elements) - - def to_pem(self, encoding=None, point_encoding="uncompressed"): - """ - Serialise the curve parameters to the :term:`PEM` format. - - :param str encoding: the format to save the curve parameters in. - Default is ``named_curve``, with fallback being the ``explicit`` - if the OID is not set for the curve. - :param str point_encoding: the point encoding of the generator when - explicit curve encoding is used. Ignored for ``named_curve`` - format. - - :return: PEM encoded ECParameters structure - :rtype: str - """ - return der.topem( - self.to_der(encoding, point_encoding), "EC PARAMETERS" - ) - - @staticmethod - def from_der(data, valid_encodings=None): - """Decode the curve parameters from DER file. - - :param data: the binary string to decode the parameters from - :type data: :term:`bytes-like object` - :param valid_encodings: set of names of allowed encodings, by default - all (set by passing ``None``), supported ones are ``named_curve`` - and ``explicit`` - :type valid_encodings: :term:`set-like object` - """ - if not valid_encodings: - valid_encodings = set(("named_curve", "explicit")) - if not all(i in ["named_curve", "explicit"] for i in valid_encodings): - raise ValueError( - "Only named_curve and explicit encodings supported" - ) - data = normalise_bytes(data) - if not der.is_sequence(data): - if "named_curve" not in valid_encodings: - raise der.UnexpectedDER( - "named_curve curve parameters not allowed" - ) - oid, empty = der.remove_object(data) - if empty: - raise der.UnexpectedDER("Unexpected data after OID") - return find_curve(oid) - - if "explicit" not in valid_encodings: - raise der.UnexpectedDER("explicit curve parameters not allowed") - - seq, empty = der.remove_sequence(data) - if empty: - raise der.UnexpectedDER( - "Unexpected data after ECParameters structure" - ) - # decode the ECParameters sequence - version, rest = der.remove_integer(seq) - if version != 1: - raise der.UnexpectedDER("Unknown parameter encoding format") - field_id, rest = der.remove_sequence(rest) - curve, rest = der.remove_sequence(rest) - base_bytes, rest = der.remove_octet_string(rest) - order, rest = der.remove_integer(rest) - cofactor = None - if rest: - # the ASN.1 specification of ECParameters allows for future - # extensions of the sequence, so ignore the remaining bytes - cofactor, _ = der.remove_integer(rest) - - # decode the ECParameters.fieldID sequence - field_type, rest = der.remove_object(field_id) - if field_type == CHARACTERISTIC_TWO_FIELD_OID: - raise UnknownCurveError("Characteristic 2 curves unsupported") - if field_type != PRIME_FIELD_OID: - raise UnknownCurveError( - "Unknown field type: {0}".format(field_type) - ) - prime, empty = der.remove_integer(rest) - if empty: - raise der.UnexpectedDER( - "Unexpected data after ECParameters.fieldID.Prime-p element" - ) - - # decode the ECParameters.curve sequence - curve_a_bytes, rest = der.remove_octet_string(curve) - curve_b_bytes, rest = der.remove_octet_string(rest) - # seed can be defined here, but we don't parse it, so ignore `rest` - - curve_a = string_to_number(curve_a_bytes) - curve_b = string_to_number(curve_b_bytes) - - curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor) - - # decode the ECParameters.base point - - base = ellipticcurve.PointJacobi.from_bytes( - curve_fp, - base_bytes, - valid_encodings=("uncompressed", "compressed", "hybrid"), - order=order, - generator=True, - ) - tmp_curve = Curve("unknown", curve_fp, base, None) - - # if the curve matches one of the well-known ones, use the well-known - # one in preference, as it will have the OID and name associated - for i in curves: - if tmp_curve == i: - return i - return tmp_curve - - @classmethod - def from_pem(cls, string, valid_encodings=None): - """Decode the curve parameters from PEM file. - - :param str string: the text string to decode the parameters from - :param valid_encodings: set of names of allowed encodings, by default - all (set by passing ``None``), supported ones are ``named_curve`` - and ``explicit`` - :type valid_encodings: :term:`set-like object` - """ - if not PY2 and isinstance(string, str): # pragma: no branch - string = string.encode() - - ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----") - if ec_param_index == -1: - raise der.UnexpectedDER("EC PARAMETERS PEM header not found") - - return cls.from_der( - der.unpem(string[ec_param_index:]), valid_encodings - ) - - -# the SEC curves -SECP112r1 = Curve( - "SECP112r1", - ecdsa.curve_112r1, - ecdsa.generator_112r1, - (1, 3, 132, 0, 6), - "secp112r1", -) - - -SECP112r2 = Curve( - "SECP112r2", - ecdsa.curve_112r2, - ecdsa.generator_112r2, - (1, 3, 132, 0, 7), - "secp112r2", -) - - -SECP128r1 = Curve( - "SECP128r1", - ecdsa.curve_128r1, - ecdsa.generator_128r1, - (1, 3, 132, 0, 28), - "secp128r1", -) - - -SECP160r1 = Curve( - "SECP160r1", - ecdsa.curve_160r1, - ecdsa.generator_160r1, - (1, 3, 132, 0, 8), - "secp160r1", -) - - -# the NIST curves -NIST192p = Curve( - "NIST192p", - ecdsa.curve_192, - ecdsa.generator_192, - (1, 2, 840, 10045, 3, 1, 1), - "prime192v1", -) - - -NIST224p = Curve( - "NIST224p", - ecdsa.curve_224, - ecdsa.generator_224, - (1, 3, 132, 0, 33), - "secp224r1", -) - - -NIST256p = Curve( - "NIST256p", - ecdsa.curve_256, - ecdsa.generator_256, - (1, 2, 840, 10045, 3, 1, 7), - "prime256v1", -) - - -NIST384p = Curve( - "NIST384p", - ecdsa.curve_384, - ecdsa.generator_384, - (1, 3, 132, 0, 34), - "secp384r1", -) - - -NIST521p = Curve( - "NIST521p", - ecdsa.curve_521, - ecdsa.generator_521, - (1, 3, 132, 0, 35), - "secp521r1", -) - - -SECP256k1 = Curve( - "SECP256k1", - ecdsa.curve_secp256k1, - ecdsa.generator_secp256k1, - (1, 3, 132, 0, 10), - "secp256k1", -) - - -BRAINPOOLP160r1 = Curve( - "BRAINPOOLP160r1", - ecdsa.curve_brainpoolp160r1, - ecdsa.generator_brainpoolp160r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), - "brainpoolP160r1", -) - - -BRAINPOOLP192r1 = Curve( - "BRAINPOOLP192r1", - ecdsa.curve_brainpoolp192r1, - ecdsa.generator_brainpoolp192r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), - "brainpoolP192r1", -) - - -BRAINPOOLP224r1 = Curve( - "BRAINPOOLP224r1", - ecdsa.curve_brainpoolp224r1, - ecdsa.generator_brainpoolp224r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), - "brainpoolP224r1", -) - - -BRAINPOOLP256r1 = Curve( - "BRAINPOOLP256r1", - ecdsa.curve_brainpoolp256r1, - ecdsa.generator_brainpoolp256r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), - "brainpoolP256r1", -) - - -BRAINPOOLP320r1 = Curve( - "BRAINPOOLP320r1", - ecdsa.curve_brainpoolp320r1, - ecdsa.generator_brainpoolp320r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), - "brainpoolP320r1", -) - - -BRAINPOOLP384r1 = Curve( - "BRAINPOOLP384r1", - ecdsa.curve_brainpoolp384r1, - ecdsa.generator_brainpoolp384r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), - "brainpoolP384r1", -) - - -BRAINPOOLP512r1 = Curve( - "BRAINPOOLP512r1", - ecdsa.curve_brainpoolp512r1, - ecdsa.generator_brainpoolp512r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), - "brainpoolP512r1", -) - - -Ed25519 = Curve( - "Ed25519", - eddsa.curve_ed25519, - eddsa.generator_ed25519, - (1, 3, 101, 112), -) - - -Ed448 = Curve( - "Ed448", - eddsa.curve_ed448, - eddsa.generator_ed448, - (1, 3, 101, 113), -) - - -# no order in particular, but keep previously added curves first -curves = [ - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - Ed25519, - Ed448, -] - - -def find_curve(oid_curve): - """Select a curve based on its OID - - :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the - curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``. - - :raises UnknownCurveError: When the oid doesn't match any of the supported - curves - - :rtype: ~ecdsa.curves.Curve - """ - for c in curves: - if c.oid == oid_curve: - return c - raise UnknownCurveError( - "I don't know about the curve with oid %s." - "I only know about these: %s" % (oid_curve, [c.name for c in curves]) - ) - - -def curve_by_name(name): - """Select a curve based on its name. - - Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name. - Note that ``name`` is case-sensitve. - - :param str name: Name of the curve to return, like ``NIST256p`` or - ``prime256v1`` - - :raises UnknownCurveError: When the name doesn't match any of the supported - curves - - :rtype: ~ecdsa.curves.Curve - """ - for c in curves: - if name == c.name or (c.openssl_name and name == c.openssl_name): - return c - raise UnknownCurveError( - "Curve with name {0!r} unknown, only curves supported: {1}".format( - name, [c.name for c in curves] - ) - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py deleted file mode 100644 index 8b27941c4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/der.py +++ /dev/null @@ -1,409 +0,0 @@ -from __future__ import division - -import binascii -import base64 -import warnings -from itertools import chain -from six import int2byte, b, text_type -from ._compat import str_idx_as_int - - -class UnexpectedDER(Exception): - pass - - -def encode_constructed(tag, value): - return int2byte(0xA0 + tag) + encode_length(len(value)) + value - - -def encode_integer(r): - assert r >= 0 # can't support negative numbers yet - h = ("%x" % r).encode() - if len(h) % 2: - h = b("0") + h - s = binascii.unhexlify(h) - num = str_idx_as_int(s, 0) - if num <= 0x7F: - return b("\x02") + encode_length(len(s)) + s - else: - # DER integers are two's complement, so if the first byte is - # 0x80-0xff then we need an extra 0x00 byte to prevent it from - # looking negative. - return b("\x02") + encode_length(len(s) + 1) + b("\x00") + s - - -# sentry object to check if an argument was specified (used to detect -# deprecated calling convention) -_sentry = object() - - -def encode_bitstring(s, unused=_sentry): - """ - Encode a binary string as a BIT STRING using :term:`DER` encoding. - - Note, because there is no native Python object that can encode an actual - bit string, this function only accepts byte strings as the `s` argument. - The byte string is the actual bit string that will be encoded, padded - on the right (least significant bits, looking from big endian perspective) - to the first full byte. If the bit string has a bit length that is multiple - of 8, then the padding should not be included. For correct DER encoding - the padding bits MUST be set to 0. - - Number of bits of padding need to be provided as the `unused` parameter. - In case they are specified as None, it means the number of unused bits - is already encoded in the string as the first byte. - - The deprecated call convention specifies just the `s` parameters and - encodes the number of unused bits as first parameter (same convention - as with None). - - Empty string must be encoded with `unused` specified as 0. - - Future version of python-ecdsa will make specifying the `unused` argument - mandatory. - - :param s: bytes to encode - :type s: bytes like object - :param unused: number of bits at the end of `s` that are unused, must be - between 0 and 7 (inclusive) - :type unused: int or None - - :raises ValueError: when `unused` is too large or too small - - :return: `s` encoded using DER - :rtype: bytes - """ - encoded_unused = b"" - len_extra = 0 - if unused is _sentry: - warnings.warn( - "Legacy call convention used, unused= needs to be specified", - DeprecationWarning, - ) - elif unused is not None: - if not 0 <= unused <= 7: - raise ValueError("unused must be integer between 0 and 7") - if unused: - if not s: - raise ValueError("unused is non-zero but s is empty") - last = str_idx_as_int(s, -1) - if last & (2**unused - 1): - raise ValueError("unused bits must be zeros in DER") - encoded_unused = int2byte(unused) - len_extra = 1 - return b("\x03") + encode_length(len(s) + len_extra) + encoded_unused + s - - -def encode_octet_string(s): - return b("\x04") + encode_length(len(s)) + s - - -def encode_oid(first, second, *pieces): - assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second - body = b"".join( - chain( - [encode_number(40 * first + second)], - (encode_number(p) for p in pieces), - ) - ) - return b"\x06" + encode_length(len(body)) + body - - -def encode_sequence(*encoded_pieces): - total_len = sum([len(p) for p in encoded_pieces]) - return b("\x30") + encode_length(total_len) + b("").join(encoded_pieces) - - -def encode_number(n): - b128_digits = [] - while n: - b128_digits.insert(0, (n & 0x7F) | 0x80) - n = n >> 7 - if not b128_digits: - b128_digits.append(0) - b128_digits[-1] &= 0x7F - return b("").join([int2byte(d) for d in b128_digits]) - - -def is_sequence(string): - return string and string[:1] == b"\x30" - - -def remove_constructed(string): - s0 = str_idx_as_int(string, 0) - if (s0 & 0xE0) != 0xA0: - raise UnexpectedDER( - "wanted type 'constructed tag' (0xa0-0xbf), got 0x%02x" % s0 - ) - tag = s0 & 0x1F - length, llen = read_length(string[1:]) - body = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - return tag, body, rest - - -def remove_sequence(string): - if not string: - raise UnexpectedDER("Empty string does not encode a sequence") - if string[:1] != b"\x30": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'sequence' (0x30), got 0x%02x" % n) - length, lengthlength = read_length(string[1:]) - if length > len(string) - 1 - lengthlength: - raise UnexpectedDER("Length longer than the provided buffer") - endseq = 1 + lengthlength + length - return string[1 + lengthlength : endseq], string[endseq:] - - -def remove_octet_string(string): - if string[:1] != b"\x04": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n) - length, llen = read_length(string[1:]) - body = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - return body, rest - - -def remove_object(string): - if not string: - raise UnexpectedDER( - "Empty string does not encode an object identifier" - ) - if string[:1] != b"\x06": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n) - length, lengthlength = read_length(string[1:]) - body = string[1 + lengthlength : 1 + lengthlength + length] - rest = string[1 + lengthlength + length :] - if not body: - raise UnexpectedDER("Empty object identifier") - if len(body) != length: - raise UnexpectedDER( - "Length of object identifier longer than the provided buffer" - ) - numbers = [] - while body: - n, ll = read_number(body) - numbers.append(n) - body = body[ll:] - n0 = numbers.pop(0) - if n0 < 80: - first = n0 // 40 - else: - first = 2 - second = n0 - (40 * first) - numbers.insert(0, first) - numbers.insert(1, second) - return tuple(numbers), rest - - -def remove_integer(string): - if not string: - raise UnexpectedDER( - "Empty string is an invalid encoding of an integer" - ) - if string[:1] != b"\x02": - n = str_idx_as_int(string, 0) - raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n) - length, llen = read_length(string[1:]) - if length > len(string) - 1 - llen: - raise UnexpectedDER("Length longer than provided buffer") - if length == 0: - raise UnexpectedDER("0-byte long encoding of integer") - numberbytes = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - msb = str_idx_as_int(numberbytes, 0) - if not msb < 0x80: - raise UnexpectedDER("Negative integers are not supported") - # check if the encoding is the minimal one (DER requirement) - if length > 1 and not msb: - # leading zero byte is allowed if the integer would have been - # considered a negative number otherwise - smsb = str_idx_as_int(numberbytes, 1) - if smsb < 0x80: - raise UnexpectedDER( - "Invalid encoding of integer, unnecessary " - "zero padding bytes" - ) - return int(binascii.hexlify(numberbytes), 16), rest - - -def read_number(string): - number = 0 - llen = 0 - if str_idx_as_int(string, 0) == 0x80: - raise UnexpectedDER("Non minimal encoding of OID subidentifier") - # base-128 big endian, with most significant bit set in all but the last - # byte - while True: - if llen >= len(string): - raise UnexpectedDER("ran out of length bytes") - number = number << 7 - d = str_idx_as_int(string, llen) - number += d & 0x7F - llen += 1 - if not d & 0x80: - break - return number, llen - - -def encode_length(l): - assert l >= 0 - if l < 0x80: - return int2byte(l) - s = ("%x" % l).encode() - if len(s) % 2: - s = b("0") + s - s = binascii.unhexlify(s) - llen = len(s) - return int2byte(0x80 | llen) + s - - -def read_length(string): - if not string: - raise UnexpectedDER("Empty string can't encode valid length value") - num = str_idx_as_int(string, 0) - if not (num & 0x80): - # short form - return (num & 0x7F), 1 - # else long-form: b0&0x7f is number of additional base256 length bytes, - # big-endian - llen = num & 0x7F - if not llen: - raise UnexpectedDER("Invalid length encoding, length of length is 0") - if llen > len(string) - 1: - raise UnexpectedDER("Length of length longer than provided buffer") - # verify that the encoding is minimal possible (DER requirement) - msb = str_idx_as_int(string, 1) - if not msb or llen == 1 and msb < 0x80: - raise UnexpectedDER("Not minimal encoding of length") - return int(binascii.hexlify(string[1 : 1 + llen]), 16), 1 + llen - - -def remove_bitstring(string, expect_unused=_sentry): - """ - Remove a BIT STRING object from `string` following :term:`DER`. - - The `expect_unused` can be used to specify if the bit string should - have the amount of unused bits decoded or not. If it's an integer, any - read BIT STRING that has number of unused bits different from specified - value will cause UnexpectedDER exception to be raised (this is especially - useful when decoding BIT STRINGS that have DER encoded object in them; - DER encoding is byte oriented, so the unused bits will always equal 0). - - If the `expect_unused` is specified as None, the first element returned - will be a tuple, with the first value being the extracted bit string - while the second value will be the decoded number of unused bits. - - If the `expect_unused` is unspecified, the decoding of byte with - number of unused bits will not be attempted and the bit string will be - returned as-is, the callee will be required to decode it and verify its - correctness. - - Future version of python will require the `expected_unused` parameter - to be specified. - - :param string: string of bytes to extract the BIT STRING from - :type string: bytes like object - :param expect_unused: number of bits that should be unused in the BIT - STRING, or None, to return it to caller - :type expect_unused: int or None - - :raises UnexpectedDER: when the encoding does not follow DER. - - :return: a tuple with first element being the extracted bit string and - the second being the remaining bytes in the string (if any); if the - `expect_unused` is specified as None, the first element of the returned - tuple will be a tuple itself, with first element being the bit string - as bytes and the second element being the number of unused bits at the - end of the byte array as an integer - :rtype: tuple - """ - if not string: - raise UnexpectedDER("Empty string does not encode a bitstring") - if expect_unused is _sentry: - warnings.warn( - "Legacy call convention used, expect_unused= needs to be" - " specified", - DeprecationWarning, - ) - num = str_idx_as_int(string, 0) - if string[:1] != b"\x03": - raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) - length, llen = read_length(string[1:]) - if not length: - raise UnexpectedDER("Invalid length of bit string, can't be 0") - body = string[1 + llen : 1 + llen + length] - rest = string[1 + llen + length :] - if expect_unused is not _sentry: - unused = str_idx_as_int(body, 0) - if not 0 <= unused <= 7: - raise UnexpectedDER("Invalid encoding of unused bits") - if expect_unused is not None and expect_unused != unused: - raise UnexpectedDER("Unexpected number of unused bits") - body = body[1:] - if unused: - if not body: - raise UnexpectedDER("Invalid encoding of empty bit string") - last = str_idx_as_int(body, -1) - # verify that all the unused bits are set to zero (DER requirement) - if last & (2**unused - 1): - raise UnexpectedDER("Non zero padding bits in bit string") - if expect_unused is None: - body = (body, unused) - return body, rest - - -# SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) - - -# signatures: (from RFC3279) -# ansi-X9-62 OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) 10045 } -# -# id-ecSigType OBJECT IDENTIFIER ::= { -# ansi-X9-62 signatures(4) } -# ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { -# id-ecSigType 1 } -# so 1,2,840,10045,4,1 -# so 0x42, .. .. - -# Ecdsa-Sig-Value ::= SEQUENCE { -# r INTEGER, -# s INTEGER } - -# id-public-key-type OBJECT IDENTIFIER ::= { ansi-X9.62 2 } -# -# id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 } - -# I think the secp224r1 identifier is (t=06,l=05,v=2b81040021) -# secp224r1 OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) curve(0) 33 } -# and the secp384r1 is (t=06,l=05,v=2b81040022) -# secp384r1 OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) curve(0) 34 } - - -def unpem(pem): - if isinstance(pem, text_type): # pragma: no branch - pem = pem.encode() - - d = b("").join( - [ - l.strip() - for l in pem.split(b("\n")) - if l and not l.startswith(b("-----")) - ] - ) - return base64.b64decode(d) - - -def topem(der, name): - b64 = base64.b64encode(der) - lines = [("-----BEGIN %s-----\n" % name).encode()] - lines.extend( - [b64[start : start + 64] + b("\n") for start in range(0, len(b64), 64)] - ) - lines.append(("-----END %s-----\n" % name).encode()) - return b("").join(lines) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py deleted file mode 100644 index 7f697d9a3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdh.py +++ /dev/null @@ -1,336 +0,0 @@ -""" -Class for performing Elliptic-curve Diffie-Hellman (ECDH) operations. -""" - -from .util import number_to_string -from .ellipticcurve import INFINITY -from .keys import SigningKey, VerifyingKey - - -__all__ = [ - "ECDH", - "NoKeyError", - "NoCurveError", - "InvalidCurveError", - "InvalidSharedSecretError", -] - - -class NoKeyError(Exception): - """ECDH. Key not found but it is needed for operation.""" - - pass - - -class NoCurveError(Exception): - """ECDH. Curve not set but it is needed for operation.""" - - pass - - -class InvalidCurveError(Exception): - """ - ECDH. Raised in case the public and private keys use different curves. - """ - - pass - - -class InvalidSharedSecretError(Exception): - """ECDH. Raised in case the shared secret we obtained is an INFINITY.""" - - pass - - -class ECDH(object): - """ - Elliptic-curve Diffie-Hellman (ECDH). A key agreement protocol. - - Allows two parties, each having an elliptic-curve public-private key - pair, to establish a shared secret over an insecure channel - """ - - def __init__(self, curve=None, private_key=None, public_key=None): - """ - ECDH init. - - Call can be initialised without parameters, then the first operation - (loading either key) will set the used curve. - All parameters must be ultimately set before shared secret - calculation will be allowed. - - :param curve: curve for operations - :type curve: Curve - :param private_key: `my` private key for ECDH - :type private_key: SigningKey - :param public_key: `their` public key for ECDH - :type public_key: VerifyingKey - """ - self.curve = curve - self.private_key = None - self.public_key = None - if private_key: - self.load_private_key(private_key) - if public_key: - self.load_received_public_key(public_key) - - def _get_shared_secret(self, remote_public_key): - if not self.private_key: - raise NoKeyError( - "Private key needs to be set to create shared secret" - ) - if not self.public_key: - raise NoKeyError( - "Public key needs to be set to create shared secret" - ) - if not ( - self.private_key.curve == self.curve == remote_public_key.curve - ): - raise InvalidCurveError( - "Curves for public key and private key is not equal." - ) - - # shared secret = PUBKEYtheirs * PRIVATEKEYours - result = ( - remote_public_key.pubkey.point - * self.private_key.privkey.secret_multiplier - ) - if result == INFINITY: - raise InvalidSharedSecretError("Invalid shared secret (INFINITY).") - - return result.x() - - def set_curve(self, key_curve): - """ - Set the working curve for ecdh operations. - - :param key_curve: curve from `curves` module - :type key_curve: Curve - """ - self.curve = key_curve - - def generate_private_key(self): - """ - Generate local private key for ecdh operation with curve that was set. - - :raises NoCurveError: Curve must be set before key generation. - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - if not self.curve: - raise NoCurveError("Curve must be set prior to key generation.") - return self.load_private_key(SigningKey.generate(curve=self.curve)) - - def load_private_key(self, private_key): - """ - Load private key from SigningKey (keys.py) object. - - Needs to have the same curve as was set with set_curve method. - If curve is not set - it sets from this SigningKey - - :param private_key: Initialised SigningKey class - :type private_key: SigningKey - - :raises InvalidCurveError: private_key curve not the same as self.curve - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - if not self.curve: - self.curve = private_key.curve - if self.curve != private_key.curve: - raise InvalidCurveError("Curve mismatch.") - self.private_key = private_key - return self.private_key.get_verifying_key() - - def load_private_key_bytes(self, private_key): - """ - Load private key from byte string. - - Uses current curve and checks if the provided key matches - the curve of ECDH key agreement. - Key loads via from_string method of SigningKey class - - :param private_key: private key in bytes string format - :type private_key: :term:`bytes-like object` - - :raises NoCurveError: Curve must be set before loading. - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - if not self.curve: - raise NoCurveError("Curve must be set prior to key load.") - return self.load_private_key( - SigningKey.from_string(private_key, curve=self.curve) - ) - - def load_private_key_der(self, private_key_der): - """ - Load private key from DER byte string. - - Compares the curve of the DER-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only DER format supported is the RFC5915 - Look at keys.py:SigningKey.from_der() - - :param private_key_der: string with the DER encoding of private ECDSA - key - :type private_key_der: string - - :raises InvalidCurveError: private_key curve not the same as self.curve - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - return self.load_private_key(SigningKey.from_der(private_key_der)) - - def load_private_key_pem(self, private_key_pem): - """ - Load private key from PEM string. - - Compares the curve of the DER-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only PEM format supported is the RFC5915 - Look at keys.py:SigningKey.from_pem() - it needs to have `EC PRIVATE KEY` section - - :param private_key_pem: string with PEM-encoded private ECDSA key - :type private_key_pem: string - - :raises InvalidCurveError: private_key curve not the same as self.curve - - :return: public (verifying) key from this private key. - :rtype: VerifyingKey - """ - return self.load_private_key(SigningKey.from_pem(private_key_pem)) - - def get_public_key(self): - """ - Provides a public key that matches the local private key. - - Needs to be sent to the remote party. - - :return: public (verifying) key from local private key. - :rtype: VerifyingKey - """ - return self.private_key.get_verifying_key() - - def load_received_public_key(self, public_key): - """ - Load public key from VerifyingKey (keys.py) object. - - Needs to have the same curve as set as current for ecdh operation. - If curve is not set - it sets it from VerifyingKey. - - :param public_key: Initialised VerifyingKey class - :type public_key: VerifyingKey - - :raises InvalidCurveError: public_key curve not the same as self.curve - """ - if not self.curve: - self.curve = public_key.curve - if self.curve != public_key.curve: - raise InvalidCurveError("Curve mismatch.") - self.public_key = public_key - - def load_received_public_key_bytes( - self, public_key_str, valid_encodings=None - ): - """ - Load public key from byte string. - - Uses current curve and checks if key length corresponds to - the current curve. - Key loads via from_string method of VerifyingKey class - - :param public_key_str: public key in bytes string format - :type public_key_str: :term:`bytes-like object` - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - """ - return self.load_received_public_key( - VerifyingKey.from_string( - public_key_str, self.curve, valid_encodings - ) - ) - - def load_received_public_key_der(self, public_key_der): - """ - Load public key from DER byte string. - - Compares the curve of the DER-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only DER format supported is the RFC5912 - Look at keys.py:VerifyingKey.from_der() - - :param public_key_der: string with the DER encoding of public ECDSA key - :type public_key_der: string - - :raises InvalidCurveError: public_key curve not the same as self.curve - """ - return self.load_received_public_key( - VerifyingKey.from_der(public_key_der) - ) - - def load_received_public_key_pem(self, public_key_pem): - """ - Load public key from PEM string. - - Compares the curve of the PEM-encoded key with the ECDH set curve, - uses the former if unset. - - Note, the only PEM format supported is the RFC5912 - Look at keys.py:VerifyingKey.from_pem() - - :param public_key_pem: string with PEM-encoded public ECDSA key - :type public_key_pem: string - - :raises InvalidCurveError: public_key curve not the same as self.curve - """ - return self.load_received_public_key( - VerifyingKey.from_pem(public_key_pem) - ) - - def generate_sharedsecret_bytes(self): - """ - Generate shared secret from local private key and remote public key. - - The objects needs to have both private key and received public key - before generation is allowed. - - :raises InvalidCurveError: public_key curve not the same as self.curve - :raises NoKeyError: public_key or private_key is not set - - :return: shared secret - :rtype: bytes - """ - return number_to_string( - self.generate_sharedsecret(), self.private_key.curve.curve.p() - ) - - def generate_sharedsecret(self): - """ - Generate shared secret from local private key and remote public key. - - The objects needs to have both private key and received public key - before generation is allowed. - - It's the same for local and remote party, - shared secret(local private key, remote public key) == - shared secret(local public key, remote private key) - - :raises InvalidCurveError: public_key curve not the same as self.curve - :raises NoKeyError: public_key or private_key is not set - - :return: shared secret - :rtype: int - """ - return self._get_shared_secret(self.public_key) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py deleted file mode 100644 index 332828168..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ecdsa.py +++ /dev/null @@ -1,859 +0,0 @@ -#! /usr/bin/env python - -""" -Low level implementation of Elliptic-Curve Digital Signatures. - -.. note :: - You're most likely looking for the :py:class:`~ecdsa.keys` module. - This is a low-level implementation of the ECDSA that operates on - integers, not byte strings. - -NOTE: This a low level implementation of ECDSA, for normal applications -you should be looking at the keys.py module. - -Classes and methods for elliptic-curve signatures: -private keys, public keys, signatures, -and definitions of prime-modulus curves. - -Example: - -.. code-block:: python - - # (In real-life applications, you would probably want to - # protect against defects in SystemRandom.) - from random import SystemRandom - randrange = SystemRandom().randrange - - # Generate a public/private key pair using the NIST Curve P-192: - - g = generator_192 - n = g.order() - secret = randrange( 1, n ) - pubkey = Public_key( g, g * secret ) - privkey = Private_key( pubkey, secret ) - - # Signing a hash value: - - hash = randrange( 1, n ) - signature = privkey.sign( hash, randrange( 1, n ) ) - - # Verifying a signature for a hash value: - - if pubkey.verifies( hash, signature ): - print_("Demo verification succeeded.") - else: - print_("*** Demo verification failed.") - - # Verification fails if the hash value is modified: - - if pubkey.verifies( hash-1, signature ): - print_("**** Demo verification failed to reject tampered hash.") - else: - print_("Demo verification correctly rejected tampered hash.") - -Revision history: - 2005.12.31 - Initial version. - - 2008.11.25 - Substantial revisions introducing new classes. - - 2009.05.16 - Warn against using random.randrange in real applications. - - 2009.05.17 - Use random.SystemRandom by default. - -Originally written in 2005 by Peter Pearson and placed in the public domain, -modified as part of the python-ecdsa package. -""" - -from six import int2byte, b -from . import ellipticcurve -from . import numbertheory -from .util import bit_length -from ._compat import remove_whitespace - - -class RSZeroError(RuntimeError): - pass - - -class InvalidPointError(RuntimeError): - pass - - -class Signature(object): - """ - ECDSA signature. - - :ivar int r: the ``r`` element of the ECDSA signature - :ivar int s: the ``s`` element of the ECDSA signature - """ - - def __init__(self, r, s): - self.r = r - self.s = s - - def recover_public_keys(self, hash, generator): - """ - Returns two public keys for which the signature is valid - - :param int hash: signed hash - :param AbstractPoint generator: is the generator used in creation - of the signature - :rtype: tuple(Public_key, Public_key) - :return: a pair of public keys that can validate the signature - """ - curve = generator.curve() - n = generator.order() - r = self.r - s = self.s - e = hash - x = r - - # Compute the curve point with x as x-coordinate - alpha = ( - pow(x, 3, curve.p()) + (curve.a() * x) + curve.b() - ) % curve.p() - beta = numbertheory.square_root_mod_prime(alpha, curve.p()) - y = beta if beta % 2 == 0 else curve.p() - beta - - # Compute the public key - R1 = ellipticcurve.PointJacobi(curve, x, y, 1, n) - Q1 = numbertheory.inverse_mod(r, n) * (s * R1 + (-e % n) * generator) - Pk1 = Public_key(generator, Q1) - - # And the second solution - R2 = ellipticcurve.PointJacobi(curve, x, -y, 1, n) - Q2 = numbertheory.inverse_mod(r, n) * (s * R2 + (-e % n) * generator) - Pk2 = Public_key(generator, Q2) - - return [Pk1, Pk2] - - -class Public_key(object): - """Public key for ECDSA.""" - - def __init__(self, generator, point, verify=True): - """Low level ECDSA public key object. - - :param generator: the Point that generates the group (the base point) - :param point: the Point that defines the public key - :param bool verify: if True check if point is valid point on curve - - :raises InvalidPointError: if the point parameters are invalid or - point does not lay on the curve - """ - - self.curve = generator.curve() - self.generator = generator - self.point = point - n = generator.order() - p = self.curve.p() - if not (0 <= point.x() < p) or not (0 <= point.y() < p): - raise InvalidPointError( - "The public point has x or y out of range." - ) - if verify and not self.curve.contains_point(point.x(), point.y()): - raise InvalidPointError("Point does not lay on the curve") - if not n: - raise InvalidPointError("Generator point must have order.") - # for curve parameters with base point with cofactor 1, all points - # that are on the curve are scalar multiples of the base point, so - # verifying that is not necessary. See Section 3.2.2.1 of SEC 1 v2 - if ( - verify - and self.curve.cofactor() != 1 - and not n * point == ellipticcurve.INFINITY - ): - raise InvalidPointError("Generator point order is bad.") - - def __eq__(self, other): - """Return True if the keys are identical, False otherwise. - - Note: for comparison, only placement on the same curve and point - equality is considered, use of the same generator point is not - considered. - """ - if isinstance(other, Public_key): - return self.curve == other.curve and self.point == other.point - return NotImplemented - - def __ne__(self, other): - """Return False if the keys are identical, True otherwise.""" - return not self == other - - def verifies(self, hash, signature): - """Verify that signature is a valid signature of hash. - Return True if the signature is valid. - """ - - # From X9.62 J.3.1. - - G = self.generator - n = G.order() - r = signature.r - s = signature.s - if r < 1 or r > n - 1: - return False - if s < 1 or s > n - 1: - return False - c = numbertheory.inverse_mod(s, n) - u1 = (hash * c) % n - u2 = (r * c) % n - if hasattr(G, "mul_add"): - xy = G.mul_add(u1, self.point, u2) - else: - xy = u1 * G + u2 * self.point - v = xy.x() % n - return v == r - - -class Private_key(object): - """Private key for ECDSA.""" - - def __init__(self, public_key, secret_multiplier): - """public_key is of class Public_key; - secret_multiplier is a large integer. - """ - - self.public_key = public_key - self.secret_multiplier = secret_multiplier - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, Private_key): - return ( - self.public_key == other.public_key - and self.secret_multiplier == other.secret_multiplier - ) - return NotImplemented - - def __ne__(self, other): - """Return False if the points are identical, True otherwise.""" - return not self == other - - def sign(self, hash, random_k): - """Return a signature for the provided hash, using the provided - random nonce. It is absolutely vital that random_k be an unpredictable - number in the range [1, self.public_key.point.order()-1]. If - an attacker can guess random_k, he can compute our private key from a - single signature. Also, if an attacker knows a few high-order - bits (or a few low-order bits) of random_k, he can compute our private - key from many signatures. The generation of nonces with adequate - cryptographic strength is very difficult and far beyond the scope - of this comment. - - May raise RuntimeError, in which case retrying with a new - random value k is in order. - """ - - G = self.public_key.generator - n = G.order() - k = random_k % n - # Fix the bit-length of the random nonce, - # so that it doesn't leak via timing. - # This does not change that ks = k mod n - ks = k + n - kt = ks + n - if bit_length(ks) == bit_length(n): - p1 = kt * G - else: - p1 = ks * G - r = p1.x() % n - if r == 0: - raise RSZeroError("amazingly unlucky random number r") - s = ( - numbertheory.inverse_mod(k, n) - * (hash + (self.secret_multiplier * r) % n) - ) % n - if s == 0: - raise RSZeroError("amazingly unlucky random number s") - return Signature(r, s) - - -def int_to_string(x): - """Convert integer x into a string of bytes, as per X9.62.""" - assert x >= 0 - if x == 0: - return b("\0") - result = [] - while x: - ordinal = x & 0xFF - result.append(int2byte(ordinal)) - x >>= 8 - - result.reverse() - return b("").join(result) - - -def string_to_int(s): - """Convert a string of bytes into an integer, as per X9.62.""" - result = 0 - for c in s: - if not isinstance(c, int): - c = ord(c) - result = 256 * result + c - return result - - -def digest_integer(m): - """Convert an integer into a string of bytes, compute - its SHA-1 hash, and convert the result to an integer.""" - # - # I don't expect this function to be used much. I wrote - # it in order to be able to duplicate the examples - # in ECDSAVS. - # - from hashlib import sha1 - - return string_to_int(sha1(int_to_string(m)).digest()) - - -def point_is_valid(generator, x, y): - """Is (x,y) a valid public key based on the specified generator?""" - - # These are the tests specified in X9.62. - - n = generator.order() - curve = generator.curve() - p = curve.p() - if not (0 <= x < p) or not (0 <= y < p): - return False - if not curve.contains_point(x, y): - return False - if ( - curve.cofactor() != 1 - and not n * ellipticcurve.PointJacobi(curve, x, y, 1) - == ellipticcurve.INFINITY - ): - return False - return True - - -# secp112r1 curve -_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) -# s = 00F50B02 8E4D696E 67687561 51752904 72783FB1 -_a = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD2088"), 16) -_b = int(remove_whitespace("659E F8BA0439 16EEDE89 11702B22"), 16) -_Gx = int(remove_whitespace("09487239 995A5EE7 6B55F9C2 F098"), 16) -_Gy = int(remove_whitespace("A89C E5AF8724 C0A23E0E 0FF77500"), 16) -_r = int(remove_whitespace("DB7C 2ABF62E3 5E7628DF AC6561C5"), 16) -_h = 1 -curve_112r1 = ellipticcurve.CurveFp(_p, _a, _b, _h) -generator_112r1 = ellipticcurve.PointJacobi( - curve_112r1, _Gx, _Gy, 1, _r, generator=True -) - - -# secp112r2 curve -_p = int(remove_whitespace("DB7C 2ABF62E3 5E668076 BEAD208B"), 16) -# s = 022757A1 114D69E 67687561 51755316 C05E0BD4 -_a = int(remove_whitespace("6127 C24C05F3 8A0AAAF6 5C0EF02C"), 16) -_b = int(remove_whitespace("51DE F1815DB5 ED74FCC3 4C85D709"), 16) -_Gx = int(remove_whitespace("4BA30AB5 E892B4E1 649DD092 8643"), 16) -_Gy = int(remove_whitespace("ADCD 46F5882E 3747DEF3 6E956E97"), 16) -_r = int(remove_whitespace("36DF 0AAFD8B8 D7597CA1 0520D04B"), 16) -_h = 4 -curve_112r2 = ellipticcurve.CurveFp(_p, _a, _b, _h) -generator_112r2 = ellipticcurve.PointJacobi( - curve_112r2, _Gx, _Gy, 1, _r, generator=True -) - - -# secp128r1 curve -_p = int(remove_whitespace("FFFFFFFD FFFFFFFF FFFFFFFF FFFFFFFF"), 16) -# S = 000E0D4D 69E6768 75615175 0CC03A44 73D03679 -# a and b are mod p, so a is equal to p-3, or simply -3 -# _a = -3 -_b = int(remove_whitespace("E87579C1 1079F43D D824993C 2CEE5ED3"), 16) -_Gx = int(remove_whitespace("161FF752 8B899B2D 0C28607C A52C5B86"), 16) -_Gy = int(remove_whitespace("CF5AC839 5BAFEB13 C02DA292 DDED7A83"), 16) -_r = int(remove_whitespace("FFFFFFFE 00000000 75A30D1B 9038A115"), 16) -_h = 1 -curve_128r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) -generator_128r1 = ellipticcurve.PointJacobi( - curve_128r1, _Gx, _Gy, 1, _r, generator=True -) - - -# secp160r1 -_p = int(remove_whitespace("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 7FFFFFFF"), 16) -# S = 1053CDE4 2C14D696 E6768756 1517533B F3F83345 -# a and b are mod p, so a is equal to p-3, or simply -3 -# _a = -3 -_b = int(remove_whitespace("1C97BEFC 54BD7A8B 65ACF89F 81D4D4AD C565FA45"), 16) -_Gx = int( - remove_whitespace("4A96B568 8EF57328 46646989 68C38BB9 13CBFC82"), - 16, -) -_Gy = int( - remove_whitespace("23A62855 3168947D 59DCC912 04235137 7AC5FB32"), - 16, -) -_r = int( - remove_whitespace("01 00000000 00000000 0001F4C8 F927AED3 CA752257"), - 16, -) -_h = 1 -curve_160r1 = ellipticcurve.CurveFp(_p, -3, _b, _h) -generator_160r1 = ellipticcurve.PointJacobi( - curve_160r1, _Gx, _Gy, 1, _r, generator=True -) - - -# NIST Curve P-192: -_p = 6277101735386680763835789423207666416083908700390324961279 -_r = 6277101735386680763835789423176059013767194773182842284081 -# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L -# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L -_b = int( - remove_whitespace( - """ - 64210519 E59C80E7 0FA7E9AB 72243049 FEB8DEEC C146B9B1""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 188DA80E B03090F6 7CBF20EB 43A18800 F4FF0AFD 82FF1012""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 07192B95 FFC8DA78 631011ED 6B24CDD5 73F977A1 1E794811""" - ), - 16, -) - -curve_192 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_192 = ellipticcurve.PointJacobi( - curve_192, _Gx, _Gy, 1, _r, generator=True -) - - -# NIST Curve P-224: -_p = int( - remove_whitespace( - """ - 2695994666715063979466701508701963067355791626002630814351 - 0066298881""" - ) -) -_r = int( - remove_whitespace( - """ - 2695994666715063979466701508701962594045780771442439172168 - 2722368061""" - ) -) -# s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L -# c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL -_b = int( - remove_whitespace( - """ - B4050A85 0C04B3AB F5413256 5044B0B7 D7BFD8BA 270B3943 - 2355FFB4""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - B70E0CBD 6BB4BF7F 321390B9 4A03C1D3 56C21122 343280D6 - 115C1D21""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - BD376388 B5F723FB 4C22DFE6 CD4375A0 5A074764 44D58199 - 85007E34""" - ), - 16, -) - -curve_224 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_224 = ellipticcurve.PointJacobi( - curve_224, _Gx, _Gy, 1, _r, generator=True -) - -# NIST Curve P-256: -_p = int( - remove_whitespace( - """ - 1157920892103562487626974469494075735300861434152903141955 - 33631308867097853951""" - ) -) -_r = int( - remove_whitespace( - """ - 115792089210356248762697446949407573529996955224135760342 - 422259061068512044369""" - ) -) -# s = 0xc49d360886e704936a6678e1139d26b7819f7e90L -# c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL -_b = int( - remove_whitespace( - """ - 5AC635D8 AA3A93E7 B3EBBD55 769886BC 651D06B0 CC53B0F6 - 3BCE3C3E 27D2604B""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 6B17D1F2 E12C4247 F8BCE6E5 63A440F2 77037D81 2DEB33A0 - F4A13945 D898C296""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 2BCE3357 6B315ECE - CBB64068 37BF51F5""" - ), - 16, -) - -curve_256 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_256 = ellipticcurve.PointJacobi( - curve_256, _Gx, _Gy, 1, _r, generator=True -) - -# NIST Curve P-384: -_p = int( - remove_whitespace( - """ - 3940200619639447921227904010014361380507973927046544666794 - 8293404245721771496870329047266088258938001861606973112319""" - ) -) -_r = int( - remove_whitespace( - """ - 3940200619639447921227904010014361380507973927046544666794 - 6905279627659399113263569398956308152294913554433653942643""" - ) -) -# s = 0xa335926aa319a27a1d00896a6773a4827acdac73L -# c = int(remove_whitespace( -# """ -# 79d1e655 f868f02f ff48dcde e14151dd b80643c1 406d0ca1 -# 0dfe6fc5 2009540a 495e8042 ea5f744f 6e184667 cc722483""" -# ), 16) -_b = int( - remove_whitespace( - """ - B3312FA7 E23EE7E4 988E056B E3F82D19 181D9C6E FE814112 - 0314088F 5013875A C656398D 8A2ED19D 2A85C8ED D3EC2AEF""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - AA87CA22 BE8B0537 8EB1C71E F320AD74 6E1D3B62 8BA79B98 - 59F741E0 82542A38 5502F25D BF55296C 3A545E38 72760AB7""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 3617DE4A 96262C6F 5D9E98BF 9292DC29 F8F41DBD 289A147C - E9DA3113 B5F0B8C0 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F""" - ), - 16, -) - -curve_384 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_384 = ellipticcurve.PointJacobi( - curve_384, _Gx, _Gy, 1, _r, generator=True -) - -# NIST Curve P-521: -_p = int( - "686479766013060971498190079908139321726943530014330540939" - "446345918554318339765605212255964066145455497729631139148" - "0858037121987999716643812574028291115057151" -) -_r = int( - "686479766013060971498190079908139321726943530014330540939" - "446345918554318339765539424505774633321719753296399637136" - "3321113864768612440380340372808892707005449" -) -# s = 0xd09e8800291cb85396cc6717393284aaa0da64baL -# c = int(remove_whitespace( -# """ -# 0b4 8bfa5f42 0a349495 39d2bdfc 264eeeeb 077688e4 -# 4fbf0ad8 f6d0edb3 7bd6b533 28100051 8e19f1b9 ffbe0fe9 -# ed8a3c22 00b8f875 e523868c 70c1e5bf 55bad637""" -# ), 16) -_b = int( - remove_whitespace( - """ - 051 953EB961 8E1C9A1F 929A21A0 B68540EE A2DA725B - 99B315F3 B8B48991 8EF109E1 56193951 EC7E937B 1652C0BD - 3BB1BF07 3573DF88 3D2C34F1 EF451FD4 6B503F00""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - C6 858E06B7 0404E9CD 9E3ECB66 2395B442 9C648139 - 053FB521 F828AF60 6B4D3DBA A14B5E77 EFE75928 FE1DC127 - A2FFA8DE 3348B3C1 856A429B F97E7E31 C2E5BD66""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 118 39296A78 9A3BC004 5C8A5FB4 2C7D1BD9 98F54449 - 579B4468 17AFBD17 273E662C 97EE7299 5EF42640 C550B901 - 3FAD0761 353C7086 A272C240 88BE9476 9FD16650""" - ), - 16, -) - -curve_521 = ellipticcurve.CurveFp(_p, -3, _b, 1) -generator_521 = ellipticcurve.PointJacobi( - curve_521, _Gx, _Gy, 1, _r, generator=True -) - -# Certicom secp256-k1 -_a = 0x0000000000000000000000000000000000000000000000000000000000000000 -_b = 0x0000000000000000000000000000000000000000000000000000000000000007 -_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F -_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 -_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 -_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - -curve_secp256k1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_secp256k1 = ellipticcurve.PointJacobi( - curve_secp256k1, _Gx, _Gy, 1, _r, generator=True -) - -# Brainpool P-160-r1 -_a = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 -_b = 0x1E589A8595423412134FAA2DBDEC95C8D8675E58 -_p = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F -_Gx = 0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3 -_Gy = 0x1667CB477A1A8EC338F94741669C976316DA6321 -_q = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 - -curve_brainpoolp160r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp160r1 = ellipticcurve.PointJacobi( - curve_brainpoolp160r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-192-r1 -_a = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF -_b = 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9 -_p = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297 -_Gx = 0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6 -_Gy = 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F -_q = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 - -curve_brainpoolp192r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp192r1 = ellipticcurve.PointJacobi( - curve_brainpoolp192r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-224-r1 -_a = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 -_b = 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B -_p = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF -_Gx = 0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D -_Gy = 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD -_q = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F - -curve_brainpoolp224r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp224r1 = ellipticcurve.PointJacobi( - curve_brainpoolp224r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-256-r1 -_a = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 -_b = 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 -_p = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 -_Gx = 0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262 -_Gy = 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 -_q = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 - -curve_brainpoolp256r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp256r1 = ellipticcurve.PointJacobi( - curve_brainpoolp256r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-320-r1 -_a = int( - remove_whitespace( - """ - 3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9 - F492F375A97D860EB4""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539 - 816F5EB4AC8FB1F1A6""" - ), - 16, -) -_p = int( - remove_whitespace( - """ - D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC - 28FCD412B1F1B32E27""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599 - C710AF8D0D39E20611""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6A - C7D35245D1692E8EE1""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - D35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658 - E98691555B44C59311""" - ), - 16, -) - -curve_brainpoolp320r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp320r1 = ellipticcurve.PointJacobi( - curve_brainpoolp320r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-384-r1 -_a = int( - remove_whitespace( - """ - 7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F9 - 0F8AA5814A503AD4EB04A8C7DD22CE2826""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62 - D57CB4390295DBC9943AB78696FA504C11""" - ), - 16, -) -_p = int( - remove_whitespace( - """ - 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB711 - 23ACD3A729901D1A71874700133107EC53""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10 - E8E826E03436D646AAEF87B2E247D4AF1E""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF991292 - 80E4646217791811142820341263C5315""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - 8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425 - A7CF3AB6AF6B7FC3103B883202E9046565""" - ), - 16, -) - -curve_brainpoolp384r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp384r1 = ellipticcurve.PointJacobi( - curve_brainpoolp384r1, _Gx, _Gy, 1, _q, generator=True -) - -# Brainpool P-512-r1 -_a = int( - remove_whitespace( - """ - 7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863 - BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA""" - ), - 16, -) -_b = int( - remove_whitespace( - """ - 3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117 - A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723""" - ), - 16, -) -_p = int( - remove_whitespace( - """ - AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 - 717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3""" - ), - 16, -) -_Gx = int( - remove_whitespace( - """ - 81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D009 - 8EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822""" - ), - 16, -) -_Gy = int( - remove_whitespace( - """ - 7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F81 - 11B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892""" - ), - 16, -) -_q = int( - remove_whitespace( - """ - AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308 - 70553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069""" - ), - 16, -) - -curve_brainpoolp512r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) -generator_brainpoolp512r1 = ellipticcurve.PointJacobi( - curve_brainpoolp512r1, _Gx, _Gy, 1, _q, generator=True -) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py deleted file mode 100644 index 9769cfd8e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/eddsa.py +++ /dev/null @@ -1,252 +0,0 @@ -"""Implementation of Edwards Digital Signature Algorithm.""" - -import hashlib -from ._sha3 import shake_256 -from . import ellipticcurve -from ._compat import ( - remove_whitespace, - bit_length, - bytes_to_int, - int_to_bytes, - compat26_str, -) - -# edwards25519, defined in RFC7748 -_p = 2**255 - 19 -_a = -1 -_d = int( - remove_whitespace( - "370957059346694393431380835087545651895421138798432190163887855330" - "85940283555" - ) -) -_h = 8 - -_Gx = int( - remove_whitespace( - "151122213495354007725011514095885315114540126930418572060461132" - "83949847762202" - ) -) -_Gy = int( - remove_whitespace( - "463168356949264781694283940034751631413079938662562256157830336" - "03165251855960" - ) -) -_r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED - - -def _sha512(data): - return hashlib.new("sha512", compat26_str(data)).digest() - - -curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) -generator_ed25519 = ellipticcurve.PointEdwards( - curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True -) - - -# edwards448, defined in RFC7748 -_p = 2**448 - 2**224 - 1 -_a = 1 -_d = -39081 % _p -_h = 4 - -_Gx = int( - remove_whitespace( - "224580040295924300187604334099896036246789641632564134246125461" - "686950415467406032909029192869357953282578032075146446173674602635" - "247710" - ) -) -_Gy = int( - remove_whitespace( - "298819210078481492676017930443930673437544040154080242095928241" - "372331506189835876003536878655418784733982303233503462500531545062" - "832660" - ) -) -_r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D - - -def _shake256(data): - return shake_256(data, 114) - - -curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) -generator_ed448 = ellipticcurve.PointEdwards( - curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True -) - - -class PublicKey(object): - """Public key for the Edwards Digital Signature Algorithm.""" - - def __init__(self, generator, public_key, public_point=None): - self.generator = generator - self.curve = generator.curve() - self.__encoded = public_key - # plus one for the sign bit and round up - self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 - if len(public_key) != self.baselen: - raise ValueError( - "Incorrect size of the public key, expected: {0} bytes".format( - self.baselen - ) - ) - if public_point: - self.__point = public_point - else: - self.__point = ellipticcurve.PointEdwards.from_bytes( - self.curve, public_key - ) - - def __eq__(self, other): - if isinstance(other, PublicKey): - return ( - self.curve == other.curve and self.__encoded == other.__encoded - ) - return NotImplemented - - def __ne__(self, other): - return not self == other - - @property - def point(self): - return self.__point - - @point.setter - def point(self, other): - if self.__point != other: - raise ValueError("Can't change the coordinates of the point") - self.__point = other - - def public_point(self): - return self.__point - - def public_key(self): - return self.__encoded - - def verify(self, data, signature): - """Verify a Pure EdDSA signature over data.""" - data = compat26_str(data) - if len(signature) != 2 * self.baselen: - raise ValueError( - "Invalid signature length, expected: {0} bytes".format( - 2 * self.baselen - ) - ) - R = ellipticcurve.PointEdwards.from_bytes( - self.curve, signature[: self.baselen] - ) - S = bytes_to_int(signature[self.baselen :], "little") - if S >= self.generator.order(): - raise ValueError("Invalid signature") - - dom = bytearray() - if self.curve == curve_ed448: - dom = bytearray(b"SigEd448" + b"\x00\x00") - - k = bytes_to_int( - self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), - "little", - ) - - if self.generator * S != self.__point * k + R: - raise ValueError("Invalid signature") - - return True - - -class PrivateKey(object): - """Private key for the Edwards Digital Signature Algorithm.""" - - def __init__(self, generator, private_key): - self.generator = generator - self.curve = generator.curve() - # plus one for the sign bit and round up - self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 - if len(private_key) != self.baselen: - raise ValueError( - "Incorrect size of private key, expected: {0} bytes".format( - self.baselen - ) - ) - self.__private_key = bytes(private_key) - self.__h = bytearray(self.curve.hash_func(private_key)) - self.__public_key = None - - a = self.__h[: self.baselen] - a = self._key_prune(a) - scalar = bytes_to_int(a, "little") - self.__s = scalar - - @property - def private_key(self): - return self.__private_key - - def __eq__(self, other): - if isinstance(other, PrivateKey): - return ( - self.curve == other.curve - and self.__private_key == other.__private_key - ) - return NotImplemented - - def __ne__(self, other): - return not self == other - - def _key_prune(self, key): - # make sure the key is not in a small subgroup - h = self.curve.cofactor() - if h == 4: - h_log = 2 - elif h == 8: - h_log = 3 - else: - raise ValueError("Only cofactor 4 and 8 curves supported") - key[0] &= ~((1 << h_log) - 1) - - # ensure the highest bit is set but no higher - l = bit_length(self.curve.p()) - if l % 8 == 0: - key[-1] = 0 - key[-2] |= 0x80 - else: - key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 - return key - - def public_key(self): - """Generate the public key based on the included private key""" - if self.__public_key: - return self.__public_key - - public_point = self.generator * self.__s - - self.__public_key = PublicKey( - self.generator, public_point.to_bytes(), public_point - ) - - return self.__public_key - - def sign(self, data): - """Perform a Pure EdDSA signature over data.""" - data = compat26_str(data) - A = self.public_key().public_key() - - prefix = self.__h[self.baselen :] - - dom = bytearray() - if self.curve == curve_ed448: - dom = bytearray(b"SigEd448" + b"\x00\x00") - - r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") - R = (self.generator * r).to_bytes() - - k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") - k %= self.generator.order() - - S = (r + k * self.__s) % self.generator.order() - - return R + int_to_bytes(S, self.baselen, "little") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py deleted file mode 100644 index d6f714630..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/ellipticcurve.py +++ /dev/null @@ -1,1584 +0,0 @@ -#! /usr/bin/env python -# -*- coding: utf-8 -*- -# -# Implementation of elliptic curves, for cryptographic applications. -# -# This module doesn't provide any way to choose a random elliptic -# curve, nor to verify that an elliptic curve was chosen randomly, -# because one can simply use NIST's standard curves. -# -# Notes from X9.62-1998 (draft): -# Nomenclature: -# - Q is a public key. -# The "Elliptic Curve Domain Parameters" include: -# - q is the "field size", which in our case equals p. -# - p is a big prime. -# - G is a point of prime order (5.1.1.1). -# - n is the order of G (5.1.1.1). -# Public-key validation (5.2.2): -# - Verify that Q is not the point at infinity. -# - Verify that X_Q and Y_Q are in [0,p-1]. -# - Verify that Q is on the curve. -# - Verify that nQ is the point at infinity. -# Signature generation (5.3): -# - Pick random k from [1,n-1]. -# Signature checking (5.4.2): -# - Verify that r and s are in [1,n-1]. -# -# Revision history: -# 2005.12.31 - Initial version. -# 2008.11.25 - Change CurveFp.is_on to contains_point. -# -# Written in 2005 by Peter Pearson and placed in the public domain. -# Modified extensively as part of python-ecdsa. - -from __future__ import division - -try: - from gmpy2 import mpz - - GMPY = True -except ImportError: # pragma: no branch - try: - from gmpy import mpz - - GMPY = True - except ImportError: - GMPY = False - - -from six import python_2_unicode_compatible -from . import numbertheory -from ._compat import normalise_bytes, int_to_bytes, bit_length, bytes_to_int -from .errors import MalformedPointError -from .util import orderlen, string_to_number, number_to_string - - -@python_2_unicode_compatible -class CurveFp(object): - """ - :term:`Short Weierstrass Elliptic Curve ` over a - prime field. - """ - - if GMPY: # pragma: no branch - - def __init__(self, p, a, b, h=None): - """ - The curve of points satisfying y^2 = x^3 + a*x + b (mod p). - - h is an integer that is the cofactor of the elliptic curve domain - parameters; it is the number of points satisfying the elliptic - curve equation divided by the order of the base point. It is used - for selection of efficient algorithm for public point verification. - """ - self.__p = mpz(p) - self.__a = mpz(a) - self.__b = mpz(b) - # h is not used in calculations and it can be None, so don't use - # gmpy with it - self.__h = h - - else: # pragma: no branch - - def __init__(self, p, a, b, h=None): - """ - The curve of points satisfying y^2 = x^3 + a*x + b (mod p). - - h is an integer that is the cofactor of the elliptic curve domain - parameters; it is the number of points satisfying the elliptic - curve equation divided by the order of the base point. It is used - for selection of efficient algorithm for public point verification. - """ - self.__p = p - self.__a = a - self.__b = b - self.__h = h - - def __eq__(self, other): - """Return True if other is an identical curve, False otherwise. - - Note: the value of the cofactor of the curve is not taken into account - when comparing curves, as it's derived from the base point and - intrinsic curve characteristic (but it's complex to compute), - only the prime and curve parameters are considered. - """ - if isinstance(other, CurveFp): - p = self.__p - return ( - self.__p == other.__p - and self.__a % p == other.__a % p - and self.__b % p == other.__b % p - ) - return NotImplemented - - def __ne__(self, other): - """Return False if other is an identical curve, True otherwise.""" - return not self == other - - def __hash__(self): - return hash((self.__p, self.__a, self.__b)) - - def p(self): - return self.__p - - def a(self): - return self.__a - - def b(self): - return self.__b - - def cofactor(self): - return self.__h - - def contains_point(self, x, y): - """Is the point (x,y) on this curve?""" - return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 - - def __str__(self): - return "CurveFp(p=%d, a=%d, b=%d, h=%d)" % ( - self.__p, - self.__a, - self.__b, - self.__h, - ) - - -class CurveEdTw(object): - """Parameters for a Twisted Edwards Elliptic Curve""" - - if GMPY: # pragma: no branch - - def __init__(self, p, a, d, h=None, hash_func=None): - """ - The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). - - h is the cofactor of the curve. - hash_func is the hash function associated with the curve - (like SHA-512 for Ed25519) - """ - self.__p = mpz(p) - self.__a = mpz(a) - self.__d = mpz(d) - self.__h = h - self.__hash_func = hash_func - - else: - - def __init__(self, p, a, d, h=None, hash_func=None): - """ - The curve of points satisfying a*x^2 + y^2 = 1 + d*x^2*y^2 (mod p). - - h is the cofactor of the curve. - hash_func is the hash function associated with the curve - (like SHA-512 for Ed25519) - """ - self.__p = p - self.__a = a - self.__d = d - self.__h = h - self.__hash_func = hash_func - - def __eq__(self, other): - """Returns True if other is an identical curve.""" - if isinstance(other, CurveEdTw): - p = self.__p - return ( - self.__p == other.__p - and self.__a % p == other.__a % p - and self.__d % p == other.__d % p - ) - return NotImplemented - - def __ne__(self, other): - """Return False if the other is an identical curve, True otherwise.""" - return not self == other - - def __hash__(self): - return hash((self.__p, self.__a, self.__d)) - - def contains_point(self, x, y): - """Is the point (x, y) on this curve?""" - return ( - self.__a * x * x + y * y - 1 - self.__d * x * x * y * y - ) % self.__p == 0 - - def p(self): - return self.__p - - def a(self): - return self.__a - - def d(self): - return self.__d - - def hash_func(self, data): - return self.__hash_func(data) - - def cofactor(self): - return self.__h - - def __str__(self): - return "CurveEdTw(p={0}, a={1}, d={2}, h={3})".format( - self.__p, - self.__a, - self.__d, - self.__h, - ) - - -class AbstractPoint(object): - """Class for common methods of elliptic curve points.""" - - @staticmethod - def _from_raw_encoding(data, raw_encoding_length): - """ - Decode public point from :term:`raw encoding`. - - :term:`raw encoding` is the same as the :term:`uncompressed` encoding, - but without the 0x04 byte at the beginning. - """ - # real assert, from_bytes() should not call us with different length - assert len(data) == raw_encoding_length - xs = data[: raw_encoding_length // 2] - ys = data[raw_encoding_length // 2 :] - # real assert, raw_encoding_length is calculated by multiplying an - # integer by two so it will always be even - assert len(xs) == raw_encoding_length // 2 - assert len(ys) == raw_encoding_length // 2 - coord_x = string_to_number(xs) - coord_y = string_to_number(ys) - - return coord_x, coord_y - - @staticmethod - def _from_compressed(data, curve): - """Decode public point from compressed encoding.""" - if data[:1] not in (b"\x02", b"\x03"): - raise MalformedPointError("Malformed compressed point encoding") - - is_even = data[:1] == b"\x02" - x = string_to_number(data[1:]) - p = curve.p() - alpha = (pow(x, 3, p) + (curve.a() * x) + curve.b()) % p - try: - beta = numbertheory.square_root_mod_prime(alpha, p) - except numbertheory.Error as e: - raise MalformedPointError( - "Encoding does not correspond to a point on curve", e - ) - if is_even == bool(beta & 1): - y = p - beta - else: - y = beta - return x, y - - @classmethod - def _from_hybrid(cls, data, raw_encoding_length, validate_encoding): - """Decode public point from hybrid encoding.""" - # real assert, from_bytes() should not call us with different types - assert data[:1] in (b"\x06", b"\x07") - - # primarily use the uncompressed as it's easiest to handle - x, y = cls._from_raw_encoding(data[1:], raw_encoding_length) - - # but validate if it's self-consistent if we're asked to do that - if validate_encoding and ( - y & 1 - and data[:1] != b"\x07" - or (not y & 1) - and data[:1] != b"\x06" - ): - raise MalformedPointError("Inconsistent hybrid point encoding") - - return x, y - - @classmethod - def _from_edwards(cls, curve, data): - """Decode a point on an Edwards curve.""" - data = bytearray(data) - p = curve.p() - # add 1 for the sign bit and then round up - exp_len = (bit_length(p) + 1 + 7) // 8 - if len(data) != exp_len: - raise MalformedPointError("Point length doesn't match the curve.") - x_0 = (data[-1] & 0x80) >> 7 - - data[-1] &= 0x80 - 1 - - y = bytes_to_int(data, "little") - if GMPY: - y = mpz(y) - - x2 = ( - (y * y - 1) - * numbertheory.inverse_mod(curve.d() * y * y - curve.a(), p) - % p - ) - - try: - x = numbertheory.square_root_mod_prime(x2, p) - except numbertheory.Error as e: - raise MalformedPointError( - "Encoding does not correspond to a point on curve", e - ) - - if x % 2 != x_0: - x = -x % p - - return x, y - - @classmethod - def from_bytes( - cls, curve, data, validate_encoding=True, valid_encodings=None - ): - """ - Initialise the object from byte encoding of a point. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - - Note: generally you will want to call the ``from_bytes()`` method of - either a child class, PointJacobi or Point. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.ellipticcurve.CurveFp - :param validate_encoding: whether to verify that the encoding of the - point is self-consistent, defaults to True, has effect only - on ``hybrid`` encoding - :type validate_encoding: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: x and y coordinates of the encoded point - :rtype: tuple(int, int) - """ - if not valid_encodings: - valid_encodings = set( - ["uncompressed", "compressed", "hybrid", "raw"] - ) - if not all( - i in set(("uncompressed", "compressed", "hybrid", "raw")) - for i in valid_encodings - ): - raise ValueError( - "Only uncompressed, compressed, hybrid or raw encoding " - "supported." - ) - data = normalise_bytes(data) - - if isinstance(curve, CurveEdTw): - return cls._from_edwards(curve, data) - - key_len = len(data) - raw_encoding_length = 2 * orderlen(curve.p()) - if key_len == raw_encoding_length and "raw" in valid_encodings: - coord_x, coord_y = cls._from_raw_encoding( - data, raw_encoding_length - ) - elif key_len == raw_encoding_length + 1 and ( - "hybrid" in valid_encodings or "uncompressed" in valid_encodings - ): - if data[:1] in (b"\x06", b"\x07") and "hybrid" in valid_encodings: - coord_x, coord_y = cls._from_hybrid( - data, raw_encoding_length, validate_encoding - ) - elif data[:1] == b"\x04" and "uncompressed" in valid_encodings: - coord_x, coord_y = cls._from_raw_encoding( - data[1:], raw_encoding_length - ) - else: - raise MalformedPointError( - "Invalid X9.62 encoding of the public point" - ) - elif ( - key_len == raw_encoding_length // 2 + 1 - and "compressed" in valid_encodings - ): - coord_x, coord_y = cls._from_compressed(data, curve) - else: - raise MalformedPointError( - "Length of string does not match lengths of " - "any of the enabled ({0}) encodings of the " - "curve.".format(", ".join(valid_encodings)) - ) - return coord_x, coord_y - - def _raw_encode(self): - """Convert the point to the :term:`raw encoding`.""" - prime = self.curve().p() - x_str = number_to_string(self.x(), prime) - y_str = number_to_string(self.y(), prime) - return x_str + y_str - - def _compressed_encode(self): - """Encode the point into the compressed form.""" - prime = self.curve().p() - x_str = number_to_string(self.x(), prime) - if self.y() & 1: - return b"\x03" + x_str - return b"\x02" + x_str - - def _hybrid_encode(self): - """Encode the point into the hybrid form.""" - raw_enc = self._raw_encode() - if self.y() & 1: - return b"\x07" + raw_enc - return b"\x06" + raw_enc - - def _edwards_encode(self): - """Encode the point according to RFC8032 encoding.""" - self.scale() - x, y, p = self.x(), self.y(), self.curve().p() - - # add 1 for the sign bit and then round up - enc_len = (bit_length(p) + 1 + 7) // 8 - y_str = int_to_bytes(y, enc_len, "little") - if x % 2: - y_str[-1] |= 0x80 - return y_str - - def to_bytes(self, encoding="raw"): - """ - Convert the point to a byte string. - - The method by default uses the :term:`raw encoding` (specified - by `encoding="raw"`. It can also output points in :term:`uncompressed`, - :term:`compressed`, and :term:`hybrid` formats. - - For points on Edwards curves `encoding` is ignored and only the - encoding defined in RFC 8032 is supported. - - :return: :term:`raw encoding` of a public on the curve - :rtype: bytes - """ - assert encoding in ("raw", "uncompressed", "compressed", "hybrid") - curve = self.curve() - if isinstance(curve, CurveEdTw): - return self._edwards_encode() - elif encoding == "raw": - return self._raw_encode() - elif encoding == "uncompressed": - return b"\x04" + self._raw_encode() - elif encoding == "hybrid": - return self._hybrid_encode() - else: - return self._compressed_encode() - - @staticmethod - def _naf(mult): - """Calculate non-adjacent form of number.""" - ret = [] - while mult: - if mult % 2: - nd = mult % 4 - if nd >= 2: - nd -= 4 - ret.append(nd) - mult -= nd - else: - ret.append(0) - mult //= 2 - return ret - - -class PointJacobi(AbstractPoint): - """ - Point on a short Weierstrass elliptic curve. Uses Jacobi coordinates. - - In Jacobian coordinates, there are three parameters, X, Y and Z. - They correspond to affine parameters 'x' and 'y' like so: - - x = X / Z² - y = Y / Z³ - """ - - def __init__(self, curve, x, y, z, order=None, generator=False): - """ - Initialise a point that uses Jacobi representation internally. - - :param CurveFp curve: curve on which the point resides - :param int x: the X parameter of Jacobi representation (equal to x when - converting from affine coordinates - :param int y: the Y parameter of Jacobi representation (equal to y when - converting from affine coordinates - :param int z: the Z parameter of Jacobi representation (equal to 1 when - converting from affine coordinates - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: the point provided is a curve generator, as - such, it will be commonly used with scalar multiplication. This will - cause to precompute multiplication table generation for it - """ - super(PointJacobi, self).__init__() - self.__curve = curve - if GMPY: # pragma: no branch - self.__coords = (mpz(x), mpz(y), mpz(z)) - self.__order = order and mpz(order) - else: # pragma: no branch - self.__coords = (x, y, z) - self.__order = order - self.__generator = generator - self.__precompute = [] - - @classmethod - def from_bytes( - cls, - curve, - data, - validate_encoding=True, - valid_encodings=None, - order=None, - generator=False, - ): - """ - Initialise the object from byte encoding of a point. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.ellipticcurve.CurveFp - :param validate_encoding: whether to verify that the encoding of the - point is self-consistent, defaults to True, has effect only - on ``hybrid`` encoding - :type validate_encoding: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: the point provided is a curve generator, as - such, it will be commonly used with scalar multiplication. This - will cause to precompute multiplication table generation for it - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: Point on curve - :rtype: PointJacobi - """ - coord_x, coord_y = super(PointJacobi, cls).from_bytes( - curve, data, validate_encoding, valid_encodings - ) - return PointJacobi(curve, coord_x, coord_y, 1, order, generator) - - def _maybe_precompute(self): - if not self.__generator or self.__precompute: - return - - # since this code will execute just once, and it's fully deterministic, - # depend on atomicity of the last assignment to switch from empty - # self.__precompute to filled one and just ignore the unlikely - # situation when two threads execute it at the same time (as it won't - # lead to inconsistent __precompute) - order = self.__order - assert order - precompute = [] - i = 1 - order *= 2 - coord_x, coord_y, coord_z = self.__coords - doubler = PointJacobi(self.__curve, coord_x, coord_y, coord_z, order) - order *= 2 - precompute.append((doubler.x(), doubler.y())) - - while i < order: - i *= 2 - doubler = doubler.double().scale() - precompute.append((doubler.x(), doubler.y())) - - self.__precompute = precompute - - def __getstate__(self): - # while this code can execute at the same time as _maybe_precompute() - # is updating the __precompute or scale() is updating the __coords, - # there is no requirement for consistency between __coords and - # __precompute - state = self.__dict__.copy() - return state - - def __setstate__(self, state): - self.__dict__.update(state) - - def __eq__(self, other): - """Compare for equality two points with each-other. - - Note: only points that lay on the same curve can be equal. - """ - x1, y1, z1 = self.__coords - if other is INFINITY: - return not y1 or not z1 - if isinstance(other, Point): - x2, y2, z2 = other.x(), other.y(), 1 - elif isinstance(other, PointJacobi): - x2, y2, z2 = other.__coords - else: - return NotImplemented - if self.__curve != other.curve(): - return False - p = self.__curve.p() - - zz1 = z1 * z1 % p - zz2 = z2 * z2 % p - - # compare the fractions by bringing them to the same denominator - # depend on short-circuit to save 4 multiplications in case of - # inequality - return (x1 * zz2 - x2 * zz1) % p == 0 and ( - y1 * zz2 * z2 - y2 * zz1 * z1 - ) % p == 0 - - def __ne__(self, other): - """Compare for inequality two points with each-other.""" - return not self == other - - def order(self): - """Return the order of the point. - - None if it is undefined. - """ - return self.__order - - def curve(self): - """Return curve over which the point is defined.""" - return self.__curve - - def x(self): - """ - Return affine x coordinate. - - This method should be used only when the 'y' coordinate is not needed. - It's computationally more efficient to use `to_affine()` and then - call x() and y() on the returned instance. Or call `scale()` - and then x() and y() on the returned instance. - """ - x, _, z = self.__coords - if z == 1: - return x - p = self.__curve.p() - z = numbertheory.inverse_mod(z, p) - return x * z**2 % p - - def y(self): - """ - Return affine y coordinate. - - This method should be used only when the 'x' coordinate is not needed. - It's computationally more efficient to use `to_affine()` and then - call x() and y() on the returned instance. Or call `scale()` - and then x() and y() on the returned instance. - """ - _, y, z = self.__coords - if z == 1: - return y - p = self.__curve.p() - z = numbertheory.inverse_mod(z, p) - return y * z**3 % p - - def scale(self): - """ - Return point scaled so that z == 1. - - Modifies point in place, returns self. - """ - x, y, z = self.__coords - if z == 1: - return self - - # scaling is deterministic, so even if two threads execute the below - # code at the same time, they will set __coords to the same value - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(z, p) - zz_inv = z_inv * z_inv % p - x = x * zz_inv % p - y = y * zz_inv * z_inv % p - self.__coords = (x, y, 1) - return self - - def to_affine(self): - """Return point in affine form.""" - _, y, z = self.__coords - if not y or not z: - return INFINITY - self.scale() - x, y, z = self.__coords - return Point(self.__curve, x, y, self.__order) - - @staticmethod - def from_affine(point, generator=False): - """Create from an affine point. - - :param bool generator: set to True to make the point to precalculate - multiplication table - useful for public point when verifying many - signatures (around 100 or so) or for generator points of a curve. - """ - return PointJacobi( - point.curve(), point.x(), point.y(), 1, point.order(), generator - ) - - # please note that all the methods that use the equations from - # hyperelliptic - # are formatted in a way to maximise performance. - # Things that make code faster: multiplying instead of taking to the power - # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and - # `pow(x, 4, p)`), - # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is - # faster than `x1 = self.x1; x2 = self.x2`), - # similarly, sometimes the `% p` is skipped if it makes the calculation - # faster and the result of calculation is later reduced modulo `p` - - def _double_with_z_1(self, X1, Y1, p, a): - """Add a point to itself with z == 1.""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl - XX, YY = X1 * X1 % p, Y1 * Y1 % p - if not YY: - return 0, 0, 1 - YYYY = YY * YY % p - S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p - M = 3 * XX + a - T = (M * M - 2 * S) % p - # X3 = T - Y3 = (M * (S - T) - 8 * YYYY) % p - Z3 = 2 * Y1 % p - return T, Y3, Z3 - - def _double(self, X1, Y1, Z1, p, a): - """Add a point to itself, arbitrary z.""" - if Z1 == 1: - return self._double_with_z_1(X1, Y1, p, a) - if not Y1 or not Z1: - return 0, 0, 1 - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl - XX, YY = X1 * X1 % p, Y1 * Y1 % p - if not YY: - return 0, 0, 1 - YYYY = YY * YY % p - ZZ = Z1 * Z1 % p - S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p - M = (3 * XX + a * ZZ * ZZ) % p - T = (M * M - 2 * S) % p - # X3 = T - Y3 = (M * (S - T) - 8 * YYYY) % p - Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p - - return T, Y3, Z3 - - def double(self): - """Add a point to itself.""" - X1, Y1, Z1 = self.__coords - - if not Y1: - return INFINITY - - p, a = self.__curve.p(), self.__curve.a() - - X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def _add_with_z_1(self, X1, Y1, X2, Y2, p): - """add points when both Z1 and Z2 equal 1""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl - H = X2 - X1 - HH = H * H - I = 4 * HH % p - J = H * I - r = 2 * (Y2 - Y1) - if not H and not r: - return self._double_with_z_1(X1, Y1, p, self.__curve.a()) - V = X1 * I - X3 = (r**2 - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * Y1 * J) % p - Z3 = 2 * H % p - return X3, Y3, Z3 - - def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p): - """add points when Z1 == Z2""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m - A = (X2 - X1) ** 2 % p - B = X1 * A % p - C = X2 * A - D = (Y2 - Y1) ** 2 % p - if not A and not D: - return self._double(X1, Y1, Z1, p, self.__curve.a()) - X3 = (D - B - C) % p - Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p - Z3 = Z1 * (X2 - X1) % p - return X3, Y3, Z3 - - def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p): - """add points when Z2 == 1""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl - Z1Z1 = Z1 * Z1 % p - U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p - H = (U2 - X1) % p - HH = H * H % p - I = 4 * HH % p - J = H * I - r = 2 * (S2 - Y1) % p - if not r and not H: - return self._double_with_z_1(X2, Y2, p, self.__curve.a()) - V = X1 * I - X3 = (r * r - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * Y1 * J) % p - Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p - return X3, Y3, Z3 - - def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p): - """add points with arbitrary z""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl - Z1Z1 = Z1 * Z1 % p - Z2Z2 = Z2 * Z2 % p - U1 = X1 * Z2Z2 % p - U2 = X2 * Z1Z1 % p - S1 = Y1 * Z2 * Z2Z2 % p - S2 = Y2 * Z1 * Z1Z1 % p - H = U2 - U1 - I = 4 * H * H % p - J = H * I % p - r = 2 * (S2 - S1) % p - if not H and not r: - return self._double(X1, Y1, Z1, p, self.__curve.a()) - V = U1 * I - X3 = (r * r - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * S1 * J) % p - Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p - - return X3, Y3, Z3 - - def __radd__(self, other): - """Add other to self.""" - return self + other - - def _add(self, X1, Y1, Z1, X2, Y2, Z2, p): - """add two points, select fastest method.""" - if not Y1 or not Z1: - return X2, Y2, Z2 - if not Y2 or not Z2: - return X1, Y1, Z1 - if Z1 == Z2: - if Z1 == 1: - return self._add_with_z_1(X1, Y1, X2, Y2, p) - return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p) - if Z1 == 1: - return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p) - if Z2 == 1: - return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p) - return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p) - - def __add__(self, other): - """Add two points on elliptic curve.""" - if self == INFINITY: - return other - if other == INFINITY: - return self - if isinstance(other, Point): - other = PointJacobi.from_affine(other) - if self.__curve != other.__curve: - raise ValueError("The other point is on different curve") - - p = self.__curve.p() - X1, Y1, Z1 = self.__coords - X2, Y2, Z2 = other.__coords - - X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __rmul__(self, other): - """Multiply point by an integer.""" - return self * other - - def _mul_precompute(self, other): - """Multiply point by integer with precomputation table.""" - X3, Y3, Z3, p = 0, 0, 1, self.__curve.p() - _add = self._add - for X2, Y2 in self.__precompute: - if other % 2: - if other % 4 >= 2: - other = (other + 1) // 2 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) - else: - other = (other - 1) // 2 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - else: - other //= 2 - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __mul__(self, other): - """Multiply point by an integer.""" - if not self.__coords[1] or not other: - return INFINITY - if other == 1: - return self - if self.__order: - # order*2 as a protection for Minerva - other = other % (self.__order * 2) - self._maybe_precompute() - if self.__precompute: - return self._mul_precompute(other) - - self = self.scale() - X2, Y2, _ = self.__coords - X3, Y3, Z3 = 0, 0, 1 - p, a = self.__curve.p(), self.__curve.a() - _double = self._double - _add = self._add - # since adding points when at least one of them is scaled - # is quicker, reverse the NAF order - for i in reversed(self._naf(other)): - X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - if i < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) - elif i > 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - - if not Y3 or not Z3: - return INFINITY - - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def mul_add(self, self_mul, other, other_mul): - """ - Do two multiplications at the same time, add results. - - calculates self*self_mul + other*other_mul - """ - if other == INFINITY or other_mul == 0: - return self * self_mul - if self_mul == 0: - return other * other_mul - if not isinstance(other, PointJacobi): - other = PointJacobi.from_affine(other) - # when the points have precomputed answers, then multiplying them alone - # is faster (as it uses NAF and no point doublings) - self._maybe_precompute() - other._maybe_precompute() - if self.__precompute and other.__precompute: - return self * self_mul + other * other_mul - - if self.__order: - self_mul = self_mul % self.__order - other_mul = other_mul % self.__order - - # (X3, Y3, Z3) is the accumulator - X3, Y3, Z3 = 0, 0, 1 - p, a = self.__curve.p(), self.__curve.a() - - # as we have 6 unique points to work with, we can't scale all of them, - # but do scale the ones that are used most often - self.scale() - X1, Y1, Z1 = self.__coords - other.scale() - X2, Y2, Z2 = other.__coords - - _double = self._double - _add = self._add - - # with NAF we have 3 options: no add, subtract, add - # so with 2 points, we have 9 combinations: - # 0, -A, +A, -B, -A-B, +A-B, +B, -A+B, +A+B - # so we need 4 combined points: - mAmB_X, mAmB_Y, mAmB_Z = _add(X1, -Y1, Z1, X2, -Y2, Z2, p) - pAmB_X, pAmB_Y, pAmB_Z = _add(X1, Y1, Z1, X2, -Y2, Z2, p) - mApB_X, mApB_Y, mApB_Z = _add(X1, -Y1, Z1, X2, Y2, Z2, p) - pApB_X, pApB_Y, pApB_Z = _add(X1, Y1, Z1, X2, Y2, Z2, p) - # when the self and other sum to infinity, we need to add them - # one by one to get correct result but as that's very unlikely to - # happen in regular operation, we don't need to optimise this case - if not pApB_Y or not pApB_Z: - return self * self_mul + other * other_mul - - # gmp object creation has cumulatively higher overhead than the - # speedup we get from calculating the NAF using gmp so ensure use - # of int() - self_naf = list(reversed(self._naf(int(self_mul)))) - other_naf = list(reversed(self._naf(int(other_mul)))) - # ensure that the lists are the same length (zip() will truncate - # longer one otherwise) - if len(self_naf) < len(other_naf): - self_naf = [0] * (len(other_naf) - len(self_naf)) + self_naf - elif len(self_naf) > len(other_naf): - other_naf = [0] * (len(self_naf) - len(other_naf)) + other_naf - - for A, B in zip(self_naf, other_naf): - X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - - # conditions ordered from most to least likely - if A == 0: - if B == 0: - pass - elif B < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, Z2, p) - else: - assert B > 0 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, Z2, p) - elif A < 0: - if B == 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X1, -Y1, Z1, p) - elif B < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, mAmB_X, mAmB_Y, mAmB_Z, p) - else: - assert B > 0 - X3, Y3, Z3 = _add(X3, Y3, Z3, mApB_X, mApB_Y, mApB_Z, p) - else: - assert A > 0 - if B == 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, Z1, p) - elif B < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, pAmB_X, pAmB_Y, pAmB_Z, p) - else: - assert B > 0 - X3, Y3, Z3 = _add(X3, Y3, Z3, pApB_X, pApB_Y, pApB_Z, p) - - if not Y3 or not Z3: - return INFINITY - - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __neg__(self): - """Return negated point.""" - x, y, z = self.__coords - return PointJacobi(self.__curve, x, -y, z, self.__order) - - -class Point(AbstractPoint): - """A point on a short Weierstrass elliptic curve. Altering x and y is - forbidden, but they can be read by the x() and y() methods.""" - - def __init__(self, curve, x, y, order=None): - """curve, x, y, order; order (optional) is the order of this point.""" - super(Point, self).__init__() - self.__curve = curve - if GMPY: - self.__x = x and mpz(x) - self.__y = y and mpz(y) - self.__order = order and mpz(order) - else: - self.__x = x - self.__y = y - self.__order = order - # self.curve is allowed to be None only for INFINITY: - if self.__curve: - assert self.__curve.contains_point(x, y) - # for curves with cofactor 1, all points that are on the curve are - # scalar multiples of the base point, so performing multiplication is - # not necessary to verify that. See Section 3.2.2.1 of SEC 1 v2 - if curve and curve.cofactor() != 1 and order: - assert self * order == INFINITY - - @classmethod - def from_bytes( - cls, - curve, - data, - validate_encoding=True, - valid_encodings=None, - order=None, - ): - """ - Initialise the object from byte encoding of a point. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.ellipticcurve.CurveFp - :param validate_encoding: whether to verify that the encoding of the - point is self-consistent, defaults to True, has effect only - on ``hybrid`` encoding - :type validate_encoding: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - :type valid_encodings: :term:`set-like object` - :param int order: the point order, must be non zero when using - generator=True - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: Point on curve - :rtype: Point - """ - coord_x, coord_y = super(Point, cls).from_bytes( - curve, data, validate_encoding, valid_encodings - ) - return Point(curve, coord_x, coord_y, order) - - def __eq__(self, other): - """Return True if the points are identical, False otherwise. - - Note: only points that lay on the same curve can be equal. - """ - if isinstance(other, Point): - return ( - self.__curve == other.__curve - and self.__x == other.__x - and self.__y == other.__y - ) - return NotImplemented - - def __ne__(self, other): - """Returns False if points are identical, True otherwise.""" - return not self == other - - def __neg__(self): - return Point(self.__curve, self.__x, self.__curve.p() - self.__y) - - def __add__(self, other): - """Add one point to another point.""" - - # X9.62 B.3: - - if not isinstance(other, Point): - return NotImplemented - if other == INFINITY: - return self - if self == INFINITY: - return other - assert self.__curve == other.__curve - if self.__x == other.__x: - if (self.__y + other.__y) % self.__curve.p() == 0: - return INFINITY - else: - return self.double() - - p = self.__curve.p() - - l = ( - (other.__y - self.__y) - * numbertheory.inverse_mod(other.__x - self.__x, p) - ) % p - - x3 = (l * l - self.__x - other.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def __mul__(self, other): - """Multiply a point by an integer.""" - - def leftmost_bit(x): - assert x > 0 - result = 1 - while result <= x: - result = 2 * result - return result // 2 - - e = other - if e == 0 or (self.__order and e % self.__order == 0): - return INFINITY - if self == INFINITY: - return INFINITY - if e < 0: - return (-self) * (-e) - - # From X9.62 D.3.2: - - e3 = 3 * e - negative_self = Point(self.__curve, self.__x, -self.__y, self.__order) - i = leftmost_bit(e3) // 2 - result = self - # print_("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) - while i > 1: - result = result.double() - if (e3 & i) != 0 and (e & i) == 0: - result = result + self - if (e3 & i) == 0 and (e & i) != 0: - result = result + negative_self - # print_(". . . i = %d, result = %s" % ( i, result )) - i = i // 2 - - return result - - def __rmul__(self, other): - """Multiply a point by an integer.""" - - return self * other - - def __str__(self): - if self == INFINITY: - return "infinity" - return "(%d,%d)" % (self.__x, self.__y) - - def double(self): - """Return a new point that is twice the old.""" - - if self == INFINITY: - return INFINITY - - # X9.62 B.3: - - p = self.__curve.p() - a = self.__curve.a() - - l = ( - (3 * self.__x * self.__x + a) - * numbertheory.inverse_mod(2 * self.__y, p) - ) % p - - x3 = (l * l - 2 * self.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def x(self): - return self.__x - - def y(self): - return self.__y - - def curve(self): - return self.__curve - - def order(self): - return self.__order - - -class PointEdwards(AbstractPoint): - """Point on Twisted Edwards curve. - - Internally represents the coordinates on the curve using four parameters, - X, Y, Z, T. They correspond to affine parameters 'x' and 'y' like so: - - x = X / Z - y = Y / Z - x*y = T / Z - """ - - def __init__(self, curve, x, y, z, t, order=None, generator=False): - """ - Initialise a point that uses the extended coordinates internally. - """ - super(PointEdwards, self).__init__() - self.__curve = curve - if GMPY: # pragma: no branch - self.__coords = (mpz(x), mpz(y), mpz(z), mpz(t)) - self.__order = order and mpz(order) - else: # pragma: no branch - self.__coords = (x, y, z, t) - self.__order = order - self.__generator = generator - self.__precompute = [] - - @classmethod - def from_bytes( - cls, - curve, - data, - validate_encoding=None, - valid_encodings=None, - order=None, - generator=False, - ): - """ - Initialise the object from byte encoding of a point. - - `validate_encoding` and `valid_encodings` are provided for - compatibility with Weierstrass curves, they are ignored for Edwards - points. - - :param data: single point encoding of the public key - :type data: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ecdsa.ellipticcurve.CurveEdTw - :param None validate_encoding: Ignored, encoding is always validated - :param None valid_encodings: Ignored, there is just one encoding - supported - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: Flag to mark the point as a curve generator, - this will cause the library to pre-compute some values to - make repeated usages of the point much faster - - :raises `~ecdsa.errors.MalformedPointError`: if the public point does - not lay on the curve or the encoding is invalid - - :return: Initialised point on an Edwards curve - :rtype: PointEdwards - """ - coord_x, coord_y = super(PointEdwards, cls).from_bytes( - curve, data, validate_encoding, valid_encodings - ) - return PointEdwards( - curve, coord_x, coord_y, 1, coord_x * coord_y, order, generator - ) - - def _maybe_precompute(self): - if not self.__generator or self.__precompute: - return self.__precompute - - # since this code will execute just once, and it's fully deterministic, - # depend on atomicity of the last assignment to switch from empty - # self.__precompute to filled one and just ignore the unlikely - # situation when two threads execute it at the same time (as it won't - # lead to inconsistent __precompute) - order = self.__order - assert order - precompute = [] - i = 1 - order *= 2 - coord_x, coord_y, coord_z, coord_t = self.__coords - prime = self.__curve.p() - - doubler = PointEdwards( - self.__curve, coord_x, coord_y, coord_z, coord_t, order - ) - # for "protection" against Minerva we need 1 or 2 more bits depending - # on order bit size, but it's easier to just calculate one - # point more always - order *= 4 - - while i < order: - doubler = doubler.scale() - coord_x, coord_y = doubler.x(), doubler.y() - coord_t = coord_x * coord_y % prime - precompute.append((coord_x, coord_y, coord_t)) - - i *= 2 - doubler = doubler.double() - - self.__precompute = precompute - return self.__precompute - - def x(self): - """Return affine x coordinate.""" - X1, _, Z1, _ = self.__coords - if Z1 == 1: - return X1 - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(Z1, p) - return X1 * z_inv % p - - def y(self): - """Return affine y coordinate.""" - _, Y1, Z1, _ = self.__coords - if Z1 == 1: - return Y1 - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(Z1, p) - return Y1 * z_inv % p - - def curve(self): - """Return the curve of the point.""" - return self.__curve - - def order(self): - return self.__order - - def scale(self): - """ - Return point scaled so that z == 1. - - Modifies point in place, returns self. - """ - X1, Y1, Z1, _ = self.__coords - if Z1 == 1: - return self - - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(Z1, p) - x = X1 * z_inv % p - y = Y1 * z_inv % p - t = x * y % p - self.__coords = (x, y, 1, t) - return self - - def __eq__(self, other): - """Compare for equality two points with each-other. - - Note: only points on the same curve can be equal. - """ - x1, y1, z1, t1 = self.__coords - if other is INFINITY: - return not x1 or not t1 - if isinstance(other, PointEdwards): - x2, y2, z2, t2 = other.__coords - else: - return NotImplemented - if self.__curve != other.curve(): - return False - p = self.__curve.p() - - # cross multiply to eliminate divisions - xn1 = x1 * z2 % p - xn2 = x2 * z1 % p - yn1 = y1 * z2 % p - yn2 = y2 * z1 % p - return xn1 == xn2 and yn1 == yn2 - - def __ne__(self, other): - """Compare for inequality two points with each-other.""" - return not self == other - - def _add(self, X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a): - """add two points, assume sane parameters.""" - # after add-2008-hwcd-2 - # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html - # NOTE: there are more efficient formulas for Z1 or Z2 == 1 - A = X1 * X2 % p - B = Y1 * Y2 % p - C = Z1 * T2 % p - D = T1 * Z2 % p - E = D + C - F = ((X1 - Y1) * (X2 + Y2) + B - A) % p - G = B + a * A - H = D - C - if not H: - return self._double(X1, Y1, Z1, T1, p, a) - X3 = E * F % p - Y3 = G * H % p - T3 = E * H % p - Z3 = F * G % p - - return X3, Y3, Z3, T3 - - def __add__(self, other): - """Add point to another.""" - if other == INFINITY: - return self - if ( - not isinstance(other, PointEdwards) - or self.__curve != other.__curve - ): - raise ValueError("The other point is on a different curve.") - - p, a = self.__curve.p(), self.__curve.a() - X1, Y1, Z1, T1 = self.__coords - X2, Y2, Z2, T2 = other.__coords - - X3, Y3, Z3, T3 = self._add(X1, Y1, Z1, T1, X2, Y2, Z2, T2, p, a) - - if not X3 or not T3: - return INFINITY - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - def __radd__(self, other): - """Add other to self.""" - return self + other - - def _double(self, X1, Y1, Z1, T1, p, a): - """Double the point, assume sane parameters.""" - # after "dbl-2008-hwcd" - # from https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html - # NOTE: there are more efficient formulas for Z1 == 1 - A = X1 * X1 % p - B = Y1 * Y1 % p - C = 2 * Z1 * Z1 % p - D = a * A % p - E = ((X1 + Y1) * (X1 + Y1) - A - B) % p - G = D + B - F = G - C - H = D - B - X3 = E * F % p - Y3 = G * H % p - T3 = E * H % p - Z3 = F * G % p - - return X3, Y3, Z3, T3 - - def double(self): - """Return point added to itself.""" - X1, Y1, Z1, T1 = self.__coords - - if not X1 or not T1: - return INFINITY - - p, a = self.__curve.p(), self.__curve.a() - - X3, Y3, Z3, T3 = self._double(X1, Y1, Z1, T1, p, a) - - if not X3 or not T3: - return INFINITY - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - def __rmul__(self, other): - """Multiply point by an integer.""" - return self * other - - def _mul_precompute(self, other): - """Multiply point by integer with precomputation table.""" - X3, Y3, Z3, T3, p, a = 0, 1, 1, 0, self.__curve.p(), self.__curve.a() - _add = self._add - for X2, Y2, T2 in self.__precompute: - rem = other % 4 - if rem == 0 or rem == 2: - other //= 2 - elif rem == 3: - other = (other + 1) // 2 - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, 1, -T2, p, a) - else: - assert rem == 1 - other = (other - 1) // 2 - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, 1, T2, p, a) - - if not X3 or not T3: - return INFINITY - - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - def __mul__(self, other): - """Multiply point by an integer.""" - X2, Y2, Z2, T2 = self.__coords - if not X2 or not T2 or not other: - return INFINITY - if other == 1: - return self - if self.__order: - # order*2 as a "protection" for Minerva - other = other % (self.__order * 2) - if self._maybe_precompute(): - return self._mul_precompute(other) - - X3, Y3, Z3, T3 = 0, 1, 1, 0 # INFINITY in extended coordinates - p, a = self.__curve.p(), self.__curve.a() - _double = self._double - _add = self._add - - for i in reversed(self._naf(other)): - X3, Y3, Z3, T3 = _double(X3, Y3, Z3, T3, p, a) - if i < 0: - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, -X2, Y2, Z2, -T2, p, a) - elif i > 0: - X3, Y3, Z3, T3 = _add(X3, Y3, Z3, T3, X2, Y2, Z2, T2, p, a) - - if not X3 or not T3: - return INFINITY - - return PointEdwards(self.__curve, X3, Y3, Z3, T3, self.__order) - - -# This one point is the Point At Infinity for all purposes: -INFINITY = Point(None, None, None) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py deleted file mode 100644 index 0184c05b1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/errors.py +++ /dev/null @@ -1,4 +0,0 @@ -class MalformedPointError(AssertionError): - """Raised in case the encoding of private or public key is malformed.""" - - pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py deleted file mode 100644 index 2b7d3168f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/keys.py +++ /dev/null @@ -1,1612 +0,0 @@ -""" -Primary classes for performing signing and verification operations. -""" - -import binascii -from hashlib import sha1 -import os -from six import PY2, b -from . import ecdsa, eddsa -from . import der -from . import rfc6979 -from . import ellipticcurve -from .curves import NIST192p, Curve, Ed25519, Ed448 -from .ecdsa import RSZeroError -from .util import string_to_number, number_to_string, randrange -from .util import sigencode_string, sigdecode_string, bit_length -from .util import ( - oid_ecPublicKey, - encoded_oid_ecPublicKey, - oid_ecDH, - oid_ecMQV, - MalformedSignature, -) -from ._compat import normalise_bytes -from .errors import MalformedPointError -from .ellipticcurve import PointJacobi, CurveEdTw - - -__all__ = [ - "BadSignatureError", - "BadDigestError", - "VerifyingKey", - "SigningKey", - "MalformedPointError", -] - - -class BadSignatureError(Exception): - """ - Raised when verification of signature failed. - - Will be raised irrespective of reason of the failure: - - * the calculated or provided hash does not match the signature - * the signature does not match the curve/public key - * the encoding of the signature is malformed - * the size of the signature does not match the curve of the VerifyingKey - """ - - pass - - -class BadDigestError(Exception): - """Raised in case the selected hash is too large for the curve.""" - - pass - - -def _truncate_and_convert_digest(digest, curve, allow_truncate): - """Truncates and converts digest to an integer.""" - if not allow_truncate: - if len(digest) > curve.baselen: - raise BadDigestError( - "this curve ({0}) is too short " - "for the length of your digest ({1})".format( - curve.name, 8 * len(digest) - ) - ) - else: - digest = digest[: curve.baselen] - number = string_to_number(digest) - if allow_truncate: - max_length = bit_length(curve.order) - # we don't use bit_length(number) as that truncates leading zeros - length = len(digest) * 8 - - # See NIST FIPS 186-4: - # - # When the length of the output of the hash function is greater - # than N (i.e., the bit length of q), then the leftmost N bits of - # the hash function output block shall be used in any calculation - # using the hash function output during the generation or - # verification of a digital signature. - # - # as such, we need to shift-out the low-order bits: - number >>= max(0, length - max_length) - - return number - - -class VerifyingKey(object): - """ - Class for handling keys that can verify signatures (public keys). - - :ivar `~ecdsa.curves.Curve` ~.curve: The Curve over which all the - cryptographic operations will take place - :ivar default_hashfunc: the function that will be used for hashing the - data. Should implement the same API as hashlib.sha1 - :vartype default_hashfunc: callable - :ivar pubkey: the actual public key - :vartype pubkey: ~ecdsa.ecdsa.Public_key - """ - - def __init__(self, _error__please_use_generate=None): - """Unsupported, please use one of the classmethods to initialise.""" - if not _error__please_use_generate: - raise TypeError( - "Please use VerifyingKey.generate() to construct me" - ) - self.curve = None - self.default_hashfunc = None - self.pubkey = None - - def __repr__(self): - pub_key = self.to_string("compressed") - if self.default_hashfunc: - hash_name = self.default_hashfunc().name - else: - hash_name = "None" - return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( - pub_key, self.curve, hash_name - ) - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, VerifyingKey): - return self.curve == other.curve and self.pubkey == other.pubkey - return NotImplemented - - def __ne__(self, other): - """Return False if the points are identical, True otherwise.""" - return not self == other - - @classmethod - def from_public_point( - cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True - ): - """ - Initialise the object from a Point object. - - This is a low-level method, generally you will not want to use it. - - :param point: The point to wrap around, the actual public key - :type point: ~ecdsa.ellipticcurve.AbstractPoint - :param curve: The curve on which the point needs to reside, defaults - to NIST192p - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface - as :py:class:`hashlib.sha1` - :type hashfunc: callable - :type bool validate_point: whether to check if the point lays on curve - should always be used if the public point is not a result - of our own calculation - - :raises MalformedPointError: if the public point does not lay on the - curve - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - self = cls(_error__please_use_generate=True) - if isinstance(curve.curve, CurveEdTw): - raise ValueError("Method incompatible with Edwards curves") - if not isinstance(point, ellipticcurve.PointJacobi): - point = ellipticcurve.PointJacobi.from_affine(point) - self.curve = curve - self.default_hashfunc = hashfunc - try: - self.pubkey = ecdsa.Public_key( - curve.generator, point, validate_point - ) - except ecdsa.InvalidPointError: - raise MalformedPointError("Point does not lay on the curve") - self.pubkey.order = curve.order - return self - - def precompute(self, lazy=False): - """ - Precompute multiplication tables for faster signature verification. - - Calling this method will cause the library to precompute the - scalar multiplication tables, used in signature verification. - While it's an expensive operation (comparable to performing - as many signatures as the bit size of the curve, i.e. 256 for NIST256p) - it speeds up verification 2 times. You should call this method - if you expect to verify hundreds of signatures (or more) using the same - VerifyingKey object. - - Note: You should call this method only once, this method generates a - new precomputation table every time it's called. - - :param bool lazy: whether to calculate the precomputation table now - (if set to False) or if it should be delayed to the time of first - use (when set to True) - """ - if isinstance(self.curve.curve, CurveEdTw): - pt = self.pubkey.point - self.pubkey.point = ellipticcurve.PointEdwards( - pt.curve(), - pt.x(), - pt.y(), - 1, - pt.x() * pt.y(), - self.curve.order, - generator=True, - ) - else: - self.pubkey.point = ellipticcurve.PointJacobi.from_affine( - self.pubkey.point, True - ) - # as precomputation in now delayed to the time of first use of the - # point and we were asked specifically to precompute now, make - # sure the precomputation is performed now to preserve the behaviour - if not lazy: - self.pubkey.point * 2 - - @classmethod - def from_string( - cls, - string, - curve=NIST192p, - hashfunc=sha1, - validate_point=True, - valid_encodings=None, - ): - """ - Initialise the object from byte encoding of public key. - - The method does accept and automatically detect the type of point - encoding used. It supports the :term:`raw encoding`, - :term:`uncompressed`, :term:`compressed`, and :term:`hybrid` encodings. - It also works with the native encoding of Ed25519 and Ed448 public - keys (technically those are compressed, but encoded differently than - in other signature systems). - - Note, while the method is named "from_string" it's a misnomer from - Python 2 days when there were no binary strings. In Python 3 the - input needs to be a bytes-like object. - - :param string: single point encoding of the public key - :type string: :term:`bytes-like object` - :param curve: the curve on which the public key is expected to lay - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as - hashlib.sha1. Ignored for EdDSA. - :type hashfunc: callable - :param validate_point: whether to verify that the point lays on the - provided curve or not, defaults to True. Ignored for EdDSA. - :type validate_point: bool - :param valid_encodings: list of acceptable point encoding formats, - supported ones are: :term:`uncompressed`, :term:`compressed`, - :term:`hybrid`, and :term:`raw encoding` (specified with ``raw`` - name). All formats by default (specified with ``None``). - Ignored for EdDSA. - :type valid_encodings: :term:`set-like object` - - :raises MalformedPointError: if the public point does not lay on the - curve or the encoding is invalid - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - if isinstance(curve.curve, CurveEdTw): - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = None # ignored for EdDSA - try: - self.pubkey = eddsa.PublicKey(curve.generator, string) - except ValueError: - raise MalformedPointError("Malformed point for the curve") - return self - - point = PointJacobi.from_bytes( - curve.curve, - string, - validate_encoding=validate_point, - valid_encodings=valid_encodings, - ) - return cls.from_public_point(point, curve, hashfunc, validate_point) - - @classmethod - def from_pem( - cls, - string, - hashfunc=sha1, - valid_encodings=None, - valid_curve_encodings=None, - ): - """ - Initialise from public key stored in :term:`PEM` format. - - The PEM header of the key should be ``BEGIN PUBLIC KEY``. - - See the :func:`~VerifyingKey.from_der()` method for details of the - format supported. - - Note: only a single PEM object decoding is supported in provided - string. - - :param string: text with PEM-encoded public ECDSA key - :type string: str - :param valid_encodings: list of allowed point encodings. - By default :term:`uncompressed`, :term:`compressed`, and - :term:`hybrid`. To read malformed files, include - :term:`raw encoding` with ``raw`` in the list. - :type valid_encodings: :term:`set-like object` - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - :type valid_curve_encodings: :term:`set-like object` - - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - return cls.from_der( - der.unpem(string), - hashfunc=hashfunc, - valid_encodings=valid_encodings, - valid_curve_encodings=valid_curve_encodings, - ) - - @classmethod - def from_der( - cls, - string, - hashfunc=sha1, - valid_encodings=None, - valid_curve_encodings=None, - ): - """ - Initialise the key stored in :term:`DER` format. - - The expected format of the key is the SubjectPublicKeyInfo structure - from RFC5912 (for RSA keys, it's known as the PKCS#1 format):: - - SubjectPublicKeyInfo {PUBLIC-KEY: IOSet} ::= SEQUENCE { - algorithm AlgorithmIdentifier {PUBLIC-KEY, {IOSet}}, - subjectPublicKey BIT STRING - } - - Note: only public EC keys are supported by this method. The - SubjectPublicKeyInfo.algorithm.algorithm field must specify - id-ecPublicKey (see RFC3279). - - Only the named curve encoding is supported, thus the - SubjectPublicKeyInfo.algorithm.parameters field needs to be an - object identifier. A sequence in that field indicates an explicit - parameter curve encoding, this format is not supported. A NULL object - in that field indicates an "implicitlyCA" encoding, where the curve - parameters come from CA certificate, those, again, are not supported. - - :param string: binary string with the DER encoding of public ECDSA key - :type string: bytes-like object - :param valid_encodings: list of allowed point encodings. - By default :term:`uncompressed`, :term:`compressed`, and - :term:`hybrid`. To read malformed files, include - :term:`raw encoding` with ``raw`` in the list. - :type valid_encodings: :term:`set-like object` - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - :type valid_curve_encodings: :term:`set-like object` - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - if valid_encodings is None: - valid_encodings = set(["uncompressed", "compressed", "hybrid"]) - string = normalise_bytes(string) - # [[oid_ecPublicKey,oid_curve], point_str_bitstring] - s1, empty = der.remove_sequence(string) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER pubkey: %s" % binascii.hexlify(empty) - ) - s2, point_str_bitstring = der.remove_sequence(s1) - # s2 = oid_ecPublicKey,oid_curve - oid_pk, rest = der.remove_object(s2) - if oid_pk in (Ed25519.oid, Ed448.oid): - if oid_pk == Ed25519.oid: - curve = Ed25519 - else: - assert oid_pk == Ed448.oid - curve = Ed448 - point_str, empty = der.remove_bitstring(point_str_bitstring, 0) - if empty: - raise der.UnexpectedDER("trailing junk after public key") - return cls.from_string(point_str, curve, None) - if not oid_pk == oid_ecPublicKey: - raise der.UnexpectedDER( - "Unexpected object identifier in DER " - "encoding: {0!r}".format(oid_pk) - ) - curve = Curve.from_der(rest, valid_curve_encodings) - point_str, empty = der.remove_bitstring(point_str_bitstring, 0) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after pubkey pointstring: %s" - % binascii.hexlify(empty) - ) - # raw encoding of point is invalid in DER files - if len(point_str) == curve.verifying_key_length: - raise der.UnexpectedDER("Malformed encoding of public point") - return cls.from_string( - point_str, - curve, - hashfunc=hashfunc, - valid_encodings=valid_encodings, - ) - - @classmethod - def from_public_key_recovery( - cls, - signature, - data, - curve, - hashfunc=sha1, - sigdecode=sigdecode_string, - allow_truncate=True, - ): - """ - Return keys that can be used as verifiers of the provided signature. - - Tries to recover the public key that can be used to verify the - signature, usually returns two keys like that. - - :param signature: the byte string with the encoded signature - :type signature: bytes-like object - :param data: the data to be hashed for signature verification - :type data: bytes-like object - :param curve: the curve over which the signature was performed - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 - :type hashfunc: callable - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :param bool allow_truncate: if True, the provided hashfunc can generate - values larger than the bit size of the order of the curve, the - extra bits (at the end of the digest) will be truncated. - :type sigdecode: callable - - :return: Initialised VerifyingKey objects - :rtype: list of VerifyingKey - """ - if isinstance(curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - data = normalise_bytes(data) - digest = hashfunc(data).digest() - return cls.from_public_key_recovery_with_digest( - signature, - digest, - curve, - hashfunc=hashfunc, - sigdecode=sigdecode, - allow_truncate=allow_truncate, - ) - - @classmethod - def from_public_key_recovery_with_digest( - cls, - signature, - digest, - curve, - hashfunc=sha1, - sigdecode=sigdecode_string, - allow_truncate=False, - ): - """ - Return keys that can be used as verifiers of the provided signature. - - Tries to recover the public key that can be used to verify the - signature, usually returns two keys like that. - - :param signature: the byte string with the encoded signature - :type signature: bytes-like object - :param digest: the hash value of the message signed by the signature - :type digest: bytes-like object - :param curve: the curve over which the signature was performed - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 - :type hashfunc: callable - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :type sigdecode: callable - :param bool allow_truncate: if True, the provided hashfunc can generate - values larger than the bit size of the order of the curve (and - the length of provided `digest`), the extra bits (at the end of the - digest) will be truncated. - - :return: Initialised VerifyingKey object - :rtype: VerifyingKey - """ - if isinstance(curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - generator = curve.generator - r, s = sigdecode(signature, generator.order()) - sig = ecdsa.Signature(r, s) - - digest = normalise_bytes(digest) - digest_as_number = _truncate_and_convert_digest( - digest, curve, allow_truncate - ) - pks = sig.recover_public_keys(digest_as_number, generator) - - # Transforms the ecdsa.Public_key object into a VerifyingKey - verifying_keys = [ - cls.from_public_point(pk.point, curve, hashfunc) for pk in pks - ] - return verifying_keys - - def to_string(self, encoding="raw"): - """ - Convert the public key to a byte string. - - The method by default uses the :term:`raw encoding` (specified - by `encoding="raw"`. It can also output keys in :term:`uncompressed`, - :term:`compressed` and :term:`hybrid` formats. - - Remember that the curve identification is not part of the encoding - so to decode the point using :func:`~VerifyingKey.from_string`, curve - needs to be specified. - - Note: while the method is called "to_string", it's a misnomer from - Python 2 days when character strings and byte strings shared type. - On Python 3 the returned type will be `bytes`. - - :return: :term:`raw encoding` of the public key (public point) on the - curve - :rtype: bytes - """ - assert encoding in ("raw", "uncompressed", "compressed", "hybrid") - return self.pubkey.point.to_bytes(encoding) - - def to_pem( - self, point_encoding="uncompressed", curve_parameters_encoding=None - ): - """ - Convert the public key to the :term:`PEM` format. - - The PEM header of the key will be ``BEGIN PUBLIC KEY``. - - The format of the key is described in the - :func:`~VerifyingKey.from_der()` method. - This method supports only "named curve" encoding of keys. - - :param str point_encoding: specification of the encoding format - of public keys. "uncompressed" is most portable, "compressed" is - smallest. "hybrid" is uncommon and unsupported by most - implementations, it is as big as "uncompressed". - :param str curve_parameters_encoding: the encoding for curve parameters - to use, by default tries to use ``named_curve`` encoding, - if that is not possible, falls back to ``explicit`` encoding. - - :return: portable encoding of the public key - :rtype: bytes - - .. warning:: The PEM is encoded to US-ASCII, it needs to be - re-encoded if the system is incompatible (e.g. uses UTF-16) - """ - return der.topem( - self.to_der(point_encoding, curve_parameters_encoding), - "PUBLIC KEY", - ) - - def to_der( - self, point_encoding="uncompressed", curve_parameters_encoding=None - ): - """ - Convert the public key to the :term:`DER` format. - - The format of the key is described in the - :func:`~VerifyingKey.from_der()` method. - This method supports only "named curve" encoding of keys. - - :param str point_encoding: specification of the encoding format - of public keys. "uncompressed" is most portable, "compressed" is - smallest. "hybrid" is uncommon and unsupported by most - implementations, it is as big as "uncompressed". - :param str curve_parameters_encoding: the encoding for curve parameters - to use, by default tries to use ``named_curve`` encoding, - if that is not possible, falls back to ``explicit`` encoding. - - :return: DER encoding of the public key - :rtype: bytes - """ - if point_encoding == "raw": - raise ValueError("raw point_encoding not allowed in DER") - point_str = self.to_string(point_encoding) - if isinstance(self.curve.curve, CurveEdTw): - return der.encode_sequence( - der.encode_sequence(der.encode_oid(*self.curve.oid)), - der.encode_bitstring(bytes(point_str), 0), - ) - return der.encode_sequence( - der.encode_sequence( - encoded_oid_ecPublicKey, - self.curve.to_der(curve_parameters_encoding, point_encoding), - ), - # 0 is the number of unused bits in the - # bit string - der.encode_bitstring(point_str, 0), - ) - - def verify( - self, - signature, - data, - hashfunc=None, - sigdecode=sigdecode_string, - allow_truncate=True, - ): - """ - Verify a signature made over provided data. - - Will hash `data` to verify the signature. - - By default expects signature in :term:`raw encoding`. Can also be used - to verify signatures in ASN.1 DER encoding by using - :func:`ecdsa.util.sigdecode_der` - as the `sigdecode` parameter. - - :param signature: encoding of the signature - :type signature: sigdecode method dependent - :param data: data signed by the `signature`, will be hashed using - `hashfunc`, if specified, or default hash function - :type data: :term:`bytes-like object` - :param hashfunc: The default hash function that will be used for - verification, needs to implement the same interface as hashlib.sha1 - :type hashfunc: callable - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :type sigdecode: callable - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when verifying - SHA-384 output using NIST256p or in similar situations. Defaults to - True. - - :raises BadSignatureError: if the signature is invalid or malformed - - :return: True if the verification was successful - :rtype: bool - """ - # signature doesn't have to be a bytes-like-object so don't normalise - # it, the decoders will do that - data = normalise_bytes(data) - if isinstance(self.curve.curve, CurveEdTw): - signature = normalise_bytes(signature) - try: - return self.pubkey.verify(data, signature) - except (ValueError, MalformedPointError) as e: - raise BadSignatureError("Signature verification failed", e) - - hashfunc = hashfunc or self.default_hashfunc - digest = hashfunc(data).digest() - return self.verify_digest(signature, digest, sigdecode, allow_truncate) - - def verify_digest( - self, - signature, - digest, - sigdecode=sigdecode_string, - allow_truncate=False, - ): - """ - Verify a signature made over provided hash value. - - By default expects signature in :term:`raw encoding`. Can also be used - to verify signatures in ASN.1 DER encoding by using - :func:`ecdsa.util.sigdecode_der` - as the `sigdecode` parameter. - - :param signature: encoding of the signature - :type signature: sigdecode method dependent - :param digest: raw hash value that the signature authenticates. - :type digest: :term:`bytes-like object` - :param sigdecode: Callable to define the way the signature needs to - be decoded to an object, needs to handle `signature` as the - first parameter, the curve order (an int) as the second and return - a tuple with two integers, "r" as the first one and "s" as the - second one. See :func:`ecdsa.util.sigdecode_string` and - :func:`ecdsa.util.sigdecode_der` for examples. - :type sigdecode: callable - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when verifying - SHA-384 output using NIST256p or in similar situations. - - :raises BadSignatureError: if the signature is invalid or malformed - :raises BadDigestError: if the provided digest is too big for the curve - associated with this VerifyingKey and allow_truncate was not set - - :return: True if the verification was successful - :rtype: bool - """ - # signature doesn't have to be a bytes-like-object so don't normalise - # it, the decoders will do that - digest = normalise_bytes(digest) - number = _truncate_and_convert_digest( - digest, - self.curve, - allow_truncate, - ) - - try: - r, s = sigdecode(signature, self.pubkey.order) - except (der.UnexpectedDER, MalformedSignature) as e: - raise BadSignatureError("Malformed formatting of signature", e) - sig = ecdsa.Signature(r, s) - if self.pubkey.verifies(number, sig): - return True - raise BadSignatureError("Signature verification failed") - - -class SigningKey(object): - """ - Class for handling keys that can create signatures (private keys). - - :ivar `~ecdsa.curves.Curve` curve: The Curve over which all the - cryptographic operations will take place - :ivar default_hashfunc: the function that will be used for hashing the - data. Should implement the same API as :py:class:`hashlib.sha1` - :ivar int baselen: the length of a :term:`raw encoding` of private key - :ivar `~ecdsa.keys.VerifyingKey` verifying_key: the public key - associated with this private key - :ivar `~ecdsa.ecdsa.Private_key` privkey: the actual private key - """ - - def __init__(self, _error__please_use_generate=None): - """Unsupported, please use one of the classmethods to initialise.""" - if not _error__please_use_generate: - raise TypeError("Please use SigningKey.generate() to construct me") - self.curve = None - self.default_hashfunc = None - self.baselen = None - self.verifying_key = None - self.privkey = None - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, SigningKey): - return ( - self.curve == other.curve - and self.verifying_key == other.verifying_key - and self.privkey == other.privkey - ) - return NotImplemented - - def __ne__(self, other): - """Return False if the points are identical, True otherwise.""" - return not self == other - - @classmethod - def _twisted_edwards_keygen(cls, curve, entropy): - """Generate a private key on a Twisted Edwards curve.""" - if not entropy: - entropy = os.urandom - random = entropy(curve.baselen) - private_key = eddsa.PrivateKey(curve.generator, random) - public_key = private_key.public_key() - - verifying_key = VerifyingKey.from_string( - public_key.public_key(), curve - ) - - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = None - self.baselen = curve.baselen - self.privkey = private_key - self.verifying_key = verifying_key - return self - - @classmethod - def _weierstrass_keygen(cls, curve, entropy, hashfunc): - """Generate a private key on a Weierstrass curve.""" - secexp = randrange(curve.order, entropy) - return cls.from_secret_exponent(secexp, curve, hashfunc) - - @classmethod - def generate(cls, curve=NIST192p, entropy=None, hashfunc=sha1): - """ - Generate a random private key. - - :param curve: The curve on which the point needs to reside, defaults - to NIST192p - :type curve: ~ecdsa.curves.Curve - :param entropy: Source of randomness for generating the private keys, - should provide cryptographically secure random numbers if the keys - need to be secure. Uses os.urandom() by default. - :type entropy: callable - :param hashfunc: The default hash function that will be used for - signing, needs to implement the same interface - as hashlib.sha1 - :type hashfunc: callable - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - if isinstance(curve.curve, CurveEdTw): - return cls._twisted_edwards_keygen(curve, entropy) - return cls._weierstrass_keygen(curve, entropy, hashfunc) - - @classmethod - def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1): - """ - Create a private key from a random integer. - - Note: it's a low level method, it's recommended to use the - :func:`~SigningKey.generate` method to create private keys. - - :param int secexp: secret multiplier (the actual private key in ECDSA). - Needs to be an integer between 1 and the curve order. - :param curve: The curve on which the point needs to reside - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - signing, needs to implement the same interface - as hashlib.sha1 - :type hashfunc: callable - - :raises MalformedPointError: when the provided secexp is too large - or too small for the curve selected - :raises RuntimeError: if the generation of public key from private - key failed - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - if isinstance(curve.curve, CurveEdTw): - raise ValueError( - "Edwards keys don't support setting the secret scalar " - "(exponent) directly" - ) - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = hashfunc - self.baselen = curve.baselen - n = curve.order - if not 1 <= secexp < n: - raise MalformedPointError( - "Invalid value for secexp, expected integer " - "between 1 and {0}".format(n) - ) - pubkey_point = curve.generator * secexp - if hasattr(pubkey_point, "scale"): - pubkey_point = pubkey_point.scale() - self.verifying_key = VerifyingKey.from_public_point( - pubkey_point, curve, hashfunc, False - ) - pubkey = self.verifying_key.pubkey - self.privkey = ecdsa.Private_key(pubkey, secexp) - self.privkey.order = n - return self - - @classmethod - def from_string(cls, string, curve=NIST192p, hashfunc=sha1): - """ - Decode the private key from :term:`raw encoding`. - - Note: the name of this method is a misnomer coming from days of - Python 2, when binary strings and character strings shared a type. - In Python 3, the expected type is `bytes`. - - :param string: the raw encoding of the private key - :type string: :term:`bytes-like object` - :param curve: The curve on which the point needs to reside - :type curve: ~ecdsa.curves.Curve - :param hashfunc: The default hash function that will be used for - signing, needs to implement the same interface - as hashlib.sha1 - :type hashfunc: callable - - :raises MalformedPointError: if the length of encoding doesn't match - the provided curve or the encoded values is too large - :raises RuntimeError: if the generation of public key from private - key failed - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - string = normalise_bytes(string) - - if len(string) != curve.baselen: - raise MalformedPointError( - "Invalid length of private key, received {0}, " - "expected {1}".format(len(string), curve.baselen) - ) - if isinstance(curve.curve, CurveEdTw): - self = cls(_error__please_use_generate=True) - self.curve = curve - self.default_hashfunc = None # Ignored for EdDSA - self.baselen = curve.baselen - self.privkey = eddsa.PrivateKey(curve.generator, string) - self.verifying_key = VerifyingKey.from_string( - self.privkey.public_key().public_key(), curve - ) - return self - secexp = string_to_number(string) - return cls.from_secret_exponent(secexp, curve, hashfunc) - - @classmethod - def from_pem(cls, string, hashfunc=sha1, valid_curve_encodings=None): - """ - Initialise from key stored in :term:`PEM` format. - - The PEM formats supported are the un-encrypted RFC5915 - (the ssleay format) supported by OpenSSL, and the more common - un-encrypted RFC5958 (the PKCS #8 format). - - The legacy format files have the header with the string - ``BEGIN EC PRIVATE KEY``. - PKCS#8 files have the header ``BEGIN PRIVATE KEY``. - Encrypted files (ones that include the string - ``Proc-Type: 4,ENCRYPTED`` - right after the PEM header) are not supported. - - See :func:`~SigningKey.from_der` for ASN.1 syntax of the objects in - this files. - - :param string: text with PEM-encoded private ECDSA key - :type string: str - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - :type valid_curve_encodings: :term:`set-like object` - - - :raises MalformedPointError: if the length of encoding doesn't match - the provided curve or the encoded values is too large - :raises RuntimeError: if the generation of public key from private - key failed - :raises UnexpectedDER: if the encoding of the PEM file is incorrect - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - if not PY2 and isinstance(string, str): # pragma: no branch - string = string.encode() - - # The privkey pem may have multiple sections, commonly it also has - # "EC PARAMETERS", we need just "EC PRIVATE KEY". PKCS#8 should not - # have the "EC PARAMETERS" section; it's just "PRIVATE KEY". - private_key_index = string.find(b"-----BEGIN EC PRIVATE KEY-----") - if private_key_index == -1: - private_key_index = string.index(b"-----BEGIN PRIVATE KEY-----") - - return cls.from_der( - der.unpem(string[private_key_index:]), - hashfunc, - valid_curve_encodings, - ) - - @classmethod - def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None): - """ - Initialise from key stored in :term:`DER` format. - - The DER formats supported are the un-encrypted RFC5915 - (the ssleay format) supported by OpenSSL, and the more common - un-encrypted RFC5958 (the PKCS #8 format). - - Both formats contain an ASN.1 object following the syntax specified - in RFC5915:: - - ECPrivateKey ::= SEQUENCE { - version INTEGER { ecPrivkeyVer1(1) }} (ecPrivkeyVer1), - privateKey OCTET STRING, - parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, - publicKey [1] BIT STRING OPTIONAL - } - - `publicKey` field is ignored completely (errors, if any, in it will - be undetected). - - Two formats are supported for the `parameters` field: the named - curve and the explicit encoding of curve parameters. - In the legacy ssleay format, this implementation requires the optional - `parameters` field to get the curve name. In PKCS #8 format, the curve - is part of the PrivateKeyAlgorithmIdentifier. - - The PKCS #8 format includes an ECPrivateKey object as the `privateKey` - field within a larger structure:: - - OneAsymmetricKey ::= SEQUENCE { - version Version, - privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, - privateKey PrivateKey, - attributes [0] Attributes OPTIONAL, - ..., - [[2: publicKey [1] PublicKey OPTIONAL ]], - ... - } - - The `attributes` and `publicKey` fields are completely ignored; errors - in them will not be detected. - - :param string: binary string with DER-encoded private ECDSA key - :type string: :term:`bytes-like object` - :param valid_curve_encodings: list of allowed encoding formats - for curve parameters. By default (``None``) all are supported: - ``named_curve`` and ``explicit``. - Ignored for EdDSA. - :type valid_curve_encodings: :term:`set-like object` - - :raises MalformedPointError: if the length of encoding doesn't match - the provided curve or the encoded values is too large - :raises RuntimeError: if the generation of public key from private - key failed - :raises UnexpectedDER: if the encoding of the DER file is incorrect - - :return: Initialised SigningKey object - :rtype: SigningKey - """ - s = normalise_bytes(string) - curve = None - - s, empty = der.remove_sequence(s) - if empty != b(""): - raise der.UnexpectedDER( - "trailing junk after DER privkey: %s" % binascii.hexlify(empty) - ) - - version, s = der.remove_integer(s) - - # At this point, PKCS #8 has a sequence containing the algorithm - # identifier and the curve identifier. The ssleay format instead has - # an octet string containing the key data, so this is how we can - # distinguish the two formats. - if der.is_sequence(s): - if version not in (0, 1): - raise der.UnexpectedDER( - "expected version '0' or '1' at start of privkey, got %d" - % version - ) - - sequence, s = der.remove_sequence(s) - algorithm_oid, algorithm_identifier = der.remove_object(sequence) - - if algorithm_oid in (Ed25519.oid, Ed448.oid): - if algorithm_identifier: - raise der.UnexpectedDER( - "Non NULL parameters for a EdDSA key" - ) - key_str_der, s = der.remove_octet_string(s) - - # As RFC5958 describe, there are may be optional Attributes - # and Publickey. Don't raise error if something after - # Privatekey - - # TODO parse attributes or validate publickey - # if s: - # raise der.UnexpectedDER( - # "trailing junk inside the privateKey" - # ) - key_str, s = der.remove_octet_string(key_str_der) - if s: - raise der.UnexpectedDER( - "trailing junk after the encoded private key" - ) - - if algorithm_oid == Ed25519.oid: - curve = Ed25519 - else: - assert algorithm_oid == Ed448.oid - curve = Ed448 - - return cls.from_string(key_str, curve, None) - - if algorithm_oid not in (oid_ecPublicKey, oid_ecDH, oid_ecMQV): - raise der.UnexpectedDER( - "unexpected algorithm identifier '%s'" % (algorithm_oid,) - ) - - curve = Curve.from_der(algorithm_identifier, valid_curve_encodings) - - if empty != b"": - raise der.UnexpectedDER( - "unexpected data after algorithm identifier: %s" - % binascii.hexlify(empty) - ) - - # Up next is an octet string containing an ECPrivateKey. Ignore - # the optional "attributes" and "publicKey" fields that come after. - s, _ = der.remove_octet_string(s) - - # Unpack the ECPrivateKey to get to the key data octet string, - # and rejoin the ssleay parsing path. - s, empty = der.remove_sequence(s) - if empty != b(""): - raise der.UnexpectedDER( - "trailing junk after DER privkey: %s" - % binascii.hexlify(empty) - ) - - version, s = der.remove_integer(s) - - # The version of the ECPrivateKey must be 1. - if version != 1: - raise der.UnexpectedDER( - "expected version '1' at start of DER privkey, got %d" - % version - ) - - privkey_str, s = der.remove_octet_string(s) - - if not curve: - tag, curve_oid_str, s = der.remove_constructed(s) - if tag != 0: - raise der.UnexpectedDER( - "expected tag 0 in DER privkey, got %d" % tag - ) - curve = Curve.from_der(curve_oid_str, valid_curve_encodings) - - # we don't actually care about the following fields - # - # tag, pubkey_bitstring, s = der.remove_constructed(s) - # if tag != 1: - # raise der.UnexpectedDER("expected tag 1 in DER privkey, got %d" - # % tag) - # pubkey_str = der.remove_bitstring(pubkey_bitstring, 0) - # if empty != "": - # raise der.UnexpectedDER("trailing junk after DER privkey " - # "pubkeystr: %s" - # % binascii.hexlify(empty)) - - # our from_string method likes fixed-length privkey strings - if len(privkey_str) < curve.baselen: - privkey_str = ( - b("\x00") * (curve.baselen - len(privkey_str)) + privkey_str - ) - return cls.from_string(privkey_str, curve, hashfunc) - - def to_string(self): - """ - Convert the private key to :term:`raw encoding`. - - Note: while the method is named "to_string", its name comes from - Python 2 days, when binary and character strings used the same type. - The type used in Python 3 is `bytes`. - - :return: raw encoding of private key - :rtype: bytes - """ - if isinstance(self.curve.curve, CurveEdTw): - return bytes(self.privkey.private_key) - secexp = self.privkey.secret_multiplier - s = number_to_string(secexp, self.privkey.order) - return s - - def to_pem( - self, - point_encoding="uncompressed", - format="ssleay", - curve_parameters_encoding=None, - ): - """ - Convert the private key to the :term:`PEM` format. - - See :func:`~SigningKey.from_pem` method for format description. - - Only the named curve format is supported. - The public key will be included in generated string. - - The PEM header will specify ``BEGIN EC PRIVATE KEY`` or - ``BEGIN PRIVATE KEY``, depending on the desired format. - - :param str point_encoding: format to use for encoding public point - :param str format: either ``ssleay`` (default) or ``pkcs8`` - :param str curve_parameters_encoding: format of encoded curve - parameters, default depends on the curve, if the curve has - an associated OID, ``named_curve`` format will be used, - if no OID is associated with the curve, the fallback of - ``explicit`` parameters will be used. - - :return: PEM encoded private key - :rtype: bytes - - .. warning:: The PEM is encoded to US-ASCII, it needs to be - re-encoded if the system is incompatible (e.g. uses UTF-16) - """ - # TODO: "BEGIN ECPARAMETERS" - assert format in ("ssleay", "pkcs8") - header = "EC PRIVATE KEY" if format == "ssleay" else "PRIVATE KEY" - return der.topem( - self.to_der(point_encoding, format, curve_parameters_encoding), - header, - ) - - def _encode_eddsa(self): - """Create a PKCS#8 encoding of EdDSA keys.""" - ec_private_key = der.encode_octet_string(self.to_string()) - return der.encode_sequence( - der.encode_integer(0), - der.encode_sequence(der.encode_oid(*self.curve.oid)), - der.encode_octet_string(ec_private_key), - ) - - def to_der( - self, - point_encoding="uncompressed", - format="ssleay", - curve_parameters_encoding=None, - ): - """ - Convert the private key to the :term:`DER` format. - - See :func:`~SigningKey.from_der` method for format specification. - - Only the named curve format is supported. - The public key will be included in the generated string. - - :param str point_encoding: format to use for encoding public point - Ignored for EdDSA - :param str format: either ``ssleay`` (default) or ``pkcs8``. - EdDSA keys require ``pkcs8``. - :param str curve_parameters_encoding: format of encoded curve - parameters, default depends on the curve, if the curve has - an associated OID, ``named_curve`` format will be used, - if no OID is associated with the curve, the fallback of - ``explicit`` parameters will be used. - Ignored for EdDSA. - - :return: DER encoded private key - :rtype: bytes - """ - # SEQ([int(1), octetstring(privkey),cont[0], oid(secp224r1), - # cont[1],bitstring]) - if point_encoding == "raw": - raise ValueError("raw encoding not allowed in DER") - assert format in ("ssleay", "pkcs8") - if isinstance(self.curve.curve, CurveEdTw): - if format != "pkcs8": - raise ValueError("Only PKCS#8 format supported for EdDSA keys") - return self._encode_eddsa() - encoded_vk = self.get_verifying_key().to_string(point_encoding) - priv_key_elems = [ - der.encode_integer(1), - der.encode_octet_string(self.to_string()), - ] - if format == "ssleay": - priv_key_elems.append( - der.encode_constructed( - 0, self.curve.to_der(curve_parameters_encoding) - ) - ) - # the 0 in encode_bitstring specifies the number of unused bits - # in the `encoded_vk` string - priv_key_elems.append( - der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)) - ) - ec_private_key = der.encode_sequence(*priv_key_elems) - - if format == "ssleay": - return ec_private_key - else: - return der.encode_sequence( - # version = 1 means the public key is not present in the - # top-level structure. - der.encode_integer(1), - der.encode_sequence( - der.encode_oid(*oid_ecPublicKey), - self.curve.to_der(curve_parameters_encoding), - ), - der.encode_octet_string(ec_private_key), - ) - - def get_verifying_key(self): - """ - Return the VerifyingKey associated with this private key. - - Equivalent to reading the `verifying_key` field of an instance. - - :return: a public key that can be used to verify the signatures made - with this SigningKey - :rtype: VerifyingKey - """ - return self.verifying_key - - def sign_deterministic( - self, - data, - hashfunc=None, - sigencode=sigencode_string, - extra_entropy=b"", - ): - """ - Create signature over data. - - For Weierstrass curves it uses the deterministic RFC6979 algorithm. - For Edwards curves it uses the standard EdDSA algorithm. - - For ECDSA the data will be hashed using the `hashfunc` function before - signing. - For EdDSA the data will be hashed with the hash associated with the - curve (SHA-512 for Ed25519 and SHAKE-256 for Ed448). - - This is the recommended method for performing signatures when hashing - of data is necessary. - - :param data: data to be hashed and computed signature over - :type data: :term:`bytes-like object` - :param hashfunc: hash function to use for computing the signature, - if unspecified, the default hash function selected during - object initialisation will be used (see - `VerifyingKey.default_hashfunc`). The object needs to implement - the same interface as hashlib.sha1. - Ignored with EdDSA. - :type hashfunc: callable - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` - as examples of such functions. - Ignored with EdDSA. - :type sigencode: callable - :param extra_entropy: additional data that will be fed into the random - number generator used in the RFC6979 process. Entirely optional. - Ignored with EdDSA. - :type extra_entropy: :term:`bytes-like object` - - :return: encoded signature over `data` - :rtype: bytes or sigencode function dependent type - """ - hashfunc = hashfunc or self.default_hashfunc - data = normalise_bytes(data) - - if isinstance(self.curve.curve, CurveEdTw): - return self.privkey.sign(data) - - extra_entropy = normalise_bytes(extra_entropy) - digest = hashfunc(data).digest() - - return self.sign_digest_deterministic( - digest, - hashfunc=hashfunc, - sigencode=sigencode, - extra_entropy=extra_entropy, - allow_truncate=True, - ) - - def sign_digest_deterministic( - self, - digest, - hashfunc=None, - sigencode=sigencode_string, - extra_entropy=b"", - allow_truncate=False, - ): - """ - Create signature for digest using the deterministic RFC6979 algorithm. - - `digest` should be the output of cryptographically secure hash function - like SHA256 or SHA-3-256. - - This is the recommended method for performing signatures when no - hashing of data is necessary. - - :param digest: hash of data that will be signed - :type digest: :term:`bytes-like object` - :param hashfunc: hash function to use for computing the random "k" - value from RFC6979 process, - if unspecified, the default hash function selected during - object initialisation will be used (see - :attr:`.VerifyingKey.default_hashfunc`). The object needs to - implement - the same interface as :func:`~hashlib.sha1` from :py:mod:`hashlib`. - :type hashfunc: callable - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See :func:`~ecdsa.util.sigencode_string` and - :func:`~ecdsa.util.sigencode_der` - as examples of such functions. - :type sigencode: callable - :param extra_entropy: additional data that will be fed into the random - number generator used in the RFC6979 process. Entirely optional. - :type extra_entropy: :term:`bytes-like object` - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when signing - SHA-384 output using NIST256p or in similar situations. - - :return: encoded signature for the `digest` hash - :rtype: bytes or sigencode function dependent type - """ - if isinstance(self.curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - secexp = self.privkey.secret_multiplier - hashfunc = hashfunc or self.default_hashfunc - digest = normalise_bytes(digest) - extra_entropy = normalise_bytes(extra_entropy) - - def simple_r_s(r, s, order): - return r, s, order - - retry_gen = 0 - while True: - k = rfc6979.generate_k( - self.curve.generator.order(), - secexp, - hashfunc, - digest, - retry_gen=retry_gen, - extra_entropy=extra_entropy, - ) - try: - r, s, order = self.sign_digest( - digest, - sigencode=simple_r_s, - k=k, - allow_truncate=allow_truncate, - ) - break - except RSZeroError: - retry_gen += 1 - - return sigencode(r, s, order) - - def sign( - self, - data, - entropy=None, - hashfunc=None, - sigencode=sigencode_string, - k=None, - allow_truncate=True, - ): - """ - Create signature over data. - - Uses the probabilistic ECDSA algorithm for Weierstrass curves - (NIST256p, etc.) and the deterministic EdDSA algorithm for the - Edwards curves (Ed25519, Ed448). - - This method uses the standard ECDSA algorithm that requires a - cryptographically secure random number generator. - - It's recommended to use the :func:`~SigningKey.sign_deterministic` - method instead of this one. - - :param data: data that will be hashed for signing - :type data: :term:`bytes-like object` - :param callable entropy: randomness source, :func:`os.urandom` by - default. Ignored with EdDSA. - :param hashfunc: hash function to use for hashing the provided - ``data``. - If unspecified the default hash function selected during - object initialisation will be used (see - :attr:`.VerifyingKey.default_hashfunc`). - Should behave like :func:`~hashlib.sha1` from :py:mod:`hashlib`. - The output length of the - hash (in bytes) must not be longer than the length of the curve - order (rounded up to the nearest byte), so using SHA256 with - NIST256p is ok, but SHA256 with NIST192p is not. (In the 2**-96ish - unlikely event of a hash output larger than the curve order, the - hash will effectively be wrapped mod n). - If you want to explicitly allow use of large hashes with small - curves set the ``allow_truncate`` to ``True``. - Use ``hashfunc=hashlib.sha1`` to match openssl's - ``-ecdsa-with-SHA1`` mode, - or ``hashfunc=hashlib.sha256`` for openssl-1.0.0's - ``-ecdsa-with-SHA256``. - Ignored for EdDSA - :type hashfunc: callable - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See :func:`~ecdsa.util.sigencode_string` and - :func:`~ecdsa.util.sigencode_der` - as examples of such functions. - Ignored for EdDSA - :type sigencode: callable - :param int k: a pre-selected nonce for calculating the signature. - In typical use cases, it should be set to None (the default) to - allow its generation from an entropy source. - Ignored for EdDSA. - :param bool allow_truncate: if ``True``, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when signing - SHA-384 output using NIST256p or in similar situations. True by - default. - Ignored for EdDSA. - - :raises RSZeroError: in the unlikely event when *r* parameter or - *s* parameter of the created signature is equal 0, as that would - leak the key. Caller should try a better entropy source, retry with - different ``k``, or use the - :func:`~SigningKey.sign_deterministic` in such case. - - :return: encoded signature of the hash of `data` - :rtype: bytes or sigencode function dependent type - """ - hashfunc = hashfunc or self.default_hashfunc - data = normalise_bytes(data) - if isinstance(self.curve.curve, CurveEdTw): - return self.sign_deterministic(data) - h = hashfunc(data).digest() - return self.sign_digest(h, entropy, sigencode, k, allow_truncate) - - def sign_digest( - self, - digest, - entropy=None, - sigencode=sigencode_string, - k=None, - allow_truncate=False, - ): - """ - Create signature over digest using the probabilistic ECDSA algorithm. - - This method uses the standard ECDSA algorithm that requires a - cryptographically secure random number generator. - - This method does not hash the input. - - It's recommended to use the - :func:`~SigningKey.sign_digest_deterministic` method - instead of this one. - - :param digest: hash value that will be signed - :type digest: :term:`bytes-like object` - :param callable entropy: randomness source, os.urandom by default - :param sigencode: function used to encode the signature. - The function needs to accept three parameters: the two integers - that are the signature and the order of the curve over which the - signature was computed. It needs to return an encoded signature. - See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der` - as examples of such functions. - :type sigencode: callable - :param int k: a pre-selected nonce for calculating the signature. - In typical use cases, it should be set to None (the default) to - allow its generation from an entropy source. - :param bool allow_truncate: if True, the provided digest can have - bigger bit-size than the order of the curve, the extra bits (at - the end of the digest) will be truncated. Use it when signing - SHA-384 output using NIST256p or in similar situations. - - :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter of the created signature is equal 0, as that would - leak the key. Caller should try a better entropy source, retry with - different 'k', or use the - :func:`~SigningKey.sign_digest_deterministic` in such case. - - :return: encoded signature for the `digest` hash - :rtype: bytes or sigencode function dependent type - """ - if isinstance(self.curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - digest = normalise_bytes(digest) - number = _truncate_and_convert_digest( - digest, - self.curve, - allow_truncate, - ) - r, s = self.sign_number(number, entropy, k) - return sigencode(r, s, self.privkey.order) - - def sign_number(self, number, entropy=None, k=None): - """ - Sign an integer directly. - - Note, this is a low level method, usually you will want to use - :func:`~SigningKey.sign_deterministic` or - :func:`~SigningKey.sign_digest_deterministic`. - - :param int number: number to sign using the probabilistic ECDSA - algorithm. - :param callable entropy: entropy source, os.urandom by default - :param int k: pre-selected nonce for signature operation. If unset - it will be selected at random using the entropy source. - - :raises RSZeroError: in the unlikely event when "r" parameter or - "s" parameter of the created signature is equal 0, as that would - leak the key. Caller should try a better entropy source, retry with - different 'k', or use the - :func:`~SigningKey.sign_digest_deterministic` in such case. - - :return: the "r" and "s" parameters of the signature - :rtype: tuple of ints - """ - if isinstance(self.curve.curve, CurveEdTw): - raise ValueError("Method unsupported for Edwards curves") - order = self.privkey.order - - if k is not None: - _k = k - else: - _k = randrange(order, entropy) - - assert 1 <= _k < order - sig = self.privkey.sign(number, _k) - return sig.r, sig.s diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py deleted file mode 100644 index d3500c709..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/numbertheory.py +++ /dev/null @@ -1,825 +0,0 @@ -#! /usr/bin/env python -# -# Provide some simple capabilities from number theory. -# -# Version of 2008.11.14. -# -# Written in 2005 and 2006 by Peter Pearson and placed in the public domain. -# Revision history: -# 2008.11.14: Use pow(base, exponent, modulus) for modular_exp. -# Make gcd and lcm accept arbitrarily many arguments. - -from __future__ import division - -import sys -from six import integer_types, PY2 -from six.moves import reduce - -try: - xrange -except NameError: - xrange = range -try: - from gmpy2 import powmod - - GMPY2 = True - GMPY = False -except ImportError: - GMPY2 = False - try: - from gmpy import mpz - - GMPY = True - except ImportError: - GMPY = False - -import math -import warnings - - -class Error(Exception): - """Base class for exceptions in this module.""" - - pass - - -class JacobiError(Error): - pass - - -class SquareRootError(Error): - pass - - -class NegativeExponentError(Error): - pass - - -def modular_exp(base, exponent, modulus): # pragma: no cover - """Raise base to exponent, reducing by modulus""" - # deprecated in 0.14 - warnings.warn( - "Function is unused in library code. If you use this code, " - "change to pow() builtin.", - DeprecationWarning, - ) - if exponent < 0: - raise NegativeExponentError( - "Negative exponents (%d) not allowed" % exponent - ) - return pow(base, exponent, modulus) - - -def polynomial_reduce_mod(poly, polymod, p): - """Reduce poly by polymod, integer arithmetic modulo p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" - - # This module has been tested only by extensive use - # in calculating modular square roots. - - # Just to make this easy, require a monic polynomial: - assert polymod[-1] == 1 - - assert len(polymod) > 1 - - while len(poly) >= len(polymod): - if poly[-1] != 0: - for i in xrange(2, len(polymod) + 1): - poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p - poly = poly[0:-1] - - return poly - - -def polynomial_multiply_mod(m1, m2, polymod, p): - """Polynomial multiplication modulo a polynomial over ints mod p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" - - # This is just a seat-of-the-pants implementation. - - # This module has been tested only by extensive use - # in calculating modular square roots. - - # Initialize the product to zero: - - prod = (len(m1) + len(m2) - 1) * [0] - - # Add together all the cross-terms: - - for i in xrange(len(m1)): - for j in xrange(len(m2)): - prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p - - return polynomial_reduce_mod(prod, polymod, p) - - -def polynomial_exp_mod(base, exponent, polymod, p): - """Polynomial exponentiation modulo a polynomial over ints mod p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" - - # Based on the Handbook of Applied Cryptography, algorithm 2.227. - - # This module has been tested only by extensive use - # in calculating modular square roots. - - assert exponent < p - - if exponent == 0: - return [1] - - G = base - k = exponent - if k % 2 == 1: - s = G - else: - s = [1] - - while k > 1: - k = k // 2 - G = polynomial_multiply_mod(G, G, polymod, p) - if k % 2 == 1: - s = polynomial_multiply_mod(G, s, polymod, p) - - return s - - -def jacobi(a, n): - """Jacobi symbol""" - - # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. - - # This function has been tested by comparison with a small - # table printed in HAC, and by extensive use in calculating - # modular square roots. - - if not n >= 3: - raise JacobiError("n must be larger than 2") - if not n % 2 == 1: - raise JacobiError("n must be odd") - a = a % n - if a == 0: - return 0 - if a == 1: - return 1 - a1, e = a, 0 - while a1 % 2 == 0: - a1, e = a1 // 2, e + 1 - if e % 2 == 0 or n % 8 == 1 or n % 8 == 7: - s = 1 - else: - s = -1 - if a1 == 1: - return s - if n % 4 == 3 and a1 % 4 == 3: - s = -s - return s * jacobi(n % a1, a1) - - -def square_root_mod_prime(a, p): - """Modular square root of a, mod p, p prime.""" - - # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. - - # This module has been tested for all values in [0,p-1] for - # every prime p from 3 to 1229. - - assert 0 <= a < p - assert 1 < p - - if a == 0: - return 0 - if p == 2: - return a - - jac = jacobi(a, p) - if jac == -1: - raise SquareRootError("%d has no square root modulo %d" % (a, p)) - - if p % 4 == 3: - return pow(a, (p + 1) // 4, p) - - if p % 8 == 5: - d = pow(a, (p - 1) // 4, p) - if d == 1: - return pow(a, (p + 3) // 8, p) - assert d == p - 1 - return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p - - if PY2: - # xrange on python2 can take integers representable as C long only - range_top = min(0x7FFFFFFF, p) - else: - range_top = p - for b in xrange(2, range_top): - if jacobi(b * b - 4 * a, p) == -1: - f = (a, -b, 1) - ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) - if ff[1]: - raise SquareRootError("p is not prime") - return ff[0] - raise RuntimeError("No b found.") - - -# because all the inverse_mod code is arch/environment specific, and coveralls -# expects it to execute equal number of times, we need to waive it by -# adding the "no branch" pragma to all branches -if GMPY2: # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - if a == 0: # pragma: no branch - return 0 - return powmod(a, -1, m) - -elif GMPY: # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - # while libgmp does support inverses modulo, it is accessible - # only using the native `pow()` function, and `pow()` in gmpy sanity - # checks the parameters before passing them on to underlying - # implementation - if a == 0: # pragma: no branch - return 0 - a = mpz(a) - m = mpz(m) - - lm, hm = mpz(1), mpz(0) - low, high = a % m, m - while low > 1: # pragma: no branch - r = high // low - lm, low, hm, high = hm - lm * r, high - low * r, lm, low - - return lm % m - -elif sys.version_info >= (3, 8): # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - if a == 0: # pragma: no branch - return 0 - return pow(a, -1, m) - -else: # pragma: no branch - - def inverse_mod(a, m): - """Inverse of a mod m.""" - - if a == 0: # pragma: no branch - return 0 - - lm, hm = 1, 0 - low, high = a % m, m - while low > 1: # pragma: no branch - r = high // low - lm, low, hm, high = hm - lm * r, high - low * r, lm, low - - return lm % m - - -try: - gcd2 = math.gcd -except AttributeError: - - def gcd2(a, b): - """Greatest common divisor using Euclid's algorithm.""" - while a: - a, b = b % a, a - return b - - -def gcd(*a): - """Greatest common divisor. - - Usage: gcd([ 2, 4, 6 ]) - or: gcd(2, 4, 6) - """ - - if len(a) > 1: - return reduce(gcd2, a) - if hasattr(a[0], "__iter__"): - return reduce(gcd2, a[0]) - return a[0] - - -def lcm2(a, b): - """Least common multiple of two integers.""" - - return (a * b) // gcd(a, b) - - -def lcm(*a): - """Least common multiple. - - Usage: lcm([ 3, 4, 5 ]) - or: lcm(3, 4, 5) - """ - - if len(a) > 1: - return reduce(lcm2, a) - if hasattr(a[0], "__iter__"): - return reduce(lcm2, a[0]) - return a[0] - - -def factorization(n): - """Decompose n into a list of (prime,exponent) pairs.""" - - assert isinstance(n, integer_types) - - if n < 2: - return [] - - result = [] - - # Test the small primes: - - for d in smallprimes: - if d > n: - break - q, r = divmod(n, d) - if r == 0: - count = 1 - while d <= n: - n = q - q, r = divmod(n, d) - if r != 0: - break - count = count + 1 - result.append((d, count)) - - # If n is still greater than the last of our small primes, - # it may require further work: - - if n > smallprimes[-1]: - if is_prime(n): # If what's left is prime, it's easy: - result.append((n, 1)) - else: # Ugh. Search stupidly for a divisor: - d = smallprimes[-1] - while 1: - d = d + 2 # Try the next divisor. - q, r = divmod(n, d) - if q < d: # n < d*d means we're done, n = 1 or prime. - break - if r == 0: # d divides n. How many times? - count = 1 - n = q - while d <= n: # As long as d might still divide n, - q, r = divmod(n, d) # see if it does. - if r != 0: - break - n = q # It does. Reduce n, increase count. - count = count + 1 - result.append((d, count)) - if n > 1: - result.append((n, 1)) - - return result - - -def phi(n): # pragma: no cover - """Return the Euler totient function of n.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - assert isinstance(n, integer_types) - - if n < 3: - return 1 - - result = 1 - ff = factorization(n) - for f in ff: - e = f[1] - if e > 1: - result = result * f[0] ** (e - 1) * (f[0] - 1) - else: - result = result * (f[0] - 1) - return result - - -def carmichael(n): # pragma: no cover - """Return Carmichael function of n. - - Carmichael(n) is the smallest integer x such that - m**x = 1 mod n for all m relatively prime to n. - """ - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - return carmichael_of_factorized(factorization(n)) - - -def carmichael_of_factorized(f_list): # pragma: no cover - """Return the Carmichael function of a number that is - represented as a list of (prime,exponent) pairs. - """ - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - if len(f_list) < 1: - return 1 - - result = carmichael_of_ppower(f_list[0]) - for i in xrange(1, len(f_list)): - result = lcm(result, carmichael_of_ppower(f_list[i])) - - return result - - -def carmichael_of_ppower(pp): # pragma: no cover - """Carmichael function of the given power of the given prime.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - p, a = pp - if p == 2 and a > 2: - return 2 ** (a - 2) - else: - return (p - 1) * p ** (a - 1) - - -def order_mod(x, m): # pragma: no cover - """Return the order of x in the multiplicative group mod m.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - # Warning: this implementation is not very clever, and will - # take a long time if m is very large. - - if m <= 1: - return 0 - - assert gcd(x, m) == 1 - - z = x - result = 1 - while z != 1: - z = (z * x) % m - result = result + 1 - return result - - -def largest_factor_relatively_prime(a, b): # pragma: no cover - """Return the largest factor of a relatively prime to b.""" - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - while 1: - d = gcd(a, b) - if d <= 1: - break - b = d - while 1: - q, r = divmod(a, d) - if r > 0: - break - a = q - return a - - -def kinda_order_mod(x, m): # pragma: no cover - """Return the order of x in the multiplicative group mod m', - where m' is the largest factor of m relatively prime to x. - """ - # deprecated in 0.14 - warnings.warn( - "Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/tlsfuzzer/python-ecdsa", - DeprecationWarning, - ) - - return order_mod(x, largest_factor_relatively_prime(m, x)) - - -def is_prime(n): - """Return True if x is prime, False otherwise. - - We use the Miller-Rabin test, as given in Menezes et al. p. 138. - This test is not exact: there are composite values n for which - it returns True. - - In testing the odd numbers from 10000001 to 19999999, - about 66 composites got past the first test, - 5 got past the second test, and none got past the third. - Since factors of 2, 3, 5, 7, and 11 were detected during - preliminary screening, the number of numbers tested by - Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) - = 4.57 million. - """ - - # (This is used to study the risk of false positives:) - global miller_rabin_test_count - - miller_rabin_test_count = 0 - - if n <= smallprimes[-1]: - if n in smallprimes: - return True - else: - return False - - if gcd(n, 2 * 3 * 5 * 7 * 11) != 1: - return False - - # Choose a number of iterations sufficient to reduce the - # probability of accepting a composite below 2**-80 - # (from Menezes et al. Table 4.4): - - t = 40 - n_bits = 1 + int(math.log(n, 2)) - for k, tt in ( - (100, 27), - (150, 18), - (200, 15), - (250, 12), - (300, 9), - (350, 8), - (400, 7), - (450, 6), - (550, 5), - (650, 4), - (850, 3), - (1300, 2), - ): - if n_bits < k: - break - t = tt - - # Run the test t times: - - s = 0 - r = n - 1 - while (r % 2) == 0: - s = s + 1 - r = r // 2 - for i in xrange(t): - a = smallprimes[i] - y = pow(a, r, n) - if y != 1 and y != n - 1: - j = 1 - while j <= s - 1 and y != n - 1: - y = pow(y, 2, n) - if y == 1: - miller_rabin_test_count = i + 1 - return False - j = j + 1 - if y != n - 1: - miller_rabin_test_count = i + 1 - return False - return True - - -def next_prime(starting_value): - """Return the smallest prime larger than the starting value.""" - - if starting_value < 2: - return 2 - result = (starting_value + 1) | 1 - while not is_prime(result): - result = result + 2 - return result - - -smallprimes = [ - 2, - 3, - 5, - 7, - 11, - 13, - 17, - 19, - 23, - 29, - 31, - 37, - 41, - 43, - 47, - 53, - 59, - 61, - 67, - 71, - 73, - 79, - 83, - 89, - 97, - 101, - 103, - 107, - 109, - 113, - 127, - 131, - 137, - 139, - 149, - 151, - 157, - 163, - 167, - 173, - 179, - 181, - 191, - 193, - 197, - 199, - 211, - 223, - 227, - 229, - 233, - 239, - 241, - 251, - 257, - 263, - 269, - 271, - 277, - 281, - 283, - 293, - 307, - 311, - 313, - 317, - 331, - 337, - 347, - 349, - 353, - 359, - 367, - 373, - 379, - 383, - 389, - 397, - 401, - 409, - 419, - 421, - 431, - 433, - 439, - 443, - 449, - 457, - 461, - 463, - 467, - 479, - 487, - 491, - 499, - 503, - 509, - 521, - 523, - 541, - 547, - 557, - 563, - 569, - 571, - 577, - 587, - 593, - 599, - 601, - 607, - 613, - 617, - 619, - 631, - 641, - 643, - 647, - 653, - 659, - 661, - 673, - 677, - 683, - 691, - 701, - 709, - 719, - 727, - 733, - 739, - 743, - 751, - 757, - 761, - 769, - 773, - 787, - 797, - 809, - 811, - 821, - 823, - 827, - 829, - 839, - 853, - 857, - 859, - 863, - 877, - 881, - 883, - 887, - 907, - 911, - 919, - 929, - 937, - 941, - 947, - 953, - 967, - 971, - 977, - 983, - 991, - 997, - 1009, - 1013, - 1019, - 1021, - 1031, - 1033, - 1039, - 1049, - 1051, - 1061, - 1063, - 1069, - 1087, - 1091, - 1093, - 1097, - 1103, - 1109, - 1117, - 1123, - 1129, - 1151, - 1153, - 1163, - 1171, - 1181, - 1187, - 1193, - 1201, - 1213, - 1217, - 1223, - 1229, -] - -miller_rabin_test_count = 0 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py deleted file mode 100644 index 0728b5a41..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/rfc6979.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -RFC 6979: - Deterministic Usage of the Digital Signature Algorithm (DSA) and - Elliptic Curve Digital Signature Algorithm (ECDSA) - - http://tools.ietf.org/html/rfc6979 - -Many thanks to Coda Hale for his implementation in Go language: - https://github.com/codahale/rfc6979 -""" - -import hmac -from binascii import hexlify -from .util import number_to_string, number_to_string_crop, bit_length -from ._compat import hmac_compat - - -# bit_length was defined in this module previously so keep it for backwards -# compatibility, will need to deprecate and remove it later -__all__ = ["bit_length", "bits2int", "bits2octets", "generate_k"] - - -def bits2int(data, qlen): - x = int(hexlify(data), 16) - l = len(data) * 8 - - if l > qlen: - return x >> (l - qlen) - return x - - -def bits2octets(data, order): - z1 = bits2int(data, bit_length(order)) - z2 = z1 - order - - if z2 < 0: - z2 = z1 - - return number_to_string_crop(z2, order) - - -# https://tools.ietf.org/html/rfc6979#section-3.2 -def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""): - """ - Generate the ``k`` value - the nonce for DSA. - - :param int order: order of the DSA generator used in the signature - :param int secexp: secure exponent (private key) in numeric form - :param hash_func: reference to the same hash function used for generating - hash, like :py:class:`hashlib.sha1` - :param bytes data: hash in binary form of the signing data - :param int retry_gen: how many good 'k' values to skip before returning - :param bytes extra_entropy: additional added data in binary form as per - section-3.6 of rfc6979 - :rtype: int - """ - - qlen = bit_length(order) - holen = hash_func().digest_size - rolen = (qlen + 7) // 8 - bx = ( - hmac_compat(number_to_string(secexp, order)), - hmac_compat(bits2octets(data, order)), - hmac_compat(extra_entropy), - ) - - # Step B - v = b"\x01" * holen - - # Step C - k = b"\x00" * holen - - # Step D - - k = hmac.new(k, digestmod=hash_func) - k.update(v + b"\x00") - for i in bx: - k.update(i) - k = k.digest() - - # Step E - v = hmac.new(k, v, hash_func).digest() - - # Step F - k = hmac.new(k, digestmod=hash_func) - k.update(v + b"\x01") - for i in bx: - k.update(i) - k = k.digest() - - # Step G - v = hmac.new(k, v, hash_func).digest() - - # Step H - while True: - # Step H1 - t = b"" - - # Step H2 - while len(t) < rolen: - v = hmac.new(k, v, hash_func).digest() - t += v - - # Step H3 - secret = bits2int(t, qlen) - - if 1 <= secret < order: - if retry_gen <= 0: - return secret - retry_gen -= 1 - - k = hmac.new(k, v + b"\x00", hash_func).digest() - v = hmac.new(k, v, hash_func).digest() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py deleted file mode 100644 index 93b6c9bde..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_curves.py +++ /dev/null @@ -1,361 +0,0 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest - -import base64 -import pytest -from .curves import ( - Curve, - NIST256p, - curves, - UnknownCurveError, - PRIME_FIELD_OID, - curve_by_name, -) -from .ellipticcurve import CurveFp, PointJacobi, CurveEdTw -from . import der -from .util import number_to_string - - -class TestParameterEncoding(unittest.TestCase): - @classmethod - def setUpClass(cls): - # minimal, but with cofactor (excludes seed when compared to - # OpenSSL output) - cls.base64_params = ( - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=" - ) - - def test_from_pem(self): - pem_params = ( - "-----BEGIN EC PARAMETERS-----\n" - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" - "-----END EC PARAMETERS-----\n" - ) - curve = Curve.from_pem(pem_params) - - self.assertIs(curve, NIST256p) - - def test_from_pem_with_explicit_when_explicit_disabled(self): - pem_params = ( - "-----BEGIN EC PARAMETERS-----\n" - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" - "-----END EC PARAMETERS-----\n" - ) - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_pem(pem_params, ["named_curve"]) - - self.assertIn("explicit curve parameters not", str(e.exception)) - - def test_from_pem_with_named_curve_with_named_curve_disabled(self): - pem_params = ( - "-----BEGIN EC PARAMETERS-----\n" - "BggqhkjOPQMBBw==\n" - "-----END EC PARAMETERS-----\n" - ) - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_pem(pem_params, ["explicit"]) - - self.assertIn("named_curve curve parameters not", str(e.exception)) - - def test_from_pem_with_wrong_header(self): - pem_params = ( - "-----BEGIN PARAMETERS-----\n" - "MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n" - "//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n" - "o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n" - "wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n" - "AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n" - "-----END PARAMETERS-----\n" - ) - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_pem(pem_params) - - self.assertIn("PARAMETERS PEM header", str(e.exception)) - - def test_to_pem(self): - pem_params = ( - b"-----BEGIN EC PARAMETERS-----\n" - b"BggqhkjOPQMBBw==\n" - b"-----END EC PARAMETERS-----\n" - ) - encoding = NIST256p.to_pem() - - self.assertEqual(pem_params, encoding) - - def test_compare_with_different_object(self): - self.assertNotEqual(NIST256p, 256) - - def test_named_curve_params_der(self): - encoded = NIST256p.to_der() - - # just the encoding of the NIST256p OID (prime256v1) - self.assertEqual(b"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07", encoded) - - def test_verify_that_default_is_named_curve_der(self): - encoded_default = NIST256p.to_der() - encoded_named = NIST256p.to_der("named_curve") - - self.assertEqual(encoded_default, encoded_named) - - def test_encoding_to_explicit_params(self): - encoded = NIST256p.to_der("explicit") - - self.assertEqual(encoded, bytes(base64.b64decode(self.base64_params))) - - def test_encoding_to_unsupported_type(self): - with self.assertRaises(ValueError) as e: - NIST256p.to_der("unsupported") - - self.assertIn("Only 'named_curve'", str(e.exception)) - - def test_encoding_to_explicit_compressed_params(self): - encoded = NIST256p.to_der("explicit", "compressed") - - compressed_base_point = ( - "MIHAAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" - "/////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" - "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEIQNrF9Hy4SxCR/i85uVjpEDydwN9" - "gS3rM6D0oTlF2JjClgIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8YyVR" - "AgEB" - ) - - self.assertEqual( - encoded, bytes(base64.b64decode(compressed_base_point)) - ) - - def test_decoding_explicit_from_openssl(self): - # generated with openssl 1.1.1k using - # openssl ecparam -name P-256 -param_enc explicit -out /tmp/file.pem - p256_explicit = ( - "MIH3AgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP//////////" - "/////zBbBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12Ko6" - "k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsDFQDEnTYIhucEk2pmeOETnSa3gZ9+" - "kARBBGsX0fLhLEJH+Lzm5WOkQPJ3A32BLeszoPShOUXYmMKWT+NC4v4af5uO5+tK" - "fA+eFivOM1drMV7Oy7ZAaDe/UfUCIQD/////AAAAAP//////////vOb6racXnoTz" - "ucrC/GMlUQIBAQ==" - ) - - decoded = Curve.from_der(bytes(base64.b64decode(p256_explicit))) - - self.assertEqual(NIST256p, decoded) - - def test_decoding_well_known_from_explicit_params(self): - curve = Curve.from_der(bytes(base64.b64decode(self.base64_params))) - - self.assertIs(curve, NIST256p) - - def test_decoding_with_incorrect_valid_encodings(self): - with self.assertRaises(ValueError) as e: - Curve.from_der(b"", ["explicitCA"]) - - self.assertIn("Only named_curve", str(e.exception)) - - def test_compare_curves_with_different_generators(self): - curve_fp = CurveFp(23, 1, 7) - base_a = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) - base_b = PointJacobi(curve_fp, 1, 20, 1, 9, generator=True) - - curve_a = Curve("unknown", curve_fp, base_a, None) - curve_b = Curve("unknown", curve_fp, base_b, None) - - self.assertNotEqual(curve_a, curve_b) - - def test_default_encode_for_custom_curve(self): - curve_fp = CurveFp(23, 1, 7) - base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) - - curve = Curve("unknown", curve_fp, base_point, None) - - encoded = curve.to_der() - - decoded = Curve.from_der(encoded) - - self.assertEqual(curve, decoded) - - expected = "MCECAQEwDAYHKoZIzj0BAQIBFzAGBAEBBAEHBAMEDQMCAQk=" - - self.assertEqual(encoded, bytes(base64.b64decode(expected))) - - def test_named_curve_encode_for_custom_curve(self): - curve_fp = CurveFp(23, 1, 7) - base_point = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True) - - curve = Curve("unknown", curve_fp, base_point, None) - - with self.assertRaises(UnknownCurveError) as e: - curve.to_der("named_curve") - - self.assertIn("Can't encode curve", str(e.exception)) - - def test_try_decoding_binary_explicit(self): - sect113r1_explicit = ( - "MIGRAgEBMBwGByqGSM49AQIwEQIBcQYJKoZIzj0BAgMCAgEJMDkEDwAwiCUMpufH" - "/mSc6Fgg9wQPAOi+5NPiJgdEGIvg6ccjAxUAEOcjqxTWluZ2h1YVF1b+v4/LSakE" - "HwQAnXNhbzX0qxQH1zViwQ8ApSgwJ3lY7oTRMV7TGIYCDwEAAAAAAAAA2czsijnl" - "bwIBAg==" - ) - - with self.assertRaises(UnknownCurveError) as e: - Curve.from_der(base64.b64decode(sect113r1_explicit)) - - self.assertIn("Characteristic 2 curves unsupported", str(e.exception)) - - def test_decode_malformed_named_curve(self): - bad_der = der.encode_oid(*NIST256p.oid) + der.encode_integer(1) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Unexpected data after OID", str(e.exception)) - - def test_decode_malformed_explicit_garbage_after_ECParam(self): - bad_der = bytes( - base64.b64decode(self.base64_params) - ) + der.encode_integer(1) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Unexpected data after ECParameters", str(e.exception)) - - def test_decode_malformed_unknown_version_number(self): - bad_der = der.encode_sequence(der.encode_integer(2)) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Unknown parameter encoding format", str(e.exception)) - - def test_decode_malformed_unknown_field_type(self): - curve_p = NIST256p.curve.p() - bad_der = der.encode_sequence( - der.encode_integer(1), - der.encode_sequence( - der.encode_oid(1, 2, 3), der.encode_integer(curve_p) - ), - der.encode_sequence( - der.encode_octet_string( - number_to_string(NIST256p.curve.a() % curve_p, curve_p) - ), - der.encode_octet_string( - number_to_string(NIST256p.curve.b(), curve_p) - ), - ), - der.encode_octet_string( - NIST256p.generator.to_bytes("uncompressed") - ), - der.encode_integer(NIST256p.generator.order()), - ) - - with self.assertRaises(UnknownCurveError) as e: - Curve.from_der(bad_der) - - self.assertIn("Unknown field type: (1, 2, 3)", str(e.exception)) - - def test_decode_malformed_garbage_after_prime(self): - curve_p = NIST256p.curve.p() - bad_der = der.encode_sequence( - der.encode_integer(1), - der.encode_sequence( - der.encode_oid(*PRIME_FIELD_OID), - der.encode_integer(curve_p), - der.encode_integer(1), - ), - der.encode_sequence( - der.encode_octet_string( - number_to_string(NIST256p.curve.a() % curve_p, curve_p) - ), - der.encode_octet_string( - number_to_string(NIST256p.curve.b(), curve_p) - ), - ), - der.encode_octet_string( - NIST256p.generator.to_bytes("uncompressed") - ), - der.encode_integer(NIST256p.generator.order()), - ) - - with self.assertRaises(der.UnexpectedDER) as e: - Curve.from_der(bad_der) - - self.assertIn("Prime-p element", str(e.exception)) - - -class TestCurveSearching(unittest.TestCase): - def test_correct_name(self): - c = curve_by_name("NIST256p") - self.assertIs(c, NIST256p) - - def test_openssl_name(self): - c = curve_by_name("prime256v1") - self.assertIs(c, NIST256p) - - def test_unknown_curve(self): - with self.assertRaises(UnknownCurveError) as e: - curve_by_name("foo bar") - - self.assertIn( - "name 'foo bar' unknown, only curves supported: " - "['NIST192p', 'NIST224p'", - str(e.exception), - ) - - def test_with_None_as_parameter(self): - with self.assertRaises(UnknownCurveError) as e: - curve_by_name(None) - - self.assertIn( - "name None unknown, only curves supported: " - "['NIST192p', 'NIST224p'", - str(e.exception), - ) - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_named(curve): - ret = Curve.from_der(curve.to_der("named_curve")) - - assert curve == ret - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_explicit(curve): - if isinstance(curve.curve, CurveEdTw): - with pytest.raises(UnknownCurveError): - curve.to_der("explicit") - else: - ret = Curve.from_der(curve.to_der("explicit")) - - assert curve == ret - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_default(curve): - ret = Curve.from_der(curve.to_der()) - - assert curve == ret - - -@pytest.mark.parametrize("curve", curves, ids=[i.name for i in curves]) -def test_curve_params_encode_decode_explicit_compressed(curve): - if isinstance(curve.curve, CurveEdTw): - with pytest.raises(UnknownCurveError): - curve.to_der("explicit", "compressed") - else: - ret = Curve.from_der(curve.to_der("explicit", "compressed")) - - assert curve == ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py deleted file mode 100644 index 0ca5bd7fd..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_der.py +++ /dev/null @@ -1,476 +0,0 @@ -# compatibility with Python 2.6, for that we need unittest2 package, -# which is not available on 3.3 or 3.4 -import warnings -from binascii import hexlify - -try: - import unittest2 as unittest -except ImportError: - import unittest -from six import b -import hypothesis.strategies as st -from hypothesis import given -import pytest -from ._compat import str_idx_as_int -from .curves import NIST256p, NIST224p -from .der import ( - remove_integer, - UnexpectedDER, - read_length, - encode_bitstring, - remove_bitstring, - remove_object, - encode_oid, - remove_constructed, - remove_octet_string, - remove_sequence, -) - - -class TestRemoveInteger(unittest.TestCase): - # DER requires the integers to be 0-padded only if they would be - # interpreted as negative, check if those errors are detected - def test_non_minimal_encoding(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b("\x02\x02\x00\x01")) - - def test_negative_with_high_bit_set(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b("\x02\x01\x80")) - - def test_minimal_with_high_bit_set(self): - val, rem = remove_integer(b("\x02\x02\x00\x80")) - - self.assertEqual(val, 0x80) - self.assertEqual(rem, b"") - - def test_two_zero_bytes_with_high_bit_set(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b("\x02\x03\x00\x00\xff")) - - def test_zero_length_integer(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b("\x02\x00")) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_integer(b("")) - - def test_encoding_of_zero(self): - val, rem = remove_integer(b("\x02\x01\x00")) - - self.assertEqual(val, 0) - self.assertEqual(rem, b"") - - def test_encoding_of_127(self): - val, rem = remove_integer(b("\x02\x01\x7f")) - - self.assertEqual(val, 127) - self.assertEqual(rem, b"") - - def test_encoding_of_128(self): - val, rem = remove_integer(b("\x02\x02\x00\x80")) - - self.assertEqual(val, 128) - self.assertEqual(rem, b"") - - def test_wrong_tag(self): - with self.assertRaises(UnexpectedDER) as e: - remove_integer(b"\x01\x02\x00\x80") - - self.assertIn("wanted type 'integer'", str(e.exception)) - - def test_wrong_length(self): - with self.assertRaises(UnexpectedDER) as e: - remove_integer(b"\x02\x03\x00\x80") - - self.assertIn("Length longer", str(e.exception)) - - -class TestReadLength(unittest.TestCase): - # DER requires the lengths between 0 and 127 to be encoded using the short - # form and lengths above that encoded with minimal number of bytes - # necessary - def test_zero_length(self): - self.assertEqual((0, 1), read_length(b("\x00"))) - - def test_two_byte_zero_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b("\x81\x00")) - - def test_two_byte_small_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b("\x81\x7f")) - - def test_long_form_with_zero_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b("\x80")) - - def test_smallest_two_byte_length(self): - self.assertEqual((128, 2), read_length(b("\x81\x80"))) - - def test_zero_padded_length(self): - with self.assertRaises(UnexpectedDER): - read_length(b("\x82\x00\x80")) - - def test_two_three_byte_length(self): - self.assertEqual((256, 3), read_length(b"\x82\x01\x00")) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - read_length(b("")) - - def test_length_overflow(self): - with self.assertRaises(UnexpectedDER): - read_length(b("\x83\x01\x00")) - - -class TestEncodeBitstring(unittest.TestCase): - # DER requires BIT STRINGS to include a number of padding bits in the - # encoded byte string, that padding must be between 0 and 7 - - def test_old_call_convention(self): - """This is the old way to use the function.""" - warnings.simplefilter("always") - with pytest.warns(DeprecationWarning) as warns: - der = encode_bitstring(b"\x00\xff") - - self.assertEqual(len(warns), 1) - self.assertIn( - "unused= needs to be specified", warns[0].message.args[0] - ) - - self.assertEqual(der, b"\x03\x02\x00\xff") - - def test_new_call_convention(self): - """This is how it should be called now.""" - warnings.simplefilter("always") - with pytest.warns(None) as warns: - der = encode_bitstring(b"\xff", 0) - - # verify that new call convention doesn't raise Warnings - self.assertEqual(len(warns), 0) - - self.assertEqual(der, b"\x03\x02\x00\xff") - - def test_implicit_unused_bits(self): - """ - Writing bit string with already included the number of unused bits. - """ - warnings.simplefilter("always") - with pytest.warns(None) as warns: - der = encode_bitstring(b"\x00\xff", None) - - # verify that new call convention doesn't raise Warnings - self.assertEqual(len(warns), 0) - - self.assertEqual(der, b"\x03\x02\x00\xff") - - def test_explicit_unused_bits(self): - der = encode_bitstring(b"\xff\xf0", 4) - - self.assertEqual(der, b"\x03\x03\x04\xff\xf0") - - def test_empty_string(self): - self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00") - - def test_invalid_unused_count(self): - with self.assertRaises(ValueError): - encode_bitstring(b"\xff\x00", 8) - - def test_invalid_unused_with_empty_string(self): - with self.assertRaises(ValueError): - encode_bitstring(b"", 1) - - def test_non_zero_padding_bits(self): - with self.assertRaises(ValueError): - encode_bitstring(b"\xff", 2) - - -class TestRemoveBitstring(unittest.TestCase): - def test_old_call_convention(self): - """This is the old way to call the function.""" - warnings.simplefilter("always") - with pytest.warns(DeprecationWarning) as warns: - bits, rest = remove_bitstring(b"\x03\x02\x00\xff") - - self.assertEqual(len(warns), 1) - self.assertIn( - "expect_unused= needs to be specified", warns[0].message.args[0] - ) - - self.assertEqual(bits, b"\x00\xff") - self.assertEqual(rest, b"") - - def test_new_call_convention(self): - warnings.simplefilter("always") - with pytest.warns(None) as warns: - bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0) - - self.assertEqual(len(warns), 0) - - self.assertEqual(bits, b"\xff") - self.assertEqual(rest, b"") - - def test_implicit_unexpected_unused(self): - warnings.simplefilter("always") - with pytest.warns(None) as warns: - bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None) - - self.assertEqual(len(warns), 0) - - self.assertEqual(bits, (b"\xff", 0)) - self.assertEqual(rest, b"") - - def test_with_padding(self): - ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None) - - self.assertEqual(ret, (b"\xf0", 4)) - self.assertEqual(rest, b"") - - def test_not_a_bitstring(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x02\x02\x00\xff", None) - - def test_empty_encoding(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x00", None) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"", None) - - def test_no_length(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03", None) - - def test_unexpected_number_of_unused_bits(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x02\x00\xff", 1) - - def test_invalid_encoding_of_unused_bits(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x03\x08\xff\x00", None) - - def test_invalid_encoding_of_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x01\x01", None) - - def test_invalid_padding_bits(self): - with self.assertRaises(UnexpectedDER): - remove_bitstring(b"\x03\x02\x01\xff", None) - - -class TestStrIdxAsInt(unittest.TestCase): - def test_str(self): - self.assertEqual(115, str_idx_as_int("str", 0)) - - def test_bytes(self): - self.assertEqual(115, str_idx_as_int(b"str", 0)) - - def test_bytearray(self): - self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0)) - - -class TestEncodeOid(unittest.TestCase): - def test_pub_key_oid(self): - oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) - self.assertEqual(hexlify(oid_ecPublicKey), b("06072a8648ce3d0201")) - - def test_nist224p_oid(self): - self.assertEqual(hexlify(NIST224p.encoded_oid), b("06052b81040021")) - - def test_nist256p_oid(self): - self.assertEqual( - hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107" - ) - - def test_large_second_subid(self): - # from X.690, section 8.19.5 - oid = encode_oid(2, 999, 3) - self.assertEqual(oid, b"\x06\x03\x88\x37\x03") - - def test_with_two_subids(self): - oid = encode_oid(2, 999) - self.assertEqual(oid, b"\x06\x02\x88\x37") - - def test_zero_zero(self): - oid = encode_oid(0, 0) - self.assertEqual(oid, b"\x06\x01\x00") - - def test_with_wrong_types(self): - with self.assertRaises((TypeError, AssertionError)): - encode_oid(0, None) - - def test_with_small_first_large_second(self): - with self.assertRaises(AssertionError): - encode_oid(1, 40) - - def test_small_first_max_second(self): - oid = encode_oid(1, 39) - self.assertEqual(oid, b"\x06\x01\x4f") - - def test_with_invalid_first(self): - with self.assertRaises(AssertionError): - encode_oid(3, 39) - - -class TestRemoveObject(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.oid_ecPublicKey = encode_oid(1, 2, 840, 10045, 2, 1) - - def test_pub_key_oid(self): - oid, rest = remove_object(self.oid_ecPublicKey) - self.assertEqual(rest, b"") - self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) - - def test_with_extra_bytes(self): - oid, rest = remove_object(self.oid_ecPublicKey + b"more") - self.assertEqual(rest, b"more") - self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) - - def test_with_large_second_subid(self): - # from X.690, section 8.19.5 - oid, rest = remove_object(b"\x06\x03\x88\x37\x03") - self.assertEqual(rest, b"") - self.assertEqual(oid, (2, 999, 3)) - - def test_with_padded_first_subid(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x02\x80\x00") - - def test_with_padded_second_subid(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x04\x88\x37\x80\x01") - - def test_with_missing_last_byte_of_multi_byte(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x03\x88\x37\x83") - - def test_with_two_subids(self): - oid, rest = remove_object(b"\x06\x02\x88\x37") - self.assertEqual(rest, b"") - self.assertEqual(oid, (2, 999)) - - def test_zero_zero(self): - oid, rest = remove_object(b"\x06\x01\x00") - self.assertEqual(rest, b"") - self.assertEqual(oid, (0, 0)) - - def test_empty_string(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"") - - def test_missing_length(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06") - - def test_empty_oid(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x00") - - def test_empty_oid_overflow(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x01") - - def test_with_wrong_type(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x04\x02\x88\x37") - - def test_with_too_long_length(self): - with self.assertRaises(UnexpectedDER): - remove_object(b"\x06\x03\x88\x37") - - -class TestRemoveConstructed(unittest.TestCase): - def test_simple(self): - data = b"\xa1\x02\xff\xaa" - - tag, body, rest = remove_constructed(data) - - self.assertEqual(tag, 0x01) - self.assertEqual(body, b"\xff\xaa") - self.assertEqual(rest, b"") - - def test_with_malformed_tag(self): - data = b"\x01\x02\xff\xaa" - - with self.assertRaises(UnexpectedDER) as e: - remove_constructed(data) - - self.assertIn("constructed tag", str(e.exception)) - - -class TestRemoveOctetString(unittest.TestCase): - def test_simple(self): - data = b"\x04\x03\xaa\xbb\xcc" - body, rest = remove_octet_string(data) - self.assertEqual(body, b"\xaa\xbb\xcc") - self.assertEqual(rest, b"") - - def test_with_malformed_tag(self): - data = b"\x03\x03\xaa\xbb\xcc" - with self.assertRaises(UnexpectedDER) as e: - remove_octet_string(data) - - self.assertIn("octetstring", str(e.exception)) - - -class TestRemoveSequence(unittest.TestCase): - def test_simple(self): - data = b"\x30\x02\xff\xaa" - body, rest = remove_sequence(data) - self.assertEqual(body, b"\xff\xaa") - self.assertEqual(rest, b"") - - def test_with_empty_string(self): - with self.assertRaises(UnexpectedDER) as e: - remove_sequence(b"") - - self.assertIn("Empty string", str(e.exception)) - - def test_with_wrong_tag(self): - data = b"\x20\x02\xff\xaa" - - with self.assertRaises(UnexpectedDER) as e: - remove_sequence(data) - - self.assertIn("wanted type 'sequence'", str(e.exception)) - - def test_with_wrong_length(self): - data = b"\x30\x03\xff\xaa" - - with self.assertRaises(UnexpectedDER) as e: - remove_sequence(data) - - self.assertIn("Length longer", str(e.exception)) - - -@st.composite -def st_oid(draw, max_value=2**512, max_size=50): - """ - Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples - - :param max_value: maximum value of any single sub-identifier - :param max_size: maximum length of the generated OID - """ - first = draw(st.integers(min_value=0, max_value=2)) - if first < 2: - second = draw(st.integers(min_value=0, max_value=39)) - else: - second = draw(st.integers(min_value=0, max_value=max_value)) - rest = draw( - st.lists( - st.integers(min_value=0, max_value=max_value), max_size=max_size - ) - ) - return (first, second) + tuple(rest) - - -@given(st_oid()) -def test_oids(ids): - encoded_oid = encode_oid(*ids) - decoded_oid, rest = remove_object(encoded_oid) - assert rest == b"" - assert decoded_oid == ids diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py deleted file mode 100644 index 872d4d14c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdh.py +++ /dev/null @@ -1,441 +0,0 @@ -import os -import shutil -import subprocess -import pytest -from binascii import unhexlify - -try: - import unittest2 as unittest -except ImportError: - import unittest - -from .curves import ( - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - BRAINPOOLP160r1, -) -from .curves import curves -from .ecdh import ( - ECDH, - InvalidCurveError, - InvalidSharedSecretError, - NoKeyError, - NoCurveError, -) -from .keys import SigningKey, VerifyingKey -from .ellipticcurve import CurveEdTw - - -@pytest.mark.parametrize( - "vcurve", - curves, - ids=[curve.name for curve in curves], -) -def test_ecdh_each(vcurve): - if isinstance(vcurve.curve, CurveEdTw): - pytest.skip("ECDH is not supported for Edwards curves") - ecdh1 = ECDH(curve=vcurve) - ecdh2 = ECDH(curve=vcurve) - - ecdh2.generate_private_key() - ecdh1.load_received_public_key(ecdh2.get_public_key()) - ecdh2.load_received_public_key(ecdh1.generate_private_key()) - - secret1 = ecdh1.generate_sharedsecret_bytes() - secret2 = ecdh2.generate_sharedsecret_bytes() - assert secret1 == secret2 - - -def test_ecdh_both_keys_present(): - key1 = SigningKey.generate(BRAINPOOLP160r1) - key2 = SigningKey.generate(BRAINPOOLP160r1) - - ecdh1 = ECDH(BRAINPOOLP160r1, key1, key2.verifying_key) - ecdh2 = ECDH(private_key=key2, public_key=key1.verifying_key) - - secret1 = ecdh1.generate_sharedsecret_bytes() - secret2 = ecdh2.generate_sharedsecret_bytes() - - assert secret1 == secret2 - - -def test_ecdh_no_public_key(): - ecdh1 = ECDH(curve=NIST192p) - - with pytest.raises(NoKeyError): - ecdh1.generate_sharedsecret_bytes() - - ecdh1.generate_private_key() - - with pytest.raises(NoKeyError): - ecdh1.generate_sharedsecret_bytes() - - -class TestECDH(unittest.TestCase): - def test_load_key_from_wrong_curve(self): - ecdh1 = ECDH() - ecdh1.set_curve(NIST192p) - - key1 = SigningKey.generate(BRAINPOOLP160r1) - - with self.assertRaises(InvalidCurveError) as e: - ecdh1.load_private_key(key1) - - self.assertIn("Curve mismatch", str(e.exception)) - - def test_generate_without_curve(self): - ecdh1 = ECDH() - - with self.assertRaises(NoCurveError) as e: - ecdh1.generate_private_key() - - self.assertIn("Curve must be set", str(e.exception)) - - def test_load_bytes_without_curve_set(self): - ecdh1 = ECDH() - - with self.assertRaises(NoCurveError) as e: - ecdh1.load_private_key_bytes(b"\x01" * 32) - - self.assertIn("Curve must be set", str(e.exception)) - - def test_set_curve_from_received_public_key(self): - ecdh1 = ECDH() - - key1 = SigningKey.generate(BRAINPOOLP160r1) - - ecdh1.load_received_public_key(key1.verifying_key) - - self.assertEqual(ecdh1.curve, BRAINPOOLP160r1) - - -def test_ecdh_wrong_public_key_curve(): - ecdh1 = ECDH(curve=NIST192p) - ecdh1.generate_private_key() - ecdh2 = ECDH(curve=NIST256p) - ecdh2.generate_private_key() - - with pytest.raises(InvalidCurveError): - ecdh1.load_received_public_key(ecdh2.get_public_key()) - - with pytest.raises(InvalidCurveError): - ecdh2.load_received_public_key(ecdh1.get_public_key()) - - ecdh1.public_key = ecdh2.get_public_key() - ecdh2.public_key = ecdh1.get_public_key() - - with pytest.raises(InvalidCurveError): - ecdh1.generate_sharedsecret_bytes() - - with pytest.raises(InvalidCurveError): - ecdh2.generate_sharedsecret_bytes() - - -def test_ecdh_invalid_shared_secret_curve(): - ecdh1 = ECDH(curve=NIST256p) - ecdh1.generate_private_key() - - ecdh1.load_received_public_key( - SigningKey.generate(NIST256p).get_verifying_key() - ) - - ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order - - with pytest.raises(InvalidSharedSecretError): - ecdh1.generate_sharedsecret_bytes() - - -# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp192r1.txt -# https://github.com/scogliani/ecc-test-vectors/blob/master/ecdh_kat/secp256r1.txt -# https://github.com/coruus/nist-testvectors/blob/master/csrc.nist.gov/groups/STM/cavp/documents/components/ecccdhtestvectors/KAS_ECC_CDH_PrimitiveTest.txt -@pytest.mark.parametrize( - "curve,privatekey,pubkey,secret", - [ - pytest.param( - NIST192p, - "f17d3fea367b74d340851ca4270dcb24c271f445bed9d527", - "42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0" - "dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523", - "803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0", - id="NIST192p-1", - ), - pytest.param( - NIST192p, - "56e853349d96fe4c442448dacb7cf92bb7a95dcf574a9bd5", - "deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7" - "7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125", - "c208847568b98835d7312cef1f97f7aa298283152313c29d", - id="NIST192p-2", - ), - pytest.param( - NIST192p, - "c6ef61fe12e80bf56f2d3f7d0bb757394519906d55500949", - "4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f" - "0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279", - "87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd", - id="NIST192p-3", - ), - pytest.param( - NIST192p, - "e6747b9c23ba7044f38ff7e62c35e4038920f5a0163d3cda", - "8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88" - "04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5", - "eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c", - id="NIST192p-4", - ), - pytest.param( - NIST192p, - "beabedd0154a1afcfc85d52181c10f5eb47adc51f655047d", - "0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594" - "542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73", - "716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4", - id="NIST192p-5", - ), - pytest.param( - NIST192p, - "cf70354226667321d6e2baf40999e2fd74c7a0f793fa8699", - "fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516" - "368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7", - "f67053b934459985a315cb017bf0302891798d45d0e19508", - id="NIST192p-6", - ), - pytest.param( - NIST224p, - "8346a60fc6f293ca5a0d2af68ba71d1dd389e5e40837942df3e43cbd", - "af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280" - "882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7", - "7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8", - id="NIST224p", - ), - pytest.param( - NIST256p, - "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534", - "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" - "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", - "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", - id="NIST256p-1", - ), - pytest.param( - NIST256p, - "38f65d6dce47676044d58ce5139582d568f64bb16098d179dbab07741dd5caf5", - "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae" - "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", - "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", - id="NIST256p-2", - ), - pytest.param( - NIST256p, - "1accfaf1b97712b85a6f54b148985a1bdc4c9bec0bd258cad4b3d603f49f32c8", - "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3" - "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536", - "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec", - id="NIST256p-3", - ), - pytest.param( - NIST256p, - "207c43a79bfee03db6f4b944f53d2fb76cc49ef1c9c4d34d51b6c65c4db6932d", - "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed" - "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", - "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", - id="NIST256p-4", - ), - pytest.param( - NIST256p, - "59137e38152350b195c9718d39673d519838055ad908dd4757152fd8255c09bf", - "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922" - "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328", - "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62", - id="NIST256p-5", - ), - pytest.param( - NIST256p, - "f5f8e0174610a661277979b58ce5c90fee6c9b3bb346a90a7196255e40b132ef", - "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792" - "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f", - "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3", - id="NIST256p-6", - ), - pytest.param( - NIST384p, - "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b1" - "5618b6818a661774ad463b205da88cf699ab4d43c9cf98a1", - "a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e76459" - "2efda27fe7513272734466b400091adbf2d68c58e0c50066" - "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b66" - "1efedf243451915ed0905a32b060992b468c64766fc8437a", - "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4" - "0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1", - id="NIST384p", - ), - pytest.param( - NIST521p, - "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4ea" - "c6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47", - "00685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a949034085433" - "4b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d" - "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83" - "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676", - "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366" - "72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831", - id="NIST521p", - ), - ], -) -def test_ecdh_NIST(curve, privatekey, pubkey, secret): - ecdh = ECDH(curve=curve) - ecdh.load_private_key_bytes(unhexlify(privatekey)) - ecdh.load_received_public_key_bytes(unhexlify(pubkey)) - - sharedsecret = ecdh.generate_sharedsecret_bytes() - - assert sharedsecret == unhexlify(secret) - - -pem_local_private_key = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" -) -der_local_private_key = ( - "305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864" - "8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06" - "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c" -) -pem_remote_public_key = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" - "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n" -) -der_remote_public_key = ( - "3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563" - "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c" -) -gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea" - - -def test_ecdh_pem(): - ecdh = ECDH() - ecdh.load_private_key_pem(pem_local_private_key) - ecdh.load_received_public_key_pem(pem_remote_public_key) - - sharedsecret = ecdh.generate_sharedsecret_bytes() - - assert sharedsecret == unhexlify(gshared_secret) - - -def test_ecdh_der(): - ecdh = ECDH() - ecdh.load_private_key_der(unhexlify(der_local_private_key)) - ecdh.load_received_public_key_der(unhexlify(der_remote_public_key)) - - sharedsecret = ecdh.generate_sharedsecret_bytes() - - assert sharedsecret == unhexlify(gshared_secret) - - -# Exception classes used by run_openssl. -class RunOpenSslError(Exception): - pass - - -def run_openssl(cmd): - OPENSSL = "openssl" - p = subprocess.Popen( - [OPENSSL] + cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - stdout, ignored = p.communicate() - if p.returncode != 0: - raise RunOpenSslError( - "cmd '%s %s' failed: rc=%s, stdout/err was %s" - % (OPENSSL, cmd, p.returncode, stdout) - ) - return stdout.decode() - - -OPENSSL_SUPPORTED_CURVES = set( - c.split(":")[0].strip() - for c in run_openssl("ecparam -list_curves").split("\n") -) - - -@pytest.mark.parametrize( - "vcurve", - curves, - ids=[curve.name for curve in curves], -) -def test_ecdh_with_openssl(vcurve): - if isinstance(vcurve.curve, CurveEdTw): - pytest.skip("Edwards curves are not supported for ECDH") - - assert vcurve.openssl_name - - if vcurve.openssl_name not in OPENSSL_SUPPORTED_CURVES: - pytest.skip("system openssl does not support " + vcurve.openssl_name) - - try: - hlp = run_openssl("pkeyutl -help") - if hlp.find("-derive") == 0: # pragma: no cover - pytest.skip("system openssl does not support `pkeyutl -derive`") - except RunOpenSslError: # pragma: no cover - pytest.skip("system openssl could not be executed") - - if os.path.isdir("t"): # pragma: no branch - shutil.rmtree("t") - os.mkdir("t") - run_openssl( - "ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name - ) - run_openssl( - "ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name - ) - run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem") - - ecdh1 = ECDH(curve=vcurve) - ecdh2 = ECDH(curve=vcurve) - with open("t/privkey1.pem") as e: - key = e.read() - ecdh1.load_private_key_pem(key) - with open("t/privkey2.pem") as e: - key = e.read() - ecdh2.load_private_key_pem(key) - - with open("t/pubkey1.pem") as e: - key = e.read() - vk1 = VerifyingKey.from_pem(key) - assert vk1.to_string() == ecdh1.get_public_key().to_string() - vk2 = ecdh2.get_public_key() - with open("t/pubkey2.pem", "wb") as e: - e.write(vk2.to_pem()) - - ecdh1.load_received_public_key(vk2) - ecdh2.load_received_public_key(vk1) - secret1 = ecdh1.generate_sharedsecret_bytes() - secret2 = ecdh2.generate_sharedsecret_bytes() - - assert secret1 == secret2 - - run_openssl( - "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" - ) - run_openssl( - "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" - ) - - with open("t/secret1", "rb") as e: - ssl_secret1 = e.read() - with open("t/secret1", "rb") as e: - ssl_secret2 = e.read() - - assert len(ssl_secret1) == vk1.curve.verifying_key_length // 2 - assert len(secret1) == vk1.curve.verifying_key_length // 2 - - assert ssl_secret1 == ssl_secret2 - assert secret1 == ssl_secret1 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py deleted file mode 100644 index dbc4a6eb5..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ecdsa.py +++ /dev/null @@ -1,661 +0,0 @@ -from __future__ import print_function -import sys -import hypothesis.strategies as st -from hypothesis import given, settings, note, example - -try: - import unittest2 as unittest -except ImportError: - import unittest -import pytest -from .ecdsa import ( - Private_key, - Public_key, - Signature, - generator_192, - digest_integer, - ellipticcurve, - point_is_valid, - generator_224, - generator_256, - generator_384, - generator_521, - generator_secp256k1, - curve_192, - InvalidPointError, - curve_112r2, - generator_112r2, - int_to_string, -) - - -HYP_SETTINGS = {} -# old hypothesis doesn't have the "deadline" setting -if sys.version_info > (2, 7): # pragma: no branch - # SEC521p is slow, allow long execution for it - HYP_SETTINGS["deadline"] = 5000 - - -class TestP192FromX9_62(unittest.TestCase): - """Check test vectors from X9.62""" - - @classmethod - def setUpClass(cls): - cls.d = 651056770906015076056810763456358567190100156695615665659 - cls.Q = cls.d * generator_192 - cls.k = 6140507067065001063065065565667405560006161556565665656654 - cls.R = cls.k * generator_192 - - cls.msg = 968236873715988614170569073515315707566766479517 - cls.pubk = Public_key(generator_192, generator_192 * cls.d) - cls.privk = Private_key(cls.pubk, cls.d) - cls.sig = cls.privk.sign(cls.msg, cls.k) - - def test_point_multiplication(self): - assert self.Q.x() == 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 - - def test_point_multiplication_2(self): - assert self.R.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - assert self.R.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - - def test_mult_and_addition(self): - u1 = 2563697409189434185194736134579731015366492496392189760599 - u2 = 6266643813348617967186477710235785849136406323338782220568 - temp = u1 * generator_192 + u2 * self.Q - assert temp.x() == 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - assert temp.y() == 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - - def test_signature(self): - r, s = self.sig.r, self.sig.s - assert r == 3342403536405981729393488334694600415596881826869351677613 - assert s == 5735822328888155254683894997897571951568553642892029982342 - - def test_verification(self): - assert self.pubk.verifies(self.msg, self.sig) - - def test_rejection(self): - assert not self.pubk.verifies(self.msg - 1, self.sig) - - -class TestPublicKey(unittest.TestCase): - def test_equality_public_keys(self): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - pub_key1 = Public_key(gen, point) - pub_key2 = Public_key(gen, point) - self.assertEqual(pub_key1, pub_key2) - - def test_inequality_public_key(self): - gen = generator_192 - x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point1 = ellipticcurve.Point(gen.curve(), x1, y1) - - x2 = 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15 - y2 = 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF - point2 = ellipticcurve.Point(gen.curve(), x2, y2) - - pub_key1 = Public_key(gen, point1) - pub_key2 = Public_key(gen, point2) - self.assertNotEqual(pub_key1, pub_key2) - - def test_inequality_different_curves(self): - gen = generator_192 - x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point1 = ellipticcurve.Point(gen.curve(), x1, y1) - - x2 = 0x722BA0FB6B8FC8898A4C6AB49E66 - y2 = 0x2B7344BB57A7ABC8CA0F1A398C7D - point2 = ellipticcurve.Point(generator_112r2.curve(), x2, y2) - - pub_key1 = Public_key(gen, point1) - pub_key2 = Public_key(generator_112r2, point2) - self.assertNotEqual(pub_key1, pub_key2) - - def test_inequality_public_key_not_implemented(self): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - pub_key = Public_key(gen, point) - self.assertNotEqual(pub_key, None) - - def test_public_key_with_generator_without_order(self): - gen = ellipticcurve.PointJacobi( - generator_192.curve(), generator_192.x(), generator_192.y(), 1 - ) - - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - - with self.assertRaises(InvalidPointError) as e: - Public_key(gen, point) - - self.assertIn("Generator point must have order", str(e.exception)) - - def test_public_point_on_curve_not_scalar_multiple_of_base_point(self): - x = 2 - y = 0xBE6AA4938EF7CFE6FE29595B6B00 - # we need a curve with cofactor != 1 - point = ellipticcurve.PointJacobi(curve_112r2, x, y, 1) - - self.assertTrue(curve_112r2.contains_point(x, y)) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_112r2, point) - - self.assertIn("Generator point order", str(e.exception)) - - def test_point_is_valid_with_not_scalar_multiple_of_base_point(self): - x = 2 - y = 0xBE6AA4938EF7CFE6FE29595B6B00 - - self.assertFalse(point_is_valid(generator_112r2, x, y)) - - # the tests to verify the extensiveness of tests in ecdsa.ecdsa - # if PointJacobi gets modified to calculate the x and y mod p the tests - # below will need to use a fake/mock object - def test_invalid_point_x_negative(self): - pt = ellipticcurve.PointJacobi(curve_192, -1, 0, 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - def test_invalid_point_x_equal_p(self): - pt = ellipticcurve.PointJacobi(curve_192, curve_192.p(), 0, 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - def test_invalid_point_y_negative(self): - pt = ellipticcurve.PointJacobi(curve_192, 0, -1, 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - def test_invalid_point_y_equal_p(self): - pt = ellipticcurve.PointJacobi(curve_192, 0, curve_192.p(), 1) - - with self.assertRaises(InvalidPointError) as e: - Public_key(generator_192, pt) - - self.assertIn("The public point has x or y", str(e.exception)) - - -class TestPublicKeyVerifies(unittest.TestCase): - # test all the different ways that a signature can be publicly invalid - @classmethod - def setUpClass(cls): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - - cls.pub_key = Public_key(gen, point) - - def test_sig_with_r_zero(self): - sig = Signature(0, 1) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - def test_sig_with_r_order(self): - sig = Signature(generator_192.order(), 1) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - def test_sig_with_s_zero(self): - sig = Signature(1, 0) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - def test_sig_with_s_order(self): - sig = Signature(1, generator_192.order()) - - self.assertFalse(self.pub_key.verifies(1, sig)) - - -class TestPrivateKey(unittest.TestCase): - @classmethod - def setUpClass(cls): - gen = generator_192 - x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 - y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F - point = ellipticcurve.Point(gen.curve(), x, y) - cls.pub_key = Public_key(gen, point) - - def test_equality_private_keys(self): - pr_key1 = Private_key(self.pub_key, 100) - pr_key2 = Private_key(self.pub_key, 100) - self.assertEqual(pr_key1, pr_key2) - - def test_inequality_private_keys(self): - pr_key1 = Private_key(self.pub_key, 100) - pr_key2 = Private_key(self.pub_key, 200) - self.assertNotEqual(pr_key1, pr_key2) - - def test_inequality_private_keys_not_implemented(self): - pr_key = Private_key(self.pub_key, 100) - self.assertNotEqual(pr_key, None) - - -# Testing point validity, as per ECDSAVS.pdf B.2.2: -P192_POINTS = [ - ( - generator_192, - 0xCD6D0F029A023E9AACA429615B8F577ABEE685D8257CC83A, - 0x00019C410987680E9FB6C0B6ECC01D9A2647C8BAE27721BACDFC, - False, - ), - ( - generator_192, - 0x00017F2FCE203639E9EAF9FB50B81FC32776B30E3B02AF16C73B, - 0x95DA95C5E72DD48E229D4748D4EEE658A9A54111B23B2ADB, - False, - ), - ( - generator_192, - 0x4F77F8BC7FCCBADD5760F4938746D5F253EE2168C1CF2792, - 0x000147156FF824D131629739817EDB197717C41AAB5C2A70F0F6, - False, - ), - ( - generator_192, - 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6, - 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F, - True, - ), - ( - generator_192, - 0xCDF56C1AA3D8AFC53C521ADF3FFB96734A6A630A4A5B5A70, - 0x97C1C44A5FB229007B5EC5D25F7413D170068FFD023CAA4E, - True, - ), - ( - generator_192, - 0x89009C0DC361C81E99280C8E91DF578DF88CDF4B0CDEDCED, - 0x27BE44A529B7513E727251F128B34262A0FD4D8EC82377B9, - True, - ), - ( - generator_192, - 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15, - 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF, - True, - ), - ( - generator_192, - 0x6DCCBDE75C0948C98DAB32EA0BC59FE125CF0FB1A3798EDA, - 0x0001171A3E0FA60CF3096F4E116B556198DE430E1FBD330C8835, - False, - ), - ( - generator_192, - 0xD266B39E1F491FC4ACBBBC7D098430931CFA66D55015AF12, - 0x193782EB909E391A3148B7764E6B234AA94E48D30A16DBB2, - False, - ), - ( - generator_192, - 0x9D6DDBCD439BAA0C6B80A654091680E462A7D1D3F1FFEB43, - 0x6AD8EFC4D133CCF167C44EB4691C80ABFFB9F82B932B8CAA, - False, - ), - ( - generator_192, - 0x146479D944E6BDA87E5B35818AA666A4C998A71F4E95EDBC, - 0xA86D6FE62BC8FBD88139693F842635F687F132255858E7F6, - False, - ), - ( - generator_192, - 0xE594D4A598046F3598243F50FD2C7BD7D380EDB055802253, - 0x509014C0C4D6B536E3CA750EC09066AF39B4C8616A53A923, - False, - ), -] - - -@pytest.mark.parametrize("generator,x,y,expected", P192_POINTS) -def test_point_validity(generator, x, y, expected): - """ - `generator` defines the curve; is `(x, y)` a point on - this curve? `expected` is True if the right answer is Yes. - """ - assert point_is_valid(generator, x, y) == expected - - -# Trying signature-verification tests from ECDSAVS.pdf B.2.4: -CURVE_192_KATS = [ - ( - generator_192, - int( - "0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee" - "425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30" - "d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79" - "8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1" - "58", - 16, - ), - 0xD9DBFB332AA8E5FF091E8CE535857C37C73F6250FFB2E7AC, - 0x282102E364FEDED3AD15DDF968F88D8321AA268DD483EBC4, - 0x64DCA58A20787C488D11D6DD96313F1B766F2D8EFE122916, - 0x1ECBA28141E84AB4ECAD92F56720E2CC83EB3D22DEC72479, - True, - ), - ( - generator_192, - int( - "0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1" - "2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a" - "91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3" - "26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63" - "f4", - 16, - ), - 0x3E53EF8D3112AF3285C0E74842090712CD324832D4277AE7, - 0xCC75F8952D30AEC2CBB719FC6AA9934590B5D0FF5A83ADB7, - 0x8285261607283BA18F335026130BAB31840DCFD9C3E555AF, - 0x356D89E1B04541AFC9704A45E9C535CE4A50929E33D7E06C, - True, - ), - ( - generator_192, - int( - "0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911" - "b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd" - "d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30" - "3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42" - "dd", - 16, - ), - 0x16335DBE95F8E8254A4E04575D736BEFB258B8657F773CB7, - 0x421B13379C59BC9DCE38A1099CA79BBD06D647C7F6242336, - 0x4141BD5D64EA36C5B0BD21EF28C02DA216ED9D04522B1E91, - 0x159A6AA852BCC579E821B7BB0994C0861FB08280C38DAA09, - False, - ), - ( - generator_192, - int( - "0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309" - "7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8" - "bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447" - "bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd" - "8a", - 16, - ), - 0xFD14CDF1607F5EFB7B1793037B15BDF4BAA6F7C16341AB0B, - 0x83FA0795CC6C4795B9016DAC928FD6BAC32F3229A96312C4, - 0x8DFDB832951E0167C5D762A473C0416C5C15BC1195667DC1, - 0x1720288A2DC13FA1EC78F763F8FE2FF7354A7E6FDDE44520, - False, - ), - ( - generator_192, - int( - "0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919" - "2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196" - "683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc" - "eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072" - "fb", - 16, - ), - 0x674F941DC1A1F8B763C9334D726172D527B90CA324DB8828, - 0x65ADFA32E8B236CB33A3E84CF59BFB9417AE7E8EDE57A7FF, - 0x9508B9FDD7DAF0D8126F9E2BC5A35E4C6D800B5B804D7796, - 0x36F2BF6B21B987C77B53BB801B3435A577E3D493744BFAB0, - False, - ), - ( - generator_192, - int( - "0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c" - "e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa" - "e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc" - "55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca" - "6d", - 16, - ), - 0x10ECCA1AAD7220B56A62008B35170BFD5E35885C4014A19F, - 0x04EB61984C6C12ADE3BC47F3C629ECE7AA0A033B9948D686, - 0x82BFA4E82C0DFE9274169B86694E76CE993FD83B5C60F325, - 0xA97685676C59A65DBDE002FE9D613431FB183E8006D05633, - False, - ), - ( - generator_192, - int( - "0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f" - "698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98" - "f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2" - "78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76" - "e1", - 16, - ), - 0x6636653CB5B894CA65C448277B29DA3AD101C4C2300F7C04, - 0xFDF1CBB3FC3FD6A4F890B59E554544175FA77DBDBEB656C1, - 0xEAC2DDECDDFB79931A9C3D49C08DE0645C783A24CB365E1C, - 0x3549FEE3CFA7E5F93BC47D92D8BA100E881A2A93C22F8D50, - False, - ), - ( - generator_192, - int( - "0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6" - "c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7" - "a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b" - "9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b" - "a2", - 16, - ), - 0xA82BD718D01D354001148CD5F69B9EBF38FF6F21898F8AAA, - 0xE67CEEDE07FC2EBFAFD62462A51E4B6C6B3D5B537B7CAF3E, - 0x4D292486C620C3DE20856E57D3BB72FCDE4A73AD26376955, - 0xA85289591A6081D5728825520E62FF1C64F94235C04C7F95, - False, - ), - ( - generator_192, - int( - "0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a" - "961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91" - "0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53" - "808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6" - "58", - 16, - ), - 0x7D3B016B57758B160C4FCA73D48DF07AE3B6B30225126C2F, - 0x4AF3790D9775742BDE46F8DA876711BE1B65244B2B39E7EC, - 0x95F778F5F656511A5AB49A5D69DDD0929563C29CBC3A9E62, - 0x75C87FC358C251B4C83D2DD979FAAD496B539F9F2EE7A289, - False, - ), - ( - generator_192, - int( - "0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102" - "88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9" - "0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e" - "a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9" - "7a", - 16, - ), - 0x9362F28C4EF96453D8A2F849F21E881CD7566887DA8BEB4A, - 0xE64D26D8D74C48A024AE85D982EE74CD16046F4EE5333905, - 0xF3923476A296C88287E8DE914B0B324AD5A963319A4FE73B, - 0xF0BAEED7624ED00D15244D8BA2AEDE085517DBDEC8AC65F5, - True, - ), - ( - generator_192, - int( - "0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645" - "0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90" - "64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c" - "e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045" - "6d", - 16, - ), - 0xCC6FC032A846AAAC25533EB033522824F94E670FA997ECEF, - 0xE25463EF77A029ECCDA8B294FD63DD694E38D223D30862F1, - 0x066B1D07F3A40E679B620EDA7F550842A35C18B80C5EBE06, - 0xA0B0FB201E8F2DF65E2C4508EF303BDC90D934016F16B2DC, - False, - ), - ( - generator_192, - int( - "0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae" - "5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e" - "ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4" - "40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839" - "d7", - 16, - ), - 0x955C908FE900A996F7E2089BEE2F6376830F76A19135E753, - 0xBA0C42A91D3847DE4A592A46DC3FDAF45A7CC709B90DE520, - 0x1F58AD77FC04C782815A1405B0925E72095D906CBF52A668, - 0xF2E93758B3AF75EDF784F05A6761C9B9A6043C66B845B599, - False, - ), - ( - generator_192, - int( - "0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866" - "70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412" - "69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52" - "e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce" - "f3", - 16, - ), - 0x31F7FA05576D78A949B24812D4383107A9A45BB5FCCDD835, - 0x8DC0EB65994A90F02B5E19BD18B32D61150746C09107E76B, - 0xBE26D59E4E883DDE7C286614A767B31E49AD88789D3A78FF, - 0x8762CA831C1CE42DF77893C9B03119428E7A9B819B619068, - False, - ), - ( - generator_192, - int( - "0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f" - "387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357" - "2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670" - "716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1", - 16, - ), - 0x66AA8EDBBDB5CF8E28CEB51B5BDA891CAE2DF84819FE25C0, - 0x0C6BC2F69030A7CE58D4A00E3B3349844784A13B8936F8DA, - 0xA4661E69B1734F4A71B788410A464B71E7FFE42334484F23, - 0x738421CF5E049159D69C57A915143E226CAC8355E149AFE9, - False, - ), - ( - generator_192, - int( - "0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af" - "a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461" - "184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d" - "b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb", - 16, - ), - 0xBCFACF45139B6F5F690A4C35A5FFFA498794136A2353FC77, - 0x6F4A6C906316A6AFC6D98FE1F0399D056F128FE0270B0F22, - 0x9DB679A3DAFE48F7CCAD122933ACFE9DA0970B71C94C21C1, - 0x984C2DB99827576C0A41A5DA41E07D8CC768BC82F18C9DA9, - False, - ), -] - - -@pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", CURVE_192_KATS) -def test_signature_validity(gen, msg, qx, qy, r, s, expected): - """ - `msg` = message, `qx` and `qy` represent the base point on - elliptic curve of `gen`, `r` and `s` are the signature, and - `expected` is True iff the signature is expected to be valid.""" - pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy)) - assert expected == pubk.verifies(digest_integer(msg), Signature(r, s)) - - -@pytest.mark.parametrize( - "gen,msg,qx,qy,r,s,expected", [x for x in CURVE_192_KATS if x[6]] -) -def test_pk_recovery(gen, msg, r, s, qx, qy, expected): - del expected - sign = Signature(r, s) - pks = sign.recover_public_keys(digest_integer(msg), gen) - - assert pks - - # Test if the signature is valid for all found public keys - for pk in pks: - q = pk.point - test_signature_validity(gen, msg, q.x(), q.y(), r, s, True) - - # Test if the original public key is in the set of found keys - original_q = ellipticcurve.Point(gen.curve(), qx, qy) - points = [pk.point for pk in pks] - assert original_q in points - - -@st.composite -def st_random_gen_key_msg_nonce(draw): - """Hypothesis strategy for test_sig_verify().""" - name_gen = { - "generator_192": generator_192, - "generator_224": generator_224, - "generator_256": generator_256, - "generator_secp256k1": generator_secp256k1, - "generator_384": generator_384, - "generator_521": generator_521, - } - name = draw(st.sampled_from(sorted(name_gen.keys()))) - note("Generator used: {0}".format(name)) - generator = name_gen[name] - order = int(generator.order()) - - key = draw(st.integers(min_value=1, max_value=order)) - msg = draw(st.integers(min_value=1, max_value=order)) - nonce = draw( - st.integers(min_value=1, max_value=order + 1) - | st.integers(min_value=order >> 1, max_value=order) - ) - return generator, key, msg, nonce - - -SIG_VER_SETTINGS = dict(HYP_SETTINGS) -SIG_VER_SETTINGS["max_examples"] = 10 - - -@settings(**SIG_VER_SETTINGS) -@example((generator_224, 4, 1, 1)) -@given(st_random_gen_key_msg_nonce()) -def test_sig_verify(args): - """ - Check if signing and verification works for arbitrary messages and - that signatures for other messages are rejected. - """ - generator, sec_mult, msg, nonce = args - - pubkey = Public_key(generator, generator * sec_mult) - privkey = Private_key(pubkey, sec_mult) - - signature = privkey.sign(msg, nonce) - - assert pubkey.verifies(msg, signature) - - assert not pubkey.verifies(msg - 1, signature) - - -def test_int_to_string_with_zero(): - assert int_to_string(0) == b"\x00" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py deleted file mode 100644 index 7a09ad7f3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_eddsa.py +++ /dev/null @@ -1,1079 +0,0 @@ -import pickle -import hashlib -import pytest - -try: - import unittest2 as unittest -except ImportError: - import unittest -from hypothesis import given, settings, example -import hypothesis.strategies as st -from .ellipticcurve import PointEdwards, INFINITY, CurveEdTw -from .eddsa import ( - generator_ed25519, - curve_ed25519, - generator_ed448, - curve_ed448, - PrivateKey, - PublicKey, -) -from .ecdsa import generator_256, curve_256 -from .errors import MalformedPointError -from ._compat import a2b_hex, compat26_str - - -class TestA2B_Hex(unittest.TestCase): - def test_invalid_input(self): - with self.assertRaises(ValueError): - a2b_hex("abcdefghi") - - -def test_ed25519_curve_compare(): - assert curve_ed25519 != curve_256 - - -def test_ed25519_and_ed448_compare(): - assert curve_ed448 != curve_ed25519 - - -def test_ed25519_and_custom_curve_compare(): - a = CurveEdTw(curve_ed25519.p(), -curve_ed25519.a(), 1) - - assert curve_ed25519 != a - - -def test_ed25519_and_almost_exact_curve_compare(): - a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), 1) - - assert curve_ed25519 != a - - -def test_ed25519_and_same_curve_params(): - a = CurveEdTw(curve_ed25519.p(), curve_ed25519.a(), curve_ed25519.d()) - - assert curve_ed25519 == a - assert not (curve_ed25519 != a) - - -def test_ed25519_contains_point(): - g = generator_ed25519 - assert curve_ed25519.contains_point(g.x(), g.y()) - - -def test_ed25519_contains_point_bad(): - assert not curve_ed25519.contains_point(1, 1) - - -def test_ed25519_double(): - a = generator_ed25519 - - z = a.double() - - assert isinstance(z, PointEdwards) - - x2 = int( - "24727413235106541002554574571675588834622768167397638456726423" - "682521233608206" - ) - y2 = int( - "15549675580280190176352668710449542251549572066445060580507079" - "593062643049417" - ) - - b = PointEdwards(curve_ed25519, x2, y2, 1, x2 * y2) - - assert z == b - assert a != b - - -def test_ed25519_add_as_double(): - a = generator_ed25519 - - z = a + a - - assert isinstance(z, PointEdwards) - - b = generator_ed25519.double() - - assert z == b - - -def test_ed25519_double_infinity(): - a = PointEdwards(curve_ed25519, 0, 1, 1, 0) - - z = a.double() - - assert z is INFINITY - - -def test_ed25519_double_badly_encoded_infinity(): - # invalid point, mostly to make instrumental happy - a = PointEdwards(curve_ed25519, 1, 1, 1, 0) - - z = a.double() - - assert z is INFINITY - - -def test_ed25519_eq_with_different_z(): - x = generator_ed25519.x() - y = generator_ed25519.y() - p = curve_ed25519.p() - - a = PointEdwards(curve_ed25519, x * 2 % p, y * 2 % p, 2, x * y * 2 % p) - b = PointEdwards(curve_ed25519, x * 3 % p, y * 3 % p, 3, x * y * 3 % p) - - assert a == b - - assert not (a != b) - - -def test_ed25519_eq_against_infinity(): - assert generator_ed25519 != INFINITY - - -def test_ed25519_eq_encoded_infinity_against_infinity(): - a = PointEdwards(curve_ed25519, 0, 1, 1, 0) - assert a == INFINITY - - -def test_ed25519_eq_bad_encode_of_infinity_against_infinity(): - # technically incorrect encoding of the point at infinity, but we check - # both X and T, so verify that just T==0 works - a = PointEdwards(curve_ed25519, 1, 1, 1, 0) - assert a == INFINITY - - -def test_ed25519_eq_against_non_Edwards_point(): - assert generator_ed25519 != generator_256 - - -def test_ed25519_eq_against_negated_point(): - g = generator_ed25519 - neg = PointEdwards(curve_ed25519, -g.x(), g.y(), 1, -g.x() * g.y()) - assert g != neg - - -def test_ed25519_eq_x_different_y(): - # not points on the curve, but __eq__ doesn't care - a = PointEdwards(curve_ed25519, 1, 1, 1, 1) - b = PointEdwards(curve_ed25519, 1, 2, 1, 2) - - assert a != b - - -def test_ed25519_test_normalisation_and_scaling(): - x = generator_ed25519.x() - y = generator_ed25519.y() - p = curve_ed25519.p() - - a = PointEdwards(curve_ed25519, x * 11 % p, y * 11 % p, 11, x * y * 11 % p) - - assert a.x() == x - assert a.y() == y - - a.scale() - - assert a.x() == x - assert a.y() == y - - a.scale() # second execution should be a noop - - assert a.x() == x - assert a.y() == y - - -def test_ed25519_add_three_times(): - a = generator_ed25519 - - z = a + a + a - - x3 = int( - "468967334644549386571235445953867877890461982801326656862413" - "21779790909858396" - ) - y3 = int( - "832484377853344397649037712036920113830141722629755531674120" - "2210403726505172" - ) - - b = PointEdwards(curve_ed25519, x3, y3, 1, x3 * y3) - - assert z == b - - -def test_ed25519_add_to_infinity(): - # generator * (order-1) - x1 = int( - "427838232691226969392843410947554224151809796397784248136826" - "78720006717057747" - ) - y1 = int( - "463168356949264781694283940034751631413079938662562256157830" - "33603165251855960" - ) - inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) - - inf = inf_m_1 + generator_ed25519 - - assert inf is INFINITY - - -def test_ed25519_add_and_mul_equivalence(): - g = generator_ed25519 - - assert g + g == g * 2 - assert g + g + g == g * 3 - - -def test_ed25519_add_literal_infinity(): - g = generator_ed25519 - z = g + INFINITY - - assert z == g - - -def test_ed25519_add_infinity(): - inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) - g = generator_ed25519 - z = g + inf - - assert z == g - - z = inf + g - - assert z == g - - -class TestEd25519(unittest.TestCase): - def test_add_wrong_curves(self): - with self.assertRaises(ValueError) as e: - generator_ed25519 + generator_ed448 - - self.assertIn("different curve", str(e.exception)) - - def test_add_wrong_point_type(self): - with self.assertRaises(ValueError) as e: - generator_ed25519 + generator_256 - - self.assertIn("different curve", str(e.exception)) - - -def test_ed25519_mul_to_order_min_1(): - x1 = int( - "427838232691226969392843410947554224151809796397784248136826" - "78720006717057747" - ) - y1 = int( - "463168356949264781694283940034751631413079938662562256157830" - "33603165251855960" - ) - inf_m_1 = PointEdwards(curve_ed25519, x1, y1, 1, x1 * y1) - - assert generator_ed25519 * (generator_ed25519.order() - 1) == inf_m_1 - - -def test_ed25519_mul_to_infinity(): - assert generator_ed25519 * generator_ed25519.order() == INFINITY - - -def test_ed25519_mul_to_infinity_plus_1(): - g = generator_ed25519 - assert g * (g.order() + 1) == g - - -def test_ed25519_mul_and_add(): - g = generator_ed25519 - a = g * 128 - b = g * 64 + g * 64 - - assert a == b - - -def test_ed25519_mul_and_add_2(): - g = generator_ed25519 - - a = g * 123 - b = g * 120 + g * 3 - - assert a == b - - -def test_ed25519_mul_infinity(): - inf = PointEdwards(curve_ed25519, 0, 1, 1, 0) - - z = inf * 11 - - assert z == INFINITY - - -def test_ed25519_mul_by_zero(): - z = generator_ed25519 * 0 - - assert z == INFINITY - - -def test_ed25519_mul_by_one(): - z = generator_ed25519 * 1 - - assert z == generator_ed25519 - - -def test_ed25519_mul_custom_point(): - # verify that multiplication without order set works - - g = generator_ed25519 - - a = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) - - z = a * 11 - - assert z == g * 11 - - -def test_ed25519_pickle(): - g = generator_ed25519 - assert pickle.loads(pickle.dumps(g)) == g - - -def test_ed448_eq_against_different_curve(): - assert generator_ed25519 != generator_ed448 - - -def test_ed448_double(): - g = generator_ed448 - z = g.double() - - assert isinstance(z, PointEdwards) - - x2 = int( - "4845591495304045936995492052586696895690942404582120401876" - "6013278705691214670908136440114445572635086627683154494739" - "7859048262938744149" - ) - y2 = int( - "4940887598674337276743026725267350893505445523037277237461" - "2648447308771911703729389009346215770388834286503647778745" - "3078312060500281069" - ) - - b = PointEdwards(curve_ed448, x2, y2, 1, x2 * y2) - - assert z == b - assert g != b - - -def test_ed448_add_as_double(): - g = generator_ed448 - z = g + g - - b = g.double() - - assert z == b - - -def test_ed448_mul_as_double(): - g = generator_ed448 - z = g * 2 - b = g.double() - - assert z == b - - -def test_ed448_add_to_infinity(): - # generator * (order - 1) - x1 = int( - "5022586839996825903617194737881084981068517190547539260353" - "6473749366191269932473977736719082931859264751085238669719" - "1187378895383117729" - ) - y1 = int( - "2988192100784814926760179304439306734375440401540802420959" - "2824137233150618983587600353687865541878473398230323350346" - "2500531545062832660" - ) - inf_m_1 = PointEdwards(curve_ed448, x1, y1, 1, x1 * y1) - - inf = inf_m_1 + generator_ed448 - - assert inf is INFINITY - - -def test_ed448_mul_to_infinity(): - g = generator_ed448 - inf = g * g.order() - - assert inf is INFINITY - - -def test_ed448_mul_to_infinity_plus_1(): - g = generator_ed448 - - z = g * (g.order() + 1) - - assert z == g - - -def test_ed448_add_and_mul_equivalence(): - g = generator_ed448 - - assert g + g == g * 2 - assert g + g + g == g * 3 - - -def test_ed25519_encode(): - g = generator_ed25519 - g_bytes = g.to_bytes() - assert len(g_bytes) == 32 - exp_bytes = ( - b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - ) - assert g_bytes == exp_bytes - - -def test_ed25519_decode(): - exp_bytes = ( - b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - ) - a = PointEdwards.from_bytes(curve_ed25519, exp_bytes) - - assert a == generator_ed25519 - - -class TestEdwardsMalformed(unittest.TestCase): - def test_invalid_point(self): - exp_bytes = ( - b"\x78\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - ) - with self.assertRaises(MalformedPointError): - PointEdwards.from_bytes(curve_ed25519, exp_bytes) - - def test_invalid_length(self): - exp_bytes = ( - b"\x58\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66\x66" - b"\x66" - ) - with self.assertRaises(MalformedPointError) as e: - PointEdwards.from_bytes(curve_ed25519, exp_bytes) - - self.assertIn("length", str(e.exception)) - - def test_ed448_invalid(self): - exp_bytes = b"\xff" * 57 - with self.assertRaises(MalformedPointError): - PointEdwards.from_bytes(curve_ed448, exp_bytes) - - -def test_ed448_encode(): - g = generator_ed448 - g_bytes = g.to_bytes() - assert len(g_bytes) == 57 - exp_bytes = ( - b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" - b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" - b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" - b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" - ) - assert g_bytes == exp_bytes - - -def test_ed448_decode(): - exp_bytes = ( - b"\x14\xfa\x30\xf2\x5b\x79\x08\x98\xad\xc8\xd7\x4e\x2c\x13\xbd" - b"\xfd\xc4\x39\x7c\xe6\x1c\xff\xd3\x3a\xd7\xc2\xa0\x05\x1e\x9c" - b"\x78\x87\x40\x98\xa3\x6c\x73\x73\xea\x4b\x62\xc7\xc9\x56\x37" - b"\x20\x76\x88\x24\xbc\xb6\x6e\x71\x46\x3f\x69\x00" - ) - - a = PointEdwards.from_bytes(curve_ed448, exp_bytes) - - assert a == generator_ed448 - - -class TestEdDSAEquality(unittest.TestCase): - def test_equal_public_points(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = PublicKey(generator_ed25519, b"\x01" * 32) - - self.assertEqual(key1, key2) - self.assertFalse(key1 != key2) - - def test_unequal_public_points(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = PublicKey(generator_ed25519, b"\x03" * 32) - - self.assertNotEqual(key1, key2) - - def test_unequal_to_string(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = b"\x01" * 32 - - self.assertNotEqual(key1, key2) - - def test_unequal_publickey_curves(self): - key1 = PublicKey(generator_ed25519, b"\x01" * 32) - key2 = PublicKey(generator_ed448, b"\x03" * 56 + b"\x00") - - self.assertNotEqual(key1, key2) - self.assertTrue(key1 != key2) - - def test_equal_private_keys(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = PrivateKey(generator_ed25519, b"\x01" * 32) - - self.assertEqual(key1, key2) - self.assertFalse(key1 != key2) - - def test_unequal_private_keys(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = PrivateKey(generator_ed25519, b"\x02" * 32) - - self.assertNotEqual(key1, key2) - self.assertTrue(key1 != key2) - - def test_unequal_privatekey_to_string(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = b"\x01" * 32 - - self.assertNotEqual(key1, key2) - - def test_unequal_privatekey_curves(self): - key1 = PrivateKey(generator_ed25519, b"\x01" * 32) - key2 = PrivateKey(generator_ed448, b"\x01" * 57) - - self.assertNotEqual(key1, key2) - - -class TestInvalidEdDSAInputs(unittest.TestCase): - def test_wrong_length_of_private_key(self): - with self.assertRaises(ValueError): - PrivateKey(generator_ed25519, b"\x01" * 31) - - def test_wrong_length_of_public_key(self): - with self.assertRaises(ValueError): - PublicKey(generator_ed25519, b"\x01" * 33) - - def test_wrong_cofactor_curve(self): - ed_c = curve_ed25519 - - def _hash(data): - return hashlib.new("sha512", compat26_str(data)).digest() - - curve = CurveEdTw(ed_c.p(), ed_c.a(), ed_c.d(), 1, _hash) - g = generator_ed25519 - fake_gen = PointEdwards(curve, g.x(), g.y(), 1, g.x() * g.y()) - - with self.assertRaises(ValueError) as e: - PrivateKey(fake_gen, g.to_bytes()) - - self.assertIn("cofactor", str(e.exception)) - - def test_invalid_signature_length(self): - key = PublicKey(generator_ed25519, b"\x01" * 32) - - with self.assertRaises(ValueError) as e: - key.verify(b"", b"\x01" * 65) - - self.assertIn("length", str(e.exception)) - - def test_changing_public_key(self): - key = PublicKey(generator_ed25519, b"\x01" * 32) - - g = key.point - - new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) - - key.point = new_g - - self.assertEqual(g, key.point) - - def test_changing_public_key_to_different_point(self): - key = PublicKey(generator_ed25519, b"\x01" * 32) - - with self.assertRaises(ValueError) as e: - key.point = generator_ed25519 - - self.assertIn("coordinates", str(e.exception)) - - def test_invalid_s_value(self): - key = PublicKey( - generator_ed25519, - b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" - b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", - ) - sig_valid = bytearray( - b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" - b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" - b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" - b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" - ) - - self.assertTrue(key.verify(b"", sig_valid)) - - sig_invalid = bytearray(sig_valid) - sig_invalid[-1] = 0xFF - - with self.assertRaises(ValueError): - key.verify(b"", sig_invalid) - - def test_invalid_r_value(self): - key = PublicKey( - generator_ed25519, - b"\xd7\x5a\x98\x01\x82\xb1\x0a\xb7\xd5\x4b\xfe\xd3\xc9\x64\x07\x3a" - b"\x0e\xe1\x72\xf3\xda\xa6\x23\x25\xaf\x02\x1a\x68\xf7\x07\x51\x1a", - ) - sig_valid = bytearray( - b"\xe5\x56\x43\x00\xc3\x60\xac\x72\x90\x86\xe2\xcc\x80\x6e\x82\x8a" - b"\x84\x87\x7f\x1e\xb8\xe5\xd9\x74\xd8\x73\xe0\x65\x22\x49\x01\x55" - b"\x5f\xb8\x82\x15\x90\xa3\x3b\xac\xc6\x1e\x39\x70\x1c\xf9\xb4\x6b" - b"\xd2\x5b\xf5\xf0\x59\x5b\xbe\x24\x65\x51\x41\x43\x8e\x7a\x10\x0b" - ) - - self.assertTrue(key.verify(b"", sig_valid)) - - sig_invalid = bytearray(sig_valid) - sig_invalid[0] = 0xE0 - - with self.assertRaises(ValueError): - key.verify(b"", sig_invalid) - - -HYP_SETTINGS = dict() -HYP_SETTINGS["max_examples"] = 10 - - -@settings(**HYP_SETTINGS) -@example(1) -@example(5) # smallest multiple that requires changing sign of x -@given(st.integers(min_value=1, max_value=int(generator_ed25519.order() - 1))) -def test_ed25519_encode_decode(multiple): - a = generator_ed25519 * multiple - - b = PointEdwards.from_bytes(curve_ed25519, a.to_bytes()) - - assert a == b - - -@settings(**HYP_SETTINGS) -@example(1) -@example(2) # smallest multiple that requires changing the sign of x -@given(st.integers(min_value=1, max_value=int(generator_ed448.order() - 1))) -def test_ed448_encode_decode(multiple): - a = generator_ed448 * multiple - - b = PointEdwards.from_bytes(curve_ed448, a.to_bytes()) - - assert a == b - - -@settings(**HYP_SETTINGS) -@example(1) -@example(2) -@given(st.integers(min_value=1, max_value=int(generator_ed25519.order()) - 1)) -def test_ed25519_mul_precompute_vs_naf(multiple): - """Compare multiplication with and without precomputation.""" - g = generator_ed25519 - new_g = PointEdwards(curve_ed25519, g.x(), g.y(), 1, g.x() * g.y()) - - assert g * multiple == multiple * new_g - - -# Test vectors from RFC 8032 -TEST_VECTORS = [ - # TEST 1 - ( - generator_ed25519, - "9d61b19deffd5a60ba844af492ec2cc4" "4449c5697b326919703bac031cae7f60", - "d75a980182b10ab7d54bfed3c964073a" "0ee172f3daa62325af021a68f707511a", - "", - "e5564300c360ac729086e2cc806e828a" - "84877f1eb8e5d974d873e06522490155" - "5fb8821590a33bacc61e39701cf9b46b" - "d25bf5f0595bbe24655141438e7a100b", - ), - # TEST 2 - ( - generator_ed25519, - "4ccd089b28ff96da9db6c346ec114e0f" "5b8a319f35aba624da8cf6ed4fb8a6fb", - "3d4017c3e843895a92b70aa74d1b7ebc" "9c982ccf2ec4968cc0cd55f12af4660c", - "72", - "92a009a9f0d4cab8720e820b5f642540" - "a2b27b5416503f8fb3762223ebdb69da" - "085ac1e43e15996e458f3613d0f11d8c" - "387b2eaeb4302aeeb00d291612bb0c00", - ), - # TEST 3 - ( - generator_ed25519, - "c5aa8df43f9f837bedb7442f31dcb7b1" "66d38535076f094b85ce3a2e0b4458f7", - "fc51cd8e6218a1a38da47ed00230f058" "0816ed13ba3303ac5deb911548908025", - "af82", - "6291d657deec24024827e69c3abe01a3" - "0ce548a284743a445e3680d7db5ac3ac" - "18ff9b538d16f290ae67f760984dc659" - "4a7c15e9716ed28dc027beceea1ec40a", - ), - # TEST 1024 - ( - generator_ed25519, - "f5e5767cf153319517630f226876b86c" "8160cc583bc013744c6bf255f5cc0ee5", - "278117fc144c72340f67d0f2316e8386" "ceffbf2b2428c9c51fef7c597f1d426e", - "08b8b2b733424243760fe426a4b54908" - "632110a66c2f6591eabd3345e3e4eb98" - "fa6e264bf09efe12ee50f8f54e9f77b1" - "e355f6c50544e23fb1433ddf73be84d8" - "79de7c0046dc4996d9e773f4bc9efe57" - "38829adb26c81b37c93a1b270b20329d" - "658675fc6ea534e0810a4432826bf58c" - "941efb65d57a338bbd2e26640f89ffbc" - "1a858efcb8550ee3a5e1998bd177e93a" - "7363c344fe6b199ee5d02e82d522c4fe" - "ba15452f80288a821a579116ec6dad2b" - "3b310da903401aa62100ab5d1a36553e" - "06203b33890cc9b832f79ef80560ccb9" - "a39ce767967ed628c6ad573cb116dbef" - "efd75499da96bd68a8a97b928a8bbc10" - "3b6621fcde2beca1231d206be6cd9ec7" - "aff6f6c94fcd7204ed3455c68c83f4a4" - "1da4af2b74ef5c53f1d8ac70bdcb7ed1" - "85ce81bd84359d44254d95629e9855a9" - "4a7c1958d1f8ada5d0532ed8a5aa3fb2" - "d17ba70eb6248e594e1a2297acbbb39d" - "502f1a8c6eb6f1ce22b3de1a1f40cc24" - "554119a831a9aad6079cad88425de6bd" - "e1a9187ebb6092cf67bf2b13fd65f270" - "88d78b7e883c8759d2c4f5c65adb7553" - "878ad575f9fad878e80a0c9ba63bcbcc" - "2732e69485bbc9c90bfbd62481d9089b" - "eccf80cfe2df16a2cf65bd92dd597b07" - "07e0917af48bbb75fed413d238f5555a" - "7a569d80c3414a8d0859dc65a46128ba" - "b27af87a71314f318c782b23ebfe808b" - "82b0ce26401d2e22f04d83d1255dc51a" - "ddd3b75a2b1ae0784504df543af8969b" - "e3ea7082ff7fc9888c144da2af58429e" - "c96031dbcad3dad9af0dcbaaaf268cb8" - "fcffead94f3c7ca495e056a9b47acdb7" - "51fb73e666c6c655ade8297297d07ad1" - "ba5e43f1bca32301651339e22904cc8c" - "42f58c30c04aafdb038dda0847dd988d" - "cda6f3bfd15c4b4c4525004aa06eeff8" - "ca61783aacec57fb3d1f92b0fe2fd1a8" - "5f6724517b65e614ad6808d6f6ee34df" - "f7310fdc82aebfd904b01e1dc54b2927" - "094b2db68d6f903b68401adebf5a7e08" - "d78ff4ef5d63653a65040cf9bfd4aca7" - "984a74d37145986780fc0b16ac451649" - "de6188a7dbdf191f64b5fc5e2ab47b57" - "f7f7276cd419c17a3ca8e1b939ae49e4" - "88acba6b965610b5480109c8b17b80e1" - "b7b750dfc7598d5d5011fd2dcc5600a3" - "2ef5b52a1ecc820e308aa342721aac09" - "43bf6686b64b2579376504ccc493d97e" - "6aed3fb0f9cd71a43dd497f01f17c0e2" - "cb3797aa2a2f256656168e6c496afc5f" - "b93246f6b1116398a346f1a641f3b041" - "e989f7914f90cc2c7fff357876e506b5" - "0d334ba77c225bc307ba537152f3f161" - "0e4eafe595f6d9d90d11faa933a15ef1" - "369546868a7f3a45a96768d40fd9d034" - "12c091c6315cf4fde7cb68606937380d" - "b2eaaa707b4c4185c32eddcdd306705e" - "4dc1ffc872eeee475a64dfac86aba41c" - "0618983f8741c5ef68d3a101e8a3b8ca" - "c60c905c15fc910840b94c00a0b9d0", - "0aab4c900501b3e24d7cdf4663326a3a" - "87df5e4843b2cbdb67cbf6e460fec350" - "aa5371b1508f9f4528ecea23c436d94b" - "5e8fcd4f681e30a6ac00a9704a188a03", - ), - # TEST SHA(abc) - ( - generator_ed25519, - "833fe62409237b9d62ec77587520911e" "9a759cec1d19755b7da901b96dca3d42", - "ec172b93ad5e563bf4932c70e1245034" "c35467ef2efd4d64ebf819683467e2bf", - "ddaf35a193617abacc417349ae204131" - "12e6fa4e89a97ea20a9eeee64b55d39a" - "2192992a274fc1a836ba3c23a3feebbd" - "454d4423643ce80e2a9ac94fa54ca49f", - "dc2a4459e7369633a52b1bf277839a00" - "201009a3efbf3ecb69bea2186c26b589" - "09351fc9ac90b3ecfdfbc7c66431e030" - "3dca179c138ac17ad9bef1177331a704", - ), - # Blank - ( - generator_ed448, - "6c82a562cb808d10d632be89c8513ebf" - "6c929f34ddfa8c9f63c9960ef6e348a3" - "528c8a3fcc2f044e39a3fc5b94492f8f" - "032e7549a20098f95b", - "5fd7449b59b461fd2ce787ec616ad46a" - "1da1342485a70e1f8a0ea75d80e96778" - "edf124769b46c7061bd6783df1e50f6c" - "d1fa1abeafe8256180", - "", - "533a37f6bbe457251f023c0d88f976ae" - "2dfb504a843e34d2074fd823d41a591f" - "2b233f034f628281f2fd7a22ddd47d78" - "28c59bd0a21bfd3980ff0d2028d4b18a" - "9df63e006c5d1c2d345b925d8dc00b41" - "04852db99ac5c7cdda8530a113a0f4db" - "b61149f05a7363268c71d95808ff2e65" - "2600", - ), - # 1 octet - ( - generator_ed448, - "c4eab05d357007c632f3dbb48489924d" - "552b08fe0c353a0d4a1f00acda2c463a" - "fbea67c5e8d2877c5e3bc397a659949e" - "f8021e954e0a12274e", - "43ba28f430cdff456ae531545f7ecd0a" - "c834a55d9358c0372bfa0c6c6798c086" - "6aea01eb00742802b8438ea4cb82169c" - "235160627b4c3a9480", - "03", - "26b8f91727bd62897af15e41eb43c377" - "efb9c610d48f2335cb0bd0087810f435" - "2541b143c4b981b7e18f62de8ccdf633" - "fc1bf037ab7cd779805e0dbcc0aae1cb" - "cee1afb2e027df36bc04dcecbf154336" - "c19f0af7e0a6472905e799f1953d2a0f" - "f3348ab21aa4adafd1d234441cf807c0" - "3a00", - ), - # 11 octets - ( - generator_ed448, - "cd23d24f714274e744343237b93290f5" - "11f6425f98e64459ff203e8985083ffd" - "f60500553abc0e05cd02184bdb89c4cc" - "d67e187951267eb328", - "dcea9e78f35a1bf3499a831b10b86c90" - "aac01cd84b67a0109b55a36e9328b1e3" - "65fce161d71ce7131a543ea4cb5f7e9f" - "1d8b00696447001400", - "0c3e544074ec63b0265e0c", - "1f0a8888ce25e8d458a21130879b840a" - "9089d999aaba039eaf3e3afa090a09d3" - "89dba82c4ff2ae8ac5cdfb7c55e94d5d" - "961a29fe0109941e00b8dbdeea6d3b05" - "1068df7254c0cdc129cbe62db2dc957d" - "bb47b51fd3f213fb8698f064774250a5" - "028961c9bf8ffd973fe5d5c206492b14" - "0e00", - ), - # 12 octets - ( - generator_ed448, - "258cdd4ada32ed9c9ff54e63756ae582" - "fb8fab2ac721f2c8e676a72768513d93" - "9f63dddb55609133f29adf86ec9929dc" - "cb52c1c5fd2ff7e21b", - "3ba16da0c6f2cc1f30187740756f5e79" - "8d6bc5fc015d7c63cc9510ee3fd44adc" - "24d8e968b6e46e6f94d19b945361726b" - "d75e149ef09817f580", - "64a65f3cdedcdd66811e2915", - "7eeeab7c4e50fb799b418ee5e3197ff6" - "bf15d43a14c34389b59dd1a7b1b85b4a" - "e90438aca634bea45e3a2695f1270f07" - "fdcdf7c62b8efeaf00b45c2c96ba457e" - "b1a8bf075a3db28e5c24f6b923ed4ad7" - "47c3c9e03c7079efb87cb110d3a99861" - "e72003cbae6d6b8b827e4e6c143064ff" - "3c00", - ), - # 13 octets - ( - generator_ed448, - "7ef4e84544236752fbb56b8f31a23a10" - "e42814f5f55ca037cdcc11c64c9a3b29" - "49c1bb60700314611732a6c2fea98eeb" - "c0266a11a93970100e", - "b3da079b0aa493a5772029f0467baebe" - "e5a8112d9d3a22532361da294f7bb381" - "5c5dc59e176b4d9f381ca0938e13c6c0" - "7b174be65dfa578e80", - "64a65f3cdedcdd66811e2915e7", - "6a12066f55331b6c22acd5d5bfc5d712" - "28fbda80ae8dec26bdd306743c5027cb" - "4890810c162c027468675ecf645a8317" - "6c0d7323a2ccde2d80efe5a1268e8aca" - "1d6fbc194d3f77c44986eb4ab4177919" - "ad8bec33eb47bbb5fc6e28196fd1caf5" - "6b4e7e0ba5519234d047155ac727a105" - "3100", - ), - # 64 octets - ( - generator_ed448, - "d65df341ad13e008567688baedda8e9d" - "cdc17dc024974ea5b4227b6530e339bf" - "f21f99e68ca6968f3cca6dfe0fb9f4fa" - "b4fa135d5542ea3f01", - "df9705f58edbab802c7f8363cfe5560a" - "b1c6132c20a9f1dd163483a26f8ac53a" - "39d6808bf4a1dfbd261b099bb03b3fb5" - "0906cb28bd8a081f00", - "bd0f6a3747cd561bdddf4640a332461a" - "4a30a12a434cd0bf40d766d9c6d458e5" - "512204a30c17d1f50b5079631f64eb31" - "12182da3005835461113718d1a5ef944", - "554bc2480860b49eab8532d2a533b7d5" - "78ef473eeb58c98bb2d0e1ce488a98b1" - "8dfde9b9b90775e67f47d4a1c3482058" - "efc9f40d2ca033a0801b63d45b3b722e" - "f552bad3b4ccb667da350192b61c508c" - "f7b6b5adadc2c8d9a446ef003fb05cba" - "5f30e88e36ec2703b349ca229c267083" - "3900", - ), - # 256 octets - ( - generator_ed448, - "2ec5fe3c17045abdb136a5e6a913e32a" - "b75ae68b53d2fc149b77e504132d3756" - "9b7e766ba74a19bd6162343a21c8590a" - "a9cebca9014c636df5", - "79756f014dcfe2079f5dd9e718be4171" - "e2ef2486a08f25186f6bff43a9936b9b" - "fe12402b08ae65798a3d81e22e9ec80e" - "7690862ef3d4ed3a00", - "15777532b0bdd0d1389f636c5f6b9ba7" - "34c90af572877e2d272dd078aa1e567c" - "fa80e12928bb542330e8409f31745041" - "07ecd5efac61ae7504dabe2a602ede89" - "e5cca6257a7c77e27a702b3ae39fc769" - "fc54f2395ae6a1178cab4738e543072f" - "c1c177fe71e92e25bf03e4ecb72f47b6" - "4d0465aaea4c7fad372536c8ba516a60" - "39c3c2a39f0e4d832be432dfa9a706a6" - "e5c7e19f397964ca4258002f7c0541b5" - "90316dbc5622b6b2a6fe7a4abffd9610" - "5eca76ea7b98816af0748c10df048ce0" - "12d901015a51f189f3888145c03650aa" - "23ce894c3bd889e030d565071c59f409" - "a9981b51878fd6fc110624dcbcde0bf7" - "a69ccce38fabdf86f3bef6044819de11", - "c650ddbb0601c19ca11439e1640dd931" - "f43c518ea5bea70d3dcde5f4191fe53f" - "00cf966546b72bcc7d58be2b9badef28" - "743954e3a44a23f880e8d4f1cfce2d7a" - "61452d26da05896f0a50da66a239a8a1" - "88b6d825b3305ad77b73fbac0836ecc6" - "0987fd08527c1a8e80d5823e65cafe2a" - "3d00", - ), - # 1023 octets - ( - generator_ed448, - "872d093780f5d3730df7c212664b37b8" - "a0f24f56810daa8382cd4fa3f77634ec" - "44dc54f1c2ed9bea86fafb7632d8be19" - "9ea165f5ad55dd9ce8", - "a81b2e8a70a5ac94ffdbcc9badfc3feb" - "0801f258578bb114ad44ece1ec0e799d" - "a08effb81c5d685c0c56f64eecaef8cd" - "f11cc38737838cf400", - "6ddf802e1aae4986935f7f981ba3f035" - "1d6273c0a0c22c9c0e8339168e675412" - "a3debfaf435ed651558007db4384b650" - "fcc07e3b586a27a4f7a00ac8a6fec2cd" - "86ae4bf1570c41e6a40c931db27b2faa" - "15a8cedd52cff7362c4e6e23daec0fbc" - "3a79b6806e316efcc7b68119bf46bc76" - "a26067a53f296dafdbdc11c77f7777e9" - "72660cf4b6a9b369a6665f02e0cc9b6e" - "dfad136b4fabe723d2813db3136cfde9" - "b6d044322fee2947952e031b73ab5c60" - "3349b307bdc27bc6cb8b8bbd7bd32321" - "9b8033a581b59eadebb09b3c4f3d2277" - "d4f0343624acc817804728b25ab79717" - "2b4c5c21a22f9c7839d64300232eb66e" - "53f31c723fa37fe387c7d3e50bdf9813" - "a30e5bb12cf4cd930c40cfb4e1fc6225" - "92a49588794494d56d24ea4b40c89fc0" - "596cc9ebb961c8cb10adde976a5d602b" - "1c3f85b9b9a001ed3c6a4d3b1437f520" - "96cd1956d042a597d561a596ecd3d173" - "5a8d570ea0ec27225a2c4aaff26306d1" - "526c1af3ca6d9cf5a2c98f47e1c46db9" - "a33234cfd4d81f2c98538a09ebe76998" - "d0d8fd25997c7d255c6d66ece6fa56f1" - "1144950f027795e653008f4bd7ca2dee" - "85d8e90f3dc315130ce2a00375a318c7" - "c3d97be2c8ce5b6db41a6254ff264fa6" - "155baee3b0773c0f497c573f19bb4f42" - "40281f0b1f4f7be857a4e59d416c06b4" - "c50fa09e1810ddc6b1467baeac5a3668" - "d11b6ecaa901440016f389f80acc4db9" - "77025e7f5924388c7e340a732e554440" - "e76570f8dd71b7d640b3450d1fd5f041" - "0a18f9a3494f707c717b79b4bf75c984" - "00b096b21653b5d217cf3565c9597456" - "f70703497a078763829bc01bb1cbc8fa" - "04eadc9a6e3f6699587a9e75c94e5bab" - "0036e0b2e711392cff0047d0d6b05bd2" - "a588bc109718954259f1d86678a579a3" - "120f19cfb2963f177aeb70f2d4844826" - "262e51b80271272068ef5b3856fa8535" - "aa2a88b2d41f2a0e2fda7624c2850272" - "ac4a2f561f8f2f7a318bfd5caf969614" - "9e4ac824ad3460538fdc25421beec2cc" - "6818162d06bbed0c40a387192349db67" - "a118bada6cd5ab0140ee273204f628aa" - "d1c135f770279a651e24d8c14d75a605" - "9d76b96a6fd857def5e0b354b27ab937" - "a5815d16b5fae407ff18222c6d1ed263" - "be68c95f32d908bd895cd76207ae7264" - "87567f9a67dad79abec316f683b17f2d" - "02bf07e0ac8b5bc6162cf94697b3c27c" - "d1fea49b27f23ba2901871962506520c" - "392da8b6ad0d99f7013fbc06c2c17a56" - "9500c8a7696481c1cd33e9b14e40b82e" - "79a5f5db82571ba97bae3ad3e0479515" - "bb0e2b0f3bfcd1fd33034efc6245eddd" - "7ee2086ddae2600d8ca73e214e8c2b0b" - "db2b047c6a464a562ed77b73d2d841c4" - "b34973551257713b753632efba348169" - "abc90a68f42611a40126d7cb21b58695" - "568186f7e569d2ff0f9e745d0487dd2e" - "b997cafc5abf9dd102e62ff66cba87", - "e301345a41a39a4d72fff8df69c98075" - "a0cc082b802fc9b2b6bc503f926b65bd" - "df7f4c8f1cb49f6396afc8a70abe6d8a" - "ef0db478d4c6b2970076c6a0484fe76d" - "76b3a97625d79f1ce240e7c576750d29" - "5528286f719b413de9ada3e8eb78ed57" - "3603ce30d8bb761785dc30dbc320869e" - "1a00", - ), -] - - -@pytest.mark.parametrize( - "generator,private_key,public_key,message,signature", - TEST_VECTORS, -) -def test_vectors(generator, private_key, public_key, message, signature): - private_key = a2b_hex(private_key) - public_key = a2b_hex(public_key) - message = a2b_hex(message) - signature = a2b_hex(signature) - - sig_key = PrivateKey(generator, private_key) - ver_key = PublicKey(generator, public_key) - - assert sig_key.public_key().public_key() == ver_key.public_key() - - gen_sig = sig_key.sign(message) - - assert gen_sig == signature - - assert ver_key.verify(message, signature) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py deleted file mode 100644 index 85faef4d7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_ellipticcurve.py +++ /dev/null @@ -1,199 +0,0 @@ -import pytest - -try: - import unittest2 as unittest -except ImportError: - import unittest -from hypothesis import given, settings -import hypothesis.strategies as st - -try: - from hypothesis import HealthCheck - - HC_PRESENT = True -except ImportError: # pragma: no cover - HC_PRESENT = False -from .numbertheory import inverse_mod -from .ellipticcurve import CurveFp, INFINITY, Point - - -HYP_SETTINGS = {} -if HC_PRESENT: # pragma: no branch - HYP_SETTINGS["suppress_health_check"] = [HealthCheck.too_slow] - HYP_SETTINGS["deadline"] = 5000 - - -# NIST Curve P-192: -p = 6277101735386680763835789423207666416083908700390324961279 -r = 6277101735386680763835789423176059013767194773182842284081 -# s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 -# c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 -b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 -Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 -Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 - -c192 = CurveFp(p, -3, b) -p192 = Point(c192, Gx, Gy, r) - -c_23 = CurveFp(23, 1, 1) -g_23 = Point(c_23, 13, 7, 7) - - -HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) -HYP_SLOW_SETTINGS["max_examples"] = 10 - - -@settings(**HYP_SLOW_SETTINGS) -@given(st.integers(min_value=1, max_value=r + 1)) -def test_p192_mult_tests(multiple): - inv_m = inverse_mod(multiple, r) - - p1 = p192 * multiple - assert p1 * inv_m == p192 - - -def add_n_times(point, n): - ret = INFINITY - i = 0 - while i <= n: - yield ret - ret = ret + point - i += 1 - - -# From X9.62 I.1 (p. 96): -@pytest.mark.parametrize( - "p, m, check", - [(g_23, n, exp) for n, exp in enumerate(add_n_times(g_23, 8))], - ids=["g_23 test with mult {0}".format(i) for i in range(9)], -) -def test_add_and_mult_equivalence(p, m, check): - assert p * m == check - - -class TestCurve(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.c_23 = CurveFp(23, 1, 1) - - def test_equality_curves(self): - self.assertEqual(self.c_23, CurveFp(23, 1, 1)) - - def test_inequality_curves(self): - c192 = CurveFp(p, -3, b) - self.assertNotEqual(self.c_23, c192) - - def test_usability_in_a_hashed_collection_curves(self): - {self.c_23: None} - - def test_hashability_curves(self): - hash(self.c_23) - - def test_conflation_curves(self): - ne1, ne2, ne3 = CurveFp(24, 1, 1), CurveFp(23, 2, 1), CurveFp(23, 1, 2) - eq1, eq2, eq3 = CurveFp(23, 1, 1), CurveFp(23, 1, 1), self.c_23 - self.assertEqual(len(set((c_23, eq1, eq2, eq3))), 1) - self.assertEqual(len(set((c_23, ne1, ne2, ne3))), 4) - self.assertDictEqual({c_23: None}, {eq1: None}) - self.assertIn(eq2, {eq3: None}) - - -class TestPoint(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.c_23 = CurveFp(23, 1, 1) - cls.g_23 = Point(cls.c_23, 13, 7, 7) - - p = 6277101735386680763835789423207666416083908700390324961279 - r = 6277101735386680763835789423176059013767194773182842284081 - # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 - # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 - b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 - Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 - Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 - - cls.c192 = CurveFp(p, -3, b) - cls.p192 = Point(cls.c192, Gx, Gy, r) - - def test_p192(self): - # Checking against some sample computations presented - # in X9.62: - d = 651056770906015076056810763456358567190100156695615665659 - Q = d * self.p192 - self.assertEqual( - Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 - ) - - k = 6140507067065001063065065565667405560006161556565665656654 - R = k * self.p192 - self.assertEqual( - R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - ) - self.assertEqual( - R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - ) - - u1 = 2563697409189434185194736134579731015366492496392189760599 - u2 = 6266643813348617967186477710235785849136406323338782220568 - temp = u1 * self.p192 + u2 * Q - self.assertEqual( - temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD - ) - self.assertEqual( - temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 - ) - - def test_double_infinity(self): - p1 = INFINITY - p3 = p1.double() - self.assertEqual(p1, p3) - self.assertEqual(p3.x(), p1.x()) - self.assertEqual(p3.y(), p3.y()) - - def test_double(self): - x1, y1, x3, y3 = (3, 10, 7, 12) - - p1 = Point(self.c_23, x1, y1) - p3 = p1.double() - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - def test_multiply(self): - x1, y1, m, x3, y3 = (3, 10, 2, 7, 12) - p1 = Point(self.c_23, x1, y1) - p3 = p1 * m - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - # Trivial tests from X9.62 B.3: - def test_add(self): - """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - - x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20) - p1 = Point(self.c_23, x1, y1) - p2 = Point(self.c_23, x2, y2) - p3 = p1 + p2 - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - def test_add_as_double(self): - """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - - x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) - p1 = Point(self.c_23, x1, y1) - p2 = Point(self.c_23, x2, y2) - p3 = p1 + p2 - self.assertEqual(p3.x(), x3) - self.assertEqual(p3.y(), y3) - - def test_equality_points(self): - self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7)) - - def test_inequality_points(self): - c = CurveFp(100, -3, 100) - p = Point(c, 100, 100, 100) - self.assertNotEqual(self.g_23, p) - - def test_inequality_points_diff_types(self): - c = CurveFp(100, -3, 100) - self.assertNotEqual(self.g_23, c) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py deleted file mode 100644 index 1f5280482..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_jacobi.py +++ /dev/null @@ -1,657 +0,0 @@ -import pickle - -try: - import unittest2 as unittest -except ImportError: - import unittest - -import os -import sys -import signal -import pytest -import threading -import platform -import hypothesis.strategies as st -from hypothesis import given, assume, settings, example - -from .ellipticcurve import CurveFp, PointJacobi, INFINITY -from .ecdsa import ( - generator_256, - curve_256, - generator_224, - generator_brainpoolp160r1, - curve_brainpoolp160r1, - generator_112r2, -) -from .numbertheory import inverse_mod -from .util import randrange - - -NO_OLD_SETTINGS = {} -if sys.version_info > (2, 7): # pragma: no branch - NO_OLD_SETTINGS["deadline"] = 5000 - - -class TestJacobi(unittest.TestCase): - def test___init__(self): - curve = object() - x = 2 - y = 3 - z = 1 - order = 4 - pj = PointJacobi(curve, x, y, z, order) - - self.assertEqual(pj.order(), order) - self.assertIs(pj.curve(), curve) - self.assertEqual(pj.x(), x) - self.assertEqual(pj.y(), y) - - def test_add_with_different_curves(self): - p_a = PointJacobi.from_affine(generator_256) - p_b = PointJacobi.from_affine(generator_224) - - with self.assertRaises(ValueError): - p_a + p_b - - def test_compare_different_curves(self): - self.assertNotEqual(generator_256, generator_224) - - def test_equality_with_non_point(self): - pj = PointJacobi.from_affine(generator_256) - - self.assertNotEqual(pj, "value") - - def test_conversion(self): - pj = PointJacobi.from_affine(generator_256) - pw = pj.to_affine() - - self.assertEqual(generator_256, pw) - - def test_single_double(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256.double() - - pj = pj.double() - - self.assertEqual(pj.x(), pw.x()) - self.assertEqual(pj.y(), pw.y()) - - def test_double_with_zero_point(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - pj = pj.double() - - self.assertIs(pj, INFINITY) - - def test_double_with_zero_equivalent_point(self): - pj = PointJacobi(curve_256, 0, curve_256.p(), 1) - - pj = pj.double() - - self.assertIs(pj, INFINITY) - - def test_double_with_zero_equivalent_point_non_1_z(self): - pj = PointJacobi(curve_256, 0, curve_256.p(), 2) - - pj = pj.double() - - self.assertIs(pj, INFINITY) - - def test_compare_with_affine_point(self): - pj = PointJacobi.from_affine(generator_256) - pa = pj.to_affine() - - self.assertEqual(pj, pa) - self.assertEqual(pa, pj) - - def test_to_affine_with_zero_point(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - pa = pj.to_affine() - - self.assertIs(pa, INFINITY) - - def test_add_with_affine_point(self): - pj = PointJacobi.from_affine(generator_256) - pa = pj.to_affine() - - s = pj + pa - - self.assertEqual(s, pj.double()) - - def test_radd_with_affine_point(self): - pj = PointJacobi.from_affine(generator_256) - pa = pj.to_affine() - - s = pa + pj - - self.assertEqual(s, pj.double()) - - def test_add_with_infinity(self): - pj = PointJacobi.from_affine(generator_256) - - s = pj + INFINITY - - self.assertEqual(s, pj) - - def test_add_zero_point_to_affine(self): - pa = PointJacobi.from_affine(generator_256).to_affine() - pj = PointJacobi(curve_256, 0, 0, 1) - - s = pj + pa - - self.assertIs(s, pa) - - def test_multiply_by_zero(self): - pj = PointJacobi.from_affine(generator_256) - - pj = pj * 0 - - self.assertIs(pj, INFINITY) - - def test_zero_point_multiply_by_one(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - pj = pj * 1 - - self.assertIs(pj, INFINITY) - - def test_multiply_by_one(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256 * 1 - - pj = pj * 1 - - self.assertEqual(pj.x(), pw.x()) - self.assertEqual(pj.y(), pw.y()) - - def test_multiply_by_two(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256 * 2 - - pj = pj * 2 - - self.assertEqual(pj.x(), pw.x()) - self.assertEqual(pj.y(), pw.y()) - - def test_rmul_by_two(self): - pj = PointJacobi.from_affine(generator_256) - pw = generator_256 * 2 - - pj = 2 * pj - - self.assertEqual(pj, pw) - - def test_compare_non_zero_with_infinity(self): - pj = PointJacobi.from_affine(generator_256) - - self.assertNotEqual(pj, INFINITY) - - def test_compare_zero_point_with_infinity(self): - pj = PointJacobi(curve_256, 0, 0, 1) - - self.assertEqual(pj, INFINITY) - - def test_compare_double_with_multiply(self): - pj = PointJacobi.from_affine(generator_256) - dbl = pj.double() - mlpl = pj * 2 - - self.assertEqual(dbl, mlpl) - - @settings(max_examples=10) - @given( - st.integers( - min_value=0, max_value=int(generator_brainpoolp160r1.order()) - ) - ) - def test_multiplications(self, mul): - pj = PointJacobi.from_affine(generator_brainpoolp160r1) - pw = pj.to_affine() * mul - - pj = pj * mul - - self.assertEqual((pj.x(), pj.y()), (pw.x(), pw.y())) - self.assertEqual(pj, pw) - - @settings(max_examples=10) - @given( - st.integers( - min_value=0, max_value=int(generator_brainpoolp160r1.order()) - ) - ) - @example(0) - @example(int(generator_brainpoolp160r1.order())) - def test_precompute(self, mul): - precomp = generator_brainpoolp160r1 - self.assertTrue(precomp._PointJacobi__precompute) - pj = PointJacobi.from_affine(generator_brainpoolp160r1) - - a = precomp * mul - b = pj * mul - - self.assertEqual(a, b) - - @settings(max_examples=10) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - ) - @example(3, 3) - def test_add_scaled_points(self, a_mul, b_mul): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - @settings(max_examples=10) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), - ) - def test_add_one_scaled_point(self, a_mul, b_mul, new_z): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - p = curve_brainpoolp160r1.p() - - assume(inverse_mod(new_z, p)) - - new_zz = new_z * new_z % p - - b = PointJacobi( - curve_brainpoolp160r1, - b.x() * new_zz % p, - b.y() * new_zz * new_z % p, - new_z, - ) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - @settings(max_examples=10) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.integers(min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1)), - ) - @example(1, 1, 1) - @example(3, 3, 3) - @example(2, int(generator_brainpoolp160r1.order() - 2), 1) - @example(2, int(generator_brainpoolp160r1.order() - 2), 3) - def test_add_same_scale_points(self, a_mul, b_mul, new_z): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - p = curve_brainpoolp160r1.p() - - assume(inverse_mod(new_z, p)) - - new_zz = new_z * new_z % p - - a = PointJacobi( - curve_brainpoolp160r1, - a.x() * new_zz % p, - a.y() * new_zz * new_z % p, - new_z, - ) - b = PointJacobi( - curve_brainpoolp160r1, - b.x() * new_zz % p, - b.y() * new_zz * new_z % p, - new_z, - ) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - def test_add_same_scale_points_static(self): - j_g = generator_brainpoolp160r1 - p = curve_brainpoolp160r1.p() - a = j_g * 11 - a.scale() - z1 = 13 - x = PointJacobi( - curve_brainpoolp160r1, - a.x() * z1**2 % p, - a.y() * z1**3 % p, - z1, - ) - y = PointJacobi( - curve_brainpoolp160r1, - a.x() * z1**2 % p, - a.y() * z1**3 % p, - z1, - ) - - c = a + a - - self.assertEqual(c, x + y) - - @settings(max_examples=14) - @given( - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.integers( - min_value=1, max_value=int(generator_brainpoolp160r1.order()) - ), - st.lists( - st.integers( - min_value=1, max_value=int(curve_brainpoolp160r1.p() - 1) - ), - min_size=2, - max_size=2, - unique=True, - ), - ) - @example(2, 2, [2, 1]) - @example(2, 2, [2, 3]) - @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 3]) - @example(2, int(generator_brainpoolp160r1.order() - 2), [2, 1]) - def test_add_different_scale_points(self, a_mul, b_mul, new_z): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1) - a = PointJacobi.from_affine(j_g * a_mul) - b = PointJacobi.from_affine(j_g * b_mul) - - p = curve_brainpoolp160r1.p() - - assume(inverse_mod(new_z[0], p)) - assume(inverse_mod(new_z[1], p)) - - new_zz0 = new_z[0] * new_z[0] % p - new_zz1 = new_z[1] * new_z[1] % p - - a = PointJacobi( - curve_brainpoolp160r1, - a.x() * new_zz0 % p, - a.y() * new_zz0 * new_z[0] % p, - new_z[0], - ) - b = PointJacobi( - curve_brainpoolp160r1, - b.x() * new_zz1 % p, - b.y() * new_zz1 * new_z[1] % p, - new_z[1], - ) - - c = a + b - - self.assertEqual(c, j_g * (a_mul + b_mul)) - - def test_add_different_scale_points_static(self): - j_g = generator_brainpoolp160r1 - p = curve_brainpoolp160r1.p() - a = j_g * 11 - a.scale() - z1 = 13 - x = PointJacobi( - curve_brainpoolp160r1, - a.x() * z1**2 % p, - a.y() * z1**3 % p, - z1, - ) - z2 = 29 - y = PointJacobi( - curve_brainpoolp160r1, - a.x() * z2**2 % p, - a.y() * z2**3 % p, - z2, - ) - - c = a + a - - self.assertEqual(c, x + y) - - def test_add_point_3_times(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertEqual(j_g * 3, j_g + j_g + j_g) - - def test_mul_without_order(self): - j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) - - self.assertEqual(j_g * generator_256.order(), INFINITY) - - def test_mul_add_inf(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertEqual(j_g, j_g.mul_add(1, INFINITY, 1)) - - def test_mul_add_same(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertEqual(j_g * 2, j_g.mul_add(1, j_g, 1)) - - def test_mul_add_precompute(self): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) - b = PointJacobi.from_affine(j_g * 255, True) - - self.assertEqual(j_g * 256, j_g + b) - self.assertEqual(j_g * (5 + 255 * 7), j_g * 5 + b * 7) - self.assertEqual(j_g * (5 + 255 * 7), j_g.mul_add(5, b, 7)) - - def test_mul_add_precompute_large(self): - j_g = PointJacobi.from_affine(generator_brainpoolp160r1, True) - b = PointJacobi.from_affine(j_g * 255, True) - - self.assertEqual(j_g * 256, j_g + b) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 - ) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) - ) - - def test_mul_add_to_mul(self): - j_g = PointJacobi.from_affine(generator_256) - - a = j_g * 3 - b = j_g.mul_add(2, j_g, 1) - - self.assertEqual(a, b) - - def test_mul_add_differnt(self): - j_g = PointJacobi.from_affine(generator_256) - - w_a = j_g * 2 - - self.assertEqual(j_g.mul_add(1, w_a, 1), j_g * 3) - - def test_mul_add_slightly_different(self): - j_g = PointJacobi.from_affine(generator_256) - - w_a = j_g * 2 - w_b = j_g * 3 - - self.assertEqual(w_a.mul_add(1, w_b, 3), w_a * 1 + w_b * 3) - - def test_mul_add(self): - j_g = PointJacobi.from_affine(generator_256) - - w_a = generator_256 * 255 - w_b = generator_256 * (0xA8 * 0xF0) - j_b = j_g * 0xA8 - - ret = j_g.mul_add(255, j_b, 0xF0) - - self.assertEqual(ret.to_affine(), w_a + w_b) - - def test_mul_add_large(self): - j_g = PointJacobi.from_affine(generator_256) - b = PointJacobi.from_affine(j_g * 255) - - self.assertEqual(j_g * 256, j_g + b) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 - ) - self.assertEqual( - j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) - ) - - def test_mul_add_with_infinity_as_result(self): - j_g = PointJacobi.from_affine(generator_256) - - order = generator_256.order() - - b = PointJacobi.from_affine(generator_256 * 256) - - self.assertEqual(j_g.mul_add(order % 256, b, order // 256), INFINITY) - - def test_mul_add_without_order(self): - j_g = PointJacobi(curve_256, generator_256.x(), generator_256.y(), 1) - - order = generator_256.order() - - w_b = generator_256 * 34 - w_b.scale() - - b = PointJacobi(curve_256, w_b.x(), w_b.y(), 1) - - self.assertEqual(j_g.mul_add(order % 34, b, order // 34), INFINITY) - - def test_mul_add_with_doubled_negation_of_itself(self): - j_g = PointJacobi.from_affine(generator_256 * 17) - - dbl_neg = 2 * (-j_g) - - self.assertEqual(j_g.mul_add(4, dbl_neg, 2), INFINITY) - - def test_equality(self): - pj1 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - pj2 = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - self.assertEqual(pj1, pj2) - - def test_equality_with_invalid_object(self): - j_g = PointJacobi.from_affine(generator_256) - - self.assertNotEqual(j_g, 12) - - def test_equality_with_wrong_curves(self): - p_a = PointJacobi.from_affine(generator_256) - p_b = PointJacobi.from_affine(generator_224) - - self.assertNotEqual(p_a, p_b) - - def test_pickle(self): - pj = PointJacobi(curve=CurveFp(23, 1, 1, 1), x=2, y=3, z=1, order=1) - self.assertEqual(pickle.loads(pickle.dumps(pj)), pj) - - @settings(**NO_OLD_SETTINGS) - @given(st.integers(min_value=1, max_value=10)) - def test_multithreading(self, thread_num): - # ensure that generator's precomputation table is filled - generator_112r2 * 2 - - # create a fresh point that doesn't have a filled precomputation table - gen = generator_112r2 - gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) - - self.assertEqual(gen._PointJacobi__precompute, []) - - def runner(generator): - order = generator.order() - for _ in range(10): - generator * randrange(order) - - threads = [] - for _ in range(thread_num): - threads.append(threading.Thread(target=runner, args=(gen,))) - - for t in threads: - t.start() - - runner(gen) - - for t in threads: - t.join() - - self.assertEqual( - gen._PointJacobi__precompute, - generator_112r2._PointJacobi__precompute, - ) - - @pytest.mark.skipif( - platform.system() == "Windows", - reason="there are no signals on Windows", - ) - def test_multithreading_with_interrupts(self): - thread_num = 10 - # ensure that generator's precomputation table is filled - generator_112r2 * 2 - - # create a fresh point that doesn't have a filled precomputation table - gen = generator_112r2 - gen = PointJacobi(gen.curve(), gen.x(), gen.y(), 1, gen.order(), True) - - self.assertEqual(gen._PointJacobi__precompute, []) - - def runner(generator): - order = generator.order() - for _ in range(50): - generator * randrange(order) - - def interrupter(barrier_start, barrier_end, lock_exit): - # wait until MainThread can handle KeyboardInterrupt - barrier_start.release() - barrier_end.acquire() - os.kill(os.getpid(), signal.SIGINT) - lock_exit.release() - - threads = [] - for _ in range(thread_num): - threads.append(threading.Thread(target=runner, args=(gen,))) - - barrier_start = threading.Lock() - barrier_start.acquire() - barrier_end = threading.Lock() - barrier_end.acquire() - lock_exit = threading.Lock() - lock_exit.acquire() - - threads.append( - threading.Thread( - target=interrupter, - args=(barrier_start, barrier_end, lock_exit), - ) - ) - - for t in threads: - t.start() - - with self.assertRaises(KeyboardInterrupt): - # signal to interrupter that we can now handle the signal - barrier_start.acquire() - barrier_end.release() - runner(gen) - # use the lock to ensure we never go past the scope of - # assertRaises before the os.kill is called - lock_exit.acquire() - - for t in threads: - t.join() - - self.assertEqual( - gen._PointJacobi__precompute, - generator_112r2._PointJacobi__precompute, - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py deleted file mode 100644 index 25386b175..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_keys.py +++ /dev/null @@ -1,959 +0,0 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest - -try: - buffer -except NameError: - buffer = memoryview - -import os -import array -import pytest -import hashlib - -from .keys import VerifyingKey, SigningKey, MalformedPointError -from .der import ( - unpem, - UnexpectedDER, - encode_sequence, - encode_oid, - encode_bitstring, -) -from .util import ( - sigencode_string, - sigencode_der, - sigencode_strings, - sigdecode_string, - sigdecode_der, - sigdecode_strings, -) -from .curves import NIST256p, Curve, BRAINPOOLP160r1, Ed25519, Ed448 -from .ellipticcurve import Point, PointJacobi, CurveFp, INFINITY -from .ecdsa import generator_brainpoolp160r1 - - -class TestVerifyingKeyFromString(unittest.TestCase): - """ - Verify that ecdsa.keys.VerifyingKey.from_string() can be used with - bytes-like objects - """ - - @classmethod - def setUpClass(cls): - cls.key_bytes = ( - b"\x04L\xa2\x95\xdb\xc7Z\xd7\x1f\x93\nz\xcf\x97\xcf" - b"\xd7\xc2\xd9o\xfe8}X!\xae\xd4\xfah\xfa^\rpI\xba\xd1" - b"Y\xfb\x92xa\xebo+\x9cG\xfav\xca" - ) - cls.vk = VerifyingKey.from_string(cls.key_bytes) - - def test_bytes(self): - self.assertIsNotNone(self.vk) - self.assertIsInstance(self.vk, VerifyingKey) - self.assertEqual( - self.vk.pubkey.point.x(), - 105419898848891948935835657980914000059957975659675736097, - ) - self.assertEqual( - self.vk.pubkey.point.y(), - 4286866841217412202667522375431381222214611213481632495306, - ) - - def test_bytes_memoryview(self): - vk = VerifyingKey.from_string(buffer(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray(self): - vk = VerifyingKey.from_string(bytearray(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytesarray_memoryview(self): - vk = VerifyingKey.from_string(buffer(bytearray(self.key_bytes))) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_string(arr) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes_memoryview(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_string(buffer(arr)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_ints(self): - arr = array.array("I", self.key_bytes) - vk = VerifyingKey.from_string(arr) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_ints_memoryview(self): - arr = array.array("I", self.key_bytes) - vk = VerifyingKey.from_string(buffer(arr)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytes_uncompressed(self): - vk = VerifyingKey.from_string(b"\x04" + self.key_bytes) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray_uncompressed(self): - vk = VerifyingKey.from_string(bytearray(b"\x04" + self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytes_compressed(self): - vk = VerifyingKey.from_string(b"\x02" + self.key_bytes[:24]) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray_compressed(self): - vk = VerifyingKey.from_string(bytearray(b"\x02" + self.key_bytes[:24])) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - -class TestVerifyingKeyFromDer(unittest.TestCase): - """ - Verify that ecdsa.keys.VerifyingKey.from_der() can be used with - bytes-like objects. - """ - - @classmethod - def setUpClass(cls): - prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" - ) - key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" - "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n" - ) - cls.key_pem = key_str - - cls.key_bytes = unpem(key_str) - assert isinstance(cls.key_bytes, bytes) - cls.vk = VerifyingKey.from_pem(key_str) - cls.sk = SigningKey.from_pem(prv_key_str) - - key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8\n" - "Poqzgjau4kfxBPyZimeRfuY/9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" - "-----END PUBLIC KEY-----\n" - ) - cls.vk2 = VerifyingKey.from_pem(key_str) - - cls.sk2 = SigningKey.generate(vk.curve) - - def test_load_key_with_explicit_parameters(self): - pub_key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" - "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" - "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" - "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" - "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" - "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" - "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" - "-----END PUBLIC KEY-----" - ) - pk = VerifyingKey.from_pem(pub_key_str) - - pk_exp = VerifyingKey.from_string( - b"\x04\x8a\xf5\x52\x48\x18\xb3\x98\xe6\x6c\x57\x3b\x8a\xdd\x7f" - b"\x60\x8d\x97\x4f\xff\xc8\x95\xa1\x23\x30\xd6\x5f\xb7\x5a\x30" - b"\x8e\xaa\x41\x60\x7d\x84\xac\x91\xe4\xe1\x4e\x4f\xa7\x85\xaf" - b"\x5a\xad\x71\x98\x7c\x08\x66\x5b\x07\xec\x88\x38\x2a\x67\x9f" - b"\x09\xf4\x7a\x4a\x65", - curve=NIST256p, - ) - self.assertEqual(pk, pk_exp) - - def test_load_key_with_explicit_with_explicit_disabled(self): - pub_key_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MIIBSzCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAABAAAA\n" - "AAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA////\n" - "///////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMVAMSd\n" - "NgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg9KE5\n" - "RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8AAAAA\n" - "//////////+85vqtpxeehPO5ysL8YyVRAgEBA0IABIr1UkgYs5jmbFc7it1/YI2X\n" - "T//IlaEjMNZft1owjqpBYH2ErJHk4U5Pp4WvWq1xmHwIZlsH7Ig4KmefCfR6SmU=\n" - "-----END PUBLIC KEY-----" - ) - with self.assertRaises(UnexpectedDER): - VerifyingKey.from_pem( - pub_key_str, valid_curve_encodings=["named_curve"] - ) - - def test_load_key_with_disabled_format(self): - with self.assertRaises(MalformedPointError) as e: - VerifyingKey.from_der(self.key_bytes, valid_encodings=["raw"]) - - self.assertIn("enabled (raw) encodings", str(e.exception)) - - def test_custom_hashfunc(self): - vk = VerifyingKey.from_der(self.key_bytes, hashlib.sha256) - - self.assertIs(vk.default_hashfunc, hashlib.sha256) - - def test_from_pem_with_custom_hashfunc(self): - vk = VerifyingKey.from_pem(self.key_pem, hashlib.sha256) - - self.assertIs(vk.default_hashfunc, hashlib.sha256) - - def test_bytes(self): - vk = VerifyingKey.from_der(self.key_bytes) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytes_memoryview(self): - vk = VerifyingKey.from_der(buffer(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytearray(self): - vk = VerifyingKey.from_der(bytearray(self.key_bytes)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_bytesarray_memoryview(self): - vk = VerifyingKey.from_der(buffer(bytearray(self.key_bytes))) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_der(arr) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_array_array_of_bytes_memoryview(self): - arr = array.array("B", self.key_bytes) - vk = VerifyingKey.from_der(buffer(arr)) - - self.assertEqual(self.vk.to_string(), vk.to_string()) - - def test_equality_on_verifying_keys(self): - self.assertEqual(self.vk, self.sk.get_verifying_key()) - - def test_inequality_on_verifying_keys(self): - self.assertNotEqual(self.vk, self.vk2) - - def test_inequality_on_verifying_keys_not_implemented(self): - self.assertNotEqual(self.vk, None) - - def test_VerifyingKey_inequality_on_same_curve(self): - self.assertNotEqual(self.vk, self.sk2.verifying_key) - - def test_SigningKey_inequality_on_same_curve(self): - self.assertNotEqual(self.sk, self.sk2) - - def test_inequality_on_wrong_types(self): - self.assertNotEqual(self.vk, self.sk) - - def test_from_public_point_old(self): - pj = self.vk.pubkey.point - point = Point(pj.curve(), pj.x(), pj.y()) - - vk = VerifyingKey.from_public_point(point, self.vk.curve) - - self.assertEqual(vk, self.vk) - - def test_ed25519_VerifyingKey_repr__(self): - sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) - string = repr(sk.verifying_key) - - self.assertEqual( - "VerifyingKey.from_string(" - "bytearray(b'K\\x0c\\xfbZH\\x8e\\x8c\\x8c\\x07\\xee\\xda\\xfb" - "\\xe1\\x97\\xcd\\x90\\x18\\x02\\x15h]\\xfe\\xbe\\xcbB\\xba\\xe6r" - "\\x10\\xae\\xf1P'), Ed25519, None)", - string, - ) - - def test_edwards_from_public_point(self): - point = Ed25519.generator - with self.assertRaises(ValueError) as e: - VerifyingKey.from_public_point(point, Ed25519) - - self.assertIn("incompatible with Edwards", str(e.exception)) - - def test_edwards_precompute_no_side_effect(self): - sk = SigningKey.from_string(Ed25519.generator.to_bytes(), Ed25519) - vk = sk.verifying_key - vk2 = VerifyingKey.from_string(vk.to_string(), Ed25519) - vk.precompute() - - self.assertEqual(vk, vk2) - - def test_parse_malfomed_eddsa_der_pubkey(self): - der_str = encode_sequence( - encode_sequence(encode_oid(*Ed25519.oid)), - encode_bitstring(bytes(Ed25519.generator.to_bytes()), 0), - encode_bitstring(b"\x00", 0), - ) - - with self.assertRaises(UnexpectedDER) as e: - VerifyingKey.from_der(der_str) - - self.assertIn("trailing junk after public key", str(e.exception)) - - def test_edwards_from_public_key_recovery(self): - with self.assertRaises(ValueError) as e: - VerifyingKey.from_public_key_recovery(b"", b"", Ed25519) - - self.assertIn("unsupported for Edwards", str(e.exception)) - - def test_edwards_from_public_key_recovery_with_digest(self): - with self.assertRaises(ValueError) as e: - VerifyingKey.from_public_key_recovery_with_digest( - b"", b"", Ed25519 - ) - - self.assertIn("unsupported for Edwards", str(e.exception)) - - def test_load_ed25519_from_pem(self): - vk_pem = ( - "-----BEGIN PUBLIC KEY-----\n" - "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(vk_pem) - - self.assertIsInstance(vk.curve, Curve) - self.assertIs(vk.curve, Ed25519) - - vk_str = ( - b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" - b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" - ) - - vk_2 = VerifyingKey.from_string(vk_str, Ed25519) - - self.assertEqual(vk, vk_2) - - def test_export_ed255_to_pem(self): - vk_str = ( - b"\x23\x00\x50\xd0\xd6\x64\x22\x28\x8e\xe3\x55\x89\x7e\x6e\x41\x57" - b"\x8d\xae\xde\x44\x26\xee\x56\x27\xbc\x85\xe6\x0b\x2f\x2a\xcb\x65" - ) - - vk = VerifyingKey.from_string(vk_str, Ed25519) - - vk_pem = ( - b"-----BEGIN PUBLIC KEY-----\n" - b"MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - b"-----END PUBLIC KEY-----\n" - ) - - self.assertEqual(vk_pem, vk.to_pem()) - - def test_ed25519_export_import(self): - sk = SigningKey.generate(Ed25519) - vk = sk.verifying_key - - vk2 = VerifyingKey.from_pem(vk.to_pem()) - - self.assertEqual(vk, vk2) - - def test_ed25519_sig_verify(self): - vk_pem = ( - "-----BEGIN PUBLIC KEY-----\n" - "MCowBQYDK2VwAyEAIwBQ0NZkIiiO41WJfm5BV42u3kQm7lYnvIXmCy8qy2U=\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(vk_pem) - - data = b"data\n" - - # signature created by OpenSSL 3.0.0 beta1 - sig = ( - b"\x64\x47\xab\x6a\x33\xcd\x79\x45\xad\x98\x11\x6c\xb9\xf2\x20\xeb" - b"\x90\xd6\x50\xe3\xc7\x8f\x9f\x60\x10\xec\x75\xe0\x2f\x27\xd3\x96" - b"\xda\xe8\x58\x7f\xe0\xfe\x46\x5c\x81\xef\x50\xec\x29\x9f\xae\xd5" - b"\xad\x46\x3c\x91\x68\x83\x4d\xea\x8d\xa8\x19\x04\x04\x79\x03\x0b" - ) - - self.assertTrue(vk.verify(sig, data)) - - def test_ed448_from_pem(self): - pem_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" - "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(pem_str) - - self.assertIsInstance(vk.curve, Curve) - self.assertIs(vk.curve, Ed448) - - vk_str = ( - b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" - b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" - b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" - b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" - ) - - vk2 = VerifyingKey.from_string(vk_str, Ed448) - - self.assertEqual(vk, vk2) - - def test_ed448_to_pem(self): - vk_str = ( - b"\x79\x0b\x5e\xb5\x2b\xbb\x08\xc1\x33\x13\xe5\xd6\x07\x5d\x01\x83" - b"\x8e\xcb\x08\x0d\x20\x88\xd8\xa4\x3b\x11\xf3\x76\x9f\xad\x67\xf7" - b"\x8a\xfc\x49\xf4\x75\x37\x58\x0f\x69\x65\xf7\x88\x39\xf0\xc8\x52" - b"\x9c\x18\x81\x40\x1f\x40\xd4\xc3\x00" - ) - vk = VerifyingKey.from_string(vk_str, Ed448) - - vk_pem = ( - b"-----BEGIN PUBLIC KEY-----\n" - b"MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" - b"dTdYD2ll94g58MhSnBiBQB9A1MMA\n" - b"-----END PUBLIC KEY-----\n" - ) - - self.assertEqual(vk_pem, vk.to_pem()) - - def test_ed448_export_import(self): - sk = SigningKey.generate(Ed448) - vk = sk.verifying_key - - vk2 = VerifyingKey.from_pem(vk.to_pem()) - - self.assertEqual(vk, vk2) - - def test_ed448_sig_verify(self): - pem_str = ( - "-----BEGIN PUBLIC KEY-----\n" - "MEMwBQYDK2VxAzoAeQtetSu7CMEzE+XWB10Bg47LCA0giNikOxHzdp+tZ/eK/En0\n" - "dTdYD2ll94g58MhSnBiBQB9A1MMA\n" - "-----END PUBLIC KEY-----\n" - ) - - vk = VerifyingKey.from_pem(pem_str) - - data = b"data\n" - - # signature created by OpenSSL 3.0.0 beta1 - sig = ( - b"\x68\xed\x2c\x70\x35\x22\xca\x1c\x35\x03\xf3\xaa\x51\x33\x3d\x00" - b"\xc0\xae\xb0\x54\xc5\xdc\x7f\x6f\x30\x57\xb4\x1d\xcb\xe9\xec\xfa" - b"\xc8\x45\x3e\x51\xc1\xcb\x60\x02\x6a\xd0\x43\x11\x0b\x5f\x9b\xfa" - b"\x32\x88\xb2\x38\x6b\xed\xac\x09\x00\x78\xb1\x7b\x5d\x7e\xf8\x16" - b"\x31\xdd\x1b\x3f\x98\xa0\xce\x19\xe7\xd8\x1c\x9f\x30\xac\x2f\xd4" - b"\x1e\x55\xbf\x21\x98\xf6\x4c\x8c\xbe\x81\xa5\x2d\x80\x4c\x62\x53" - b"\x91\xd5\xee\x03\x30\xc6\x17\x66\x4b\x9e\x0c\x8d\x40\xd0\xad\xae" - b"\x0a\x00" - ) - - self.assertTrue(vk.verify(sig, data)) - - -class TestSigningKey(unittest.TestCase): - """ - Verify that ecdsa.keys.SigningKey.from_der() can be used with - bytes-like objects. - """ - - @classmethod - def setUpClass(cls): - prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" - ) - cls.sk1 = SigningKey.from_pem(prv_key_str) - - prv_key_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MG8CAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEVTBTAgEBBBheyEIL1u+SUqlC6YkE\n" - "PKKfVh+lJXcOscWhNAMyAAS4gXfQhO8X9eRWOUCAKDYPn1m0pNcmTmLaBlHc5Ho1\n" - "pMW0XPUVk0I6i1V7nCCZ82w=\n" - "-----END PRIVATE KEY-----\n" - ) - cls.sk1_pkcs8 = SigningKey.from_pem(prv_key_str) - - prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MHcCAQEEIKlL2EAm5NPPZuXwxRf4nXMk0A80y6UUbiQ17be/qFhRoAoGCCqGSM49\n" - "AwEHoUQDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8Poqzgjau4kfxBPyZimeRfuY/\n" - "9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" - "-----END EC PRIVATE KEY-----\n" - ) - cls.sk2 = SigningKey.from_pem(prv_key_str) - - def test_decoding_explicit_curve_parameters(self): - prv_key_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" - "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" - "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" - "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" - "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" - "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" - "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" - "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" - "-----END PRIVATE KEY-----\n" - ) - - sk = SigningKey.from_pem(prv_key_str) - - sk2 = SigningKey.from_string( - b"\x21\x7b\x51\x11\xf5\x26\x47\x5e\xab\x65\xb9\xaf\x0c\x60\xf6" - b"\x94\x01\x05\x3d\x96\xb6\x0c\xb3\xf2\xcf\x47\x33\x4a\x36\xb9" - b"\xf3\x20", - curve=NIST256p, - ) - - self.assertEqual(sk, sk2) - - def test_decoding_explicit_curve_parameters_with_explicit_disabled(self): - prv_key_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MIIBeQIBADCCAQMGByqGSM49AgEwgfcCAQEwLAYHKoZIzj0BAQIhAP////8AAAAB\n" - "AAAAAAAAAAAAAAAA////////////////MFsEIP////8AAAABAAAAAAAAAAAAAAAA\n" - "///////////////8BCBaxjXYqjqT57PrvVV2mIa8ZR0GsMxTsPY7zjw+J9JgSwMV\n" - "AMSdNgiG5wSTamZ44ROdJreBn36QBEEEaxfR8uEsQkf4vOblY6RA8ncDfYEt6zOg\n" - "9KE5RdiYwpZP40Li/hp/m47n60p8D54WK84zV2sxXs7LtkBoN79R9QIhAP////8A\n" - "AAAA//////////+85vqtpxeehPO5ysL8YyVRAgEBBG0wawIBAQQgIXtREfUmR16r\n" - "ZbmvDGD2lAEFPZa2DLPyz0czSja58yChRANCAASK9VJIGLOY5mxXO4rdf2CNl0//\n" - "yJWhIzDWX7daMI6qQWB9hKyR5OFOT6eFr1qtcZh8CGZbB+yIOCpnnwn0ekpl\n" - "-----END PRIVATE KEY-----\n" - ) - - with self.assertRaises(UnexpectedDER): - SigningKey.from_pem( - prv_key_str, valid_curve_encodings=["named_curve"] - ) - - def test_equality_on_signing_keys(self): - sk = SigningKey.from_secret_exponent( - self.sk1.privkey.secret_multiplier, self.sk1.curve - ) - self.assertEqual(self.sk1, sk) - self.assertEqual(self.sk1_pkcs8, sk) - - def test_verify_with_empty_message(self): - sig = self.sk1.sign(b"") - - self.assertTrue(sig) - - vk = self.sk1.verifying_key - - self.assertTrue(vk.verify(sig, b"")) - - def test_verify_with_precompute(self): - sig = self.sk1.sign(b"message") - - vk = self.sk1.verifying_key - - vk.precompute() - - self.assertTrue(vk.verify(sig, b"message")) - - def test_compare_verifying_key_with_precompute(self): - vk1 = self.sk1.verifying_key - vk1.precompute() - - vk2 = self.sk1_pkcs8.verifying_key - - self.assertEqual(vk1, vk2) - - def test_verify_with_lazy_precompute(self): - sig = self.sk2.sign(b"other message") - - vk = self.sk2.verifying_key - - vk.precompute(lazy=True) - - self.assertTrue(vk.verify(sig, b"other message")) - - def test_inequality_on_signing_keys(self): - self.assertNotEqual(self.sk1, self.sk2) - - def test_inequality_on_signing_keys_not_implemented(self): - self.assertNotEqual(self.sk1, None) - - def test_ed25519_from_pem(self): - pem_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" - "-----END PRIVATE KEY-----\n" - ) - - sk = SigningKey.from_pem(pem_str) - - sk_str = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - - self.assertEqual(sk, sk_str) - - def test_ed25519_to_pem(self): - sk = SigningKey.from_string( - b"\x34\xBA\xC7\xD1\x4E\xD4\xF1\xBC\x4F\x8C\x48\x3E\x0F\x19\x77\x4C" - b"\xFC\xB8\xBE\xAC\x54\x66\x45\x11\x9A\xD7\xD7\xB8\x07\x0B\xF5\xD4", - Ed25519, - ) - - pem_str = ( - b"-----BEGIN PRIVATE KEY-----\n" - b"MC4CAQAwBQYDK2VwBCIEIDS6x9FO1PG8T4xIPg8Zd0z8uL6sVGZFEZrX17gHC/XU\n" - b"-----END PRIVATE KEY-----\n" - ) - - self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) - - def test_ed25519_to_and_from_pem(self): - sk = SigningKey.generate(Ed25519) - - decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) - - self.assertEqual(sk, decoded) - - def test_ed448_from_pem(self): - pem_str = ( - "-----BEGIN PRIVATE KEY-----\n" - "MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" - "OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" - "-----END PRIVATE KEY-----\n" - ) - sk = SigningKey.from_pem(pem_str) - - sk_str = SigningKey.from_string( - b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" - b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" - b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" - b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", - Ed448, - ) - - self.assertEqual(sk, sk_str) - - def test_ed448_to_pem(self): - sk = SigningKey.from_string( - b"\x3C\x85\xB9\x7A\x85\x2D\x78\x09\x95\x5F\x2E\x0E\xA7\x0E\xC3\xD9" - b"\xC6\xE0\x8A\xB3\x2E\x26\x7F\x8B\x93\x5F\x04\x3A\x07\x3F\x39\x8F" - b"\x38\xFD\x09\x31\x86\x8B\x1A\x54\xF3\xC2\x8B\xE6\xBC\x20\xC9\xDB" - b"\x32\xDA\x7B\x3B\xBD\x34\xBC\xFD\x69", - Ed448, - ) - pem_str = ( - b"-----BEGIN PRIVATE KEY-----\n" - b"MEcCAQAwBQYDK2VxBDsEOTyFuXqFLXgJlV8uDqcOw9nG4IqzLiZ/i5NfBDoHPzmP\n" - b"OP0JMYaLGlTzwovmvCDJ2zLaezu9NLz9aQ==\n" - b"-----END PRIVATE KEY-----\n" - ) - - self.assertEqual(sk.to_pem(format="pkcs8"), pem_str) - - def test_ed448_encode_decode(self): - sk = SigningKey.generate(Ed448) - - decoded = SigningKey.from_pem(sk.to_pem(format="pkcs8")) - - self.assertEqual(decoded, sk) - - -class TestTrivialCurve(unittest.TestCase): - @classmethod - def setUpClass(cls): - # To test what happens with r or s in signing happens to be zero we - # need to find a scalar that creates one of the points on a curve that - # has x coordinate equal to zero. - # Even for secp112r2 curve that's non trivial so use this toy - # curve, for which we can iterate over all points quickly - curve = CurveFp(163, 84, 58) - gen = PointJacobi(curve, 2, 87, 1, 167, generator=True) - - cls.toy_curve = Curve("toy_p8", curve, gen, (1, 2, 0)) - - cls.sk = SigningKey.from_secret_exponent( - 140, - cls.toy_curve, - hashfunc=hashlib.sha1, - ) - - def test_generator_sanity(self): - gen = self.toy_curve.generator - - self.assertEqual(gen * gen.order(), INFINITY) - - def test_public_key_sanity(self): - self.assertEqual(self.sk.verifying_key.to_string(), b"\x98\x1e") - - def test_deterministic_sign(self): - sig = self.sk.sign_deterministic(b"message") - - self.assertEqual(sig, b"-.") - - self.assertTrue(self.sk.verifying_key.verify(sig, b"message")) - - def test_deterministic_sign_random_message(self): - msg = os.urandom(32) - sig = self.sk.sign_deterministic(msg) - self.assertEqual(len(sig), 2) - self.assertTrue(self.sk.verifying_key.verify(sig, msg)) - - def test_deterministic_sign_that_rises_R_zero_error(self): - # the raised RSZeroError is caught and handled internally by - # sign_deterministic methods - msg = b"\x00\x4f" - sig = self.sk.sign_deterministic(msg) - self.assertEqual(sig, b"\x36\x9e") - self.assertTrue(self.sk.verifying_key.verify(sig, msg)) - - def test_deterministic_sign_that_rises_S_zero_error(self): - msg = b"\x01\x6d" - sig = self.sk.sign_deterministic(msg) - self.assertEqual(sig, b"\x49\x6c") - self.assertTrue(self.sk.verifying_key.verify(sig, msg)) - - -# test VerifyingKey.verify() -prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" -) -key_bytes = unpem(prv_key_str) -assert isinstance(key_bytes, bytes) -sk = SigningKey.from_der(key_bytes) -vk = sk.verifying_key - -data = ( - b"some string for signing" - b"contents don't really matter" - b"but do include also some crazy values: " - b"\x00\x01\t\r\n\x00\x00\x00\xff\xf0" -) -assert len(data) % 4 == 0 -sha1 = hashlib.sha1() -sha1.update(data) -data_hash = sha1.digest() -assert isinstance(data_hash, bytes) -sig_raw = sk.sign(data, sigencode=sigencode_string) -assert isinstance(sig_raw, bytes) -sig_der = sk.sign(data, sigencode=sigencode_der) -assert isinstance(sig_der, bytes) -sig_strings = sk.sign(data, sigencode=sigencode_strings) -assert isinstance(sig_strings[0], bytes) - -verifiers = [] -for modifier, fun in [ - ("bytes", lambda x: x), - ("bytes memoryview", lambda x: buffer(x)), - ("bytearray", lambda x: bytearray(x)), - ("bytearray memoryview", lambda x: buffer(bytearray(x))), - ("array.array of bytes", lambda x: array.array("B", x)), - ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), - ("array.array of ints", lambda x: array.array("I", x)), - ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), -]: - if "ints" in modifier: - conv = lambda x: x - else: - conv = fun - for sig_format, signature, decoder, mod_apply in [ - ("raw", sig_raw, sigdecode_string, lambda x: conv(x)), - ("der", sig_der, sigdecode_der, lambda x: conv(x)), - ( - "strings", - sig_strings, - sigdecode_strings, - lambda x: tuple(conv(i) for i in x), - ), - ]: - for method_name, vrf_mthd, vrf_data in [ - ("verify", vk.verify, data), - ("verify_digest", vk.verify_digest, data_hash), - ]: - verifiers.append( - pytest.param( - signature, - decoder, - mod_apply, - fun, - vrf_mthd, - vrf_data, - id="{2}-{0}-{1}".format(modifier, sig_format, method_name), - ) - ) - - -@pytest.mark.parametrize( - "signature,decoder,mod_apply,fun,vrf_mthd,vrf_data", verifiers -) -def test_VerifyingKey_verify( - signature, decoder, mod_apply, fun, vrf_mthd, vrf_data -): - sig = mod_apply(signature) - - assert vrf_mthd(sig, fun(vrf_data), sigdecode=decoder) - - -# test SigningKey.from_string() -prv_key_bytes = ( - b"^\xc8B\x0b\xd6\xef\x92R\xa9B\xe9\x89\x04<\xa2" - b"\x9fV\x1f\xa5%w\x0e\xb1\xc5" -) -assert len(prv_key_bytes) == 24 -converters = [] -for modifier, convert in [ - ("bytes", lambda x: x), - ("bytes memoryview", buffer), - ("bytearray", bytearray), - ("bytearray memoryview", lambda x: buffer(bytearray(x))), - ("array.array of bytes", lambda x: array.array("B", x)), - ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), - ("array.array of ints", lambda x: array.array("I", x)), - ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), -]: - converters.append(pytest.param(convert, id=modifier)) - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_from_string(convert): - key = convert(prv_key_bytes) - sk = SigningKey.from_string(key) - - assert sk.to_string() == prv_key_bytes - - -# test SigningKey.from_der() -prv_key_str = ( - "-----BEGIN EC PRIVATE KEY-----\n" - "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" - "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" - "bA==\n" - "-----END EC PRIVATE KEY-----\n" -) -key_bytes = unpem(prv_key_str) -assert isinstance(key_bytes, bytes) - -# last two converters are for array.array of ints, those require input -# that's multiple of 4, which no curve we support produces -@pytest.mark.parametrize("convert", converters[:-2]) -def test_SigningKey_from_der(convert): - key = convert(key_bytes) - sk = SigningKey.from_der(key) - - assert sk.to_string() == prv_key_bytes - - -# test SigningKey.sign_deterministic() -extra_entropy = b"\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11" - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign_deterministic(convert): - sig = sk.sign_deterministic( - convert(data), extra_entropy=convert(extra_entropy) - ) - - vk.verify(sig, data) - - -# test SigningKey.sign_digest_deterministic() -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign_digest_deterministic(convert): - sig = sk.sign_digest_deterministic( - convert(data_hash), extra_entropy=convert(extra_entropy) - ) - - vk.verify(sig, data) - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign(convert): - sig = sk.sign(convert(data)) - - vk.verify(sig, data) - - -@pytest.mark.parametrize("convert", converters) -def test_SigningKey_sign_digest(convert): - sig = sk.sign_digest(convert(data_hash)) - - vk.verify(sig, data) - - -def test_SigningKey_with_unlikely_value(): - sk = SigningKey.from_secret_exponent(NIST256p.order - 1, curve=NIST256p) - vk = sk.verifying_key - sig = sk.sign(b"hello") - assert vk.verify(sig, b"hello") - - -def test_SigningKey_with_custom_curve_old_point(): - generator = generator_brainpoolp160r1 - generator = Point( - generator.curve(), - generator.x(), - generator.y(), - generator.order(), - ) - - curve = Curve( - "BRAINPOOLP160r1", - generator.curve(), - generator, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), - ) - - sk = SigningKey.from_secret_exponent(12, curve) - - sk2 = SigningKey.from_secret_exponent(12, BRAINPOOLP160r1) - - assert sk.privkey == sk2.privkey - - -def test_VerifyingKey_inequality_with_different_curves(): - sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) - sk2 = SigningKey.from_secret_exponent(2, NIST256p) - - assert sk1.verifying_key != sk2.verifying_key - - -def test_VerifyingKey_inequality_with_different_secret_points(): - sk1 = SigningKey.from_secret_exponent(2, BRAINPOOLP160r1) - sk2 = SigningKey.from_secret_exponent(3, BRAINPOOLP160r1) - - assert sk1.verifying_key != sk2.verifying_key - - -def test_SigningKey_from_pem_pkcs8v2_EdDSA(): - pem = """-----BEGIN PRIVATE KEY----- - MFMCAQEwBQYDK2VwBCIEICc2F2ag1n1QP0jY+g9qWx5sDkx0s/HdNi3cSRHw+zsI - oSMDIQA+HQ2xCif8a/LMWR2m5HaCm5I2pKe/cc8OiRANMHxjKQ== - -----END PRIVATE KEY-----""" - - sk = SigningKey.from_pem(pem) - assert sk.curve == Ed25519 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py deleted file mode 100644 index 8e1b611d1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_malformed_sigs.py +++ /dev/null @@ -1,370 +0,0 @@ -from __future__ import with_statement, division - -import hashlib - -try: - from hashlib import algorithms_available -except ImportError: # pragma: no cover - algorithms_available = [ - "md5", - "sha1", - "sha224", - "sha256", - "sha384", - "sha512", - ] -# skip algorithms broken by change to OpenSSL 3.0 and early versions -# of hashlib that list algorithms that require the legacy provider to work -# https://bugs.python.org/issue38820 -algorithms_available = [ - i - for i in algorithms_available - if i not in ("mdc2", "md2", "md4", "whirlpool", "ripemd160") -] -from functools import partial -import pytest -import sys -import hypothesis.strategies as st -from hypothesis import note, assume, given, settings, example - -from .keys import SigningKey -from .keys import BadSignatureError -from .util import sigencode_der, sigencode_string -from .util import sigdecode_der, sigdecode_string -from .curves import curves -from .der import ( - encode_integer, - encode_bitstring, - encode_octet_string, - encode_oid, - encode_sequence, - encode_constructed, -) -from .ellipticcurve import CurveEdTw - - -example_data = b"some data to sign" -"""Since the data is hashed for processing, really any string will do.""" - - -hash_and_size = [ - (name, hashlib.new(name).digest_size) for name in algorithms_available -] -"""Pairs of hash names and their output sizes. -Needed for pairing with curves as we don't support hashes -bigger than order sizes of curves.""" - - -keys_and_sigs = [] -"""Name of the curve+hash combination, VerifyingKey and DER signature.""" - - -# for hypothesis strategy shrinking we want smallest curves and hashes first -for curve in sorted(curves, key=lambda x: x.baselen): - for hash_alg in [ - name - for name, size in sorted(hash_and_size, key=lambda x: x[1]) - if 0 < size <= curve.baselen - ]: - sk = SigningKey.generate( - curve, hashfunc=partial(hashlib.new, hash_alg) - ) - - keys_and_sigs.append( - ( - "{0} {1}".format(curve, hash_alg), - sk.verifying_key, - sk.sign(example_data, sigencode=sigencode_der), - ) - ) - - -# first make sure that the signatures can be verified -@pytest.mark.parametrize( - "verifying_key,signature", - [pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs], -) -def test_signatures(verifying_key, signature): - assert verifying_key.verify( - signature, example_data, sigdecode=sigdecode_der - ) - - -@st.composite -def st_fuzzed_sig(draw, keys_and_sigs): - """ - Hypothesis strategy that generates pairs of VerifyingKey and malformed - signatures created by fuzzing of a valid signature. - """ - name, verifying_key, old_sig = draw(st.sampled_from(keys_and_sigs)) - note("Configuration: {0}".format(name)) - - sig = bytearray(old_sig) - - # decide which bytes should be removed - to_remove = draw( - st.lists(st.integers(min_value=0, max_value=len(sig) - 1), unique=True) - ) - to_remove.sort() - for i in reversed(to_remove): - del sig[i] - note("Remove bytes: {0}".format(to_remove)) - - # decide which bytes of the original signature should be changed - if sig: # pragma: no branch - xors = draw( - st.dictionaries( - st.integers(min_value=0, max_value=len(sig) - 1), - st.integers(min_value=1, max_value=255), - ) - ) - for i, val in xors.items(): - sig[i] ^= val - note("xors: {0}".format(xors)) - - # decide where new data should be inserted - insert_pos = draw(st.integers(min_value=0, max_value=len(sig))) - # NIST521p signature is about 140 bytes long, test slightly longer - insert_data = draw(st.binary(max_size=256)) - - sig = sig[:insert_pos] + insert_data + sig[insert_pos:] - note( - "Inserted at position {0} bytes: {1!r}".format(insert_pos, insert_data) - ) - - sig = bytes(sig) - # make sure that there was performed at least one mutation on the data - assume(to_remove or xors or insert_data) - # and that the mutations didn't cancel each-other out - assume(sig != old_sig) - - return verifying_key, sig - - -params = {} -# not supported in hypothesis 2.0.0 -if sys.version_info >= (2, 7): # pragma: no branch - from hypothesis import HealthCheck - - # deadline=5s because NIST521p are slow to verify - params["deadline"] = 5000 - params["suppress_health_check"] = [ - HealthCheck.data_too_large, - HealthCheck.filter_too_much, - HealthCheck.too_slow, - ] - -slow_params = dict(params) -slow_params["max_examples"] = 10 - - -@settings(**params) -@given(st_fuzzed_sig(keys_and_sigs)) -def test_fuzzed_der_signatures(args): - verifying_key, sig = args - - with pytest.raises(BadSignatureError): - verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) - - -@st.composite -def st_random_der_ecdsa_sig_value(draw): - """ - Hypothesis strategy for selecting random values and encoding them - to ECDSA-Sig-Value object:: - - ECDSA-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - """ - name, verifying_key, _ = draw(st.sampled_from(keys_and_sigs)) - note("Configuration: {0}".format(name)) - order = int(verifying_key.curve.order) - - # the encode_integer doesn't support negative numbers, would be nice - # to generate them too, but we have coverage for remove_integer() - # verifying that it doesn't accept them, so meh. - # Test all numbers around the ones that can show up (around order) - # way smaller and slightly bigger - r = draw( - st.integers(min_value=0, max_value=order << 4) - | st.integers(min_value=order >> 2, max_value=order + 1) - ) - s = draw( - st.integers(min_value=0, max_value=order << 4) - | st.integers(min_value=order >> 2, max_value=order + 1) - ) - - sig = encode_sequence(encode_integer(r), encode_integer(s)) - - return verifying_key, sig - - -@settings(**slow_params) -@given(st_random_der_ecdsa_sig_value()) -def test_random_der_ecdsa_sig_value(params): - """ - Check if random values encoded in ECDSA-Sig-Value structure are rejected - as signature. - """ - verifying_key, sig = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(sig, example_data, sigdecode=sigdecode_der) - - -def st_der_integer(*args, **kwargs): - """ - Hypothesis strategy that returns a random positive integer as DER - INTEGER. - Parameters are passed to hypothesis.strategy.integer. - """ - if "min_value" not in kwargs: # pragma: no branch - kwargs["min_value"] = 0 - return st.builds(encode_integer, st.integers(*args, **kwargs)) - - -@st.composite -def st_der_bit_string(draw, *args, **kwargs): - """ - Hypothesis strategy that returns a random DER BIT STRING. - Parameters are passed to hypothesis.strategy.binary. - """ - data = draw(st.binary(*args, **kwargs)) - if data: - unused = draw(st.integers(min_value=0, max_value=7)) - data = bytearray(data) - data[-1] &= -(2**unused) - data = bytes(data) - else: - unused = 0 - return encode_bitstring(data, unused) - - -def st_der_octet_string(*args, **kwargs): - """ - Hypothesis strategy that returns a random DER OCTET STRING object. - Parameters are passed to hypothesis.strategy.binary - """ - return st.builds(encode_octet_string, st.binary(*args, **kwargs)) - - -def st_der_null(): - """ - Hypothesis strategy that returns DER NULL object. - """ - return st.just(b"\x05\x00") - - -@st.composite -def st_der_oid(draw): - """ - Hypothesis strategy that returns DER OBJECT IDENTIFIER objects. - """ - first = draw(st.integers(min_value=0, max_value=2)) - if first < 2: - second = draw(st.integers(min_value=0, max_value=39)) - else: - second = draw(st.integers(min_value=0, max_value=2**512)) - rest = draw( - st.lists(st.integers(min_value=0, max_value=2**512), max_size=50) - ) - return encode_oid(first, second, *rest) - - -def st_der(): - """ - Hypothesis strategy that returns random DER structures. - - A valid DER structure is any primitive object, an octet encoding - of a valid DER structure, sequence of valid DER objects or a constructed - encoding of any of the above. - """ - return st.recursive( - st.just(b"") - | st_der_integer(max_value=2**4096) - | st_der_bit_string(max_size=1024**2) - | st_der_octet_string(max_size=1024**2) - | st_der_null() - | st_der_oid(), - lambda children: st.builds( - lambda x: encode_octet_string(x), st.one_of(children) - ) - | st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children)) - | st.builds( - lambda x: encode_sequence(*x), st.lists(children, max_size=200) - ) - | st.builds( - lambda tag, x: encode_constructed(tag, x), - st.integers(min_value=0, max_value=0x3F), - st.one_of(children), - ), - max_leaves=40, - ) - - -@settings(**params) -@given(st.sampled_from(keys_and_sigs), st_der()) -def test_random_der_as_signature(params, der): - """Check if random DER structures are rejected as signature""" - name, verifying_key, _ = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(der, example_data, sigdecode=sigdecode_der) - - -@settings(**params) -@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) -@example( - keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) -) -@example( - keys_and_sigs[0], - encode_sequence(encode_integer(1), encode_integer(1)) + b"\x00", -) -@example(keys_and_sigs[0], encode_sequence(*[encode_integer(1)] * 3)) -def test_random_bytes_as_signature(params, der): - """Check if random bytes are rejected as signature""" - name, verifying_key, _ = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(der, example_data, sigdecode=sigdecode_der) - - -keys_and_string_sigs = [ - ( - name, - verifying_key, - sigencode_string( - *sigdecode_der(sig, verifying_key.curve.order), - order=verifying_key.curve.order - ), - ) - for name, verifying_key, sig in keys_and_sigs - if not isinstance(verifying_key.curve.curve, CurveEdTw) -] -""" -Name of the curve+hash combination, VerifyingKey and signature as a -byte string. -""" - - -keys_and_string_sigs += [ - ( - name, - verifying_key, - sig, - ) - for name, verifying_key, sig in keys_and_sigs - if isinstance(verifying_key.curve.curve, CurveEdTw) -] - - -@settings(**params) -@given(st_fuzzed_sig(keys_and_string_sigs)) -def test_fuzzed_string_signatures(params): - verifying_key, sig = params - - with pytest.raises(BadSignatureError): - verifying_key.verify(sig, example_data, sigdecode=sigdecode_string) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py deleted file mode 100644 index 8bc787f1c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_numbertheory.py +++ /dev/null @@ -1,433 +0,0 @@ -import operator -from functools import reduce - -try: - import unittest2 as unittest -except ImportError: - import unittest -import hypothesis.strategies as st -import pytest -from hypothesis import given, settings, example - -try: - from hypothesis import HealthCheck - - HC_PRESENT = True -except ImportError: # pragma: no cover - HC_PRESENT = False -from .numbertheory import ( - SquareRootError, - JacobiError, - factorization, - gcd, - lcm, - jacobi, - inverse_mod, - is_prime, - next_prime, - smallprimes, - square_root_mod_prime, -) - - -BIGPRIMES = ( - 999671, - 999683, - 999721, - 999727, - 999749, - 999763, - 999769, - 999773, - 999809, - 999853, - 999863, - 999883, - 999907, - 999917, - 999931, - 999953, - 999959, - 999961, - 999979, - 999983, -) - - -@pytest.mark.parametrize( - "prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])] -) -def test_next_prime(prime, next_p): - assert next_prime(prime) == next_p - - -@pytest.mark.parametrize("val", [-1, 0, 1]) -def test_next_prime_with_nums_less_2(val): - assert next_prime(val) == 2 - - -@pytest.mark.parametrize("prime", smallprimes) -def test_square_root_mod_prime_for_small_primes(prime): - squares = set() - for num in range(0, 1 + prime // 2): - sq = num * num % prime - squares.add(sq) - root = square_root_mod_prime(sq, prime) - # tested for real with TestNumbertheory.test_square_root_mod_prime - assert root * root % prime == sq - - for nonsquare in range(0, prime): - if nonsquare in squares: - continue - with pytest.raises(SquareRootError): - square_root_mod_prime(nonsquare, prime) - - -def test_square_root_mod_prime_for_2(): - a = square_root_mod_prime(1, 2) - assert a == 1 - - -def test_square_root_mod_prime_for_small_prime(): - root = square_root_mod_prime(98**2 % 101, 101) - assert root * root % 101 == 9 - - -def test_square_root_mod_prime_for_p_congruent_5(): - p = 13 - assert p % 8 == 5 - - root = square_root_mod_prime(3, p) - assert root * root % p == 3 - - -def test_square_root_mod_prime_for_p_congruent_5_large_d(): - p = 29 - assert p % 8 == 5 - - root = square_root_mod_prime(4, p) - assert root * root % p == 4 - - -class TestSquareRootModPrime(unittest.TestCase): - def test_power_of_2_p(self): - with self.assertRaises(JacobiError): - square_root_mod_prime(12, 32) - - def test_no_square(self): - with self.assertRaises(SquareRootError) as e: - square_root_mod_prime(12, 31) - - self.assertIn("no square root", str(e.exception)) - - def test_non_prime(self): - with self.assertRaises(SquareRootError) as e: - square_root_mod_prime(12, 33) - - self.assertIn("p is not prime", str(e.exception)) - - def test_non_prime_with_negative(self): - with self.assertRaises(SquareRootError) as e: - square_root_mod_prime(697 - 1, 697) - - self.assertIn("p is not prime", str(e.exception)) - - -@st.composite -def st_two_nums_rel_prime(draw): - # 521-bit is the biggest curve we operate on, use 1024 for a bit - # of breathing space - mod = draw(st.integers(min_value=2, max_value=2**1024)) - num = draw( - st.integers(min_value=1, max_value=mod - 1).filter( - lambda x: gcd(x, mod) == 1 - ) - ) - return num, mod - - -@st.composite -def st_primes(draw, *args, **kwargs): - if "min_value" not in kwargs: # pragma: no branch - kwargs["min_value"] = 1 - prime = draw( - st.sampled_from(smallprimes) - | st.integers(*args, **kwargs).filter(is_prime) - ) - return prime - - -@st.composite -def st_num_square_prime(draw): - prime = draw(st_primes(max_value=2**1024)) - num = draw(st.integers(min_value=0, max_value=1 + prime // 2)) - sq = num * num % prime - return sq, prime - - -@st.composite -def st_comp_with_com_fac(draw): - """ - Strategy that returns lists of numbers, all having a common factor. - """ - primes = draw( - st.lists(st_primes(max_value=2**512), min_size=1, max_size=10) - ) - # select random prime(s) that will make the common factor of composites - com_fac_primes = draw( - st.lists(st.sampled_from(primes), min_size=1, max_size=20) - ) - com_fac = reduce(operator.mul, com_fac_primes, 1) - - # select at most 20 lists (returned numbers), - # each having at most 30 primes (factors) including none (then the number - # will be 1) - comp_primes = draw( - st.integers(min_value=1, max_value=20).flatmap( - lambda n: st.lists( - st.lists(st.sampled_from(primes), max_size=30), - min_size=1, - max_size=n, - ) - ) - ) - - return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes] - - -@st.composite -def st_comp_no_com_fac(draw): - """ - Strategy that returns lists of numbers that don't have a common factor. - """ - primes = draw( - st.lists( - st_primes(max_value=2**512), min_size=2, max_size=10, unique=True - ) - ) - # first select the primes that will create the uncommon factor - # between returned numbers - uncom_fac_primes = draw( - st.lists( - st.sampled_from(primes), - min_size=1, - max_size=len(primes) - 1, - unique=True, - ) - ) - uncom_fac = reduce(operator.mul, uncom_fac_primes, 1) - - # then build composites from leftover primes - leftover_primes = [i for i in primes if i not in uncom_fac_primes] - - assert leftover_primes - assert uncom_fac_primes - - # select at most 20 lists, each having at most 30 primes - # selected from the leftover_primes list - number_primes = draw( - st.integers(min_value=1, max_value=20).flatmap( - lambda n: st.lists( - st.lists(st.sampled_from(leftover_primes), max_size=30), - min_size=1, - max_size=n, - ) - ) - ) - - numbers = [reduce(operator.mul, nums, 1) for nums in number_primes] - - insert_at = draw(st.integers(min_value=0, max_value=len(numbers))) - numbers.insert(insert_at, uncom_fac) - return numbers - - -HYP_SETTINGS = {} -if HC_PRESENT: # pragma: no branch - HYP_SETTINGS["suppress_health_check"] = [ - HealthCheck.filter_too_much, - HealthCheck.too_slow, - ] - # the factorization() sometimes takes a long time to finish - HYP_SETTINGS["deadline"] = 5000 - - -HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) -HYP_SLOW_SETTINGS["max_examples"] = 10 - - -class TestIsPrime(unittest.TestCase): - def test_very_small_prime(self): - assert is_prime(23) - - def test_very_small_composite(self): - assert not is_prime(22) - - def test_small_prime(self): - assert is_prime(123456791) - - def test_special_composite(self): - assert not is_prime(10261) - - def test_medium_prime_1(self): - # nextPrime[2^256] - assert is_prime(2**256 + 0x129) - - def test_medium_prime_2(self): - # nextPrime(2^256+0x129) - assert is_prime(2**256 + 0x12D) - - def test_medium_trivial_composite(self): - assert not is_prime(2**256 + 0x130) - - def test_medium_non_trivial_composite(self): - assert not is_prime(2**256 + 0x12F) - - def test_large_prime(self): - # nextPrime[2^2048] - assert is_prime(2**2048 + 0x3D5) - - -class TestNumbertheory(unittest.TestCase): - def test_gcd(self): - assert gcd(3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13) == 3 * 5 - assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5 - assert gcd(3) == 3 - - @unittest.skipUnless( - HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it", - ) - @settings(**HYP_SLOW_SETTINGS) - @given(st_comp_with_com_fac()) - def test_gcd_with_com_factor(self, numbers): - n = gcd(numbers) - assert 1 in numbers or n != 1 - for i in numbers: - assert i % n == 0 - - @unittest.skipUnless( - HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it", - ) - @settings(**HYP_SLOW_SETTINGS) - @given(st_comp_no_com_fac()) - def test_gcd_with_uncom_factor(self, numbers): - n = gcd(numbers) - assert n == 1 - - @given( - st.lists( - st.integers(min_value=1, max_value=2**8192), - min_size=1, - max_size=20, - ) - ) - def test_gcd_with_random_numbers(self, numbers): - n = gcd(numbers) - for i in numbers: - # check that at least it's a divider - assert i % n == 0 - - def test_lcm(self): - assert lcm(3, 5 * 3, 7 * 3) == 3 * 5 * 7 - assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7 - assert lcm(3) == 3 - - @given( - st.lists( - st.integers(min_value=1, max_value=2**8192), - min_size=1, - max_size=20, - ) - ) - def test_lcm_with_random_numbers(self, numbers): - n = lcm(numbers) - for i in numbers: - assert n % i == 0 - - @unittest.skipUnless( - HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it", - ) - @settings(**HYP_SETTINGS) - @given(st_num_square_prime()) - def test_square_root_mod_prime(self, vals): - square, prime = vals - - calc = square_root_mod_prime(square, prime) - assert calc * calc % prime == square - - @settings(**HYP_SETTINGS) - @given(st.integers(min_value=1, max_value=10**12)) - @example(265399 * 1526929) - @example(373297**2 * 553991) - def test_factorization(self, num): - factors = factorization(num) - mult = 1 - for i in factors: - mult *= i[0] ** i[1] - assert mult == num - - def test_factorisation_smallprimes(self): - exp = 101 * 103 - assert 101 in smallprimes - assert 103 in smallprimes - factors = factorization(exp) - mult = 1 - for i in factors: - mult *= i[0] ** i[1] - assert mult == exp - - def test_factorisation_not_smallprimes(self): - exp = 1231 * 1237 - assert 1231 not in smallprimes - assert 1237 not in smallprimes - factors = factorization(exp) - mult = 1 - for i in factors: - mult *= i[0] ** i[1] - assert mult == exp - - def test_jacobi_with_zero(self): - assert jacobi(0, 3) == 0 - - def test_jacobi_with_one(self): - assert jacobi(1, 3) == 1 - - @settings(**HYP_SETTINGS) - @given(st.integers(min_value=3, max_value=1000).filter(lambda x: x % 2)) - def test_jacobi(self, mod): - if is_prime(mod): - squares = set() - for root in range(1, mod): - assert jacobi(root * root, mod) == 1 - squares.add(root * root % mod) - for i in range(1, mod): - if i not in squares: - assert jacobi(i, mod) == -1 - else: - factors = factorization(mod) - for a in range(1, mod): - c = 1 - for i in factors: - c *= jacobi(a, i[0]) ** i[1] - assert c == jacobi(a, mod) - - @given(st_two_nums_rel_prime()) - def test_inverse_mod(self, nums): - num, mod = nums - - inv = inverse_mod(num, mod) - - assert 0 < inv < mod - assert num * inv % mod == 1 - - def test_inverse_mod_with_zero(self): - assert 0 == inverse_mod(0, 11) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py deleted file mode 100644 index d61f50838..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_pyecdsa.py +++ /dev/null @@ -1,2267 +0,0 @@ -from __future__ import with_statement, division - -try: - import unittest2 as unittest -except ImportError: - import unittest -import os -import sys -import shutil -import subprocess -import pytest -from binascii import hexlify, unhexlify -from hashlib import sha1, sha256, sha384, sha512 -import hashlib -from functools import partial - -from hypothesis import given -import hypothesis.strategies as st - -from six import b, print_, binary_type -from .keys import SigningKey, VerifyingKey -from .keys import BadSignatureError, MalformedPointError, BadDigestError -from . import util -from .util import sigencode_der, sigencode_strings -from .util import sigdecode_der, sigdecode_strings -from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature -from .curves import Curve, UnknownCurveError -from .curves import ( - SECP112r1, - SECP112r2, - SECP128r1, - SECP160r1, - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - SECP256k1, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - Ed25519, - Ed448, - curves, -) -from .ecdsa import ( - curve_brainpoolp224r1, - curve_brainpoolp256r1, - curve_brainpoolp384r1, - curve_brainpoolp512r1, -) -from .ellipticcurve import Point -from . import der -from . import rfc6979 -from . import ecdsa - - -class SubprocessError(Exception): - pass - - -def run_openssl(cmd): - OPENSSL = "openssl" - p = subprocess.Popen( - [OPENSSL] + cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - stdout, ignored = p.communicate() - if p.returncode != 0: - raise SubprocessError( - "cmd '%s %s' failed: rc=%s, stdout/err was %s" - % (OPENSSL, cmd, p.returncode, stdout) - ) - return stdout.decode() - - -class ECDSA(unittest.TestCase): - def test_basic(self): - priv = SigningKey.generate() - pub = priv.get_verifying_key() - - data = b("blahblah") - sig = priv.sign(data) - - self.assertTrue(pub.verify(sig, data)) - self.assertRaises(BadSignatureError, pub.verify, sig, data + b("bad")) - - pub2 = VerifyingKey.from_string(pub.to_string()) - self.assertTrue(pub2.verify(sig, data)) - - def test_deterministic(self): - data = b("blahblah") - secexp = int("9d0219792467d7d37b4d43298a7d0c05", 16) - - priv = SigningKey.from_secret_exponent(secexp, SECP256k1, sha256) - pub = priv.get_verifying_key() - - k = rfc6979.generate_k( - SECP256k1.generator.order(), secexp, sha256, sha256(data).digest() - ) - - sig1 = priv.sign(data, k=k) - self.assertTrue(pub.verify(sig1, data)) - - sig2 = priv.sign(data, k=k) - self.assertTrue(pub.verify(sig2, data)) - - sig3 = priv.sign_deterministic(data, sha256) - self.assertTrue(pub.verify(sig3, data)) - - self.assertEqual(sig1, sig2) - self.assertEqual(sig1, sig3) - - def test_bad_usage(self): - # sk=SigningKey() is wrong - self.assertRaises(TypeError, SigningKey) - self.assertRaises(TypeError, VerifyingKey) - - def test_lengths(self): - default = NIST192p - priv = SigningKey.generate() - pub = priv.get_verifying_key() - self.assertEqual(len(pub.to_string()), default.verifying_key_length) - sig = priv.sign(b("data")) - self.assertEqual(len(sig), default.signature_length) - for curve in ( - NIST192p, - NIST224p, - NIST256p, - NIST384p, - NIST521p, - BRAINPOOLP160r1, - BRAINPOOLP192r1, - BRAINPOOLP224r1, - BRAINPOOLP256r1, - BRAINPOOLP320r1, - BRAINPOOLP384r1, - BRAINPOOLP512r1, - ): - priv = SigningKey.generate(curve=curve) - pub1 = priv.get_verifying_key() - pub2 = VerifyingKey.from_string(pub1.to_string(), curve) - self.assertEqual(pub1.to_string(), pub2.to_string()) - self.assertEqual(len(pub1.to_string()), curve.verifying_key_length) - sig = priv.sign(b("data")) - self.assertEqual(len(sig), curve.signature_length) - - def test_serialize(self): - seed = b("secret") - curve = NIST192p - secexp1 = util.randrange_from_seed__trytryagain(seed, curve.order) - secexp2 = util.randrange_from_seed__trytryagain(seed, curve.order) - self.assertEqual(secexp1, secexp2) - priv1 = SigningKey.from_secret_exponent(secexp1, curve) - priv2 = SigningKey.from_secret_exponent(secexp2, curve) - self.assertEqual( - hexlify(priv1.to_string()), hexlify(priv2.to_string()) - ) - self.assertEqual(priv1.to_pem(), priv2.to_pem()) - pub1 = priv1.get_verifying_key() - pub2 = priv2.get_verifying_key() - data = b("data") - sig1 = priv1.sign(data) - sig2 = priv2.sign(data) - self.assertTrue(pub1.verify(sig1, data)) - self.assertTrue(pub2.verify(sig1, data)) - self.assertTrue(pub1.verify(sig2, data)) - self.assertTrue(pub2.verify(sig2, data)) - self.assertEqual(hexlify(pub1.to_string()), hexlify(pub2.to_string())) - - def test_nonrandom(self): - s = b("all the entropy in the entire world, compressed into one line") - - def not_much_entropy(numbytes): - return s[:numbytes] - - # we control the entropy source, these two keys should be identical: - priv1 = SigningKey.generate(entropy=not_much_entropy) - priv2 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual( - hexlify(priv1.get_verifying_key().to_string()), - hexlify(priv2.get_verifying_key().to_string()), - ) - # likewise, signatures should be identical. Obviously you'd never - # want to do this with keys you care about, because the secrecy of - # the private key depends upon using different random numbers for - # each signature - sig1 = priv1.sign(b("data"), entropy=not_much_entropy) - sig2 = priv2.sign(b("data"), entropy=not_much_entropy) - self.assertEqual(hexlify(sig1), hexlify(sig2)) - - def assertTruePrivkeysEqual(self, priv1, priv2): - self.assertEqual( - priv1.privkey.secret_multiplier, priv2.privkey.secret_multiplier - ) - self.assertEqual( - priv1.privkey.public_key.generator, - priv2.privkey.public_key.generator, - ) - - def test_privkey_creation(self): - s = b("all the entropy in the entire world, compressed into one line") - - def not_much_entropy(numbytes): - return s[:numbytes] - - priv1 = SigningKey.generate() - self.assertEqual(priv1.baselen, NIST192p.baselen) - - priv1 = SigningKey.generate(curve=NIST224p) - self.assertEqual(priv1.baselen, NIST224p.baselen) - - priv1 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual(priv1.baselen, NIST192p.baselen) - priv2 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual(priv2.baselen, NIST192p.baselen) - self.assertTruePrivkeysEqual(priv1, priv2) - - priv1 = SigningKey.from_secret_exponent(secexp=3) - self.assertEqual(priv1.baselen, NIST192p.baselen) - priv2 = SigningKey.from_secret_exponent(secexp=3) - self.assertTruePrivkeysEqual(priv1, priv2) - - priv1 = SigningKey.from_secret_exponent(secexp=4, curve=NIST224p) - self.assertEqual(priv1.baselen, NIST224p.baselen) - - def test_privkey_strings(self): - priv1 = SigningKey.generate() - s1 = priv1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), NIST192p.baselen) - priv2 = SigningKey.from_string(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_pem() - self.assertEqual(type(s1), binary_type) - self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) - self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) - priv2 = SigningKey.from_pem(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_der() - self.assertEqual(type(s1), binary_type) - priv2 = SigningKey.from_der(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - priv1 = SigningKey.generate(curve=NIST256p) - s1 = priv1.to_pem() - self.assertEqual(type(s1), binary_type) - self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) - self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) - priv2 = SigningKey.from_pem(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_der() - self.assertEqual(type(s1), binary_type) - priv2 = SigningKey.from_der(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - def test_privkey_strings_brainpool(self): - priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) - s1 = priv1.to_pem() - self.assertEqual(type(s1), binary_type) - self.assertTrue(s1.startswith(b("-----BEGIN EC PRIVATE KEY-----"))) - self.assertTrue(s1.strip().endswith(b("-----END EC PRIVATE KEY-----"))) - priv2 = SigningKey.from_pem(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - s1 = priv1.to_der() - self.assertEqual(type(s1), binary_type) - priv2 = SigningKey.from_der(s1) - self.assertTruePrivkeysEqual(priv1, priv2) - - def assertTruePubkeysEqual(self, pub1, pub2): - self.assertEqual(pub1.pubkey.point, pub2.pubkey.point) - self.assertEqual(pub1.pubkey.generator, pub2.pubkey.generator) - self.assertEqual(pub1.curve, pub2.curve) - - def test_pubkey_strings(self): - priv1 = SigningKey.generate() - pub1 = priv1.get_verifying_key() - s1 = pub1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), NIST192p.verifying_key_length) - pub2 = VerifyingKey.from_string(s1) - self.assertTruePubkeysEqual(pub1, pub2) - - priv1 = SigningKey.generate(curve=NIST256p) - pub1 = priv1.get_verifying_key() - s1 = pub1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), NIST256p.verifying_key_length) - pub2 = VerifyingKey.from_string(s1, curve=NIST256p) - self.assertTruePubkeysEqual(pub1, pub2) - - pub1_der = pub1.to_der() - self.assertEqual(type(pub1_der), binary_type) - pub2 = VerifyingKey.from_der(pub1_der) - self.assertTruePubkeysEqual(pub1, pub2) - - self.assertRaises( - der.UnexpectedDER, VerifyingKey.from_der, pub1_der + b("junk") - ) - badpub = VerifyingKey.from_der(pub1_der) - - class FakeGenerator: - def order(self): - return 123456789 - - class FakeCurveFp: - def p(self): - return int( - "6525534529039240705020950546962731340" - "4541085228058844382513856749047873406763" - ) - - badcurve = Curve( - "unknown", FakeCurveFp(), FakeGenerator(), (1, 2, 3, 4, 5, 6), None - ) - badpub.curve = badcurve - badder = badpub.to_der() - self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) - - pem = pub1.to_pem() - self.assertEqual(type(pem), binary_type) - self.assertTrue(pem.startswith(b("-----BEGIN PUBLIC KEY-----")), pem) - self.assertTrue( - pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem - ) - pub2 = VerifyingKey.from_pem(pem) - self.assertTruePubkeysEqual(pub1, pub2) - - def test_pubkey_strings_brainpool(self): - priv1 = SigningKey.generate(curve=BRAINPOOLP512r1) - pub1 = priv1.get_verifying_key() - s1 = pub1.to_string() - self.assertEqual(type(s1), binary_type) - self.assertEqual(len(s1), BRAINPOOLP512r1.verifying_key_length) - pub2 = VerifyingKey.from_string(s1, curve=BRAINPOOLP512r1) - self.assertTruePubkeysEqual(pub1, pub2) - - pub1_der = pub1.to_der() - self.assertEqual(type(pub1_der), binary_type) - pub2 = VerifyingKey.from_der(pub1_der) - self.assertTruePubkeysEqual(pub1, pub2) - - def test_vk_to_der_with_invalid_point_encoding(self): - sk = SigningKey.generate() - vk = sk.verifying_key - - with self.assertRaises(ValueError): - vk.to_der("raw") - - def test_sk_to_der_with_invalid_point_encoding(self): - sk = SigningKey.generate() - - with self.assertRaises(ValueError): - sk.to_der("raw") - - def test_vk_from_der_garbage_after_curve_oid(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + b( - "garbage" - ) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x00\xff", None) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_invalid_key_type(self): - type_oid_der = der.encode_oid(*(1, 2, 3)) - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x00\xff", None) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_garbage_after_point_string(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x00\xff", None) + b("garbage") - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_invalid_bitstring(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\x08\xff", None) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_with_invalid_length_of_encoding(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\xff" * 64, 0) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_der(to_decode) - - def test_vk_from_der_with_raw_encoding(self): - type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b"\xff" * 48, 0) - to_decode = der.encode_sequence(enc_type_der, point_der) - - with self.assertRaises(der.UnexpectedDER): - VerifyingKey.from_der(to_decode) - - def test_signature_strings(self): - priv1 = SigningKey.generate() - pub1 = priv1.get_verifying_key() - data = b("data") - - sig = priv1.sign(data) - self.assertEqual(type(sig), binary_type) - self.assertEqual(len(sig), NIST192p.signature_length) - self.assertTrue(pub1.verify(sig, data)) - - sig = priv1.sign(data, sigencode=sigencode_strings) - self.assertEqual(type(sig), tuple) - self.assertEqual(len(sig), 2) - self.assertEqual(type(sig[0]), binary_type) - self.assertEqual(type(sig[1]), binary_type) - self.assertEqual(len(sig[0]), NIST192p.baselen) - self.assertEqual(len(sig[1]), NIST192p.baselen) - self.assertTrue(pub1.verify(sig, data, sigdecode=sigdecode_strings)) - - sig_der = priv1.sign(data, sigencode=sigencode_der) - self.assertEqual(type(sig_der), binary_type) - self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der)) - - def test_sig_decode_strings_with_invalid_count(self): - with self.assertRaises(MalformedSignature): - sigdecode_strings([b("one"), b("two"), b("three")], 0xFF) - - def test_sig_decode_strings_with_wrong_r_len(self): - with self.assertRaises(MalformedSignature): - sigdecode_strings([b("one"), b("two")], 0xFF) - - def test_sig_decode_strings_with_wrong_s_len(self): - with self.assertRaises(MalformedSignature): - sigdecode_strings([b("\xa0"), b("\xb0\xff")], 0xFF) - - def test_verify_with_too_long_input(self): - sk = SigningKey.generate() - vk = sk.verifying_key - - with self.assertRaises(BadDigestError): - vk.verify_digest(None, b("\x00") * 128) - - def test_sk_from_secret_exponent_with_wrong_sec_exponent(self): - with self.assertRaises(MalformedPointError): - SigningKey.from_secret_exponent(0) - - def test_sk_from_string_with_wrong_len_string(self): - with self.assertRaises(MalformedPointError): - SigningKey.from_string(b("\x01")) - - def test_sk_from_der_with_junk_after_sequence(self): - ver_der = der.encode_integer(1) - to_decode = der.encode_sequence(ver_der) + b("garbage") - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_with_wrong_version(self): - ver_der = der.encode_integer(0) - to_decode = der.encode_sequence(ver_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_invalid_const_tag(self): - ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b("\x00\xff")) - curve_oid_der = der.encode_oid(*(1, 2, 3)) - const_der = der.encode_constructed(1, curve_oid_der) - to_decode = der.encode_sequence( - ver_der, privkey_der, const_der, curve_oid_der - ) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_garbage_after_privkey_oid(self): - ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b("\x00\xff")) - curve_oid_der = der.encode_oid(*(1, 2, 3)) + b("garbage") - const_der = der.encode_constructed(0, curve_oid_der) - to_decode = der.encode_sequence( - ver_der, privkey_der, const_der, curve_oid_der - ) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_der_with_short_privkey(self): - ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b("\x00\xff")) - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) - const_der = der.encode_constructed(0, curve_oid_der) - to_decode = der.encode_sequence( - ver_der, privkey_der, const_der, curve_oid_der - ) - - sk = SigningKey.from_der(to_decode) - self.assertEqual(sk.privkey.secret_multiplier, 255) - - def test_sk_from_p8_der_with_wrong_version(self): - ver_der = der.encode_integer(2) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 840, 10045, 2, 1), - der.encode_oid(1, 2, 840, 10045, 3, 1, 1), - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - ) - to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_p8_der_with_wrong_algorithm(self): - ver_der = der.encode_integer(1) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 3), der.encode_oid(1, 2, 840, 10045, 3, 1, 1) - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - ) - to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_p8_der_with_trailing_junk_after_algorithm(self): - ver_der = der.encode_integer(1) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 840, 10045, 2, 1), - der.encode_oid(1, 2, 840, 10045, 3, 1, 1), - der.encode_octet_string(b"junk"), - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - ) - to_decode = der.encode_sequence(ver_der, algorithm_der, privkey_der) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sk_from_p8_der_with_trailing_junk_after_key(self): - ver_der = der.encode_integer(1) - algorithm_der = der.encode_sequence( - der.encode_oid(1, 2, 840, 10045, 2, 1), - der.encode_oid(1, 2, 840, 10045, 3, 1, 1), - ) - privkey_der = der.encode_octet_string( - der.encode_sequence( - der.encode_integer(1), der.encode_octet_string(b"\x00\xff") - ) - + der.encode_integer(999) - ) - to_decode = der.encode_sequence( - ver_der, - algorithm_der, - privkey_der, - der.encode_octet_string(b"junk"), - ) - - with self.assertRaises(der.UnexpectedDER): - SigningKey.from_der(to_decode) - - def test_sign_with_too_long_hash(self): - sk = SigningKey.from_secret_exponent(12) - - with self.assertRaises(BadDigestError): - sk.sign_digest(b("\xff") * 64) - - def test_hashfunc(self): - sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256) - data = b("security level is 128 bits") - sig = sk.sign(data) - vk = VerifyingKey.from_string( - sk.get_verifying_key().to_string(), curve=NIST256p, hashfunc=sha256 - ) - self.assertTrue(vk.verify(sig, data)) - - sk2 = SigningKey.generate(curve=NIST256p) - sig2 = sk2.sign(data, hashfunc=sha256) - vk2 = VerifyingKey.from_string( - sk2.get_verifying_key().to_string(), - curve=NIST256p, - hashfunc=sha256, - ) - self.assertTrue(vk2.verify(sig2, data)) - - vk3 = VerifyingKey.from_string( - sk.get_verifying_key().to_string(), curve=NIST256p - ) - self.assertTrue(vk3.verify(sig, data, hashfunc=sha256)) - - def test_public_key_recovery(self): - # Create keys - curve = BRAINPOOLP160r1 - - sk = SigningKey.generate(curve=curve) - vk = sk.get_verifying_key() - - # Sign a message - data = b("blahblah") - signature = sk.sign(data) - - # Recover verifying keys - recovered_vks = VerifyingKey.from_public_key_recovery( - signature, data, curve - ) - - # Test if each pk is valid - for recovered_vk in recovered_vks: - # Test if recovered vk is valid for the data - self.assertTrue(recovered_vk.verify(signature, data)) - - # Test if properties are equal - self.assertEqual(vk.curve, recovered_vk.curve) - self.assertEqual( - vk.default_hashfunc, recovered_vk.default_hashfunc - ) - - # Test if original vk is the list of recovered keys - self.assertIn( - vk.pubkey.point, - [recovered_vk.pubkey.point for recovered_vk in recovered_vks], - ) - - def test_public_key_recovery_with_custom_hash(self): - # Create keys - curve = BRAINPOOLP160r1 - - sk = SigningKey.generate(curve=curve, hashfunc=sha256) - vk = sk.get_verifying_key() - - # Sign a message - data = b("blahblah") - signature = sk.sign(data) - - # Recover verifying keys - recovered_vks = VerifyingKey.from_public_key_recovery( - signature, data, curve, hashfunc=sha256, allow_truncate=True - ) - - # Test if each pk is valid - for recovered_vk in recovered_vks: - # Test if recovered vk is valid for the data - self.assertTrue(recovered_vk.verify(signature, data)) - - # Test if properties are equal - self.assertEqual(vk.curve, recovered_vk.curve) - self.assertEqual(sha256, recovered_vk.default_hashfunc) - - # Test if original vk is the list of recovered keys - self.assertIn( - vk.pubkey.point, - [recovered_vk.pubkey.point for recovered_vk in recovered_vks], - ) - - def test_encoding(self): - sk = SigningKey.from_secret_exponent(123456789) - vk = sk.verifying_key - - exp = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - self.assertEqual(vk.to_string(), exp) - self.assertEqual(vk.to_string("raw"), exp) - self.assertEqual(vk.to_string("uncompressed"), b("\x04") + exp) - self.assertEqual(vk.to_string("compressed"), b("\x02") + exp[:24]) - self.assertEqual(vk.to_string("hybrid"), b("\x06") + exp) - - def test_decoding(self): - sk = SigningKey.from_secret_exponent(123456789) - vk = sk.verifying_key - - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - from_raw = VerifyingKey.from_string(enc) - self.assertEqual(from_raw.pubkey.point, vk.pubkey.point) - - from_uncompressed = VerifyingKey.from_string(b("\x04") + enc) - self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) - - from_compressed = VerifyingKey.from_string(b("\x02") + enc[:24]) - self.assertEqual(from_compressed.pubkey.point, vk.pubkey.point) - - from_uncompressed = VerifyingKey.from_string(b("\x06") + enc) - self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) - - def test_uncompressed_decoding_as_only_alowed(self): - enc = b( - "\x04" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - vk = VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) - sk = SigningKey.from_secret_exponent(123456789) - - self.assertEqual(vk, sk.verifying_key) - - def test_raw_decoding_with_blocked_format(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) - - self.assertIn("hybrid", str(exp.exception)) - - def test_decoding_with_unknown_format(self): - with self.assertRaises(ValueError) as e: - VerifyingKey.from_string(b"", valid_encodings=("raw", "foobar")) - - self.assertIn("Only uncompressed, compressed", str(e.exception)) - - def test_uncompressed_decoding_with_blocked_format(self): - enc = b( - "\x04" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("hybrid",)) - - self.assertIn("Invalid X9.62 encoding", str(exp.exception)) - - def test_hybrid_decoding_with_blocked_format(self): - enc = b( - "\x06" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("uncompressed",)) - - self.assertIn("Invalid X9.62 encoding", str(exp.exception)) - - def test_compressed_decoding_with_blocked_format(self): - enc = b( - "\x02" - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - )[:25] - with self.assertRaises(MalformedPointError) as exp: - VerifyingKey.from_string(enc, valid_encodings=("hybrid", "raw")) - - self.assertIn("(hybrid, raw)", str(exp.exception)) - - def test_decoding_with_malformed_uncompressed(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x02") + enc) - - def test_decoding_with_malformed_compressed(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x01") + enc[:24]) - - def test_decoding_with_inconsistent_hybrid(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x07") + enc) - - def test_decoding_with_point_not_on_curve(self): - enc = b( - "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" - "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" - "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" - ) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(enc[:47] + b("\x00")) - - def test_decoding_with_point_at_infinity(self): - # decoding it is unsupported, as it's not necessary to encode it - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x00")) - - def test_not_lying_on_curve(self): - enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p() + 1) - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x02") + enc) - - def test_from_string_with_invalid_curve_too_short_ver_key_len(self): - # both verifying_key_length and baselen are calculated internally - # by the Curve constructor, but since we depend on them verify - # that inconsistent values are detected - curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) - curve.verifying_key_length = 16 - curve.baselen = 32 - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x00") * 16, curve) - - def test_from_string_with_invalid_curve_too_long_ver_key_len(self): - # both verifying_key_length and baselen are calculated internally - # by the Curve constructor, but since we depend on them verify - # that inconsistent values are detected - curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2)) - curve.verifying_key_length = 16 - curve.baselen = 16 - - with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b("\x00") * 16, curve) - - -@pytest.mark.parametrize( - "val,even", [(i, j) for i in range(256) for j in [True, False]] -) -def test_VerifyingKey_decode_with_small_values(val, even): - enc = number_to_string(val, NIST192p.order) - - if even: - enc = b("\x02") + enc - else: - enc = b("\x03") + enc - - # small values can both be actual valid public keys and not, verify that - # only expected exceptions are raised if they are not - try: - vk = VerifyingKey.from_string(enc) - assert isinstance(vk, VerifyingKey) - except MalformedPointError: - assert True - - -params = [] -for curve in curves: - for enc in ["raw", "uncompressed", "compressed", "hybrid"]: - params.append( - pytest.param(curve, enc, id="{0}-{1}".format(curve.name, enc)) - ) - - -@pytest.mark.parametrize("curve,encoding", params) -def test_VerifyingKey_encode_decode(curve, encoding): - sk = SigningKey.generate(curve=curve) - vk = sk.verifying_key - - encoded = vk.to_string(encoding) - - from_enc = VerifyingKey.from_string(encoded, curve=curve) - - assert vk.pubkey.point == from_enc.pubkey.point - - -class OpenSSL(unittest.TestCase): - # test interoperability with OpenSSL tools. Note that openssl's ECDSA - # sign/verify arguments changed between 0.9.8 and 1.0.0: the early - # versions require "-ecdsa-with-SHA1", the later versions want just - # "-SHA1" (or to leave out that argument entirely, which means the - # signature will use some default digest algorithm, probably determined - # by the key, probably always SHA1). - # - # openssl ecparam -name secp224r1 -genkey -out privkey.pem - # openssl ec -in privkey.pem -text -noout # get the priv/pub keys - # openssl dgst -ecdsa-with-SHA1 -sign privkey.pem -out data.sig data.txt - # openssl asn1parse -in data.sig -inform DER - # data.sig is 64 bytes, probably 56b plus ASN1 overhead - # openssl dgst -ecdsa-with-SHA1 -prverify privkey.pem -signature data.sig data.txt ; echo $? - # openssl ec -in privkey.pem -pubout -out pubkey.pem - # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der - - OPENSSL_SUPPORTED_CURVES = set( - c.split(":")[0].strip() - for c in run_openssl("ecparam -list_curves").split("\n") - ) - - def get_openssl_messagedigest_arg(self, hash_name): - v = run_openssl("version") - # e.g. "OpenSSL 1.0.0 29 Mar 2010", or "OpenSSL 1.0.0a 1 Jun 2010", - # or "OpenSSL 0.9.8o 01 Jun 2010" - vs = v.split()[1].split(".") - if vs >= ["1", "0", "0"]: # pragma: no cover - return "-{0}".format(hash_name) - else: # pragma: no cover - return "-ecdsa-with-{0}".format(hash_name) - - # sk: 1:OpenSSL->python 2:python->OpenSSL - # vk: 3:OpenSSL->python 4:python->OpenSSL - # sig: 5:OpenSSL->python 6:python->OpenSSL - - @pytest.mark.skipif( - "secp112r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r1", - ) - def test_from_openssl_secp112r1(self): - return self.do_test_from_openssl(SECP112r1) - - @pytest.mark.skipif( - "secp112r2" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r2", - ) - def test_from_openssl_secp112r2(self): - return self.do_test_from_openssl(SECP112r2) - - @pytest.mark.skipif( - "secp128r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp128r1", - ) - def test_from_openssl_secp128r1(self): - return self.do_test_from_openssl(SECP128r1) - - @pytest.mark.skipif( - "secp160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp160r1", - ) - def test_from_openssl_secp160r1(self): - return self.do_test_from_openssl(SECP160r1) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_from_openssl_nist192p(self): - return self.do_test_from_openssl(NIST192p) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_from_openssl_nist192p_sha256(self): - return self.do_test_from_openssl(NIST192p, "SHA256") - - @pytest.mark.skipif( - "secp224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp224r1", - ) - def test_from_openssl_nist224p(self): - return self.do_test_from_openssl(NIST224p) - - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_from_openssl_nist256p(self): - return self.do_test_from_openssl(NIST256p) - - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_from_openssl_nist256p_sha384(self): - return self.do_test_from_openssl(NIST256p, "SHA384") - - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_from_openssl_nist256p_sha512(self): - return self.do_test_from_openssl(NIST256p, "SHA512") - - @pytest.mark.skipif( - "secp384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp384r1", - ) - def test_from_openssl_nist384p(self): - return self.do_test_from_openssl(NIST384p) - - @pytest.mark.skipif( - "secp521r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp521r1", - ) - def test_from_openssl_nist521p(self): - return self.do_test_from_openssl(NIST521p) - - @pytest.mark.skipif( - "secp256k1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp256k1", - ) - def test_from_openssl_secp256k1(self): - return self.do_test_from_openssl(SECP256k1) - - @pytest.mark.skipif( - "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160r1", - ) - def test_from_openssl_brainpoolp160r1(self): - return self.do_test_from_openssl(BRAINPOOLP160r1) - - @pytest.mark.skipif( - "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192r1", - ) - def test_from_openssl_brainpoolp192r1(self): - return self.do_test_from_openssl(BRAINPOOLP192r1) - - @pytest.mark.skipif( - "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224r1", - ) - def test_from_openssl_brainpoolp224r1(self): - return self.do_test_from_openssl(BRAINPOOLP224r1) - - @pytest.mark.skipif( - "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256r1", - ) - def test_from_openssl_brainpoolp256r1(self): - return self.do_test_from_openssl(BRAINPOOLP256r1) - - @pytest.mark.skipif( - "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320r1", - ) - def test_from_openssl_brainpoolp320r1(self): - return self.do_test_from_openssl(BRAINPOOLP320r1) - - @pytest.mark.skipif( - "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384r1", - ) - def test_from_openssl_brainpoolp384r1(self): - return self.do_test_from_openssl(BRAINPOOLP384r1) - - @pytest.mark.skipif( - "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512r1", - ) - def test_from_openssl_brainpoolp512r1(self): - return self.do_test_from_openssl(BRAINPOOLP512r1) - - def do_test_from_openssl(self, curve, hash_name="SHA1"): - curvename = curve.openssl_name - assert curvename - # OpenSSL: create sk, vk, sign. - # Python: read vk(3), checksig(5), read sk(1), sign, check - mdarg = self.get_openssl_messagedigest_arg(hash_name) - if os.path.isdir("t"): # pragma: no cover - shutil.rmtree("t") - os.mkdir("t") - run_openssl("ecparam -name %s -genkey -out t/privkey.pem" % curvename) - run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") - data = b("data") - with open("t/data.txt", "wb") as e: - e.write(data) - run_openssl( - "dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" - % mdarg - ) - with open("t/pubkey.pem", "rb") as e: - pubkey_pem = e.read() - vk = VerifyingKey.from_pem(pubkey_pem) # 3 - with open("t/data.sig", "rb") as e: - sig_der = e.read() - self.assertTrue( - vk.verify( - sig_der, - data, # 5 - hashfunc=partial(hashlib.new, hash_name), - sigdecode=sigdecode_der, - ) - ) - - with open("t/privkey.pem") as e: - fp = e.read() - sk = SigningKey.from_pem(fp) # 1 - sig = sk.sign(data, hashfunc=partial(hashlib.new, hash_name)) - self.assertTrue( - vk.verify(sig, data, hashfunc=partial(hashlib.new, hash_name)) - ) - - run_openssl( - "pkcs8 -topk8 -nocrypt " - "-in t/privkey.pem -outform pem -out t/privkey-p8.pem" - ) - with open("t/privkey-p8.pem", "rb") as e: - privkey_p8_pem = e.read() - sk_from_p8 = SigningKey.from_pem(privkey_p8_pem) - self.assertEqual(sk, sk_from_p8) - - @pytest.mark.skipif( - "secp112r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r1", - ) - def test_to_openssl_secp112r1(self): - self.do_test_to_openssl(SECP112r1) - - @pytest.mark.skipif( - "secp112r2" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp112r2", - ) - def test_to_openssl_secp112r2(self): - self.do_test_to_openssl(SECP112r2) - - @pytest.mark.skipif( - "secp128r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp128r1", - ) - def test_to_openssl_secp128r1(self): - self.do_test_to_openssl(SECP128r1) - - @pytest.mark.skipif( - "secp160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp160r1", - ) - def test_to_openssl_secp160r1(self): - self.do_test_to_openssl(SECP160r1) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_to_openssl_nist192p(self): - self.do_test_to_openssl(NIST192p) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_to_openssl_nist192p_sha256(self): - self.do_test_to_openssl(NIST192p, "SHA256") - - @pytest.mark.skipif( - "secp224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp224r1", - ) - def test_to_openssl_nist224p(self): - self.do_test_to_openssl(NIST224p) - - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_to_openssl_nist256p(self): - self.do_test_to_openssl(NIST256p) - - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_to_openssl_nist256p_sha384(self): - self.do_test_to_openssl(NIST256p, "SHA384") - - @pytest.mark.skipif( - "prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1", - ) - def test_to_openssl_nist256p_sha512(self): - self.do_test_to_openssl(NIST256p, "SHA512") - - @pytest.mark.skipif( - "secp384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp384r1", - ) - def test_to_openssl_nist384p(self): - self.do_test_to_openssl(NIST384p) - - @pytest.mark.skipif( - "secp521r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp521r1", - ) - def test_to_openssl_nist521p(self): - self.do_test_to_openssl(NIST521p) - - @pytest.mark.skipif( - "secp256k1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp256k1", - ) - def test_to_openssl_secp256k1(self): - self.do_test_to_openssl(SECP256k1) - - @pytest.mark.skipif( - "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160r1", - ) - def test_to_openssl_brainpoolp160r1(self): - self.do_test_to_openssl(BRAINPOOLP160r1) - - @pytest.mark.skipif( - "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192r1", - ) - def test_to_openssl_brainpoolp192r1(self): - self.do_test_to_openssl(BRAINPOOLP192r1) - - @pytest.mark.skipif( - "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224r1", - ) - def test_to_openssl_brainpoolp224r1(self): - self.do_test_to_openssl(BRAINPOOLP224r1) - - @pytest.mark.skipif( - "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256r1", - ) - def test_to_openssl_brainpoolp256r1(self): - self.do_test_to_openssl(BRAINPOOLP256r1) - - @pytest.mark.skipif( - "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320r1", - ) - def test_to_openssl_brainpoolp320r1(self): - self.do_test_to_openssl(BRAINPOOLP320r1) - - @pytest.mark.skipif( - "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384r1", - ) - def test_to_openssl_brainpoolp384r1(self): - self.do_test_to_openssl(BRAINPOOLP384r1) - - @pytest.mark.skipif( - "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512r1", - ) - def test_to_openssl_brainpoolp512r1(self): - self.do_test_to_openssl(BRAINPOOLP512r1) - - def do_test_to_openssl(self, curve, hash_name="SHA1"): - curvename = curve.openssl_name - assert curvename - # Python: create sk, vk, sign. - # OpenSSL: read vk(4), checksig(6), read sk(2), sign, check - mdarg = self.get_openssl_messagedigest_arg(hash_name) - if os.path.isdir("t"): # pragma: no cover - shutil.rmtree("t") - os.mkdir("t") - sk = SigningKey.generate(curve=curve) - vk = sk.get_verifying_key() - data = b("data") - with open("t/pubkey.der", "wb") as e: - e.write(vk.to_der()) # 4 - with open("t/pubkey.pem", "wb") as e: - e.write(vk.to_pem()) # 4 - sig_der = sk.sign( - data, - hashfunc=partial(hashlib.new, hash_name), - sigencode=sigencode_der, - ) - - with open("t/data.sig", "wb") as e: - e.write(sig_der) # 6 - with open("t/data.txt", "wb") as e: - e.write(data) - with open("t/baddata.txt", "wb") as e: - e.write(data + b("corrupt")) - - self.assertRaises( - SubprocessError, - run_openssl, - "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" - % mdarg, - ) - run_openssl( - "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" - % mdarg - ) - - with open("t/privkey.pem", "wb") as e: - e.write(sk.to_pem()) # 2 - run_openssl( - "dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" - % mdarg - ) - - with open("t/privkey-explicit.pem", "wb") as e: - e.write(sk.to_pem(curve_parameters_encoding="explicit")) - run_openssl( - "dgst %s -sign t/privkey-explicit.pem -out t/data.sig2 t/data.txt" - % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" - % mdarg - ) - - with open("t/privkey-p8.pem", "wb") as e: - e.write(sk.to_pem(format="pkcs8")) - run_openssl( - "dgst %s -sign t/privkey-p8.pem -out t/data.sig3 t/data.txt" - % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" - % mdarg - ) - - with open("t/privkey-p8-explicit.pem", "wb") as e: - e.write( - sk.to_pem(format="pkcs8", curve_parameters_encoding="explicit") - ) - run_openssl( - "dgst %s -sign t/privkey-p8-explicit.pem -out t/data.sig3 t/data.txt" - % mdarg - ) - run_openssl( - "dgst %s -verify t/pubkey.pem -signature t/data.sig3 t/data.txt" - % mdarg - ) - - OPENSSL_SUPPORTED_TYPES = set() - try: - if "-rawin" in run_openssl("pkeyutl -help"): - OPENSSL_SUPPORTED_TYPES = set( - c.lower() - for c in ("ED25519", "ED448") - if c in run_openssl("list -public-key-methods") - ) - except SubprocessError: - pass - - def do_eddsa_test_to_openssl(self, curve): - curvename = curve.name.upper() - - if os.path.isdir("t"): - shutil.rmtree("t") - os.mkdir("t") - - sk = SigningKey.generate(curve=curve) - vk = sk.get_verifying_key() - - data = b"data" - with open("t/pubkey.der", "wb") as e: - e.write(vk.to_der()) - with open("t/pubkey.pem", "wb") as e: - e.write(vk.to_pem()) - - sig = sk.sign(data) - - with open("t/data.sig", "wb") as e: - e.write(sig) - with open("t/data.txt", "wb") as e: - e.write(data) - with open("t/baddata.txt", "wb") as e: - e.write(data + b"corrupt") - - with self.assertRaises(SubprocessError): - run_openssl( - "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " - "-in t/baddata.txt -sigfile t/data.sig" - ) - run_openssl( - "pkeyutl -verify -pubin -inkey t/pubkey.pem -rawin " - "-in t/data.txt -sigfile t/data.sig" - ) - - shutil.rmtree("t") - - # in practice at least OpenSSL 3.0.0 is needed to make EdDSA signatures - # earlier versions support EdDSA only in X.509 certificates - @pytest.mark.skipif( - "ed25519" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed25519", - ) - def test_to_openssl_ed25519(self): - return self.do_eddsa_test_to_openssl(Ed25519) - - @pytest.mark.skipif( - "ed448" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed448", - ) - def test_to_openssl_ed448(self): - return self.do_eddsa_test_to_openssl(Ed448) - - def do_eddsa_test_from_openssl(self, curve): - curvename = curve.name - - if os.path.isdir("t"): - shutil.rmtree("t") - os.mkdir("t") - - data = b"data" - - run_openssl( - "genpkey -algorithm {0} -outform PEM -out t/privkey.pem".format( - curvename - ) - ) - run_openssl( - "pkey -outform PEM -pubout -in t/privkey.pem -out t/pubkey.pem" - ) - - with open("t/data.txt", "wb") as e: - e.write(data) - run_openssl( - "pkeyutl -sign -inkey t/privkey.pem " - "-rawin -in t/data.txt -out t/data.sig" - ) - - with open("t/data.sig", "rb") as e: - sig = e.read() - with open("t/pubkey.pem", "rb") as e: - vk = VerifyingKey.from_pem(e.read()) - - self.assertIs(vk.curve, curve) - - vk.verify(sig, data) - - shutil.rmtree("t") - - @pytest.mark.skipif( - "ed25519" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed25519", - ) - def test_from_openssl_ed25519(self): - return self.do_eddsa_test_from_openssl(Ed25519) - - @pytest.mark.skipif( - "ed448" not in OPENSSL_SUPPORTED_TYPES, - reason="system openssl does not support signing with Ed448", - ) - def test_from_openssl_ed448(self): - return self.do_eddsa_test_from_openssl(Ed448) - - -class TooSmallCurve(unittest.TestCase): - OPENSSL_SUPPORTED_CURVES = set( - c.split(":")[0].strip() - for c in run_openssl("ecparam -list_curves").split("\n") - ) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_sign_too_small_curve_dont_allow_truncate_raises(self): - sk = SigningKey.generate(curve=NIST192p) - data = b("data") - with self.assertRaises(BadDigestError): - sk.sign( - data, - hashfunc=partial(hashlib.new, "SHA256"), - sigencode=sigencode_der, - allow_truncate=False, - ) - - @pytest.mark.skipif( - "prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1", - ) - def test_verify_too_small_curve_dont_allow_truncate_raises(self): - sk = SigningKey.generate(curve=NIST192p) - vk = sk.get_verifying_key() - data = b("data") - sig_der = sk.sign( - data, - hashfunc=partial(hashlib.new, "SHA256"), - sigencode=sigencode_der, - allow_truncate=True, - ) - with self.assertRaises(BadDigestError): - vk.verify( - sig_der, - data, - hashfunc=partial(hashlib.new, "SHA256"), - sigdecode=sigdecode_der, - allow_truncate=False, - ) - - -class DER(unittest.TestCase): - def test_integer(self): - self.assertEqual(der.encode_integer(0), b("\x02\x01\x00")) - self.assertEqual(der.encode_integer(1), b("\x02\x01\x01")) - self.assertEqual(der.encode_integer(127), b("\x02\x01\x7f")) - self.assertEqual(der.encode_integer(128), b("\x02\x02\x00\x80")) - self.assertEqual(der.encode_integer(256), b("\x02\x02\x01\x00")) - # self.assertEqual(der.encode_integer(-1), b("\x02\x01\xff")) - - def s(n): - return der.remove_integer(der.encode_integer(n) + b("junk")) - - self.assertEqual(s(0), (0, b("junk"))) - self.assertEqual(s(1), (1, b("junk"))) - self.assertEqual(s(127), (127, b("junk"))) - self.assertEqual(s(128), (128, b("junk"))) - self.assertEqual(s(256), (256, b("junk"))) - self.assertEqual( - s(1234567890123456789012345678901234567890), - (1234567890123456789012345678901234567890, b("junk")), - ) - - def test_number(self): - self.assertEqual(der.encode_number(0), b("\x00")) - self.assertEqual(der.encode_number(127), b("\x7f")) - self.assertEqual(der.encode_number(128), b("\x81\x00")) - self.assertEqual(der.encode_number(3 * 128 + 7), b("\x83\x07")) - # self.assertEqual(der.read_number("\x81\x9b" + "more"), (155, 2)) - # self.assertEqual(der.encode_number(155), b("\x81\x9b")) - for n in (0, 1, 2, 127, 128, 3 * 128 + 7, 840, 10045): # , 155): - x = der.encode_number(n) + b("more") - n1, llen = der.read_number(x) - self.assertEqual(n1, n) - self.assertEqual(x[llen:], b("more")) - - def test_length(self): - self.assertEqual(der.encode_length(0), b("\x00")) - self.assertEqual(der.encode_length(127), b("\x7f")) - self.assertEqual(der.encode_length(128), b("\x81\x80")) - self.assertEqual(der.encode_length(255), b("\x81\xff")) - self.assertEqual(der.encode_length(256), b("\x82\x01\x00")) - self.assertEqual(der.encode_length(3 * 256 + 7), b("\x82\x03\x07")) - self.assertEqual(der.read_length(b("\x81\x9b") + b("more")), (155, 2)) - self.assertEqual(der.encode_length(155), b("\x81\x9b")) - for n in (0, 1, 2, 127, 128, 255, 256, 3 * 256 + 7, 155): - x = der.encode_length(n) + b("more") - n1, llen = der.read_length(x) - self.assertEqual(n1, n) - self.assertEqual(x[llen:], b("more")) - - def test_sequence(self): - x = der.encode_sequence(b("ABC"), b("DEF")) + b("GHI") - self.assertEqual(x, b("\x30\x06ABCDEFGHI")) - x1, rest = der.remove_sequence(x) - self.assertEqual(x1, b("ABCDEF")) - self.assertEqual(rest, b("GHI")) - - def test_constructed(self): - x = der.encode_constructed(0, NIST224p.encoded_oid) - self.assertEqual(hexlify(x), b("a007") + b("06052b81040021")) - x = der.encode_constructed(1, unhexlify(b("0102030a0b0c"))) - self.assertEqual(hexlify(x), b("a106") + b("0102030a0b0c")) - - -class Util(unittest.TestCase): - def test_trytryagain(self): - tta = util.randrange_from_seed__trytryagain - for i in range(1000): - seed = "seed-%d" % i - for order in ( - 2**8 - 2, - 2**8 - 1, - 2**8, - 2**8 + 1, - 2**8 + 2, - 2**16 - 1, - 2**16 + 1, - ): - n = tta(seed, order) - self.assertTrue(1 <= n < order, (1, n, order)) - # this trytryagain *does* provide long-term stability - self.assertEqual( - ("%x" % (tta("seed", NIST224p.order))).encode(), - b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc"), - ) - - def test_trytryagain_single(self): - tta = util.randrange_from_seed__trytryagain - order = 2**8 - 2 - seed = b"text" - n = tta(seed, order) - # known issue: https://github.com/warner/python-ecdsa/issues/221 - if sys.version_info < (3, 0): # pragma: no branch - self.assertEqual(n, 228) - else: - self.assertEqual(n, 18) - - @given(st.integers(min_value=0, max_value=10**200)) - def test_randrange(self, i): - # util.randrange does not provide long-term stability: we might - # change the algorithm in the future. - entropy = util.PRNG("seed-%d" % i) - for order in ( - 2**8 - 2, - 2**8 - 1, - 2**8, - 2**16 - 1, - 2**16 + 1, - ): - # that oddball 2**16+1 takes half our runtime - n = util.randrange(order, entropy=entropy) - self.assertTrue(1 <= n < order, (1, n, order)) - - def OFF_test_prove_uniformity(self): # pragma: no cover - order = 2**8 - 2 - counts = dict([(i, 0) for i in range(1, order)]) - assert 0 not in counts - assert order not in counts - for i in range(1000000): - seed = "seed-%d" % i - n = util.randrange_from_seed__trytryagain(seed, order) - counts[n] += 1 - # this technique should use the full range - self.assertTrue(counts[order - 1]) - for i in range(1, order): - print_("%3d: %s" % (i, "*" * (counts[i] // 100))) - - -class RFC6979(unittest.TestCase): - # https://tools.ietf.org/html/rfc6979#appendix-A.1 - def _do(self, generator, secexp, hsh, hash_func, expected): - actual = rfc6979.generate_k(generator.order(), secexp, hash_func, hsh) - self.assertEqual(expected, actual) - - def test_SECP256k1(self): - """RFC doesn't contain test vectors for SECP256k1 used in bitcoin. - This vector has been computed by Golang reference implementation instead.""" - self._do( - generator=SECP256k1.generator, - secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16), - hsh=sha256(b("sample")).digest(), - hash_func=sha256, - expected=int( - "8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", - 16, - ), - ) - - def test_SECP256k1_2(self): - self._do( - generator=SECP256k1.generator, - secexp=int( - "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", - 16, - ), - hsh=sha256(b("sample")).digest(), - hash_func=sha256, - expected=int( - "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", - 16, - ), - ) - - def test_SECP256k1_3(self): - self._do( - generator=SECP256k1.generator, - secexp=0x1, - hsh=sha256(b("Satoshi Nakamoto")).digest(), - hash_func=sha256, - expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, - ) - - def test_SECP256k1_4(self): - self._do( - generator=SECP256k1.generator, - secexp=0x1, - hsh=sha256( - b( - "All those moments will be lost in time, like tears in rain. Time to die..." - ) - ).digest(), - hash_func=sha256, - expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, - ) - - def test_SECP256k1_5(self): - self._do( - generator=SECP256k1.generator, - secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, - hsh=sha256(b("Satoshi Nakamoto")).digest(), - hash_func=sha256, - expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, - ) - - def test_SECP256k1_6(self): - self._do( - generator=SECP256k1.generator, - secexp=0xF8B8AF8CE3C7CCA5E300D33939540C10D45CE001B8F252BFBC57BA0342904181, - hsh=sha256(b("Alan Turing")).digest(), - hash_func=sha256, - expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, - ) - - def test_1(self): - # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 - self._do( - generator=Point( - None, - 0, - 0, - int("4000000000000000000020108A2E0CC0D99F8A5EF", 16), - ), - secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), - hsh=unhexlify( - b( - "AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF" - ) - ), - hash_func=sha256, - expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16), - ) - - def test_2(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=sha1(b("sample")).digest(), - hash_func=sha1, - expected=int( - "37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16 - ), - ) - - def test_3(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=sha256(b("sample")).digest(), - hash_func=sha256, - expected=int( - "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16 - ), - ) - - def test_4(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=sha512(b("sample")).digest(), - hash_func=sha512, - expected=int( - "A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16 - ), - ) - - def test_5(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=sha1(b("test")).digest(), - hash_func=sha1, - expected=int( - "D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16 - ), - ) - - def test_6(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=sha256(b("test")).digest(), - hash_func=sha256, - expected=int( - "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16 - ), - ) - - def test_7(self): - self._do( - generator=NIST192p.generator, - secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), - hsh=sha512(b("test")).digest(), - hash_func=sha512, - expected=int( - "0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16 - ), - ) - - def test_8(self): - self._do( - generator=NIST521p.generator, - secexp=int( - "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", - 16, - ), - hsh=sha1(b("sample")).digest(), - hash_func=sha1, - expected=int( - "089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", - 16, - ), - ) - - def test_9(self): - self._do( - generator=NIST521p.generator, - secexp=int( - "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", - 16, - ), - hsh=sha256(b("sample")).digest(), - hash_func=sha256, - expected=int( - "0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", - 16, - ), - ) - - def test_10(self): - self._do( - generator=NIST521p.generator, - secexp=int( - "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", - 16, - ), - hsh=sha512(b("test")).digest(), - hash_func=sha512, - expected=int( - "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", - 16, - ), - ) - - -class ECDH(unittest.TestCase): - def _do(self, curve, generator, dA, x_qA, y_qA, dB, x_qB, y_qB, x_Z, y_Z): - qA = dA * generator - qB = dB * generator - Z = dA * qB - self.assertEqual(Point(curve, x_qA, y_qA), qA) - self.assertEqual(Point(curve, x_qB, y_qB), qB) - self.assertTrue( - (dA * qB) - == (dA * dB * generator) - == (dB * dA * generator) - == (dB * qA) - ) - self.assertEqual(Point(curve, x_Z, y_Z), Z) - - -class RFC6932(ECDH): - # https://tools.ietf.org/html/rfc6932#appendix-A.1 - - def test_brainpoolP224r1(self): - self._do( - curve=curve_brainpoolp224r1, - generator=BRAINPOOLP224r1.generator, - dA=int( - "7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", 16 - ), - x_qA=int( - "B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", 16 - ), - y_qA=int( - "46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", 16 - ), - dB=int( - "63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", 16 - ), - x_qB=int( - "2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", 16 - ), - y_qB=int( - "9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", 16 - ), - x_Z=int( - "312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", 16 - ), - y_Z=int( - "6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", 16 - ), - ) - - def test_brainpoolP256r1(self): - self._do( - curve=curve_brainpoolp256r1, - generator=BRAINPOOLP256r1.generator, - dA=int( - "041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" - "49699", - 16, - ), - x_qA=int( - "78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" - "CC0D206", - 16, - ), - y_qA=int( - "A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" - "B05F92B", - 16, - ), - dB=int( - "06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" - "804D3", - 16, - ), - x_qB=int( - "8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" - "341F0DB", - 16, - ), - y_qB=int( - "148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" - "4FDE40D", - 16, - ), - x_Z=int( - "05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" - "E23708", - 16, - ), - y_Z=int( - "6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" - "FD134C", - 16, - ), - ) - - def test_brainpoolP384r1(self): - self._do( - curve=curve_brainpoolp384r1, - generator=BRAINPOOLP384r1.generator, - dA=int( - "014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" - "828D97E095884CA72B73FDABD5910DF0FA76A", - 16, - ), - x_qA=int( - "45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" - "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", - 16, - ), - y_qA=int( - "8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" - "2DC530A2CD89C859BB4B4B768497F49AB8CC859", - 16, - ), - dB=int( - "6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" - "5C90095B976F3529957506E1224A861711D54", - 16, - ), - x_qB=int( - "01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" - "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", - 16, - ), - y_qB=int( - "3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" - "8EE47487ED8044858D31D848F7A95C635A347AC", - 16, - ), - x_Z=int( - "04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" - "2F370A1F98D3FA73FD0C0747C632E12F1423EC", - 16, - ), - y_Z=int( - "7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" - "5D05085F5A01A9382D05BF72D96698FE3FF64E", - 16, - ), - ) - - def test_brainpoolP512r1(self): - self._do( - curve=curve_brainpoolp512r1, - generator=BRAINPOOLP512r1.generator, - dA=int( - "636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" - "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" - "4CCE6FE333", - 16, - ), - x_qA=int( - "0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" - "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" - "AFC3EF0971CCDA", - 16, - ), - y_qA=int( - "A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" - "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" - "8EFC7343EA642E", - 16, - ), - dB=int( - "0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" - "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" - "99B86E5C8B", - 16, - ), - x_qB=int( - "5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" - "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" - "E739E11720D323", - 16, - ), - y_qB=int( - "96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" - "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" - "54EE1BA35E04BE", - 16, - ), - x_Z=int( - "1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" - "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" - "10E5811B191E", - 16, - ), - y_Z=int( - "2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" - "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" - "4F9FE9EAABA6", - 16, - ), - ) - - -class RFC7027(ECDH): - # https://tools.ietf.org/html/rfc7027#appendix-A - - def test_brainpoolP256r1(self): - self._do( - curve=curve_brainpoolp256r1, - generator=BRAINPOOLP256r1.generator, - dA=int( - "81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" - "04F1D", - 16, - ), - x_qA=int( - "44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" - "3100BE5", - 16, - ), - y_qA=int( - "8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" - "B089BDC", - 16, - ), - dB=int( - "55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" - "76BD3", - 16, - ), - x_qB=int( - "8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" - "1B39F7B", - 16, - ), - y_qB=int( - "990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" - "47CEC6A", - 16, - ), - x_Z=int( - "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" - "18BF2B", - 16, - ), - y_Z=int( - "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" - "2504DE", - 16, - ), - ) - - def test_brainpoolP384r1(self): - self._do( - curve=curve_brainpoolp384r1, - generator=BRAINPOOLP384r1.generator, - dA=int( - "1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" - "D65D6F15EB5D1EE1610DF870795143627D042", - 16, - ), - x_qA=int( - "68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" - "588F885AB698C852D4A6E77A252D6380FCAF068", - 16, - ), - y_qA=int( - "55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" - "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", - 16, - ), - dB=int( - "032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" - "4E01F8BA5E0324309DB6A9831497ABAC96670", - 16, - ), - x_qB=int( - "4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" - "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", - 16, - ), - y_qB=int( - "62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" - "9185329B5B275903D192F8D4E1F32FE9CC78C48", - 16, - ), - x_Z=int( - "0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" - "E239BBADF6403715C35D4FB2A5444F575D4F42", - 16, - ), - y_Z=int( - "0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" - "E9E598157290F8756066975F1DB34B2324B7BD", - 16, - ), - ) - - def test_brainpoolP512r1(self): - self._do( - curve=curve_brainpoolp512r1, - generator=BRAINPOOLP512r1.generator, - dA=int( - "16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" - "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" - "AD57665422", - 16, - ), - x_qA=int( - "0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" - "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" - "36D11640FD09FD", - 16, - ), - y_qA=int( - "72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" - "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" - "E82A6AD147FDE7", - 16, - ), - dB=int( - "230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" - "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" - "3666B25429", - 16, - ), - x_qB=int( - "9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" - "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" - "EDA55A5473199F", - 16, - ), - y_qB=int( - "2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" - "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" - "512B71876285FA", - 16, - ), - x_Z=int( - "A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" - "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" - "454B21C4CD1F", - 16, - ), - y_Z=int( - "7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" - "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" - "2BE6A26680A2", - 16, - ), - ) - - -# https://tools.ietf.org/html/rfc4754#page-5 -@pytest.mark.parametrize( - "w, gwx, gwy, k, msg, md, r, s, curve", - [ - pytest.param( - "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", - "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", - "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", - "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", - b"abc", - sha256, - "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", - "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", - NIST256p, - id="ECDSA-256", - ), - pytest.param( - "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" - "62E528C38B2A81B35309668D73524D9F", - "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" - "5CE10C9DFEC21797415710721F437922", - "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" - "3D383B91C5E7EDAA2B714CC99D5743CA", - "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" - "854D7FA992F934D927376285E63414FA", - b"abc", - sha384, - "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" - "08E07C9C63F2D21A07DCB56A6AF56EB3", - "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" - "CBB9F516CE0FA7D2FF630863A00E8B9F", - NIST384p, - id="ECDSA-384", - ), - pytest.param( - "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" - "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" - "5FA1", - "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" - "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" - "52A8", - "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" - "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" - "E643", - "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" - "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" - "6C2F", - b"abc", - sha512, - "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" - "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" - "2251", - "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" - "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" - "2660", - NIST521p, - id="ECDSA-521", - ), - ], -) -def test_RFC4754_vectors(w, gwx, gwy, k, msg, md, r, s, curve): - sk = SigningKey.from_string(unhexlify(w), curve) - vk = VerifyingKey.from_string(unhexlify(gwx + gwy), curve) - assert sk.verifying_key == vk - sig = sk.sign(msg, hashfunc=md, sigencode=sigencode_strings, k=int(k, 16)) - - assert sig == (unhexlify(r), unhexlify(s)) - - assert vk.verify(sig, msg, md, sigdecode_strings) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py deleted file mode 100644 index 0a84b9c70..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_rw_lock.py +++ /dev/null @@ -1,180 +0,0 @@ -# Copyright Mateusz Kobos, (c) 2011 -# https://code.activestate.com/recipes/577803-reader-writer-lock-with-priority-for-writers/ -# released under the MIT licence - -try: - import unittest2 as unittest -except ImportError: - import unittest -import threading -import time -import copy -from ._rwlock import RWLock - - -class Writer(threading.Thread): - def __init__( - self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write - ): - """ - @param buffer_: common buffer_ shared by the readers and writers - @type buffer_: list - @type rw_lock: L{RWLock} - @param init_sleep_time: sleep time before doing any action - @type init_sleep_time: C{float} - @param sleep_time: sleep time while in critical section - @type sleep_time: C{float} - @param to_write: data that will be appended to the buffer - """ - threading.Thread.__init__(self) - self.__buffer = buffer_ - self.__rw_lock = rw_lock - self.__init_sleep_time = init_sleep_time - self.__sleep_time = sleep_time - self.__to_write = to_write - self.entry_time = None - """Time of entry to the critical section""" - self.exit_time = None - """Time of exit from the critical section""" - - def run(self): - time.sleep(self.__init_sleep_time) - self.__rw_lock.writer_acquire() - self.entry_time = time.time() - time.sleep(self.__sleep_time) - self.__buffer.append(self.__to_write) - self.exit_time = time.time() - self.__rw_lock.writer_release() - - -class Reader(threading.Thread): - def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time): - """ - @param buffer_: common buffer shared by the readers and writers - @type buffer_: list - @type rw_lock: L{RWLock} - @param init_sleep_time: sleep time before doing any action - @type init_sleep_time: C{float} - @param sleep_time: sleep time while in critical section - @type sleep_time: C{float} - """ - threading.Thread.__init__(self) - self.__buffer = buffer_ - self.__rw_lock = rw_lock - self.__init_sleep_time = init_sleep_time - self.__sleep_time = sleep_time - self.buffer_read = None - """a copy of a the buffer read while in critical section""" - self.entry_time = None - """Time of entry to the critical section""" - self.exit_time = None - """Time of exit from the critical section""" - - def run(self): - time.sleep(self.__init_sleep_time) - self.__rw_lock.reader_acquire() - self.entry_time = time.time() - time.sleep(self.__sleep_time) - self.buffer_read = copy.deepcopy(self.__buffer) - self.exit_time = time.time() - self.__rw_lock.reader_release() - - -class RWLockTestCase(unittest.TestCase): - def test_readers_nonexclusive_access(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Reader(buffer_, rw_lock, 0, 0)) - threads.append(Writer(buffer_, rw_lock, 0.2, 0.4, 1)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0.3)) - threads.append(Reader(buffer_, rw_lock, 0.5, 0)) - - self.__start_and_join_threads(threads) - - ## The third reader should enter after the second one but it should - ## exit before the second one exits - ## (i.e. the readers should be in the critical section - ## at the same time) - - self.assertEqual([], threads[0].buffer_read) - self.assertEqual([1], threads[2].buffer_read) - self.assertEqual([1], threads[3].buffer_read) - self.assertTrue(threads[1].exit_time <= threads[2].entry_time) - self.assertTrue(threads[2].entry_time <= threads[3].entry_time) - self.assertTrue(threads[3].exit_time < threads[2].exit_time) - - def test_writers_exclusive_access(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Writer(buffer_, rw_lock, 0, 0.4, 1)) - threads.append(Writer(buffer_, rw_lock, 0.1, 0, 2)) - threads.append(Reader(buffer_, rw_lock, 0.2, 0)) - - self.__start_and_join_threads(threads) - - ## The second writer should wait for the first one to exit - - self.assertEqual([1, 2], threads[2].buffer_read) - self.assertTrue(threads[0].exit_time <= threads[1].entry_time) - self.assertTrue(threads[1].exit_time <= threads[2].exit_time) - - def test_writer_priority(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) - threads.append(Reader(buffer_, rw_lock, 0.1, 0.4)) - threads.append(Writer(buffer_, rw_lock, 0.2, 0, 2)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0)) - - self.__start_and_join_threads(threads) - - ## The second writer should go before the second and the third reader - - self.assertEqual([1], threads[1].buffer_read) - self.assertEqual([1, 2], threads[3].buffer_read) - self.assertEqual([1, 2], threads[4].buffer_read) - self.assertTrue(threads[0].exit_time < threads[1].entry_time) - self.assertTrue(threads[1].exit_time <= threads[2].entry_time) - self.assertTrue(threads[2].exit_time <= threads[3].entry_time) - self.assertTrue(threads[2].exit_time <= threads[4].entry_time) - - def test_many_writers_priority(self): - (buffer_, rw_lock, threads) = self.__init_variables() - - threads.append(Writer(buffer_, rw_lock, 0, 0, 1)) - threads.append(Reader(buffer_, rw_lock, 0.1, 0.6)) - threads.append(Writer(buffer_, rw_lock, 0.2, 0.1, 2)) - threads.append(Reader(buffer_, rw_lock, 0.3, 0)) - threads.append(Reader(buffer_, rw_lock, 0.4, 0)) - threads.append(Writer(buffer_, rw_lock, 0.5, 0.1, 3)) - - self.__start_and_join_threads(threads) - - ## The two last writers should go first -- after the first reader and - ## before the second and the third reader - - self.assertEqual([1], threads[1].buffer_read) - self.assertEqual([1, 2, 3], threads[3].buffer_read) - self.assertEqual([1, 2, 3], threads[4].buffer_read) - self.assertTrue(threads[0].exit_time < threads[1].entry_time) - self.assertTrue(threads[1].exit_time <= threads[2].entry_time) - self.assertTrue(threads[1].exit_time <= threads[5].entry_time) - self.assertTrue(threads[2].exit_time <= threads[3].entry_time) - self.assertTrue(threads[2].exit_time <= threads[4].entry_time) - self.assertTrue(threads[5].exit_time <= threads[3].entry_time) - self.assertTrue(threads[5].exit_time <= threads[4].entry_time) - - @staticmethod - def __init_variables(): - buffer_ = [] - rw_lock = RWLock() - threads = [] - return (buffer_, rw_lock, threads) - - @staticmethod - def __start_and_join_threads(threads): - for t in threads: - t.start() - for t in threads: - t.join() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py deleted file mode 100644 index 2c6bd15cd..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/test_sha3.py +++ /dev/null @@ -1,111 +0,0 @@ -try: - import unittest2 as unittest -except ImportError: - import unittest -import pytest - -try: - from gmpy2 import mpz - - GMPY = True -except ImportError: - try: - from gmpy import mpz - - GMPY = True - except ImportError: - GMPY = False - -from ._sha3 import shake_256 -from ._compat import bytes_to_int, int_to_bytes - -B2I_VECTORS = [ - (b"\x00\x01", "big", 1), - (b"\x00\x01", "little", 0x0100), - (b"", "big", 0), - (b"\x00", "little", 0), -] - - -@pytest.mark.parametrize("bytes_in,endian,int_out", B2I_VECTORS) -def test_bytes_to_int(bytes_in, endian, int_out): - out = bytes_to_int(bytes_in, endian) - assert out == int_out - - -class TestBytesToInt(unittest.TestCase): - def test_bytes_to_int_wrong_endian(self): - with self.assertRaises(ValueError): - bytes_to_int(b"\x00", "middle") - - def test_int_to_bytes_wrong_endian(self): - with self.assertRaises(ValueError): - int_to_bytes(0, byteorder="middle") - - -@pytest.mark.skipif(GMPY == False, reason="requites gmpy or gmpy2") -def test_int_to_bytes_with_gmpy(): - assert int_to_bytes(mpz(1)) == b"\x01" - - -I2B_VECTORS = [ - (0, None, "big", b""), - (0, 1, "big", b"\x00"), - (1, None, "big", b"\x01"), - (0x0100, None, "little", b"\x00\x01"), - (0x0100, 4, "little", b"\x00\x01\x00\x00"), - (1, 4, "big", b"\x00\x00\x00\x01"), -] - - -@pytest.mark.parametrize("int_in,length,endian,bytes_out", I2B_VECTORS) -def test_int_to_bytes(int_in, length, endian, bytes_out): - out = int_to_bytes(int_in, length, endian) - assert out == bytes_out - - -SHAKE_256_VECTORS = [ - ( - b"Message.", - 32, - b"\x78\xa1\x37\xbb\x33\xae\xe2\x72\xb1\x02\x4f\x39\x43\xe5\xcf\x0c" - b"\x4e\x9c\x72\x76\x2e\x34\x4c\xf8\xf9\xc3\x25\x9d\x4f\x91\x2c\x3a", - ), - ( - b"", - 32, - b"\x46\xb9\xdd\x2b\x0b\xa8\x8d\x13\x23\x3b\x3f\xeb\x74\x3e\xeb\x24" - b"\x3f\xcd\x52\xea\x62\xb8\x1b\x82\xb5\x0c\x27\x64\x6e\xd5\x76\x2f", - ), - ( - b"message", - 32, - b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" - b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75", - ), - ( - b"message", - 16, - b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51", - ), - ( - b"message", - 64, - b"\x86\x16\xe1\xe4\xcf\xd8\xb5\xf7\xd9\x2d\x43\xd8\x6e\x1b\x14\x51" - b"\xa2\xa6\x5a\xf8\x64\xfc\xb1\x26\xc2\x66\x0a\xb3\x46\x51\xb1\x75" - b"\x30\xd6\xba\x2a\x46\x65\xf1\x9d\xf0\x62\x25\xb1\x26\xd1\x3e\xed" - b"\x91\xd5\x0d\xe7\xb9\xcb\x65\xf3\x3a\x46\xae\xd3\x6c\x7d\xc5\xe8", - ), - ( - b"A" * 1024, - 32, - b"\xa5\xef\x7e\x30\x8b\xe8\x33\x64\xe5\x9c\xf3\xb5\xf3\xba\x20\xa3" - b"\x5a\xe7\x30\xfd\xbc\x33\x11\xbf\x83\x89\x50\x82\xb4\x41\xe9\xb3", - ), -] - - -@pytest.mark.parametrize("msg,olen,ohash", SHAKE_256_VECTORS) -def test_shake_256(msg, olen, ohash): - out = shake_256(msg, olen) - assert out == bytearray(ohash) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py b/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py deleted file mode 100644 index 9a5611053..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/ecdsa/util.py +++ /dev/null @@ -1,433 +0,0 @@ -from __future__ import division - -import os -import math -import binascii -import sys -from hashlib import sha256 -from six import PY2, int2byte, b, next -from . import der -from ._compat import normalise_bytes - -# RFC5480: -# The "unrestricted" algorithm identifier is: -# id-ecPublicKey OBJECT IDENTIFIER ::= { -# iso(1) member-body(2) us(840) ansi-X9-62(10045) keyType(2) 1 } - -oid_ecPublicKey = (1, 2, 840, 10045, 2, 1) -encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) - -# RFC5480: -# The ECDH algorithm uses the following object identifier: -# id-ecDH OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) schemes(1) -# ecdh(12) } - -oid_ecDH = (1, 3, 132, 1, 12) - -# RFC5480: -# The ECMQV algorithm uses the following object identifier: -# id-ecMQV OBJECT IDENTIFIER ::= { -# iso(1) identified-organization(3) certicom(132) schemes(1) -# ecmqv(13) } - -oid_ecMQV = (1, 3, 132, 1, 13) - -if sys.version_info >= (3,): # pragma: no branch - - def entropy_to_bits(ent_256): - """Convert a bytestring to string of 0's and 1's""" - return bin(int.from_bytes(ent_256, "big"))[2:].zfill(len(ent_256) * 8) - -else: - - def entropy_to_bits(ent_256): - """Convert a bytestring to string of 0's and 1's""" - return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256) - - -if sys.version_info < (2, 7): # pragma: no branch - # Can't add a method to a built-in type so we are stuck with this - def bit_length(x): - return len(bin(x)) - 2 - -else: - - def bit_length(x): - return x.bit_length() or 1 - - -def orderlen(order): - return (1 + len("%x" % order)) // 2 # bytes - - -def randrange(order, entropy=None): - """Return a random integer k such that 1 <= k < order, uniformly - distributed across that range. Worst case should be a mean of 2 loops at - (2**k)+2. - - Note that this function is not declared to be forwards-compatible: we may - change the behavior in future releases. The entropy= argument (which - should get a callable that behaves like os.urandom) can be used to - achieve stability within a given release (for repeatable unit tests), but - should not be used as a long-term-compatible key generation algorithm. - """ - assert order > 1 - if entropy is None: - entropy = os.urandom - upper_2 = bit_length(order - 2) - upper_256 = upper_2 // 8 + 1 - while True: # I don't think this needs a counter with bit-wise randrange - ent_256 = entropy(upper_256) - ent_2 = entropy_to_bits(ent_256) - rand_num = int(ent_2[:upper_2], base=2) + 1 - if 0 < rand_num < order: - return rand_num - - -class PRNG: - # this returns a callable which, when invoked with an integer N, will - # return N pseudorandom bytes. Note: this is a short-term PRNG, meant - # primarily for the needs of randrange_from_seed__trytryagain(), which - # only needs to run it a few times per seed. It does not provide - # protection against state compromise (forward security). - def __init__(self, seed): - self.generator = self.block_generator(seed) - - def __call__(self, numbytes): - a = [next(self.generator) for i in range(numbytes)] - - if PY2: # pragma: no branch - return "".join(a) - else: - return bytes(a) - - def block_generator(self, seed): - counter = 0 - while True: - for byte in sha256( - ("prng-%d-%s" % (counter, seed)).encode() - ).digest(): - yield byte - counter += 1 - - -def randrange_from_seed__overshoot_modulo(seed, order): - # hash the data, then turn the digest into a number in [1,order). - # - # We use David-Sarah Hopwood's suggestion: turn it into a number that's - # sufficiently larger than the group order, then modulo it down to fit. - # This should give adequate (but not perfect) uniformity, and simple - # code. There are other choices: try-try-again is the main one. - base = PRNG(seed)(2 * orderlen(order)) - number = (int(binascii.hexlify(base), 16) % (order - 1)) + 1 - assert 1 <= number < order, (1, number, order) - return number - - -def lsb_of_ones(numbits): - return (1 << numbits) - 1 - - -def bits_and_bytes(order): - bits = int(math.log(order - 1, 2) + 1) - bytes = bits // 8 - extrabits = bits % 8 - return bits, bytes, extrabits - - -# the following randrange_from_seed__METHOD() functions take an -# arbitrarily-sized secret seed and turn it into a number that obeys the same -# range limits as randrange() above. They are meant for deriving consistent -# signing keys from a secret rather than generating them randomly, for -# example a protocol in which three signing keys are derived from a master -# secret. You should use a uniformly-distributed unguessable seed with about -# curve.baselen bytes of entropy. To use one, do this: -# seed = os.urandom(curve.baselen) # or other starting point -# secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) -# sk = SigningKey.from_secret_exponent(secexp, curve) - - -def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): - # hash the seed, then turn the digest into a number in [1,order), but - # don't worry about trying to uniformly fill the range. This will lose, - # on average, four bits of entropy. - bits, _bytes, extrabits = bits_and_bytes(order) - if extrabits: - _bytes += 1 - base = hashmod(seed).digest()[:_bytes] - base = "\x00" * (_bytes - len(base)) + base - number = 1 + int(binascii.hexlify(base), 16) - assert 1 <= number < order - return number - - -def randrange_from_seed__truncate_bits(seed, order, hashmod=sha256): - # like string_to_randrange_truncate_bytes, but only lose an average of - # half a bit - bits = int(math.log(order - 1, 2) + 1) - maxbytes = (bits + 7) // 8 - base = hashmod(seed).digest()[:maxbytes] - base = "\x00" * (maxbytes - len(base)) + base - topbits = 8 * maxbytes - bits - if topbits: - base = int2byte(ord(base[0]) & lsb_of_ones(topbits)) + base[1:] - number = 1 + int(binascii.hexlify(base), 16) - assert 1 <= number < order - return number - - -def randrange_from_seed__trytryagain(seed, order): - # figure out exactly how many bits we need (rounded up to the nearest - # bit), so we can reduce the chance of looping to less than 0.5 . This is - # specified to feed from a byte-oriented PRNG, and discards the - # high-order bits of the first byte as necessary to get the right number - # of bits. The average number of loops will range from 1.0 (when - # order=2**k-1) to 2.0 (when order=2**k+1). - assert order > 1 - bits, bytes, extrabits = bits_and_bytes(order) - generate = PRNG(seed) - while True: - extrabyte = b("") - if extrabits: - extrabyte = int2byte(ord(generate(1)) & lsb_of_ones(extrabits)) - guess = string_to_number(extrabyte + generate(bytes)) + 1 - if 1 <= guess < order: - return guess - - -def number_to_string(num, order): - l = orderlen(order) - fmt_str = "%0" + str(2 * l) + "x" - string = binascii.unhexlify((fmt_str % num).encode()) - assert len(string) == l, (len(string), l) - return string - - -def number_to_string_crop(num, order): - l = orderlen(order) - fmt_str = "%0" + str(2 * l) + "x" - string = binascii.unhexlify((fmt_str % num).encode()) - return string[:l] - - -def string_to_number(string): - return int(binascii.hexlify(string), 16) - - -def string_to_number_fixedlen(string, order): - l = orderlen(order) - assert len(string) == l, (len(string), l) - return int(binascii.hexlify(string), 16) - - -# these methods are useful for the sigencode= argument to SK.sign() and the -# sigdecode= argument to VK.verify(), and control how the signature is packed -# or unpacked. - - -def sigencode_strings(r, s, order): - r_str = number_to_string(r, order) - s_str = number_to_string(s, order) - return (r_str, s_str) - - -def sigencode_string(r, s, order): - """ - Encode the signature to raw format (:term:`raw encoding`) - - It's expected that this function will be used as a `sigencode=` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: raw encoding of ECDSA signature - :rtype: bytes - """ - # for any given curve, the size of the signature numbers is - # fixed, so just use simple concatenation - r_str, s_str = sigencode_strings(r, s, order) - return r_str + s_str - - -def sigencode_der(r, s, order): - """ - Encode the signature into the ECDSA-Sig-Value structure using :term:`DER`. - - Encodes the signature to the following :term:`ASN.1` structure:: - - Ecdsa-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - - It's expected that this function will be used as a `sigencode=` parameter - in :func:`ecdsa.keys.SigningKey.sign` method. - - :param int r: first parameter of the signature - :param int s: second parameter of the signature - :param int order: the order of the curve over which the signature was - computed - - :return: DER encoding of ECDSA signature - :rtype: bytes - """ - return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) - - -# canonical versions of sigencode methods -# these enforce low S values, by negating the value (modulo the order) if -# above order/2 see CECKey::Sign() -# https://github.com/bitcoin/bitcoin/blob/master/src/key.cpp#L214 -def sigencode_strings_canonize(r, s, order): - if s > order / 2: - s = order - s - return sigencode_strings(r, s, order) - - -def sigencode_string_canonize(r, s, order): - if s > order / 2: - s = order - s - return sigencode_string(r, s, order) - - -def sigencode_der_canonize(r, s, order): - if s > order / 2: - s = order - s - return sigencode_der(r, s, order) - - -class MalformedSignature(Exception): - """ - Raised by decoding functions when the signature is malformed. - - Malformed in this context means that the relevant strings or integers - do not match what a signature over provided curve would create. Either - because the byte strings have incorrect lengths or because the encoded - values are too large. - """ - - pass - - -def sigdecode_string(signature, order): - """ - Decoder for :term:`raw encoding` of ECDSA signatures. - - raw encoding is a simple concatenation of the two integers that comprise - the signature, with each encoded using the same amount of bytes depending - on curve size/order. - - It's expected that this function will be used as the `sigdecode=` - parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. - - :param signature: encoded signature - :type signature: bytes like object - :param order: order of the curve over which the signature was computed - :type order: int - - :raises MalformedSignature: when the encoding of the signature is invalid - - :return: tuple with decoded 'r' and 's' values of signature - :rtype: tuple of ints - """ - signature = normalise_bytes(signature) - l = orderlen(order) - if not len(signature) == 2 * l: - raise MalformedSignature( - "Invalid length of signature, expected {0} bytes long, " - "provided string is {1} bytes long".format(2 * l, len(signature)) - ) - r = string_to_number_fixedlen(signature[:l], order) - s = string_to_number_fixedlen(signature[l:], order) - return r, s - - -def sigdecode_strings(rs_strings, order): - """ - Decode the signature from two strings. - - First string needs to be a big endian encoding of 'r', second needs to - be a big endian encoding of the 's' parameter of an ECDSA signature. - - It's expected that this function will be used as the `sigdecode=` - parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. - - :param list rs_strings: list of two bytes-like objects, each encoding one - parameter of signature - :param int order: order of the curve over which the signature was computed - - :raises MalformedSignature: when the encoding of the signature is invalid - - :return: tuple with decoded 'r' and 's' values of signature - :rtype: tuple of ints - """ - if not len(rs_strings) == 2: - raise MalformedSignature( - "Invalid number of strings provided: {0}, expected 2".format( - len(rs_strings) - ) - ) - (r_str, s_str) = rs_strings - r_str = normalise_bytes(r_str) - s_str = normalise_bytes(s_str) - l = orderlen(order) - if not len(r_str) == l: - raise MalformedSignature( - "Invalid length of first string ('r' parameter), " - "expected {0} bytes long, provided string is {1} " - "bytes long".format(l, len(r_str)) - ) - if not len(s_str) == l: - raise MalformedSignature( - "Invalid length of second string ('s' parameter), " - "expected {0} bytes long, provided string is {1} " - "bytes long".format(l, len(s_str)) - ) - r = string_to_number_fixedlen(r_str, order) - s = string_to_number_fixedlen(s_str, order) - return r, s - - -def sigdecode_der(sig_der, order): - """ - Decoder for DER format of ECDSA signatures. - - DER format of signature is one that uses the :term:`ASN.1` :term:`DER` - rules to encode it as a sequence of two integers:: - - Ecdsa-Sig-Value ::= SEQUENCE { - r INTEGER, - s INTEGER - } - - It's expected that this function will be used as as the `sigdecode=` - parameter to the :func:`ecdsa.keys.VerifyingKey.verify` method. - - :param sig_der: encoded signature - :type sig_der: bytes like object - :param order: order of the curve over which the signature was computed - :type order: int - - :raises UnexpectedDER: when the encoding of signature is invalid - - :return: tuple with decoded 'r' and 's' values of signature - :rtype: tuple of ints - """ - sig_der = normalise_bytes(sig_der) - # return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) - rs_strings, empty = der.remove_sequence(sig_der) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER sig: %s" % binascii.hexlify(empty) - ) - r, rest = der.remove_integer(rs_strings) - s, empty = der.remove_integer(rest) - if empty != b"": - raise der.UnexpectedDER( - "trailing junk after DER numbers: %s" % binascii.hexlify(empty) - ) - return r, s diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py deleted file mode 100644 index c742493dd..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__init__.py +++ /dev/null @@ -1,284 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import os -import sys -from collections import namedtuple -from io import StringIO - -import espefuse.efuse.esp32 as esp32_efuse -import espefuse.efuse.esp32c2 as esp32c2_efuse -import espefuse.efuse.esp32c3 as esp32c3_efuse -import espefuse.efuse.esp32c6 as esp32c6_efuse -import espefuse.efuse.esp32h2beta1 as esp32h2beta1_efuse -import espefuse.efuse.esp32s2 as esp32s2_efuse -import espefuse.efuse.esp32s3 as esp32s3_efuse -import espefuse.efuse.esp32s3beta2 as esp32s3beta2_efuse - -import esptool - -DefChip = namedtuple("DefChip", ["chip_name", "efuse_lib", "chip_class"]) - -SUPPORTED_BURN_COMMANDS = [ - "read_protect_efuse", - "write_protect_efuse", - "burn_efuse", - "burn_block_data", - "burn_bit", - "burn_key", - "burn_key_digest", - "burn_custom_mac", - "set_flash_voltage", - "execute_scripts", -] - -SUPPORTED_COMMANDS = [ - "summary", - "dump", - "get_custom_mac", - "adc_info", - "check_error", -] + SUPPORTED_BURN_COMMANDS - -SUPPORTED_CHIPS = { - "esp32": DefChip("ESP32", esp32_efuse, esptool.targets.ESP32ROM), - "esp32c2": DefChip("ESP32-C2", esp32c2_efuse, esptool.targets.ESP32C2ROM), - "esp32c3": DefChip("ESP32-C3", esp32c3_efuse, esptool.targets.ESP32C3ROM), - "esp32c6": DefChip("ESP32-C6", esp32c6_efuse, esptool.targets.ESP32C6ROM), - "esp32h2beta1": DefChip( - "ESP32-H2(beta1)", esp32h2beta1_efuse, esptool.targets.ESP32H2BETA1ROM - ), - "esp32s2": DefChip("ESP32-S2", esp32s2_efuse, esptool.targets.ESP32S2ROM), - "esp32s3": DefChip("ESP32-S3", esp32s3_efuse, esptool.targets.ESP32S3ROM), - "esp32s3beta2": DefChip( - "ESP32-S3(beta2)", esp32s3beta2_efuse, esptool.targets.ESP32S3BETA2ROM - ), -} - - -def get_esp( - port, - baud, - connect_mode, - chip="auto", - skip_connect=False, - virt=False, - debug=False, - virt_efuse_file=None, -): - if chip not in ["auto"] + list(SUPPORTED_CHIPS.keys()): - raise esptool.FatalError("get_esp: Unsupported chip (%s)" % chip) - if virt: - efuse = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).efuse_lib - esp = efuse.EmulateEfuseController(virt_efuse_file, debug) - else: - if chip == "auto" and not skip_connect: - esp = esptool.cmds.detect_chip(port, baud, connect_mode) - else: - esp = SUPPORTED_CHIPS.get(chip, SUPPORTED_CHIPS["esp32"]).chip_class( - port if not skip_connect else StringIO(), baud - ) - if not skip_connect: - esp.connect(connect_mode) - return esp - - -def get_efuses(esp, skip_connect=False, debug_mode=False, do_not_confirm=False): - for name in SUPPORTED_CHIPS: - if SUPPORTED_CHIPS[name].chip_name == esp.CHIP_NAME: - efuse = SUPPORTED_CHIPS[name].efuse_lib - return ( - efuse.EspEfuses(esp, skip_connect, debug_mode, do_not_confirm), - efuse.operations, - ) - else: - raise esptool.FatalError("get_efuses: Unsupported chip (%s)" % esp.CHIP_NAME) - - -def split_on_groups(all_args): - """ - This function splits the all_args list into groups, - where each item is a cmd with all its args. - - Example: - all_args: - ['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem', - 'burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', - 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS'] - - used_cmds: ['burn_key_digest', 'burn_key'] - groups: - [['burn_key_digest', 'secure_images/ecdsa256_secure_boot_signing_key_v2.pem'], - ['burn_key', 'BLOCK_KEY0', 'images/efuse/128bit_key', - 'XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS']] - """ - - groups = [] - cmd = [] - used_cmds = [] - for item in all_args: - if item in SUPPORTED_COMMANDS: - used_cmds.append(item) - if cmd != []: - groups.append(cmd) - cmd = [] - cmd.append(item) - if cmd: - groups.append(cmd) - return groups, used_cmds - - -def main(custom_commandline=None): - """ - Main function for espefuse - - custom_commandline - Optional override for default arguments parsing - (that uses sys.argv), can be a list of custom arguments as strings. - Arguments and their values need to be added as individual items to the list - e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. - """ - init_parser = argparse.ArgumentParser( - description="espefuse.py v%s - [ESP32xx] efuse get/set tool" - % esptool.__version__, - prog="espefuse", - add_help=False, - ) - - init_parser.add_argument( - "--chip", - "-c", - help="Target chip type", - choices=["auto"] + list(SUPPORTED_CHIPS.keys()), - default=os.environ.get("ESPTOOL_CHIP", "auto"), - ) - - init_parser.add_argument( - "--baud", - "-b", - help="Serial port baud rate used when flashing/reading", - type=esptool.arg_auto_int, - default=os.environ.get("ESPTOOL_BAUD", esptool.loader.ESPLoader.ESP_ROM_BAUD), - ) - - init_parser.add_argument( - "--port", - "-p", - help="Serial port device", - default=os.environ.get("ESPTOOL_PORT", esptool.loader.ESPLoader.DEFAULT_PORT), - ) - - init_parser.add_argument( - "--before", - help="What to do before connecting to the chip", - choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], - default="default_reset", - ) - - init_parser.add_argument( - "--debug", - "-d", - help="Show debugging information (loglevel=DEBUG)", - action="store_true", - ) - init_parser.add_argument( - "--virt", - help="For host tests, the tool will work in the virtual mode " - "(without connecting to a chip).", - action="store_true", - ) - init_parser.add_argument( - "--path-efuse-file", - help="For host tests, saves efuse memory to file.", - type=str, - default=None, - ) - init_parser.add_argument( - "--do-not-confirm", - help="Do not pause for confirmation before permanently writing efuses. " - "Use with caution.", - action="store_true", - ) - - common_args, remaining_args = init_parser.parse_known_args(custom_commandline) - debug_mode = common_args.debug or ("dump" in remaining_args) - just_print_help = [ - True for arg in remaining_args if arg in ["--help", "-h"] - ] or remaining_args == [] - - print("espefuse.py v{}".format(esptool.__version__)) - - try: - esp = get_esp( - common_args.port, - common_args.baud, - common_args.before, - common_args.chip, - just_print_help, - common_args.virt, - common_args.debug, - common_args.path_efuse_file, - ) - except esptool.FatalError as e: - raise esptool.FatalError( - f"{e}\nPlease make sure that you have specified " - "the right port with the --port argument" - ) # TODO: Require the --port argument in the next major release, ESPTOOL-490 - - efuses, efuse_operations = get_efuses( - esp, just_print_help, debug_mode, common_args.do_not_confirm - ) - - parser = argparse.ArgumentParser(parents=[init_parser]) - subparsers = parser.add_subparsers( - dest="operation", help="Run espefuse.py {command} -h for additional help" - ) - - efuse_operations.add_commands(subparsers, efuses) - - grouped_remaining_args, used_cmds = split_on_groups(remaining_args) - if len(grouped_remaining_args) == 0: - parser.print_help() - parser.exit(1) - there_are_multiple_burn_commands_in_args = ( - sum(cmd in SUPPORTED_BURN_COMMANDS for cmd in used_cmds) > 1 - ) - if there_are_multiple_burn_commands_in_args: - efuses.batch_mode_cnt += 1 - - for rem_args in grouped_remaining_args: - args, unused_args = parser.parse_known_args(rem_args, namespace=common_args) - if args.operation is None: - parser.print_help() - parser.exit(1) - assert len(unused_args) == 0, 'Not all commands were recognized "{}"'.format( - unused_args - ) - - operation_func = vars(efuse_operations)[args.operation] - # each 'operation' is a module-level function of the same name - print('\n=== Run "{}" command ==='.format(args.operation)) - operation_func(esp, efuses, args) - - if there_are_multiple_burn_commands_in_args: - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - raise esptool.FatalError("BURN was not done") - - if common_args.virt is False: - esp._port.close() - - -def _main(): - try: - main() - except esptool.FatalError as e: - print("\nA fatal error occurred: %s" % e) - sys.exit(2) - - -if __name__ == "__main__": - _main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py deleted file mode 100644 index 4b068d6e0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/__main__.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import espefuse - -if __name__ == "__main__": - espefuse._main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py deleted file mode 100644 index 7bc640704..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_fields.py +++ /dev/null @@ -1,725 +0,0 @@ -#!/usr/bin/env python -# -# This file describes the common eFuses structures for chips -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import re -import sys - -from bitstring import BitArray, BitString, CreationError - -import esptool - -from . import util - - -class CheckArgValue(object): - def __init__(self, efuses, name): - self.efuses = efuses - self.name = name - - def __call__(self, new_value_str): - def check_arg_value(efuse, new_value): - if efuse.efuse_type.startswith("bool"): - new_value = 1 if new_value is None else int(new_value, 0) - if new_value != 1: - raise esptool.FatalError( - "New value is not accepted for efuse '{}' " - "(will always burn 0->1), given value={}".format( - efuse.name, new_value - ) - ) - elif efuse.efuse_type.startswith(("int", "uint")): - if efuse.efuse_class == "bitcount": - if new_value is None: - # find the first unset bit and set it - old_value = efuse.get_raw() - new_value = old_value - bit = 1 - while new_value == old_value: - new_value = bit | old_value - bit <<= 1 - else: - new_value = int(new_value, 0) - else: - if new_value is None: - raise esptool.FatalError( - "New value required for efuse '{}' (given None)".format( - efuse.name - ) - ) - new_value = int(new_value, 0) - if new_value == 0: - raise esptool.FatalError( - "New value should not be 0 for '{}' " - "(given value= {})".format(efuse.name, new_value) - ) - elif efuse.efuse_type.startswith("bytes"): - if new_value is None: - raise esptool.FatalError( - "New value required for efuse '{}' " - "(given None)".format(efuse.name) - ) - if len(new_value) * 8 != efuse.bitarray.len: - raise esptool.FatalError( - "The length of efuse '{}' ({} bits) " - "(given len of the new value= {} bits)".format( - efuse.name, efuse.bitarray.len, len(new_value) * 8 - ) - ) - else: - raise esptool.FatalError( - "The '{}' type for the '{}' efuse is not supported yet.".format( - efuse.efuse_type, efuse.name - ) - ) - return new_value - - efuse = self.efuses[self.name] - new_value = efuse.check_format(new_value_str) - return check_arg_value(efuse, new_value) - - -class EfuseProtectBase(object): - # This class is used by EfuseBlockBase and EfuseFieldBase - - def get_read_disable_mask(self): - mask = 0 - if isinstance(self.read_disable_bit, list): - for i in self.read_disable_bit: - mask |= 1 << i - else: - mask = 1 << self.read_disable_bit - return mask - - def is_readable(self): - """Return true if the efuse is readable by software""" - num_bit = self.read_disable_bit - if num_bit is None: - return True # read cannot be disabled - return (self.parent["RD_DIS"].get() & (self.get_read_disable_mask())) == 0 - - def disable_read(self): - num_bit = self.read_disable_bit - if num_bit is None: - raise esptool.FatalError("This efuse cannot be read-disabled") - if not self.parent["RD_DIS"].is_writeable(): - raise esptool.FatalError( - "This efuse cannot be read-disabled due the to RD_DIS field is " - "already write-disabled" - ) - self.parent["RD_DIS"].save(self.get_read_disable_mask()) - - def is_writeable(self): - num_bit = self.write_disable_bit - if num_bit is None: - return True # write cannot be disabled - return (self.parent["WR_DIS"].get() & (1 << num_bit)) == 0 - - def disable_write(self): - num_bit = self.write_disable_bit - if not self.parent["WR_DIS"].is_writeable(): - raise esptool.FatalError( - "This efuse cannot be write-disabled due to the WR_DIS field is " - "already write-disabled" - ) - self.parent["WR_DIS"].save(1 << num_bit) - - def check_wr_rd_protect(self): - if not self.is_readable(): - error_msg = "\t{} is read-protected.".format(self.name) - "The written value can not be read, the efuse/block looks as all 0.\n" - error_msg += "\tBurn in this case may damage an already written value." - self.parent.print_error_msg(error_msg) - if not self.is_writeable(): - error_msg = "\t{} is write-protected. Burn is not possible.".format( - self.name - ) - self.parent.print_error_msg(error_msg) - - -class EfuseBlockBase(EfuseProtectBase): - def __init__(self, parent, param, skip_read=False): - self.parent = parent - self.name = param.name - self.alias = param.alias - self.id = param.id - self.rd_addr = param.rd_addr - self.wr_addr = param.wr_addr - self.write_disable_bit = param.write_disable_bit - self.read_disable_bit = param.read_disable_bit - self.len = param.len - self.key_purpose_name = param.key_purpose - bit_block_len = self.get_block_len() * 8 - self.bitarray = BitString(bit_block_len) - self.bitarray.set(0) - self.wr_bitarray = BitString(bit_block_len) - self.wr_bitarray.set(0) - self.fail = False - self.num_errors = 0 - if self.id == 0: - self.err_bitarray = BitString(bit_block_len) - self.err_bitarray.set(0) - else: - self.err_bitarray = None - - if not skip_read: - self.read() - - def get_block_len(self): - coding_scheme = self.get_coding_scheme() - if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: - return self.len * 4 - elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: - return (self.len * 3 // 4) * 4 - elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: - return self.len * 4 - else: - raise esptool.FatalError( - "Coding scheme (%d) not supported" % (coding_scheme) - ) - - def get_coding_scheme(self): - if self.id == 0: - return self.parent.REGS.CODING_SCHEME_NONE - else: - return self.parent.coding_scheme - - def get_raw(self, from_read=True): - if from_read: - return self.bitarray.bytes - else: - return self.wr_bitarray.bytes - - def get(self, from_read=True): - self.get_bitstring(from_read=from_read) - - def get_bitstring(self, from_read=True): - if from_read: - return self.bitarray - else: - return self.wr_bitarray - - def convert_to_bitstring(self, new_data): - if isinstance(new_data, BitArray): - return new_data - else: - return BitArray(bytes=new_data, length=len(new_data) * 8) - - def get_words(self): - def get_offsets(self): - return [x + self.rd_addr for x in range(0, self.get_block_len(), 4)] - - return [self.parent.read_reg(offs) for offs in get_offsets(self)] - - def read(self): - words = self.get_words() - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - self.bitarray.overwrite(data, pos=0) - self.print_block(self.bitarray, "read_regs") - - def print_block(self, bit_string, comment, debug=False): - if self.parent.debug or debug: - bit_string.pos = 0 - print( - "%-15s (%-16s) [%-2d] %s:" - % (self.name, " ".join(self.alias)[:16], self.id, comment), - " ".join( - [ - "%08x" % word - for word in bit_string.readlist( - "%d*uint:32" % (bit_string.len / 32) - )[::-1] - ] - ), - ) - - def check_wr_data(self): - wr_data = self.wr_bitarray - if wr_data.all(False): - # nothing to burn - if self.parent.debug: - print("[{:02}] {:20} nothing to burn".format(self.id, self.name)) - return False - if len(wr_data.bytes) != len(self.bitarray.bytes): - raise esptool.FatalError( - "Data does not fit: the block%d size is %d bytes, data is %d bytes" - % (self.id, len(self.bitarray.bytes), len(wr_data.bytes)) - ) - self.check_wr_rd_protect() - - if self.get_bitstring().all(False): - print( - "[{:02}] {:20} is empty, will burn the new value".format( - self.id, self.name - ) - ) - else: - # the written block in chip is not empty - if self.get_bitstring() == wr_data: - print( - "[{:02}] {:20} is already written the same value, " - "continue with EMPTY_BLOCK".format(self.id, self.name) - ) - wr_data.set(0) - else: - print("[{:02}] {:20} is not empty".format(self.id, self.name)) - print("\t(written ):", self.get_bitstring()) - print("\t(to write):", wr_data) - mask = self.get_bitstring() & wr_data - if mask == wr_data: - print( - "\tAll wr_data bits are set in the written block, " - "continue with EMPTY_BLOCK." - ) - wr_data.set(0) - else: - coding_scheme = self.get_coding_scheme() - if coding_scheme == self.parent.REGS.CODING_SCHEME_NONE: - print("\t(coding scheme = NONE)") - elif coding_scheme == self.parent.REGS.CODING_SCHEME_RS: - print("\t(coding scheme = RS)") - error_msg = ( - "\tBurn into %s is forbidden " - "(RS coding scheme does not allow this)." % (self.name) - ) - self.parent.print_error_msg(error_msg) - elif coding_scheme == self.parent.REGS.CODING_SCHEME_34: - print("\t(coding scheme = 3/4)") - data_can_not_be_burn = False - for i in range(0, self.get_bitstring().len, 6 * 8): - rd_chunk = self.get_bitstring()[i : i + 6 * 8 :] - wr_chunk = wr_data[i : i + 6 * 8 :] - if rd_chunk.any(True): - if wr_chunk.any(True): - print( - "\twritten chunk [%d] and wr_chunk " - "are not empty. " % (i // (6 * 8)), - end="", - ) - if rd_chunk == wr_chunk: - print( - "wr_chunk == rd_chunk. " - "Countinue with empty chunk." - ) - wr_data[i : i + 6 * 8 :].set(0) - else: - print("wr_chunk != rd_chunk. Can not burn.") - print("\twritten ", rd_chunk) - print("\tto write", wr_chunk) - data_can_not_be_burn = True - if data_can_not_be_burn: - error_msg = ( - "\tBurn into %s is forbidden " - "(3/4 coding scheme does not allow this)." % (self.name) - ) - self.parent.print_error_msg(error_msg) - else: - raise esptool.FatalError( - "The coding scheme ({}) is not supported".format( - coding_scheme - ) - ) - - def save(self, new_data): - # new_data will be checked by check_wr_data() during burn_all() - # new_data (bytes) = [0][1][2] ... [N] (original data) - # in string format = [0] [1] [2] ... [N] (util.hexify(data, " ")) - # in hex format = 0x[N]....[2][1][0] (from bitstring print(data)) - # in reg format = [3][2][1][0] ... [N][][][] (as it will be in the device) - # in bitstring = [N] ... [2][1][0] (to get a correct bitstring - # need to reverse new_data) - # *[x] - means a byte. - data = BitString(bytes=new_data[::-1], length=len(new_data) * 8) - if self.parent.debug: - print( - "\twritten : {} ->\n\tto write: {}".format(self.get_bitstring(), data) - ) - self.wr_bitarray.overwrite(self.wr_bitarray | data, pos=0) - - def burn_words(self, words): - for burns in range(3): - self.parent.efuse_controller_setup() - if self.parent.debug: - print("Write data to BLOCK%d" % (self.id)) - write_reg_addr = self.wr_addr - for word in words: - # for ep32s2: using EFUSE_PGM_DATA[0..7]_REG for writing data - # 32 bytes to EFUSE_PGM_DATA[0..7]_REG - # 12 bytes to EFUSE_CHECK_VALUE[0..2]_REG. These regs are next after - # EFUSE_PGM_DATA_REG - # for esp32: - # each block has the special regs EFUSE_BLK[0..3]_WDATA[0..7]_REG - # for writing data - if self.parent.debug: - print("Addr 0x%08x, data=0x%08x" % (write_reg_addr, word)) - self.parent.write_reg(write_reg_addr, word) - write_reg_addr += 4 - - self.parent.write_efuses(self.id) - for _ in range(5): - self.parent.efuse_read() - self.parent.get_coding_scheme_warnings(silent=True) - if self.fail or self.num_errors: - print( - "Error in BLOCK%d, re-burn it again (#%d), to fix it. " - "fail_bit=%d, num_errors=%d" - % (self.id, burns, self.fail, self.num_errors) - ) - break - if not self.fail and self.num_errors == 0: - break - - def burn(self): - if self.wr_bitarray.all(False): - # nothing to burn - return - before_burn_bitarray = self.bitarray[:] - assert before_burn_bitarray is not self.bitarray - self.print_block(self.wr_bitarray, "to_write") - words = self.apply_coding_scheme() - self.burn_words(words) - self.read() - if not self.is_readable(): - print( - "{} ({}) is read-protected. " - "Read back the burn value is not possible.".format( - self.name, self.alias - ) - ) - if self.bitarray.all(False): - print("Read all '0'") - else: - # Should never happen - raise esptool.FatalError( - "The {} is read-protected but not all '0' ({})".format( - self.name, self.bitarray.hex - ) - ) - else: - if self.wr_bitarray == self.bitarray: - print("BURN BLOCK%-2d - OK (write block == read block)" % self.id) - elif ( - self.wr_bitarray & self.bitarray == self.wr_bitarray - and self.bitarray & before_burn_bitarray == before_burn_bitarray - ): - print("BURN BLOCK%-2d - OK (all write block bits are set)" % self.id) - else: - # Happens only when an efuse is written and read-protected - # in one command - self.print_block(self.wr_bitarray, "Expected") - self.print_block(self.bitarray, "Real ") - # Read-protected BLK0 values are reported back as zeros, - # raise error only for other blocks - if self.id != 0: - raise esptool.FatalError( - "Burn {} ({}) was not successful".format(self.name, self.alias) - ) - self.wr_bitarray.set(0) - - -class EspEfusesBase(object): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - _esp = None - blocks = [] - efuses = [] - coding_scheme = None - force_write_always = None - batch_mode_cnt = 0 - - def __iter__(self): - return self.efuses.__iter__() - - def get_crystal_freq(self): - return self._esp.get_crystal_freq() - - def read_efuse(self, n): - """Read the nth word of the ESP3x EFUSE region.""" - return self._esp.read_efuse(n) - - def read_reg(self, addr): - return self._esp.read_reg(addr) - - def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): - return self._esp.write_reg(addr, value, mask, delay_us, delay_after_us) - - def update_reg(self, addr, mask, new_val): - return self._esp.update_reg(addr, mask, new_val) - - def efuse_controller_setup(self): - pass - - def reconnect_chip(self, esp): - print("Re-connecting...") - baudrate = esp._port.baudrate - port = esp._port.port - esp._port.close() - return esptool.cmds.detect_chip(port, baudrate) - - def get_index_block_by_name(self, name): - for block in self.blocks: - if block.name == name or name in block.alias: - return block.id - return None - - def read_blocks(self): - for block in self.blocks: - block.read() - - def update_efuses(self): - for efuse in self.efuses: - efuse.update(self.blocks[efuse.block].bitarray) - - def burn_all(self, check_batch_mode=False): - if check_batch_mode: - if self.batch_mode_cnt != 0: - print( - "\nBatch mode is enabled, " - "the burn will be done at the end of the command." - ) - return False - print("\nCheck all blocks for burn...") - print("idx, BLOCK_NAME, Conclusion") - have_wr_data_for_burn = False - for block in self.blocks: - block.check_wr_data() - if not have_wr_data_for_burn and block.get_bitstring(from_read=False).any( - True - ): - have_wr_data_for_burn = True - if not have_wr_data_for_burn: - print("Nothing to burn, see messages above.") - return - EspEfusesBase.confirm("", self.do_not_confirm) - - # Burn from BLKn -> BLK0. Because BLK0 can set rd or/and wr protection bits. - for block in reversed(self.blocks): - old_fail = block.fail - old_num_errors = block.num_errors - block.burn() - if (block.fail and old_fail != block.fail) or ( - block.num_errors and block.num_errors > old_num_errors - ): - raise esptool.FatalError("Error(s) were detected in eFuses") - print("Reading updated efuses...") - self.read_coding_scheme() - self.read_blocks() - self.update_efuses() - return True - - @staticmethod - def confirm(action, do_not_confirm): - print( - "%s%s\nThis is an irreversible operation!" - % (action, "" if action.endswith("\n") else ". ") - ) - if not do_not_confirm: - print("Type 'BURN' (all capitals) to continue.") - # required for Pythons which disable line buffering, ie mingw in mintty - sys.stdout.flush() - yes = input() - if yes != "BURN": - print("Aborting.") - sys.exit(0) - - def print_error_msg(self, error_msg): - if self.force_write_always is not None: - if not self.force_write_always: - error_msg += "(use '--force-write-always' option to ignore it)" - if self.force_write_always: - print(error_msg, "Skipped because '--force-write-always' option.") - else: - raise esptool.FatalError(error_msg) - - -class EfuseFieldBase(EfuseProtectBase): - def __init__(self, parent, param): - self.category = param.category - self.parent = parent - self.block = param.block - self.word = param.word - self.pos = param.pos - self.write_disable_bit = param.write_disable_bit - self.read_disable_bit = param.read_disable_bit - self.name = param.name - self.efuse_class = param.class_type - self.efuse_type = param.type - self.description = param.description - self.dict_value = param.dictionary - self.fail = False - self.num_errors = 0 - if self.efuse_type.startswith("bool"): - field_len = 1 - else: - field_len = int(re.search(r"\d+", self.efuse_type).group()) - if self.efuse_type.startswith("bytes"): - field_len *= 8 - self.bitarray = BitString(field_len) - self.bit_len = field_len - self.bitarray.set(0) - self.update(self.parent.blocks[self.block].bitarray) - - def check_format(self, new_value_str): - if new_value_str is None: - return new_value_str - else: - if self.efuse_type.startswith("bytes"): - if new_value_str.startswith("0x"): - # cmd line: 0x0102030405060708 .... 112233ff (hex) - # regs: 112233ff ... 05060708 01020304 - # BLK: ff 33 22 11 ... 08 07 06 05 04 03 02 01 - return binascii.unhexlify(new_value_str[2:])[::-1] - else: - # cmd line: 0102030405060708 .... 112233ff (string) - # regs: 04030201 08070605 ... ff332211 - # BLK: 01 02 03 04 05 06 07 08 ... 11 22 33 ff - return binascii.unhexlify(new_value_str) - else: - return new_value_str - - def convert_to_bitstring(self, new_value): - if isinstance(new_value, BitArray): - return new_value - else: - if self.efuse_type.startswith("bytes"): - # new_value (bytes) = [0][1][2] ... [N] - # (original data) - # in string format = [0] [1] [2] ... [N] - # (util.hexify(data, " ")) - # in hex format = 0x[N]....[2][1][0] - # (from bitstring print(data)) - # in reg format = [3][2][1][0] ... [N][][][] - # (as it will be in the device) - # in bitstring = [N] ... [2][1][0] - # (to get a correct bitstring need to reverse new_value) - # *[x] - means a byte. - return BitArray(bytes=new_value[::-1], length=len(new_value) * 8) - else: - try: - return BitArray(self.efuse_type + "={}".format(new_value)) - except CreationError as err: - print( - "New value '{}' is not suitable for {} ({})".format( - new_value, self.name, self.efuse_type - ) - ) - raise esptool.FatalError(err) - - def check_new_value(self, bitarray_new_value): - bitarray_old_value = self.get_bitstring() | self.get_bitstring(from_read=False) - if bitarray_new_value.len != bitarray_old_value.len: - raise esptool.FatalError( - "For {} efuse, the length of the new value is wrong, " - "expected {} bits, was {} bits.".format( - self.name, bitarray_old_value.len, bitarray_new_value.len - ) - ) - if bitarray_new_value == bitarray_old_value: - error_msg = "\tThe same value for {} ".format(self.name) - error_msg += "is already burned. Do not change the efuse." - print(error_msg) - bitarray_new_value.set(0) - elif bitarray_new_value == self.get_bitstring(from_read=False): - error_msg = "\tThe same value for {} ".format(self.name) - error_msg += "is already prepared for the burn operation." - print(error_msg) - bitarray_new_value.set(0) - else: - if self.name not in ["WR_DIS", "RD_DIS"]: - # WR_DIS, RD_DIS fields can have already set bits. - # Do not neeed to check below condition for them. - if bitarray_new_value | bitarray_old_value != bitarray_new_value: - error_msg = "\tNew value contains some bits that cannot be cleared " - error_msg += "(value will be {})".format( - bitarray_old_value | bitarray_new_value - ) - self.parent.print_error_msg(error_msg) - self.check_wr_rd_protect() - - def save_to_block(self, bitarray_field): - block = self.parent.blocks[self.block] - wr_bitarray_temp = block.wr_bitarray.copy() - position = wr_bitarray_temp.length - ( - self.word * 32 + self.pos + bitarray_field.len - ) - wr_bitarray_temp.overwrite(bitarray_field, pos=position) - block.wr_bitarray |= wr_bitarray_temp - - def save(self, new_value): - bitarray_field = self.convert_to_bitstring(new_value) - self.check_new_value(bitarray_field) - self.save_to_block(bitarray_field) - - def update(self, bit_array_block): - if self.word is None or self.pos is None: - self.bitarray.overwrite(self.convert_to_bitstring(self.get()), pos=0) - return - field_len = self.bitarray.len - bit_array_block.pos = bit_array_block.length - ( - self.word * 32 + self.pos + field_len - ) - self.bitarray.overwrite(bit_array_block.read(field_len), pos=0) - err_bitarray = self.parent.blocks[self.block].err_bitarray - if err_bitarray is not None: - err_bitarray.pos = err_bitarray.length - ( - self.word * 32 + self.pos + field_len - ) - self.fail = not err_bitarray.read(field_len).all(False) - else: - self.fail = self.parent.blocks[self.block].fail - self.num_errors = self.parent.blocks[self.block].num_errors - - def get_raw(self, from_read=True): - """Return the raw (unformatted) numeric value of the efuse bits - - Returns a simple integer or (for some subclasses) a bitstring. - type: int or bool -> int - type: bytes -> bytearray - """ - return self.get_bitstring(from_read).read(self.efuse_type) - - def get(self, from_read=True): - """Get a formatted version of the efuse value, suitable for display - type: int or bool -> int - type: bytes -> string "01 02 03 04 05 06 07 08 ... ". - Byte order [0] ... [N]. dump regs: 0x04030201 0x08070605 ... - """ - if self.efuse_type.startswith("bytes"): - return util.hexify(self.get_bitstring(from_read).bytes[::-1], " ") - else: - return self.get_raw(from_read) - - def get_meaning(self, from_read=True): - """Get the meaning of efuse from dict if possible, suitable for display""" - if self.dict_value: - try: - return self.dict_value[self.get_raw(from_read)] - except KeyError: - pass - return self.get(from_read) - - def get_bitstring(self, from_read=True): - if from_read: - self.bitarray.pos = 0 - return self.bitarray - else: - field_len = self.bitarray.len - block = self.parent.blocks[self.block] - block.wr_bitarray.pos = block.wr_bitarray.length - ( - self.word * 32 + self.pos + field_len - ) - return block.wr_bitarray.read(self.bitarray.len) - - def burn(self, new_value): - # Burn a efuse. Added for compatibility reason. - self.save(new_value) - self.parent.burn_all() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py deleted file mode 100644 index c74bb5003..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/base_operations.py +++ /dev/null @@ -1,675 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the common operations with eFuses for chips -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import json -import sys - -from bitstring import BitString - -import esptool - -from . import base_fields -from . import util - - -def add_common_commands(subparsers, efuses): - class ActionEfuseValuePair(argparse.Action): - def __init__(self, option_strings, dest, nargs=None, **kwargs): - self._nargs = nargs - self._choices = kwargs.get("efuse_choices") - self.efuses = kwargs.get("efuses") - del kwargs["efuse_choices"] - del kwargs["efuses"] - super(ActionEfuseValuePair, self).__init__( - option_strings, dest, nargs=nargs, **kwargs - ) - - def __call__(self, parser, namespace, values, option_string=None): - def check_efuse_name(efuse_name, efuse_list): - if efuse_name not in self._choices: - raise esptool.FatalError( - "Invalid the efuse name '{}'. " - "Available the efuse names: {}".format( - efuse_name, self._choices - ) - ) - - efuse_value_pairs = {} - if len(values) > 1: - if len(values) % 2: - raise esptool.FatalError( - "The list does not have a valid pair (name value) {}".format( - values - ) - ) - for i in range(0, len(values), 2): - efuse_name, new_value = values[i : i + 2 :] - check_efuse_name(efuse_name, self._choices) - check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) - efuse_value_pairs[efuse_name] = check_arg(new_value) - else: - # For the case of compatibility, when only the efuse_name is given - # Fields with 'bitcount' and 'bool' types can be without new_value arg - efuse_name = values[0] - check_efuse_name(efuse_name, self._choices) - check_arg = base_fields.CheckArgValue(self.efuses, efuse_name) - efuse_value_pairs[efuse_name] = check_arg(None) - setattr(namespace, self.dest, efuse_value_pairs) - - burn = subparsers.add_parser( - "burn_efuse", help="Burn the efuse with the specified name" - ) - burn.add_argument( - "name_value_pairs", - help="Name of efuse register and New value pairs to burn", - action=ActionEfuseValuePair, - nargs="+", - metavar="[EFUSE_NAME VALUE] [{} VALUE".format( - " VALUE] [".join([e.name for e in efuses.efuses]) - ), - efuse_choices=[e.name for e in efuses.efuses], - efuses=efuses, - ) - - read_protect_efuse = subparsers.add_parser( - "read_protect_efuse", - help="Disable readback for the efuse with the specified name", - ) - read_protect_efuse.add_argument( - "efuse_name", - help="Name of efuse register to burn", - nargs="+", - choices=[e.name for e in efuses.efuses if e.read_disable_bit is not None], - ) - - write_protect_efuse = subparsers.add_parser( - "write_protect_efuse", - help="Disable writing to the efuse with the specified name", - ) - write_protect_efuse.add_argument( - "efuse_name", - help="Name of efuse register to burn", - nargs="+", - choices=[e.name for e in efuses.efuses if e.write_disable_bit is not None], - ) - - burn_block_data = subparsers.add_parser( - "burn_block_data", - help="Burn non-key data to EFUSE blocks. " - "(Don't use this command to burn key data for Flash Encryption or " - "ESP32 Secure Boot V1, as the byte order of keys is swapped (use burn_key)).", - ) - add_force_write_always(burn_block_data) - burn_block_data.add_argument( - "--offset", "-o", help="Byte offset in the efuse block", type=int, default=0 - ) - burn_block_data.add_argument( - "block", - help="Efuse block to burn.", - action="append", - choices=efuses.BURN_BLOCK_DATA_NAMES, - ) - burn_block_data.add_argument( - "datafile", - help="File containing data to burn into the efuse block", - action="append", - type=argparse.FileType("rb"), - ) - for _ in range(0, len(efuses.BURN_BLOCK_DATA_NAMES)): - burn_block_data.add_argument( - "block", - help="Efuse block to burn.", - metavar="BLOCK", - nargs="?", - action="append", - choices=efuses.BURN_BLOCK_DATA_NAMES, - ) - burn_block_data.add_argument( - "datafile", - nargs="?", - help="File containing data to burn into the efuse block", - metavar="DATAFILE", - action="append", - type=argparse.FileType("rb"), - ) - - set_bit_cmd = subparsers.add_parser("burn_bit", help="Burn bit in the efuse block.") - add_force_write_always(set_bit_cmd) - set_bit_cmd.add_argument( - "block", help="Efuse block to burn.", choices=efuses.BURN_BLOCK_DATA_NAMES - ) - set_bit_cmd.add_argument( - "bit_number", - help="Bit number in the efuse block [0..BLK_LEN-1]", - nargs="+", - type=int, - ) - - subparsers.add_parser( - "adc_info", - help="Display information about ADC calibration data stored in efuse.", - ) - - dump_cmd = subparsers.add_parser("dump", help="Dump raw hex values of all efuses") - dump_cmd.add_argument( - "--file_name", - help="Saves dump for each block into separate file. Provide the common " - "path name /path/blk.bin, it will create: blk0.bin, blk1.bin ... blkN.bin. " - "Use burn_block_data to write it back to another chip.", - ) - - summary_cmd = subparsers.add_parser( - "summary", help="Print human-readable summary of efuse values" - ) - summary_cmd.add_argument( - "--format", - help="Select the summary format", - choices=["summary", "json"], - default="summary", - ) - summary_cmd.add_argument( - "--file", - help="File to save the efuse summary", - type=argparse.FileType("w"), - default=sys.stdout, - ) - - execute_scripts = subparsers.add_parser( - "execute_scripts", help="Executes scripts to burn at one time." - ) - execute_scripts.add_argument( - "scripts", - help="The special format of python scripts.", - nargs="+", - type=argparse.FileType("r"), - ) - execute_scripts.add_argument( - "--index", - help="integer index. " - "It allows to retrieve unique data per chip from configfiles " - "and then burn them (ex. CUSTOM_MAC, UNIQUE_ID).", - type=int, - ) - execute_scripts.add_argument( - "--configfiles", - help="List of configfiles with data", - nargs="?", - action="append", - type=argparse.FileType("r"), - ) - - check_error_cmd = subparsers.add_parser("check_error", help="Checks eFuse errors") - check_error_cmd.add_argument( - "--recovery", - help="Recovery of BLOCKs after encoding errors", - action="store_true", - ) - - -def add_force_write_always(p): - p.add_argument( - "--force-write-always", - help="Write the efuse even if it looks like it's already been written, " - "or is write protected. Note that this option can't disable write protection, " - "or clear any bit which has already been set.", - action="store_true", - ) - - -def summary(esp, efuses, args): - """Print a human-readable summary of efuse contents""" - ROW_FORMAT = "%-50s %-50s%s = %s %s %s" - human_output = args.format == "summary" - json_efuse = {} - if args.file != sys.stdout: - print("Saving efuse values to " + args.file.name) - if human_output: - print( - ROW_FORMAT.replace("-50", "-12") - % ( - "EFUSE_NAME (Block)", - "Description", - "", - "[Meaningful Value]", - "[Readable/Writeable]", - "(Hex Value)", - ), - file=args.file, - ) - print("-" * 88, file=args.file) - for category in sorted(set(e.category for e in efuses), key=lambda c: c.title()): - if human_output: - print("%s fuses:" % category.title(), file=args.file) - for e in (e for e in efuses if e.category == category): - if e.efuse_type.startswith("bytes"): - raw = "" - else: - raw = "({})".format(e.get_bitstring()) - (readable, writeable) = (e.is_readable(), e.is_writeable()) - if readable and writeable: - perms = "R/W" - elif readable: - perms = "R/-" - elif writeable: - perms = "-/W" - else: - perms = "-/-" - base_value = e.get_meaning() - value = str(base_value) - if not readable: - value = value.replace("0", "?") - if human_output: - print( - ROW_FORMAT - % ( - e.get_info(), - e.description[:50], - "\n " if len(value) > 20 else "", - value, - perms, - raw, - ), - file=args.file, - ) - desc_len = len(e.description[50:]) - if desc_len: - desc_len += 50 - for i in range(50, desc_len, 50): - print( - "%-50s %-50s" % ("", e.description[i : (50 + i)]), - file=args.file, - ) - if args.format == "json": - json_efuse[e.name] = { - "name": e.name, - "value": base_value if readable else value, - "readable": readable, - "writeable": writeable, - "description": e.description, - "category": e.category, - "block": e.block, - "word": e.word, - "pos": e.pos, - "efuse_type": e.efuse_type, - "bit_len": e.bit_len, - } - if human_output: - print("", file=args.file) - if human_output: - print(efuses.summary(), file=args.file) - warnings = efuses.get_coding_scheme_warnings() - if warnings: - print( - "WARNING: Coding scheme has encoding bit error warnings", file=args.file - ) - if args.file != sys.stdout: - args.file.close() - print("Done") - if args.format == "json": - json.dump(json_efuse, args.file, sort_keys=True, indent=4) - print("") - - -def dump(esp, efuses, args): - """Dump raw efuse data registers""" - # Using --debug option allows to print dump. - # Nothing to do here. The log will be printed - # during EspEfuses.__init__() in self.read_blocks() - if args.file_name: - # save dump to the file - for block in efuses.blocks: - file_dump_name = args.file_name - place_for_index = file_dump_name.find(".bin") - file_dump_name = ( - file_dump_name[:place_for_index] - + str(block.id) - + file_dump_name[place_for_index:] - ) - print(file_dump_name) - with open(file_dump_name, "wb") as f: - block.get_bitstring().byteswap() - block.get_bitstring().tofile(f) - - -def burn_efuse(esp, efuses, args): - def print_attention(blocked_efuses_after_burn): - if len(blocked_efuses_after_burn): - print( - " ATTENTION! This BLOCK uses NOT the NONE coding scheme " - "and after 'BURN', these efuses can not be burned in the feature:" - ) - for i in range(0, len(blocked_efuses_after_burn), 5): - print( - " ", - "".join("{}".format(blocked_efuses_after_burn[i : i + 5 :])), - ) - - efuse_name_list = [name for name in args.name_value_pairs.keys()] - burn_efuses_list = [efuses[name] for name in efuse_name_list] - old_value_list = [efuses[name].get_raw() for name in efuse_name_list] - new_value_list = [value for value in args.name_value_pairs.values()] - util.check_duplicate_name_in_list(efuse_name_list) - - attention = "" - print("The efuses to burn:") - for block in efuses.blocks: - burn_list_a_block = [e for e in burn_efuses_list if e.block == block.id] - if len(burn_list_a_block): - print(" from BLOCK%d" % (block.id)) - for field in burn_list_a_block: - print(" - %s" % (field.name)) - if ( - efuses.blocks[field.block].get_coding_scheme() - != efuses.REGS.CODING_SCHEME_NONE - ): - using_the_same_block_names = [ - e.name for e in efuses if e.block == field.block - ] - wr_names = [e.name for e in burn_list_a_block] - blocked_efuses_after_burn = [ - name - for name in using_the_same_block_names - if name not in wr_names - ] - attention = " (see 'ATTENTION!' above)" - if attention: - print_attention(blocked_efuses_after_burn) - - print("\nBurning efuses{}:".format(attention)) - for efuse, new_value in zip(burn_efuses_list, new_value_list): - print( - "\n - '{}' ({}) {} -> {}".format( - efuse.name, - efuse.description, - efuse.get_bitstring(), - efuse.convert_to_bitstring(new_value), - ) - ) - efuse.save(new_value) - - print() - if "ENABLE_SECURITY_DOWNLOAD" in efuse_name_list: - print( - "ENABLE_SECURITY_DOWNLOAD -> 1: eFuses will not be read back " - "for confirmation because this mode disables " - "any SRAM and register operations." - ) - print(" espefuse will not work.") - print(" esptool can read/write only flash.") - - if "DIS_DOWNLOAD_MODE" in efuse_name_list: - print( - "DIS_DOWNLOAD_MODE -> 1: eFuses will not be read back for " - "confirmation because this mode disables any communication with the chip." - ) - print( - " espefuse/esptool will not work because " - "they will not be able to connect to the chip." - ) - - if ( - esp.CHIP_NAME == "ESP32" - and esp.get_chip_revision() >= 300 - and "UART_DOWNLOAD_DIS" in efuse_name_list - ): - print( - "UART_DOWNLOAD_DIS -> 1: eFuses will be read for confirmation, " - "but after that connection to the chip will become impossible." - ) - print(" espefuse/esptool will not work.") - - if not efuses.burn_all(check_batch_mode=True): - return - - print("Checking efuses...") - raise_error = False - for efuse, old_value, new_value in zip( - burn_efuses_list, old_value_list, new_value_list - ): - if not efuse.is_readable(): - print( - "Efuse %s is read-protected. Read back the burn value is not possible." - % efuse.name - ) - else: - new_value = efuse.convert_to_bitstring(new_value) - burned_value = efuse.get_bitstring() - if burned_value != new_value: - print( - burned_value, - "->", - new_value, - "Efuse %s failed to burn. Protected?" % efuse.name, - ) - raise_error = True - if raise_error: - raise esptool.FatalError("The burn was not successful.") - else: - print("Successful") - - -def read_protect_efuse(esp, efuses, args): - util.check_duplicate_name_in_list(args.efuse_name) - - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if not efuse.is_readable(): - print("Efuse %s is already read protected" % efuse.name) - else: - if esp.CHIP_NAME == "ESP32": - if ( - efuse_name == "BLOCK2" - and not efuses["ABS_DONE_0"].get() - and esp.get_chip_revision() >= 300 - ): - if efuses["ABS_DONE_1"].get(): - raise esptool.FatalError( - "Secure Boot V2 is on (ABS_DONE_1 = True), " - "BLOCK2 must be readable, stop this operation!" - ) - else: - print( - "If Secure Boot V2 is used, BLOCK2 must be readable, " - "please stop this operation!" - ) - elif esp.CHIP_NAME == "ESP32-C2": - error = ( - not efuses["XTS_KEY_LENGTH_256"].get() - and efuse_name == "BLOCK_KEY0" - ) - error |= efuses["SECURE_BOOT_EN"].get() and efuse_name in [ - "BLOCK_KEY0", - "BLOCK_KEY0_HI_128", - ] - if error: - raise esptool.FatalError( - "%s must be readable, stop this operation!" % efuse_name - ) - else: - for block in efuses.Blocks.BLOCKS: - block = efuses.Blocks.get(block) - if block.name == efuse_name and block.key_purpose is not None: - if not efuses[block.key_purpose].need_rd_protect( - efuses[block.key_purpose].get() - ): - raise esptool.FatalError( - "%s must be readable, stop this operation!" % efuse_name - ) - break - # make full list of which efuses will be disabled - # (ie share a read disable bit) - all_disabling = [ - e for e in efuses if e.read_disable_bit == efuse.read_disable_bit - ] - names = ", ".join(e.name for e in all_disabling) - print( - "Permanently read-disabling efuse%s %s" - % ("s" if len(all_disabling) > 1 else "", names) - ) - efuse.disable_read() - - if not efuses.burn_all(check_batch_mode=True): - return - - print("Checking efuses...") - raise_error = False - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if efuse.is_readable(): - print("Efuse %s is not read-protected." % efuse.name) - raise_error = True - if raise_error: - raise esptool.FatalError("The burn was not successful.") - else: - print("Successful") - - -def write_protect_efuse(esp, efuses, args): - util.check_duplicate_name_in_list(args.efuse_name) - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if not efuse.is_writeable(): - print("Efuse %s is already write protected" % efuse.name) - else: - # make full list of which efuses will be disabled - # (ie share a write disable bit) - all_disabling = [ - e for e in efuses if e.write_disable_bit == efuse.write_disable_bit - ] - names = ", ".join(e.name for e in all_disabling) - print( - "Permanently write-disabling efuse%s %s" - % ("s" if len(all_disabling) > 1 else "", names) - ) - efuse.disable_write() - - if not efuses.burn_all(check_batch_mode=True): - return - - print("Checking efuses...") - raise_error = False - for efuse_name in args.efuse_name: - efuse = efuses[efuse_name] - if efuse.is_writeable(): - print("Efuse %s is not write-protected." % efuse.name) - raise_error = True - if raise_error: - raise esptool.FatalError("The burn was not successful.") - else: - print("Successful") - - -def burn_block_data(esp, efuses, args): - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - datafile_list = args.datafile[ - 0 : len([name for name in args.datafile if name is not None]) : - ] - efuses.force_write_always = args.force_write_always - - util.check_duplicate_name_in_list(block_name_list) - if args.offset and len(block_name_list) > 1: - raise esptool.FatalError( - "The 'offset' option is not applicable when a few blocks are passed. " - "With 'offset', should only one block be used." - ) - else: - offset = args.offset - if offset: - num_block = efuses.get_index_block_by_name(block_name_list[0]) - block = efuses.blocks[num_block] - num_bytes = block.get_block_len() - if offset >= num_bytes: - raise esptool.FatalError( - "Invalid offset: the block%d only holds %d bytes." - % (block.id, num_bytes) - ) - if len(block_name_list) != len(datafile_list): - raise esptool.FatalError( - "The number of block_name (%d) and datafile (%d) should be the same." - % (len(block_name_list), len(datafile_list)) - ) - - for block_name, datafile in zip(block_name_list, datafile_list): - num_block = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[num_block] - data = datafile.read() - num_bytes = block.get_block_len() - if offset != 0: - data = (b"\x00" * offset) + data - data = data + (b"\x00" * (num_bytes - len(data))) - if len(data) != num_bytes: - raise esptool.FatalError( - "Data does not fit: the block%d size is %d bytes, " - "data file is %d bytes, offset %d" - % (block.id, num_bytes, len(data), offset) - ) - print( - "[{:02}] {:20} size={:02} bytes, offset={:02} - > [{}].".format( - block.id, block.name, len(data), offset, util.hexify(data, " ") - ) - ) - block.save(data) - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_bit(esp, efuses, args): - efuses.force_write_always = args.force_write_always - num_block = efuses.get_index_block_by_name(args.block) - block = efuses.blocks[num_block] - data_block = BitString(block.get_block_len() * 8) - data_block.set(0) - try: - data_block.set(True, args.bit_number) - except IndexError: - raise esptool.FatalError( - "%s has bit_number in [0..%d]" % (args.block, data_block.len - 1) - ) - data_block.reverse() - print( - "bit_number: " - "[%-03d]........................................................[0]" - % (data_block.len - 1) - ) - print("BLOCK%-2d :" % block.id, data_block) - block.print_block(data_block, "regs_to_write", debug=True) - block.save(data_block.bytes[::-1]) - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def check_error(esp, efuses, args): - error_in_blocks = efuses.get_coding_scheme_warnings() - if args.recovery: - if error_in_blocks: - confirmed = False - for block in reversed(efuses.blocks): - if block.fail or block.num_errors > 0: - if not block.get_bitstring().all(False): - block.save(block.get_bitstring().bytes[::-1]) - if not confirmed: - confirmed = True - efuses.confirm( - "Recovery of block coding errors", args.do_not_confirm - ) - block.burn() - # Reset the recovery flag to run check_error() without it, - # just to check the new state of eFuse blocks. - args.recovery = False - check_error(esp, efuses, args) - else: - if error_in_blocks: - raise esptool.FatalError("Error(s) were detected in eFuses") - print("No errors detected") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py deleted file mode 100644 index 03329a314..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/emulate_efuse_controller_base.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import re - -from bitstring import BitString - - -class EmulateEfuseControllerBase(object): - """The class for virtual efuse operations. Using for HOST_TEST.""" - - CHIP_NAME = "" - mem = None - debug = False - Blocks = None - Fields = None - REGS = None - - def __init__(self, efuse_file=None, debug=False): - self.debug = debug - self.efuse_file = efuse_file - if self.efuse_file: - try: - self.mem = BitString( - open(self.efuse_file, "a+b"), length=self.REGS.EFUSE_MEM_SIZE * 8 - ) - except ValueError: - # the file is empty or does not fit the length. - self.mem = BitString(length=self.REGS.EFUSE_MEM_SIZE * 8) - self.mem.set(0) - self.mem.tofile(open(self.efuse_file, "a+b")) - else: - # efuse_file is not provided - # it means we do not want to keep the result of efuse operations - self.mem = BitString(self.REGS.EFUSE_MEM_SIZE * 8) - self.mem.set(0) - - """ esptool method start >> """ - - def get_chip_description(self): - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" - - def get_chip_revision(self): - return self.get_major_chip_version() * 100 + self.get_minor_chip_version() - - def read_efuse(self, n, block=0): - """Read the nth word of the ESP3x EFUSE region.""" - blk = self.Blocks.get(self.Blocks.BLOCKS[block]) - return self.read_reg(blk.rd_addr + (4 * n)) - - def read_reg(self, addr): - self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) - return self.mem.read("uint:32") - - def write_reg(self, addr, value, mask=0xFFFFFFFF, delay_us=0, delay_after_us=0): - self.mem.pos = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) - self.mem.overwrite("uint:32={}".format(value & mask)) - self.handle_writing_event(addr, value) - - def update_reg(self, addr, mask, new_val): - position = self.mem.length - ((addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + 32) - self.mem.pos = position - cur_val = self.mem.read("uint:32") - self.mem.pos = position - self.mem.overwrite("uint:32={}".format(cur_val | (new_val & mask))) - - def write_efuse(self, n, value, block=0): - """Write the nth word of the ESP3x EFUSE region.""" - blk = self.Blocks.get(self.Blocks.BLOCKS[block]) - self.write_reg(blk.wr_addr + (4 * n), value) - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - self.save_to_file() - - def save_to_file(self): - if self.efuse_file: - with open(self.efuse_file, "wb") as f: - self.mem.tofile(f) - - def handle_coding_scheme(self, blk, data): - return data - - def copy_blocks_wr_regs_to_rd_regs(self, updated_block=None): - for b in reversed(self.Blocks.BLOCKS): - blk = self.Blocks.get(b) - if updated_block is not None: - if blk.id != updated_block: - continue - data = self.read_block(blk.id, wr_regs=True) - if self.debug: - print(blk.name, data.hex) - plain_data = self.handle_coding_scheme(blk, data) - plain_data = self.check_wr_protection_area(blk.id, plain_data) - self.update_block(blk, plain_data) - - def clean_blocks_wr_regs(self): - for b in self.Blocks.BLOCKS: - blk = self.Blocks.get(b) - for offset in range(0, blk.len * 4, 4): - wr_addr = blk.wr_addr + offset - self.write_reg(wr_addr, 0) - - def read_field(self, name, bitstring=True): - for e in self.Fields.EFUSES: - field = self.Fields.get(e) - if field.name == name: - self.read_block(field.block) - block = self.read_block(field.block) - if field.type.startswith("bool"): - field_len = 1 - else: - field_len = int(re.search(r"\d+", field.type).group()) - if field.type.startswith("bytes"): - field_len *= 8 - block.pos = block.length - (field.word * 32 + field.pos + field_len) - if bitstring: - return block.read(field_len) - else: - return block.read(field.type) - return None - - def get_bitlen_of_block(self, blk, wr=False): - return 32 * blk.len - - def read_block(self, idx, wr_regs=False): - block = None - for b in self.Blocks.BLOCKS: - blk = self.Blocks.get(b) - if blk.id == idx: - blk_len_bits = self.get_bitlen_of_block(blk, wr=wr_regs) - addr = blk.wr_addr if wr_regs else blk.rd_addr - self.mem.pos = self.mem.length - ( - (addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + blk_len_bits - ) - block = self.mem.read(blk_len_bits) - break - return block - - def update_block(self, blk, wr_data): - wr_data = self.read_block(blk.id) | wr_data - self.overwrite_mem_from_block(blk, wr_data) - - def overwrite_mem_from_block(self, blk, wr_data): - self.mem.pos = self.mem.length - ( - (blk.rd_addr - self.REGS.DR_REG_EFUSE_BASE) * 8 + wr_data.len - ) - self.mem.overwrite(wr_data) - - def check_wr_protection_area(self, num_blk, wr_data): - # checks fields which have the write protection bit. - # if the write protection bit is set, we need to protect that area from changes. - write_disable_bit = self.read_field("WR_DIS", bitstring=False) - mask_wr_data = BitString(len(wr_data)) - mask_wr_data.set(0) - blk = self.Blocks.get(self.Blocks.BLOCKS[num_blk]) - if blk.write_disable_bit is not None and write_disable_bit & ( - 1 << blk.write_disable_bit - ): - mask_wr_data.set(1) - else: - for e in self.Fields.EFUSES: - field = self.Fields.get(e) - if blk.id == field.block and field.block == num_blk: - if field.write_disable_bit is not None and write_disable_bit & ( - 1 << field.write_disable_bit - ): - data = self.read_field(field.name) - data.set(1) - mask_wr_data.pos = mask_wr_data.length - ( - field.word * 32 + field.pos + data.len - ) - mask_wr_data.overwrite(data) - mask_wr_data.invert() - return wr_data & mask_wr_data - - def check_rd_protection_area(self): - # checks fields which have the read protection bits. - # if the read protection bit is set then we need to reset this field to 0. - read_disable_bit = self.read_field("RD_DIS", bitstring=False) - for b in self.Blocks.BLOCKS: - blk = self.Blocks.get(b) - block = self.read_block(blk.id) - if blk.read_disable_bit is not None and read_disable_bit & ( - 1 << blk.read_disable_bit - ): - block.set(0) - else: - for e in self.Fields.EFUSES: - field = self.Fields.get(e) - if ( - blk.id == field.block - and field.read_disable_bit is not None - and read_disable_bit & (1 << field.read_disable_bit) - ): - raw_data = self.read_field(field.name) - raw_data.set(0) - block.pos = block.length - ( - field.word * 32 + field.pos + raw_data.length - ) - block.overwrite(BitString(raw_data.length)) - self.overwrite_mem_from_block(blk, block) - - def clean_mem(self): - self.mem.set(0) - if self.efuse_file: - with open(self.efuse_file, "wb") as f: - self.mem.tofile(f) - - -class FatalError(RuntimeError): - """ - Wrapper class for runtime errors that aren't caused by internal bugs - """ - - def __init__(self, message): - RuntimeError.__init__(self, message) - - @staticmethod - def WithResult(message, result): - return FatalError(result) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py deleted file mode 100644 index b0011e0b6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/emulate_efuse_controller.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import time - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operations. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - - """ esptool method start >> """ - - def get_major_chip_version(self): - return 3 - - def get_minor_chip_version(self): - return 0 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - def read_reg(self, addr): - if addr == self.REGS.APB_CTL_DATE_ADDR: - return self.REGS.APB_CTL_DATE_V << self.REGS.APB_CTL_DATE_S - else: - val = 0 - if addr == self.REGS.EFUSE_BLK0_RDATA3_REG: - val = self.REGS.EFUSE_RD_CHIP_VER_REV1 - if addr == self.REGS.EFUSE_BLK0_RDATA5_REG: - val = self.REGS.EFUSE_RD_CHIP_VER_REV2 - return val | super(EmulateEfuseController, self).read_reg(addr) - - """ << esptool method end """ - - def send_burn_cmd(self): - def wait_idle(): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: - return - raise FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) - wait_idle() - self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) - self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) - wait_idle() - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_REG_CMD: - if value == self.REGS.EFUSE_CMD_WRITE: - self.write_reg(addr, 0) - elif value == self.REGS.EFUSE_CMD_READ: - self.copy_blocks_wr_regs_to_rd_regs() - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.save_to_file() - - def read_raw_coding_scheme(self): - return ( - self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) - & self.REGS.EFUSE_CODING_SCHEME_MASK - ) - - def write_raw_coding_scheme(self, value): - self.write_efuse( - self.REGS.EFUSE_CODING_SCHEME_WORD, - value & self.REGS.EFUSE_CODING_SCHEME_MASK, - ) - self.send_burn_cmd() - if value != self.read_raw_coding_scheme(): - raise FatalError( - "Error during a burning process to set the new coding scheme" - ) - print("Set coding scheme = %d" % self.read_raw_coding_scheme()) - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - return 32 * blk.len - else: - coding_scheme = self.read_raw_coding_scheme() - if coding_scheme == self.REGS.CODING_SCHEME_NONE: - return 32 * blk.len - elif coding_scheme == self.REGS.CODING_SCHEME_34: - if wr: - return 32 * 8 - else: - return 32 * blk.len * 3 // 4 - else: - raise FatalError( - "The {} coding scheme is not supported".format(coding_scheme) - ) - - def handle_coding_scheme(self, blk, data): - # it verifies the coding scheme part of data and returns just data - if blk.id != 0 and self.read_raw_coding_scheme() == self.REGS.CODING_SCHEME_34: - # CODING_SCHEME 3/4 applied only for BLK1..3 - # Takes 24 byte sequence to be represented in 3/4 encoding, - # returns 8 words suitable for writing "encoded" to an efuse block - data.pos = 0 - for _ in range(0, 4): - xor_res = 0 - mul_res = 0 - chunk_data = data.readlist("8*uint:8") - chunk_data = chunk_data[::-1] - for i in range(0, 6): - byte_data = chunk_data[i] - xor_res ^= byte_data - mul_res += (i + 1) * bin(byte_data).count("1") - if xor_res != chunk_data[6] or mul_res != chunk_data[7]: - print( - "xor_res ", - xor_res, - chunk_data[6], - "mul_res", - mul_res, - chunk_data[7], - ) - raise FatalError("Error in coding scheme data") - # cut the coded data - for i in range(0, 4): - del data[i * 6 * 8 : (i * 6 * 8) + 16] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py deleted file mode 100644 index dc9088026..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/fields.py +++ /dev/null @@ -1,500 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -import esptool - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is the same as len of a block. - return self.len - - def __init__(self, parent, param, skip_read=False): - if skip_read: - parent.coding_scheme = parent.REGS.CODING_SCHEME_NONE - else: - if parent.coding_scheme is None: - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_34: - # CODING_SCHEME 3/4 applied only for BLK1..3 - # Takes 24 byte sequence to be represented in 3/4 encoding, - # returns 8 words suitable for writing "encoded" to an efuse block - if len(data) != 24: - raise esptool.FatalError("Should take 24 bytes for 3/4 encoding.") - data = data[:24] - outbits = b"" - while len(data) > 0: # process in chunks of 6 bytes - bits = data[0:6] - data = data[6:] - xor_res = 0 - mul_res = 0 - index = 1 - for b in struct.unpack("B" * 6, bits): - xor_res ^= b - mul_res += index * util.popcnt(b) - index += 1 - outbits += bits - outbits += struct.pack("BB", xor_res, mul_res) - words = struct.unpack("<" + "I" * (len(outbits) // 4), outbits) - # returns 8 words - else: - # CODING_SCHEME NONE applied for BLK0 and BLK1..3 - # BLK0 len = 7 words, BLK1..3 len = 8 words. - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 7 words for BLK0 or 8 words for BLK1..3 - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32 chip but got for '%s'." - % (esp.CHIP_NAME) - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS_256 - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CUSTOM_MAC - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.ADC_CALIBRATION - ] - else: - if self.coding_scheme == self.REGS.CODING_SCHEME_NONE: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS_256 - ] - elif self.coding_scheme == self.REGS.CODING_SCHEME_34: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS_192 - ] - else: - raise esptool.FatalError( - "The coding scheme (%d) - is not supported" % self.coding_scheme - ) - if self["MAC_VERSION"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CUSTOM_MAC - ] - if self["BLK3_PART_RESERVE"].get(): - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.ADC_CALIBRATION - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CALC - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.CUSTOM_MAC: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CUSTOM_MAC - ] - new_fields = True - for efuse in self.Fields.ADC_CALIBRATION: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.ADC_CALIBRATION - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = ( - self.read_efuse(self.REGS.EFUSE_CODING_SCHEME_WORD) - & self.REGS.EFUSE_CODING_SCHEME_MASK - ) - - def print_status_regs(self): - print("") - print( - "{:27} 0x{:08x}".format( - "EFUSE_REG_DEC_STATUS", self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) - ) - ) - - def write_efuses(self, block): - """Write the values in the efuse write registers to - the efuse hardware, then refresh the efuse read registers. - """ - - # Configure clock - apb_freq = self.get_crystal_freq() - clk_sel0, clk_sel1, dac_clk_div = self.REGS.EFUSE_CLK_SETTINGS[apb_freq] - - self.update_reg( - self.REGS.EFUSE_DAC_CONF_REG, self.REGS.EFUSE_DAC_CLK_DIV_MASK, dac_clk_div - ) - self.update_reg( - self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL0_MASK, clk_sel0 - ) - self.update_reg( - self.REGS.EFUSE_CLK_REG, self.REGS.EFUSE_CLK_SEL1_MASK, clk_sel1 - ) - - self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_WRITE) - self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_WRITE) - - self.efuse_read() - return self.get_coding_scheme_warnings(silent=True) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - if self.read_reg(self.REGS.EFUSE_REG_CMD) == 0: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_REG_CONF, self.REGS.EFUSE_CONF_READ) - self.write_reg(self.REGS.EFUSE_REG_CMD, self.REGS.EFUSE_CMD_READ) - self.wait_efuse_idle() - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors. - Meaningless for default coding scheme (0) - """ - err = ( - self.read_reg(self.REGS.EFUSE_REG_DEC_STATUS) - & self.REGS.EFUSE_REG_DEC_STATUS_MASK - ) - for block in self.blocks: - if block.id != 0: - block.num_errors = 0 - block.fail = err != 0 - if not silent and block.fail: - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or err) and not silent: - self.print_status_regs() - return err != 0 - - def summary(self): - if self["XPD_SDIO_FORCE"].get() == 0: - output = "Flash voltage (VDD_SDIO) determined by GPIO12 on reset " - "(High for 1.8V, Low/NC for 3.3V)." - elif self["XPD_SDIO_REG"].get() == 0: - output = "Flash voltage (VDD_SDIO) internal regulator disabled by efuse." - elif self["XPD_SDIO_TIEH"].get() == 0: - output = "Flash voltage (VDD_SDIO) set to 1.8V by efuse." - else: - output = "Flash voltage (VDD_SDIO) set to 3.3V by efuse." - return output - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "spipin": EfuseSpiPinField, - "vref": EfuseVRefField, - "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, - "pkg": EfusePkg, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - return "%s (BLOCK%d):" % (self.name, self.block) - - -class EfuseMacField(EfuseField): - """ - Supports: MAC and CUSTOM_MAC fields. - (if MAC_VERSION == 1 then the CUSTOM_MAC is used) - """ - - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - @staticmethod - def get_and_check(raw_mac, stored_crc): - computed_crc = EfuseMacField.calc_crc(raw_mac) - if computed_crc == stored_crc: - valid_msg = "(CRC 0x%02x OK)" % stored_crc - else: - valid_msg = "(CRC 0x%02x invalid - calculated 0x%02x)" % ( - stored_crc, - computed_crc, - ) - return "%s %s" % (util.hexify(raw_mac, ":"), valid_msg) - - @staticmethod - def calc_crc(raw_mac): - """ - This algorithm is the equivalent of esp_crc8() in ESP32 ROM code - - This is CRC-8 w/ inverted polynomial value 0x8C & initial value 0x00. - """ - result = 0x00 - for b in struct.unpack("B" * 6, raw_mac): - result ^= b - for _ in range(8): - lsb = result & 1 - result >>= 1 - if lsb != 0: - result ^= 0x8C - return result - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - stored_crc = self.parent["CUSTOM_MAC_CRC"].get(from_read) - else: - mac = self.get_raw(from_read) - stored_crc = self.parent["MAC_CRC"].get(from_read) - return EfuseMacField.get_and_check(mac, stored_crc) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - # Writing the BLK3: - # - MAC_VERSION = 1 - # - CUSTOM_MAC = AB:CD:EF:01:02:03 - # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) - mac_version = self.parent["MAC_VERSION"] - if mac_version.get() == 0: - mac_version_value = 1 - print_field(mac_version, hex(mac_version_value)) - mac_version.save(mac_version_value) - else: - if mac_version.get() != 1: - if not self.parent.force_write_always: - raise esptool.FatalError( - "MAC_VERSION = {}, should be 0 or 1.".format( - mac_version.get() - ) - ) - - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - - crc_val = self.calc_crc(new_value) - crc_field = self.parent["CUSTOM_MAC_CRC"] - print_field(crc_field, hex(crc_val)) - crc_field.save(crc_val) - else: - # Writing the BLK0 default MAC is not possible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -class EfuseWafer(EfuseField): - def get(self, from_read=True): - rev_bit0 = self.parent["CHIP_VER_REV1"].get(from_read) - rev_bit1 = self.parent["CHIP_VER_REV2"].get(from_read) - apb_ctl_date = self.parent.read_reg(self.parent.REGS.APB_CTL_DATE_ADDR) - rev_bit2 = ( - apb_ctl_date >> self.parent.REGS.APB_CTL_DATE_S - ) & self.parent.REGS.APB_CTL_DATE_V - combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 - - revision = { - 0: 0, - 1: 1, - 3: 2, - 7: 3, - }.get(combine_value, 0) - return revision - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfusePkg(EfuseField): - def get(self, from_read=True): - lo_bits = self.parent["CHIP_PACKAGE"].get(from_read) - hi_bits = self.parent["CHIP_PACKAGE_4BIT"].get(from_read) - return (hi_bits << 3) + lo_bits - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfuseSpiPinField(EfuseField): - def get(self, from_read=True): - val = self.get_raw(from_read) - if val >= 30: - val += 2 # values 30,31 map to 32, 33 - return val - - def check_format(self, new_value_str): - if new_value_str is None: - return new_value_str - - new_value_int = int(new_value_str, 0) - - if new_value_int in [30, 31]: - raise esptool.FatalError( - "IO pins 30 & 31 cannot be set for SPI flash. 0-29, 32 & 33 only." - ) - elif new_value_int > 33: - raise esptool.FatalError( - "IO pin %d cannot be set for SPI flash. 0-29, 32 & 33 only." - % new_value_int - ) - elif new_value_int in [32, 33]: - return str(new_value_int - 2) - else: - return new_value_str - - -class EfuseVRefField(EfuseField): - VREF_OFFSET = 1100 # ideal efuse value in mV - VREF_STEP_SIZE = 7 # 1 count in efuse == 7mV - VREF_SIGN_BIT = 0x10 - VREF_MAG_BITS = 0x0F - - def get(self, from_read=True): - val = self.get_raw(from_read) - # sign-magnitude format - if val & self.VREF_SIGN_BIT: - val = -(val & self.VREF_MAG_BITS) - else: - val = val & self.VREF_MAG_BITS - val *= self.VREF_STEP_SIZE - return self.VREF_OFFSET + val - - def save(self, new_value): - raise esptool.FatalError("Writing to VRef is not supported.") - - -class EfuseAdcPointCalibration(EfuseField): - TP_OFFSET = { # See TP_xxxx_OFFSET in esp_adc_cal.c in ESP-IDF - "ADC1_TP_LOW": 278, - "ADC2_TP_LOW": 421, - "ADC1_TP_HIGH": 3265, - "ADC2_TP_HIGH": 3406, - } - SIGN_BIT = (0x40, 0x100) # LOW, HIGH (2s complement format) - STEP_SIZE = 4 - - def get(self, from_read=True): - idx = 0 if self.name.endswith("LOW") else 1 - sign_bit = self.SIGN_BIT[idx] - offset = self.TP_OFFSET[self.name] - raw = self.get_raw() - delta = (raw & (sign_bit - 1)) - (raw & sign_bit) - return offset + (delta * self.STEP_SIZE) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py deleted file mode 100644 index 589e7db7d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/mem_definition.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_MEM_SIZE = 0x011C + 4 - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x3FF5A000 - EFUSE_REG_CONF = DR_REG_EFUSE_BASE + 0x0FC - EFUSE_CONF_WRITE = 0x5A5A - EFUSE_CONF_READ = 0x5AA5 - EFUSE_REG_CMD = DR_REG_EFUSE_BASE + 0x104 - EFUSE_CMD_OP_MASK = 0x3 - EFUSE_CMD_WRITE = 0x2 - EFUSE_CMD_READ = 0x1 - - # 3/4 Coding scheme warnings registers - EFUSE_REG_DEC_STATUS = DR_REG_EFUSE_BASE + 0x11C - EFUSE_REG_DEC_STATUS_MASK = 0xFFF - - # Coding Scheme - EFUSE_CODING_SCHEME_WORD = 6 - EFUSE_CODING_SCHEME_MASK = 0x3 - - # Efuse clock control - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x118 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x0F8 - EFUSE_DAC_CLK_DIV_MASK = 0xFF - EFUSE_CLK_SEL0_MASK = 0x00FF - EFUSE_CLK_SEL1_MASK = 0xFF00 - - EFUSE_CLK_SETTINGS = { - # APB freq: clk_sel0, clk_sel1, dac_clk_div - # Taken from TRM chapter "eFuse Controller": Timing Configuration - # 80 is here for completeness only as esptool never sets an 80MHz APB clock - 26: (250, 255, 52), - 40: (160, 255, 80), - 80: (80, 128, 100), - } - - DR_REG_SYSCON_BASE = 0x3FF66000 - APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C - APB_CTL_DATE_V = 0x1 - APB_CTL_DATE_S = 31 - - EFUSE_BLK0_RDATA3_REG = DR_REG_EFUSE_BASE + 0x00C - EFUSE_RD_CHIP_VER_REV1 = 1 << 15 - - EFUSE_BLK0_RDATA5_REG = DR_REG_EFUSE_BASE + 0x014 - EFUSE_RD_CHIP_VER_REV2 = 1 << 20 - - -# fmt: off -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_regs + 0x000, __base_regs + 0x01C, None, None, 7, None), - ("BLOCK1", ["flash_encryption"], 1, __base_regs + 0x038, __base_regs + 0x098, 7, 0, 8, None), - ("BLOCK2", ["secure_boot_v1", "secure_boot_v2"], 2, __base_regs + 0x058, __base_regs + 0x0B8, 8, 1, 8, None), - ("BLOCK3", [], 3, __base_regs + 0x078, __base_regs + 0x0D8, 9, 2, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # Lists of efuse fields - EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('WR_DIS', "efuse", 0, 0, 0, "uint:16", 1, None, None, "Efuse write disable mask", None), - ('RD_DIS', "efuse", 0, 0, 16, "uint:4", 0, None, None, "Efuse read disable mask", None), - ('CODING_SCHEME', "efuse", 0, 6, 0, "uint:2", 10, 3, None, "Efuse variable block length scheme", - {0: "NONE (BLK1-3 len=256 bits)", - 1: "3/4 (BLK1-3 len=192 bits)", - 2: "REPEAT (BLK1-3 len=128 bits) not supported", - 3: "NONE (BLK1-3 len=256 bits)"}), - ('KEY_STATUS', "efuse", 0, 6, 10, "bool", 10, 3, None, "Usage of efuse block 3 (reserved)", None), - ('MAC', "identity", 0, 1, 0, "bytes:6", 3, None, "mac", "Factory MAC Address", None), - ('MAC_CRC', "identity", 0, 2, 16, "uint:8", 3, None, None, "CRC8 for factory MAC address", None), - ('CHIP_VER_REV1', "identity", 0, 3, 15, "bool", 3, None, None, "Silicon Revision 1", None), - ('CHIP_VER_REV2', "identity", 0, 5, 20, "bool", 6, None, None, "Silicon Revision 2", None), - ("WAFER_VERSION_MINOR", "identity", 0, 5, 24, "uint:2", 6, None, None, "WAFER VERSION MINOR", None), - ('CHIP_PACKAGE', "identity", 0, 3, 9, "uint:3", 3, None, None, "Chip package identifier", None), - ('CHIP_PACKAGE_4BIT', "identity", 0, 3, 2, "uint:1", 3, None, None, "Chip package identifier #4bit", None), - ('XPD_SDIO_FORCE', "config", 0, 4, 16, "bool", 5, None, None, "Ignore MTDI pin (GPIO12) for VDD_SDIO on reset", None), - ('XPD_SDIO_REG', "config", 0, 4, 14, "bool", 5, None, None, "If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset", None), - ('XPD_SDIO_TIEH', "config", 0, 4, 15, "bool", 5, None, None, "If XPD_SDIO_FORCE & XPD_SDIO_REG", - {1: "3.3V", - 0: "1.8V"}), - ('CLK8M_FREQ', "config", 0, 4, 0, "uint:8", None, None, None, "8MHz clock freq override", None), - ('SPI_PAD_CONFIG_CLK', "config", 0, 5, 0, "uint:5", 6, None, "spipin", "Override SD_CLK pad (GPIO6/SPICLK)", None), - ('SPI_PAD_CONFIG_Q', "config", 0, 5, 5, "uint:5", 6, None, "spipin", "Override SD_DATA_0 pad (GPIO7/SPIQ)", None), - ('SPI_PAD_CONFIG_D', "config", 0, 5, 10, "uint:5", 6, None, "spipin", "Override SD_DATA_1 pad (GPIO8/SPID)", None), - ('SPI_PAD_CONFIG_HD', "config", 0, 3, 4, "uint:5", 6, None, "spipin", "Override SD_DATA_2 pad (GPIO9/SPIHD)", None), - ('SPI_PAD_CONFIG_CS0', "config", 0, 5, 15, "uint:5", 6, None, "spipin", "Override SD_CMD pad (GPIO11/SPICS0)", None), - ('DISABLE_SDIO_HOST', "config", 0, 6, 3, "bool", None, None, None, "Disable SDIO host", None), - ('FLASH_CRYPT_CNT', "security", 0, 0, 20, "uint:7", 2, None, "bitcount", "Flash encryption mode counter", None), - ('UART_DOWNLOAD_DIS', "security", 0, 0, 27, "bool", 2, None, None, "Disable UART download mode (ESP32 rev3 only)", None), - ('FLASH_CRYPT_CONFIG', "security", 0, 5, 28, "uint:4", 10, 3, None, "Flash encryption config (key tweak bits)", None), - ('CONSOLE_DEBUG_DISABLE', "security", 0, 6, 2, "bool", 15, None, None, "Disable ROM BASIC interpreter fallback", None), - ('ABS_DONE_0', "security", 0, 6, 4, "bool", 12, None, None, "Secure boot V1 is enabled for bootloader image", None), - ('ABS_DONE_1', "security", 0, 6, 5, "bool", 13, None, None, "Secure boot V2 is enabled for bootloader image", None), - ('JTAG_DISABLE', "security", 0, 6, 6, "bool", 14, None, None, "Disable JTAG", None), - ('DISABLE_DL_ENCRYPT', "security", 0, 6, 7, "bool", 15, None, None, "Disable flash encryption in UART bootloader", None), - ('DISABLE_DL_DECRYPT', "security", 0, 6, 8, "bool", 15, None, None, "Disable flash decryption in UART bootloader", None), - ('DISABLE_DL_CACHE', "security", 0, 6, 9, "bool", 15, None, None, "Disable flash cache in UART bootloader", None), - ('BLK3_PART_RESERVE', "calibration", 0, 3, 14, "bool", 10, 3, None, "BLOCK3 partially served for ADC calibration data", None), - ('ADC_VREF', "calibration", 0, 4, 8, "uint:5", 0, None, "vref", "Voltage reference calibration", None), - ('MAC_VERSION', "identity", 3, 5, 24, "uint:8", 9, 2, None, "Version of the MAC field", - {1: "Custom MAC in BLOCK3"}), - ] - - # if MAC_VERSION is set "1", these efuse fields are in BLOCK3: - CUSTOM_MAC = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('CUSTOM_MAC', "identity", 3, 0, 8, "bytes:6", 9, 2, "mac", "Custom MAC", None), - ('CUSTOM_MAC_CRC', "identity", 3, 0, 0, "uint:8", 9, 2, None, "CRC of custom MAC", None), - ] - - # The len of fields depends on coding scheme: for CODING_SCHEME_NONE - KEYBLOCKS_256 = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK1', "security", 1, 0, 0, "bytes:32", 7, 0, "keyblock", "Flash encryption key", None), - ('BLOCK2', "security", 2, 0, 0, "bytes:32", 8, 1, "keyblock", "Secure boot key", None), - ('BLOCK3', "security", 3, 0, 0, "bytes:32", 9, 2, "keyblock", "Variable Block 3", None), - ] - - # The len of fields depends on coding scheme: for CODING_SCHEME_34 - KEYBLOCKS_192 = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK1', "security", 1, 0, 0, "bytes:24", 7, 0, "keyblock", "Flash encryption key", None), - ('BLOCK2', "security", 2, 0, 0, "bytes:24", 8, 1, "keyblock", "Secure boot key", None), - ('BLOCK3', "security", 3, 0, 0, "bytes:24", 9, 2, "keyblock", "Variable Block 3", None), - ] - - # if BLK3_PART_RESERVE is set, these efuse fields are in BLOCK3: - ADC_CALIBRATION = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('ADC1_TP_LOW', "calibration", 3, 3, 0, "uint:7", 9, 2, "adc_tp", "ADC1 150mV reading", None), - ('ADC1_TP_HIGH', "calibration", 3, 3, 7, "uint:9", 9, 2, "adc_tp", "ADC1 850mV reading", None), - ('ADC2_TP_LOW', "calibration", 3, 3, 16, "uint:7", 9, 2, "adc_tp", "ADC2 150mV reading", None), - ('ADC2_TP_HIGH', "calibration", 3, 3, 23, "uint:9", 9, 2, "adc_tp", "ADC2 850mV reading", None), - ] - - CALC = [ - ("WAFER_VERSION_MAJOR", "identity", 0, None, None, "uint:3", None, None, "wafer", "calc WAFER VERSION MAJOR from CHIP_VER_REV1 and CHIP_VER_REV2 and apb_ctl_date (read only)", None), - ('PKG_VERSION', "identity", 0, None, None, "uint:4", None, None, "pkg", "calc Chip package = CHIP_PACKAGE_4BIT << 3 + CHIP_PACKAGE (read only)", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py deleted file mode 100644 index 421bc186a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32/operations.py +++ /dev/null @@ -1,350 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the operations with eFuses for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - p = subparsers.add_parser( - "burn_key", - help="Burn a 256-bit key to EFUSE: %s" % ", ".join(efuses.BLOCKS_FOR_KEYS), - ) - p.add_argument( - "--no-protect-key", - help="Disable default read- and write-protecting of the key. " - "If this option is not set, once the key is flashed " - "it cannot be read back or changed.", - action="store_true", - ) - add_force_write_always(p) - p.add_argument( - "block", - help='Key block to burn. "flash_encryption" (block1), ' - '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - p.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - for _ in efuses.BLOCKS_FOR_KEYS: - p.add_argument( - "block", - help='Key block to burn. "flash_encryption" (block1), ' - '"secure_boot_v1" (block2), "secure_boot_v2" (block2)', - metavar="BLOCK", - nargs="?", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - p.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - metavar="KEYFILE", - nargs="?", - action="append", - type=argparse.FileType("rb"), - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest " - "to eFuse for use with Secure Boot V2", - ) - burn_key_digest.add_argument( - "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") - ) - burn_key_digest.add_argument( - "--no-protect-key", - help="Disable default write-protecting of the key digest. " - "If this option is not set, once the key is flashed it cannot be changed.", - action="store_true", - ) - add_force_write_always(burn_key_digest) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. This means GPIO12 can be high or low at reset " - "without changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format " - "with bytes separated by colons " - "(e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - # Writing to BLK3: - # - MAC_VERSION = 1 - # - CUSTOM_MAC = AA:CD:EF:01:02:03 - # - CUSTOM_MAC_CRC = crc8(CUSTOM_MAC) - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - version = efuses["MAC_VERSION"].get() - if version > 0: - print( - "Custom MAC Address version {}: {}".format( - version, efuses["CUSTOM_MAC"].get() - ) - ) - else: - print("Custom MAC Address is not set in the device.") - - -def set_flash_voltage(esp, efuses, args): - sdio_force = efuses["XPD_SDIO_FORCE"] - sdio_tieh = efuses["XPD_SDIO_TIEH"] - sdio_reg = efuses["XPD_SDIO_REG"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as XPD_SDIO_REG efuse is already burned" - ) - - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is XPD_SDIO_TIEH efuse is already burned" - ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SDIO). " - "SPI flash will need to be powered from an external source.\n" - "The following efuse is burned: XPD_SDIO_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" - ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SDIO) to 1.8V.\n" - "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse XPD_SDIO_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SDIO) to 3.3V.\n" - "The following efuses are burned: XPD_SDIO_FORCE, XPD_SDIO_REG, XPD_SDIO_TIEH." - print(msg) - sdio_force.save(1) # Disable GPIO12 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SDIO setting complete.") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - adc_vref = efuses["ADC_VREF"] - blk3_reserve = efuses["BLK3_PART_RESERVE"] - - vref_raw = adc_vref.get_raw() - if vref_raw == 0: - print("ADC VRef calibration: None (1100mV nominal)") - else: - print("ADC VRef calibration: %dmV" % adc_vref.get()) - - if blk3_reserve.get(): - print("ADC readings stored in efuse BLOCK3:") - print(" ADC1 Low reading (150mV): %d" % efuses["ADC1_TP_LOW"].get()) - print(" ADC1 High reading (850mV): %d" % efuses["ADC1_TP_HIGH"].get()) - print(" ADC2 Low reading (150mV): %d" % efuses["ADC2_TP_LOW"].get()) - print(" ADC2 High reading (850mV): %d" % efuses["ADC2_TP_HIGH"].get()) - - -def burn_key(esp, efuses, args): - datafile_list = args.keyfile[ - 0 : len([keyfile for keyfile in args.keyfile if keyfile is not None]) : - ] - block_name_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - efuses.force_write_always = args.force_write_always - no_protect_key = args.no_protect_key - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list): - raise esptool.FatalError( - "The number of blocks (%d) and datafile (%d) should be the same." - % (len(block_name_list), len(datafile_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile in zip(block_name_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - data = datafile.read() - revers_msg = None - if block_name in ("flash_encryption", "secure_boot_v1"): - revers_msg = "\tReversing the byte order" - data = data[::-1] - print(" - %s -> [%s]" % (efuse.name, util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. " - "Key file must be %d bytes (%d bits) of raw binary key data." - % (len(data), num_bytes, num_bytes * 8) - ) - - efuse.save(data) - - if block_name in ("flash_encryption", "secure_boot_v1"): - if not no_protect_key: - print("\tDisabling read to key block") - efuse.disable_read() - - if not no_protect_key: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if args.no_protect_key: - print("Key is left unprotected as per --no-protect-key argument.") - - msg = "Burn keys in efuse blocks.\n" - if no_protect_key: - msg += ( - "The key block will left readable and writeable (due to --no-protect-key)" - ) - else: - msg += "The key block will be read and write protected " - "(no further changes or readback)" - print(msg, "\n") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - if efuses.coding_scheme == efuses.REGS.CODING_SCHEME_34: - raise esptool.FatalError("burn_key_digest only works with 'None' coding scheme") - - chip_revision = esp.get_chip_revision() - if chip_revision < 300: - raise esptool.FatalError( - "Incorrect chip revision for Secure boot v2. " - "Detected: v%d.%d. Expected: >= v3.0" - % (chip_revision / 100, chip_revision % 100) - ) - - digest = espsecure._digest_sbv2_public_key(args.keyfile) - efuse = efuses["BLOCK2"] - num_bytes = efuse.bit_len // 8 - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. " - "Digest must be %d bytes (%d bits) of raw binary key data." - % (len(digest), num_bytes, num_bytes * 8) - ) - print(" - %s -> [%s]" % (efuse.name, util.hexify(digest, " "))) - - efuse.save(digest) - if not args.no_protect_key: - print("Disabling write to efuse %s..." % (efuse.name)) - efuse.disable_write() - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py deleted file mode 100644 index 43e714c9a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/emulate_efuse_controller.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-C2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from bitstring import BitString - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-C2" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 1 - - def get_minor_chip_version(self): - return 0 - - def get_crystal_freq(self): - return 40 # MHz - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": 0, - "api_version": 0, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data - - def check_rd_protection_area(self): - # checks fields which have the read protection bits. - # if the read protection bit is set then we need to reset this field to 0. - - def get_read_disable_mask(blk): - mask = 0 - if isinstance(blk.read_disable_bit, list): - for i in blk.read_disable_bit: - mask |= 1 << i - else: - mask = 1 << blk.read_disable_bit - return mask - - read_disable_bit = self.read_field("RD_DIS", bitstring=False) - for b in self.Blocks.BLOCKS: - blk = self.Blocks.get(b) - block = self.read_block(blk.id) - if ( - blk.read_disable_bit is not None - and read_disable_bit & get_read_disable_mask(blk) - ): - if isinstance(blk.read_disable_bit, list): - if read_disable_bit & (1 << blk.read_disable_bit[0]): - block.set( - 0, [i for i in range(blk.len * 32 // 2, blk.len * 32)] - ) - if read_disable_bit & (1 << blk.read_disable_bit[1]): - block.set(0, [i for i in range(0, blk.len * 32 // 2)]) - else: - block.set(0) - else: - for e in self.Fields.EFUSES: - field = self.Fields.get(e) - if ( - blk.id == field.block - and field.read_disable_bit is not None - and read_disable_bit & get_read_disable_mask(field) - ): - raw_data = self.read_field(field.name) - raw_data.set(0) - block.pos = block.length - ( - field.word * 32 + field.pos + raw_data.length - ) - block.overwrite(BitString(raw_data.length)) - self.overwrite_mem_from_block(blk, block) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py deleted file mode 100644 index fc634ac26..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/fields.py +++ /dev/null @@ -1,417 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32-C2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-C2": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-C2 chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLK_VERSION_MINOR"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - xtal_freq = self.get_crystal_freq() - if xtal_freq not in [26, 40]: - raise esptool.FatalError( - "The eFuse supports only xtal=26M and 40M (xtal was %d)" % xtal_freq - ) - - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - tpgm_inactive_val = 200 if xtal_freq == 40 else 130 - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF0_REG, - self.REGS.EFUSE_TPGM_INACTIVE_M, - tpgm_inactive_val, - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - old_addr_reg = 0 - reg_value = 0 - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR_REG + offs * 4) - for offs in range(1) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ - block.id - ] - if err_num_mask is None or err_num_offs is None or fail_bit is None: - continue - if addr_reg != old_addr_reg: - old_addr_reg = addr_reg - reg_value = self.read_reg(addr_reg) - block.fail = reg_value & (1 << fail_bit) != 0 - block.num_errors = (reg_value >> err_num_offs) & err_num_mask - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" - return "" - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None), # User purposes (software-only use) - ( - "XTS_AES_128_KEY", - 1, - None, - ), # (whole 256bits) XTS_AES_128_KEY (flash/PSRAM encryption) - ( - "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", - 2, - None, - ), # (lo 128bits) XTS_AES_128_KEY (flash/PSRAM encryption) - ( - "SECURE_BOOT_DIGEST", - 3, - "DIGEST", - ), # (hi 128bits)SECURE_BOOT_DIGEST (Secure Boot key digest) - ] - - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py deleted file mode 100644 index 9540a3290..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/mem_definition.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32-C2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x60008800 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_PGM_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x88 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x8C - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x90 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x94 - EFUSE_RD_REPEAT_ERR_REG = DR_REG_EFUSE_BASE + 0x80 - EFUSE_RD_RS_ERR_REG = DR_REG_EFUSE_BASE + 0x84 - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - BLOCK_ERRORS = [ - # error_reg, err_num_mask, err_num_offs, fail_bit - (EFUSE_RD_REPEAT_ERR_REG, None, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR_REG, 0x7, 0, 3), # BLOCK1 - (EFUSE_RD_RS_ERR_REG, 0x7, 4, 7), # BLOCK2 - (EFUSE_RD_RS_ERR_REG, 0x7, 8, 11), # BLOCK3 - ] - - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x118 - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - EFUSE_WR_TIM_CONF0_REG = DR_REG_EFUSE_BASE + 0x110 - EFUSE_TPGM_INACTIVE_S = 8 - EFUSE_TPGM_INACTIVE_M = 0xFF << EFUSE_TPGM_INACTIVE_S - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", ["BLOCK0"], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 2, None), - ("BLOCK1", ["BLOCK1"], 1, __base_rd_regs + 0x034, __base_wr_regs, 5, None, 3, None), - ("BLOCK2", ["BLOCK2"], 2, __base_rd_regs + 0x040, __base_wr_regs, 6, None, 8, None), - ("BLOCK_KEY0", ["BLOCK3"], 3, __base_rd_regs + 0x060, __base_wr_regs, 7, [0, 1], 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - def get_blocks_for_keys(self): - return ['BLOCK_KEY0'] - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:8", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:2", 0, None, None, "Disables software reading from BLOCK3", None), - ("WDT_DELAY_SEL", "WDT config", 0, 1, 2, "uint:2", 1, None, None, "RTC WDT timeout threshold", None), - ("DIS_PAD_JTAG", "jtag config", 0, 1, 4, "bool", 1, None, None, "Permanently disable JTAG access via pads" - "USB JTAG is controlled separately", None), - ("DIS_DOWNLOAD_ICACHE", "security", 0, 1, 5, "bool", 1, None, None, "Disables iCache in download mode", None), - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 6, "bool", 2, None, None, "Disables flash encryption in Download boot modes", - None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 1, 7, "uint:3", 2, None, None, "Enables encryption and decryption, when an SPI boot" - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("XTS_KEY_LENGTH_256", "security", 0, 1, 10, "bool", 2, None, None, "Flash encryption key length", - {0: "128 bits key", - 1: "256 bits key"}), - ("UART_PRINT_CONTROL", "config", 0, 1, 11, "uint:2", 3, None, None, "Set UART boot message output mode", - {0: "Force print", - 1: "Low-level print controlled by GPIO 8", - 3: "High-level print controlled by GPIO 8", - 7: "Print force disabled"}), - ("FORCE_SEND_RESUME", "config", 0, 1, 13, "bool", 3, None, None, "Force ROM code to send a resume cmd during SPI boot", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 1, 14, "bool", 3, None, None, "Disables all Download boot modes", None), - ("DIS_DIRECT_BOOT", "config", 0, 1, 15, "bool", 3, None, None, "Disable direct_boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 1, 16, "bool", 3, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("FLASH_TPUW", "flash config", 0, 1, 17, "uint:4", 3, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("SECURE_BOOT_EN", "security", 0, 1, 21, "bool", 2, None, None, "Configures secure boot", None), - ("SECURE_VERSION", "identity", 0, 1, 22, "uint:4", 4, None, "bitcount", "Secure version (anti-rollback feature)", None), - ("CUSTOM_MAC_USED", "identity", 0, 1, 26, "bool", 4, None, None, "Enable CUSTOM_MAC programming", None), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 1, 27, "bool", 4, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 1, 28, "bool", 4, None, None, "Disables check of blk version major", None), - - # - # Parameters in BLOCK1 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("CUSTOM_MAC", "identity", 1, 0, 0, "bytes:6", 5, None, 'mac', "Custom MAC addr", None), - - # - # Parameters in BLOCK2 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 2, 0, 0, "bytes:6", 6, None, 'mac', "Factory MAC Address", None), - ("WAFER_VERSION_MINOR", "identity", 2, 1, 16, "uint:4", 6, None, None, "Minor WAFER version", None), - ("WAFER_VERSION_MAJOR", "identity", 2, 1, 20, "uint:2", 6, None, None, "Major WAFER version", None), - ("PKG_VERSION", "identity", 2, 1, 22, "uint:3", 6, None, None, "Package version", None), - ("BLK_VERSION_MINOR", "identity", 2, 1, 25, "uint:3", 6, None, None, "Minor version of BLOCK2", - {0: "No calibration", 1: "With calibration"}), - - ("BLK_VERSION_MAJOR", "identity", 2, 1, 28, "uint:2", 6, None, None, "Major version of BLOCK2", None), - ("LDO_VOL_BIAS_CONFIG_HIGH", "ldo", 2, 2, 0, "uint:27", 6, None, None, "", None), - ("PVT_LOW", "pvt", 2, 2, 27, "uint:5", 6, None, None, "", None), - ("PVT_HIGH", "pvt", 2, 3, 0, "uint:10", 6, None, None, "", None), - ("ADC_CALIBRATION_0", "adc_calib", 2, 3, 10, "uint:22", 6, None, None, "", None), - ("ADC_CALIBRATION_1", "adc_calib", 2, 4, 0, "uint:32", 6, None, None, "", None), - ("ADC_CALIBRATION_2", "adc_calib", 2, 5, 0, "uint:32", 6, None, None, "", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_KEY0', "security", 3, 0, 0, "bytes:32", 7, [0, 1], "keyblock", "BLOCK_KEY0 - 256-bits. 256-bit key of Flash Encryption", None), - ('BLOCK_KEY0_LOW_128', "security", 3, 0, 0, "bytes:16", 7, 0, "keyblock", "BLOCK_KEY0 - lower 128-bits. 128-bit key of Flash Encryption", None), - ('BLOCK_KEY0_HI_128', "security", 3, 4, 0, "bytes:16", 7, 1, "keyblock", "BLOCK_KEY0 - higher 128-bits. 128-bits key of Secure Boot.", None), - ] - - # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py deleted file mode 100644 index e3297efc8..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c2/operations.py +++ /dev/null @@ -1,349 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the operations with eFuses for ESP32-C2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support " - "post-write data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software.", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 128/256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in range(1): - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 128/256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse an ECDSA public key and burn the digest " - "to higher 128-bits of BLOCK_KEY0", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "keyfile", help="Key file to digest (PEM format)", type=argparse.FileType("rb") - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " - "at reset without changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK1." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format " - "with bytes separated by colons (e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - efuses["CUSTOM_MAC_USED"].save(1) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - raise esptool.FatalError("set_flash_voltage is not supported!") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 1: - print(" RF_REF_I_BIAS_CONFIG: {}".format(efuses["RF_REF_I_BIAS_CONFIG"].get())) - - print(" LDO_VOL_BIAS_CONFIG_LOW: {}".format(efuses["LDO_VOL_BIAS_CONFIG_LOW"].get())) - print(" LDO_VOL_BIAS_CONFIG_HIGH: {}".format(efuses["LDO_VOL_BIAS_CONFIG_HIGH"].get())) - - print(" PVT_LOW: {}".format(efuses["PVT_LOW"].get())) - print(" PVT_HIGH: {}".format(efuses["PVT_HIGH"].get())) - - print(" ADC_CALIBRATION_0: {}".format(efuses["ADC_CALIBRATION_0"].get())) - print(" ADC_CALIBRATION_1: {}".format(efuses["ADC_CALIBRATION_1"].get())) - print(" ADC_CALIBRATION_2: {}".format(efuses["ADC_CALIBRATION_2"].get())) - - else: - print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) - # fmt: on - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - util.check_duplicate_name_in_list(keypurpose_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and " - "keypurpose (%d) should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - assert 1 <= len(block_name_list) <= 2, "Unexpected case" - - if len(block_name_list) == 2: - incompatible = True if "XTS_AES_128_KEY" in keypurpose_list else False - permitted_purposes = [ - "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS", - "SECURE_BOOT_DIGEST", - ] - incompatible |= ( - keypurpose_list[0] in permitted_purposes - and keypurpose_list[1] not in permitted_purposes - ) - if incompatible: - raise esptool.FatalError( - "These keypurposes are incompatible %s" % (keypurpose_list) - ) - - print("Burn keys to blocks:") - for datafile, keypurpose in zip(datafile_list, keypurpose_list): - data = datafile if isinstance(datafile, bytes) else datafile.read() - - if keypurpose == "XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS": - efuse = efuses["BLOCK_KEY0_LOW_128"] - elif keypurpose == "SECURE_BOOT_DIGEST": - efuse = efuses["BLOCK_KEY0_HI_128"] - if len(data) == 32: - print( - "\tProgramming only left-most 128-bits from SHA256 hash of " - "public key to highest 128-bits of BLOCK KEY0" - ) - data = data[:16] - elif len(data) != efuse.bit_len // 8: - raise esptool.FatalError( - "Wrong length of this file for SECURE_BOOT_DIGEST. " - "Got %d (expected %d or %d)" % (len(data), 32, efuse.bit_len // 8) - ) - assert len(data) == 16, "Only 16 bytes expected" - else: - efuse = efuses["BLOCK_KEY0"] - - num_bytes = efuse.bit_len // 8 - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if keypurpose.startswith("XTS_AES_"): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. " - "Key file must be %d bytes (%d bits) of raw binary key data." - % (len(data), num_bytes, num_bytes * 8) - ) - - if keypurpose.startswith("XTS_AES_"): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage - # of checking it as the whole field. - efuse.save(data) - - if keypurpose == "XTS_AES_128_KEY": - if efuses["XTS_KEY_LENGTH_256"].get(): - print("\t'XTS_KEY_LENGTH_256' is already '1'") - else: - print("\tXTS_KEY_LENGTH_256 -> 1") - efuses["XTS_KEY_LENGTH_256"].save(1) - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - datafile = args.keyfile - args.keypurpose = ["SECURE_BOOT_DIGEST"] - args.block = ["BLOCK_KEY0"] - digest = espsecure._digest_sbv2_public_key(datafile) - digest = digest[:16] - num_bytes = efuses["BLOCK_KEY0_HI_128"].bit_len // 8 - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. " - "Digest must be %d bytes (%d bits) of raw binary key data." - % (len(digest), num_bytes, num_bytes * 8) - ) - burn_key(esp, efuses, args, digest=[digest]) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py deleted file mode 100644 index d65ac0293..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/emulate_efuse_controller.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-C3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-C3" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 0 - - def get_minor_chip_version(self): - return 4 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": 0, - "api_version": 0, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py deleted file mode 100644 index 29088db6c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/fields.py +++ /dev/null @@ -1,466 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32-C3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-C3": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-C3 chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLK_VERSION_MAJOR"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CALC - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) - ) - ) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - apb_freq = self.get_crystal_freq() - if apb_freq != 40: - raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq - ) - - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) - for offs in range(5) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg_f, fail_bit = self.REGS.BLOCK_FAIL_BIT[block.id] - if fail_bit is None: - block.fail = False - else: - block.fail = self.read_reg(addr_reg_f) & (1 << fail_bit) != 0 - - addr_reg_n, num_mask, num_offs = self.REGS.BLOCK_NUM_ERRORS[block.id] - if num_mask is None or num_offs is None: - block.num_errors = 0 - else: - block.num_errors = ( - self.read_reg(addr_reg_n) >> num_offs - ) & num_mask - - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" - return "" - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseWafer(EfuseField): - def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - return (hi_bits << 3) + lo_bits - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) - ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode - ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) - ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) - ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode - ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) - ] -# fmt: on - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] - - def check_format(self, new_value_str): - # str convert to int: "XTS_AES_128_KEY" - > str(4) - # if int: 4 -> str(4) - raw_val = new_value_str - for purpose_name in self.KEY_PURPOSES: - if purpose_name[0] == new_value_str: - raw_val = str(purpose_name[1]) - break - if raw_val.isdigit(): - if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: - raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) - else: - raise esptool.FatalError("'%s' unknown name" % raw_val) - return raw_val - - def need_reverse(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[3] == "Reverse" - - def need_rd_protect(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[4] == "need_rd_protect" - - def get(self, from_read=True): - for p in self.KEY_PURPOSES: - if p[1] == self.get_raw(from_read): - return p[0] - return "FORBIDDEN_STATE" - - def save(self, new_value): - raw_val = int(self.check_format(str(new_value))) - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py deleted file mode 100644 index d8a4b2bd8..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/mem_definition.py +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32-C3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x60008800 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 - EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C - EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 - EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 - EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 - EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - # this chip has a design error so fail_bit is shifted by one block but err_num is in the correct place - BLOCK_FAIL_BIT = [ - # error_reg, fail_bit - (EFUSE_RD_REPEAT_ERR0_REG, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 7), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 11), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 15), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 19), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 23), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 27), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 31), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR1_REG, 3), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 7), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, None), # BLOCK_SYS_DATA2 - ] - - BLOCK_NUM_ERRORS = [ - # error_reg, err_num_mask, err_num_offs - (EFUSE_RD_REPEAT_ERR0_REG, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 0), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 4), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 8), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 12), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 16), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 0x7, 20), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 0x7, 24), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR0_REG, 0x7, 28), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 0x7, 0), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, 0x7, 4), # BLOCK_SYS_DATA2 - ] - - # EFUSE_WR_TIM_CONF2_REG - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), - ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), - ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), - ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), - ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), - ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), - ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), - ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), - ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), - ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), - ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Table 51: Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), - ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), - ("DIS_USB_JTAG", "usb config", 0, 1, 9, "bool", 2, None, None, "Disables USB JTAG. " - "JTAG access via pads is controlled separately", None), - ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), - ("DIS_USB_DEVICE", "usb config", 0, 1, 11, "bool", 2, None, None, "Disables USB DEVICE", None), - ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), - ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), - ("SOFT_DIS_JTAG", "jtag config", 0, 1, 16, "uint:3", 2, None, None, "Software disables JTAG. When software disabled, " - "JTAG can be activated temporarily by HMAC peripheral", - None), - ("DIS_PAD_JTAG", "jtag config", 0, 1, 19, "bool", 2, None, None, "Permanently disable JTAG access via pads. " - "USB JTAG is controlled separately.", None), - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", - None), - ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), - ("VDD_SPI_AS_GPIO", "config", 0, 1, 26, "bool", 30, None, None, "Set this bit to vdd spi pin function as gpio", None), - ("BTLC_GPIO_ENABLE", "config", 0, 1, 27, "uint:2", 30, None, None, "Enable btlc gpio", None), - ("POWERGLITCH_EN", "config", 0, 1, 29, "bool", 30, None, None, "Set this bit to enable power glitch function", None), - ("POWER_GLITCH_DSENSE", "config", 0, 1, 30, "uint:2", 30, None, None, "Sample delay configuration of power glitch", None), - ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "bool", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), - ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), - ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), - ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), - ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), - ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), - ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), - ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), - ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), - ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), - ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", - None), - ("FLASH_TPUW", "flash config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), - ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), - ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), - ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "usb config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " - "UART download boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", - {0: "Enabled", - 1: "Enable when GPIO8 is low at reset", - 2: "Enable when GPIO8 is high at reset", - 3: "Disabled"}), - ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Force ROM code to send a resume command during SPI boot" - "during SPI boot", None), - ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", - None), - ("ERR_RST_ENABLE", "config", 0, 4, 31, "bool", 19, None, None, "Use BLOCK0 to check error record registers", - {0: "without check", - 1: "with check"}), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), - # - # Table 53: Parameters in BLOCK1-10 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), - ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), - ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), - ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), - ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), - ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), - ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), - ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), - ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), - ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), - ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), - ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), - - ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), - ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), - ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), - ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), - ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), - - ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), - ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", - {0: "No calibration", - 1: "With calibration"}), - ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), - ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), - ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), - ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), - ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), - ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), - ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), - ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, "keyblock", "System data (part 2)", None), - ] - - # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), - ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), - ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), - ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), - ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), - ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), - ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), - ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), - ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), - ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), - ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), - ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), - ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), - ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), - ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), - ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), - ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), - ] - - CALC = [ - ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py deleted file mode 100644 index efbbdcbee..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c3/operations.py +++ /dev/null @@ -1,415 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the operations with eFuses for ESP32-C3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support " - "post-write data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software." - "The key with keypurpose[USER, RESERVED and *_DIGEST] " - "will remain readable anyway. For the rest keypurposes the read-protection " - "will be defined the option (Read-protect by default).", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest to key efuse block", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - action="append", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key_digest.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. " - "This means GPIO45 can be high or low at reset without " - "changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format with bytes " - "separated by colons (e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - raise esptool.FatalError("set_flash_voltage is not supported!") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) - - print("") - print("ADC1 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) - - print("") - print("ADC2 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) - else: - print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) - # fmt: on - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and keypurpose (%d) " - "should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list - ): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - if digest is None: - data = datafile.read() - else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. Key file must be %d bytes (%d bits) " - "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) - ) - - if efuses[block.key_purpose_name].need_rd_protect(keypurpose): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage of checking it as the whole field. - efuse.save(data) - - disable_wr_protect_key_purpose = False - if efuses[block.key_purpose_name].get() != keypurpose: - if efuses[block.key_purpose_name].is_writeable(): - print( - "\t'%s': '%s' -> '%s'." - % ( - block.key_purpose_name, - efuses[block.key_purpose_name].get(), - keypurpose, - ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: - raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) - ) - else: - print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) - if efuses[block.key_purpose_name].is_writeable(): - disable_wr_protect_key_purpose = True - - if disable_wr_protect_key_purpose: - print("\tDisabling write to '%s'." % block.key_purpose_name) - efuses[block.key_purpose_name].disable_write() - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - digest_list = [] - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - block_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - for block_name, datafile in zip(block_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - digest = espsecure._digest_sbv2_public_key(datafile) - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. Digest must be %d bytes (%d bits) " - "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) - ) - digest_list.append(digest) - burn_key(esp, efuses, args, digest=digest_list) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py deleted file mode 100644 index 35d316189..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/emulate_efuse_controller.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-C6 chip -# -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-C6" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 0 - - def get_minor_chip_version(self): - return 0 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": 0, - "api_version": 0, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py deleted file mode 100644 index 9e652596b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/fields.py +++ /dev/null @@ -1,466 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32-C6 chip -# -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-C6": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-C6 chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLK_VERSION_MAJOR"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CALC - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) - ) - ) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - apb_freq = self.get_crystal_freq() - if apb_freq != 40: - raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq - ) - - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) - for offs in range(5) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg_f, fail_bit = self.REGS.BLOCK_FAIL_BIT[block.id] - if fail_bit is None: - block.fail = False - else: - block.fail = self.read_reg(addr_reg_f) & (1 << fail_bit) != 0 - - addr_reg_n, num_mask, num_offs = self.REGS.BLOCK_NUM_ERRORS[block.id] - if num_mask is None or num_offs is None: - block.num_errors = 0 - else: - block.num_errors = ( - self.read_reg(addr_reg_n) >> num_offs - ) & num_mask - - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" - return "" - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseWafer(EfuseField): - def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - return (hi_bits << 3) + lo_bits - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) - ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode - ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) - ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) - ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode - ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) - ] -# fmt: on - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] - - def check_format(self, new_value_str): - # str convert to int: "XTS_AES_128_KEY" - > str(4) - # if int: 4 -> str(4) - raw_val = new_value_str - for purpose_name in self.KEY_PURPOSES: - if purpose_name[0] == new_value_str: - raw_val = str(purpose_name[1]) - break - if raw_val.isdigit(): - if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: - raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) - else: - raise esptool.FatalError("'%s' unknown name" % raw_val) - return raw_val - - def need_reverse(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[3] == "Reverse" - - def need_rd_protect(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[4] == "need_rd_protect" - - def get(self, from_read=True): - for p in self.KEY_PURPOSES: - if p[1] == self.get_raw(from_read): - return p[0] - return "FORBIDDEN_STATE" - - def save(self, new_value): - raw_val = int(self.check_format(str(new_value))) - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py deleted file mode 100644 index ab0c8b500..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/mem_definition.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32-C6 chip -# -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x600B0800 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 - EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C - EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 - EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 - EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 - EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - # this chip has a design error so fail_bit is shifted by one block but err_num is in the correct place - BLOCK_FAIL_BIT = [ - # error_reg, fail_bit - (EFUSE_RD_REPEAT_ERR0_REG, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 7), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 11), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 15), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 19), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 23), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 27), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 31), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR1_REG, 3), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 7), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, None), # BLOCK_SYS_DATA2 - ] - - BLOCK_NUM_ERRORS = [ - # error_reg, err_num_mask, err_num_offs - (EFUSE_RD_REPEAT_ERR0_REG, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 0), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 4), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 8), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 12), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 16), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 0x7, 20), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 0x7, 24), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR0_REG, 0x7, 28), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 0x7, 0), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, 0x7, 4), # BLOCK_SYS_DATA2 - ] - - # EFUSE_WR_TIM_CONF2_REG - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), - ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), - ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), - ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), - ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), - ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), - ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), - ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), - ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), - ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), - ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Table 51: Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), - ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), - ("DIS_USB_JTAG", "usb config", 0, 1, 9, "bool", 2, None, None, "Disables USB JTAG. " - "JTAG access via pads is controlled separately", None), - ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), - ("DIS_USB_DEVICE", "usb config", 0, 1, 11, "bool", 2, None, None, "Disables USB DEVICE", None), - ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), - ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), - ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), - ("JTAG_SEL_ENABLE", "jtag config", 0, 1, 15, "bool", 2, None, None, "Set this bit to enable selection between " - "usb_to_jtag and pad_to_jtag through strapping " - "gpio10 when both reg_dis_usb_jtag and " - "reg_dis_pad_jtag are equal to 0.", None), - ("SOFT_DIS_JTAG", "jtag config", 0, 1, 16, "uint:3", 2, None, None, "Software disables JTAG. When software disabled, " - "JTAG can be activated temporarily by HMAC peripheral", - None), - ("DIS_PAD_JTAG", "jtag config", 0, 1, 19, "bool", 2, None, None, "Permanently disable JTAG access via pads. " - "USB JTAG is controlled separately.", None), - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", - None), - ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), - ("VDD_SPI_AS_GPIO", "config", 0, 1, 26, "bool", 30, None, None, "Set this bit to vdd spi pin function as gpio", None), - ("BTLC_GPIO_ENABLE", "config", 0, 1, 27, "uint:2", 30, None, None, "Enable btlc gpio", None), - ("POWERGLITCH_EN", "config", 0, 1, 29, "bool", 30, None, None, "Set this bit to enable power glitch function", None), - ("POWER_GLITCH_DSENSE", "config", 0, 1, 30, "uint:2", 30, None, None, "Sample delay configuration of power glitch", None), - ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "bool", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), - ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), - ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), - ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), - ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), - ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), - ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), - ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), - ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), - ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), - ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", - None), - ("FLASH_TPUW", "flash config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), - ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), - ("UART_PRINT_CHANNEL", "config", 0, 4, 2, "bool", 18, None, None, "Selects the default UART for printing boot msg", - {0: "UART0", - 1: "UART1"}), - ("FLASH_ECC_MODE", "flash config", 0, 4, 3, "bool", 18, None, None, "Set this bit to set flsah ecc mode.", - {0: "flash ecc 16to18 byte mode", - 1: "flash ecc 16to17 byte mode"}), - ("DIS_USB_DOWNLOAD_MODE", "usb config", 0, 4, 4, "bool", 18, None, None, "Disables use of USB in UART download boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", - {0: "Enabled", - 1: "Enable when GPIO8 is low at reset", - 2: "Enable when GPIO8 is high at reset", - 3: "Disabled"}), - ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "GPIO33-GPIO37 power supply selection in ROM code", - {0: "VDD3P3_CPU", - 1: "VDD_SPI"}), - ("FLASH_TYPE", "flash config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", - {0: "4 data lines", - 1: "8 data lines"}), - ("FLASH_PAGE_SIZE", "flash config", 0, 4, 10, "uint:2", 18, None, None, "Flash page size", None), - ("FLASH_ECC_EN", "flash config", 0, 4, 12, "bool", 18, None, None, "Enable ECC for flash boot", None), - ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Force ROM code to send a resume command during SPI boot" - "during SPI boot", None), - ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", - None), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), - # - # Table 53: Parameters in BLOCK1-10 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), - ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), - ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), - ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), - ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), - ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), - ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), - ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), - ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), - ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), - ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), - ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), - - ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), - ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), - ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), - ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), - ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), - - ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), - ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", - {0: "No calibration", - 1: "With calibration"}), - ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), - ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), - ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), - ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), - ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), - ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), - ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), - ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, "keyblock", "System data (part 2)", None), - ] - - # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), - ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), - ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), - ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), - ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), - ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), - ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), - ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), - ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), - ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), - ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), - ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), - ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), - ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), - ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), - ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), - ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), - ] - - CALC = [ - ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py deleted file mode 100644 index b4b2cff65..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32c6/operations.py +++ /dev/null @@ -1,415 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the operations with eFuses for ESP32-C6 chip -# -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support " - "post-write data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software." - "The key with keypurpose[USER, RESERVED and *_DIGEST] " - "will remain readable anyway. For the rest keypurposes the read-protection " - "will be defined the option (Read-protect by default).", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest to key efuse block", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - action="append", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key_digest.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. " - "This means GPIO45 can be high or low at reset without " - "changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format with bytes " - "separated by colons (e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - raise esptool.FatalError("set_flash_voltage is not supported!") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) - - print("") - print("ADC1 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) - - print("") - print("ADC2 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) - else: - print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) - # fmt: on - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and keypurpose (%d) " - "should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list - ): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - if digest is None: - data = datafile.read() - else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. Key file must be %d bytes (%d bits) " - "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) - ) - - if efuses[block.key_purpose_name].need_rd_protect(keypurpose): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage of checking it as the whole field. - efuse.save(data) - - disable_wr_protect_key_purpose = False - if efuses[block.key_purpose_name].get() != keypurpose: - if efuses[block.key_purpose_name].is_writeable(): - print( - "\t'%s': '%s' -> '%s'." - % ( - block.key_purpose_name, - efuses[block.key_purpose_name].get(), - keypurpose, - ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: - raise esptool.FatalError( - "It is not possible to change '%s' to '%s' " - "because write protection bit is set." - % (block.key_purpose_name, keypurpose) - ) - else: - print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) - if efuses[block.key_purpose_name].is_writeable(): - disable_wr_protect_key_purpose = True - - if disable_wr_protect_key_purpose: - print("\tDisabling write to '%s'." % block.key_purpose_name) - efuses[block.key_purpose_name].disable_write() - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - digest_list = [] - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - block_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - for block_name, datafile in zip(block_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - digest = espsecure._digest_sbv2_public_key(datafile) - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. Digest must be %d bytes (%d bits) " - "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) - ) - digest_list.append(digest) - burn_key(esp, efuses, args, digest=digest_list) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py deleted file mode 100644 index 4721d4e6b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-H2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-H2(beta1)" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 0 - - def get_minor_chip_version(self): - return 0 - - def get_crystal_freq(self): - return 32 # MHz (common for all chips) - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": 0, - "api_version": 0, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py deleted file mode 100644 index 49b76c21c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/fields.py +++ /dev/null @@ -1,454 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32-H2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-H2(beta1)": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-H2(beta1) chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLOCK2_VERSION"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) - ) - ) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.REGS.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - apb_freq = self.get_crystal_freq() - if apb_freq != 32: - raise esptool.FatalError( - "The eFuse supports only xtal=32M (xtal was %d)" % apb_freq - ) - - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - old_addr_reg = 0 - reg_value = 0 - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) - for offs in range(5) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ - block.id - ] - if err_num_mask is None or err_num_offs is None or fail_bit is None: - continue - if addr_reg != old_addr_reg: - old_addr_reg = addr_reg - reg_value = self.read_reg(addr_reg) - block.fail = reg_value & (1 << fail_bit) != 0 - block.num_errors = (reg_value >> err_num_offs) & err_num_mask - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - # TODO add support set_flash_voltage - "Flash voltage (VDD_SPI)" - return "" - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] + self.parent["MAC_EXT"].get_raw( - from_read - ) - elif self.name == "MAC": - mac = self.get_raw(from_read) + self.parent["MAC_EXT"].get_raw(from_read) - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) - ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) - ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) - ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode - ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) - ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) - ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode - ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) - ] -# fmt: on - - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] - - def check_format(self, new_value_str): - # str convert to int: "XTS_AES_128_KEY" - > str(4) - # if int: 4 -> str(4) - raw_val = new_value_str - for purpose_name in self.KEY_PURPOSES: - if purpose_name[0] == new_value_str: - raw_val = str(purpose_name[1]) - break - if raw_val.isdigit(): - if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: - raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) - else: - raise esptool.FatalError("'%s' unknown name" % raw_val) - return raw_val - - def need_reverse(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[3] == "Reverse" - - def need_rd_protect(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[4] == "need_rd_protect" - - def get(self, from_read=True): - for p in self.KEY_PURPOSES: - if p[1] == self.get_raw(from_read): - return p[0] - return "FORBIDDEN_STATE" - - def save(self, new_value): - raw_val = int(self.check_format(str(new_value))) - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py deleted file mode 100644 index f3c123d60..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/mem_definition.py +++ /dev/null @@ -1,229 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32-H2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x6001A000 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 - EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C - EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 - EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 - EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 - EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F0 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - BLOCK_ERRORS = [ - # error_reg, err_num_mask, err_num_offs, fail_bit - (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 - ] - - # EFUSE_WR_TIM_CONF2_REG - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), - ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), - ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), - ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), - ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), - ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), - ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), - ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), - ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), - ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), - ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Table 51: Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), - ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), - ("DIS_USB_JTAG", "usb config", 0, 1, 9, "bool", 2, None, None, "Disables USB JTAG. " - "JTAG access via pads is controlled separately", None), - ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), - ("DIS_USB_DEVICE", "usb config", 0, 1, 11, "bool", 2, None, None, "Disables USB DEVICE", None), - ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), - ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), - ("JTAG_SEL_ENABLE", "jtag config", 0, 1, 15, "bool", 2, None, None, "Set this bit to enable selection between " - "usb_to_jtag and pad_to_jtag through strapping " - "gpio10 when both reg_dis_usb_jtag and " - "reg_dis_pad_jtag are equal to 0.", None), - ("SOFT_DIS_JTAG", "jtag config", 0, 1, 16, "uint:3", 2, None, None, "Software disables JTAG. When software disabled, " - "JTAG can be activated temporarily by HMAC peripheral", - None), - ("DIS_PAD_JTAG", "jtag config", 0, 1, 19, "bool", 2, None, None, "Permanently disable JTAG access via pads. " - "USB JTAG is controlled separately.", None), - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", - None), - ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), - ("VDD_SPI_AS_GPIO", "config", 0, 1, 26, "bool", 30, None, None, "Set this bit to vdd spi pin function as gpio", None), - ("BTLC_GPIO_ENABLE", "config", 0, 1, 27, "uint:2", 30, None, None, "Enable btlc gpio", None), - ("POWERGLITCH_EN", "config", 0, 1, 29, "bool", 30, None, None, "Set this bit to enable power glitch function", None), - ("POWER_GLITCH_DSENSE", "config", 0, 1, 30, "uint:2", 30, None, None, "Sample delay configuration of power glitch", None), - ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "bool", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), - ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), - ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), - ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), - ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), - ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), - ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), - ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), - ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), - ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), - ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", - None), - ("FLASH_TPUW", "flash config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), - ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), - ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), - ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "usb config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " - "UART download boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", - {0: "Enabled", - 1: "Enable when GPIO8 is low at reset", - 2: "Enable when GPIO8 is high at reset", - 3: "Disabled"}), - ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Force ROM code to send a resume command during SPI boot" - "during SPI boot", None), - ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", - None), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), - # - # Table 53: Parameters in BLOCK1-10 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), - ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), - ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), - ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), - ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), - ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), - ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), - ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), - ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), - ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), - ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), - ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), - ("WAFER_VERSION", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER version", - {0: "(revision 0)", 1: "(revision 1)"}), - ("PKG_VERSION", "identity", 1, 3, 21, "uint:4", 20, None, None, "Package version", - {0: "ESP32-H2(beta1)"}), - ("BLOCK1_VERSION", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK1 efuse version", None), - ("MAC_EXT", "identity", 1, 3, 27, "bytes:2", 20, None, "mac", "MAC extension", None), - ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), - ("BLOCK2_VERSION", "identity", 2, 4, 4, "uint:3", 21, None, None, "Version of BLOCK2", - {0: "No calibration", - 1: "With calibration"}), - ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), - ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), - ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), - ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), - ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), - ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), - ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), - ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, "keyblock", "System data (part 2)", None), - ] - - # if BLOCK2_VERSION is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), - ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), - ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), - ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), - ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), - ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), - ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), - ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), - ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), - ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), - ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), - ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), - ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), - ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), - ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), - ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), - ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py deleted file mode 100644 index 744df5e08..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32h2beta1/operations.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the operations with eFuses for ESP32-H2 chip -# -# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support post-write " - "data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software." - "The key with keypurpose[USER, RESERVED and *_DIGEST] will remain " - "readable anyway. For the rest keypurposes the read-protection will be " - "defined the option (Read-protect by default).", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest to key efuse block", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - action="append", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key_digest.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low " - "at reset without changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format with bytes " - "separated by colons (e.g. AA:CD:EF:01:02:03). " - "Final CUSTOM_MAC = CUSTOM_MAC[48] + MAC_EXT[16]", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - raise esptool.FatalError("set_flash_voltage is not supported!") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLOCK2_VERSION"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) - - print("") - print("ADC1 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) - - print("") - print("ADC2 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) - else: - print("BLOCK2_VERSION = {}".format(efuses["BLOCK2_VERSION"].get_meaning())) - # fmt: on - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and keypurpose (%d) " - "should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list - ): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - if digest is None: - data = datafile.read() - else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. Key file must be %d bytes (%d bits) " - "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) - ) - - if efuses[block.key_purpose_name].need_rd_protect(keypurpose): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage of checking it as the whole field. - efuse.save(data) - - disable_wr_protect_key_purpose = False - if efuses[block.key_purpose_name].get() != keypurpose: - if efuses[block.key_purpose_name].is_writeable(): - print( - "\t'%s': '%s' -> '%s'." - % ( - block.key_purpose_name, - efuses[block.key_purpose_name].get(), - keypurpose, - ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: - raise esptool.FatalError( - "It is not possible to change '%s' to '%s' because write " - "protection bit is set." % (block.key_purpose_name, keypurpose) - ) - else: - print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) - if efuses[block.key_purpose_name].is_writeable(): - disable_wr_protect_key_purpose = True - - if disable_wr_protect_key_purpose: - print("\tDisabling write to '%s'." % block.key_purpose_name) - efuses[block.key_purpose_name].disable_write() - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - digest_list = [] - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - block_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - for block_name, datafile in zip(block_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - digest = espsecure._digest_sbv2_public_key(datafile) - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. Digest must be %d bytes (%d bits) of raw " - "binary key data." % (len(digest), num_bytes, num_bytes * 8) - ) - digest_list.append(digest) - burn_key(esp, efuses, args, digest=digest_list) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py deleted file mode 100644 index 241c79c00..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/emulate_efuse_controller.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-S2 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-S2" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 1 - - def get_minor_chip_version(self): - return 0 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": None, - "api_version": None, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py deleted file mode 100644 index 87120e40c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/fields.py +++ /dev/null @@ -1,524 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32S2 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-S2": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-S2 chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLK_VERSION_MINOR"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CALC - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) - ) - ) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - apb_freq = self.get_crystal_freq() - ( - EFUSE_TSUP_A, - EFUSE_TPGM, - EFUSE_THP_A, - EFUSE_TPGM_INACTIVE, - ) = self.REGS.EFUSE_PROGRAMMING_TIMING_PARAMETERS[apb_freq] - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF1_REG, self.REGS.EFUSE_TSUP_A_M, EFUSE_TSUP_A - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_TPGM_M, EFUSE_TPGM - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF0_REG, self.REGS.EFUSE_THP_A_M, EFUSE_THP_A - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF0_REG, - self.REGS.EFUSE_TPGM_INACTIVE_M, - EFUSE_TPGM_INACTIVE, - ) - - ( - EFUSE_DAC_CLK_DIV, - EFUSE_PWR_ON_NUM, - EFUSE_PWR_OFF_NUM, - ) = self.REGS.VDDQ_TIMING_PARAMETERS[apb_freq] - self.update_reg( - self.REGS.EFUSE_DAC_CONF_REG, - self.REGS.EFUSE_DAC_CLK_DIV_M, - EFUSE_DAC_CLK_DIV, - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF1_REG, - self.REGS.EFUSE_PWR_ON_NUM_M, - EFUSE_PWR_ON_NUM, - ) - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, - self.REGS.EFUSE_PWR_OFF_NUM_M, - EFUSE_PWR_OFF_NUM, - ) - - EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A = self.REGS.EFUSE_READING_PARAMETERS[ - apb_freq - ] - # self.update_reg( - # self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TSUR_A_M, EFUSE_TSUR_A - # ) - self.update_reg( - self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_TRD_M, EFUSE_TRD - ) - self.update_reg( - self.REGS.EFUSE_RD_TIM_CONF_REG, self.REGS.EFUSE_THR_A_M, EFUSE_THR_A - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - old_addr_reg = 0 - reg_value = 0 - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) - for offs in range(5) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ - block.id - ] - if err_num_mask is None or err_num_offs is None or fail_bit is None: - continue - if addr_reg != old_addr_reg: - old_addr_reg = addr_reg - reg_value = self.read_reg(addr_reg) - block.fail = reg_value & (1 << fail_bit) != 0 - block.num_errors = (reg_value >> err_num_offs) & err_num_mask - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - if self["VDD_SPI_FORCE"].get() == 0: - output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " - "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" - output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " - "VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V)." - elif self["VDD_SPI_XPD"].get() == 0: - output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." - elif self["VDD_SPI_TIEH"].get() == 0: - output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." - else: - output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." - return output - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseWafer(EfuseField): - def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - return (hi_bits << 3) + lo_bits - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) - ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) - ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) - ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode - ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) - ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) - ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode - ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) - ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - ] -# fmt: on - - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] - - def check_format(self, new_value_str): - # str convert to int: "XTS_AES_128_KEY" - > str(4) - # if int: 4 -> str(4) - raw_val = new_value_str - for purpose_name in self.KEY_PURPOSES: - if purpose_name[0] == new_value_str: - raw_val = str(purpose_name[1]) - break - if raw_val.isdigit(): - if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: - raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) - else: - raise esptool.FatalError("'%s' unknown name" % raw_val) - return raw_val - - def need_reverse(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[3] == "Reverse" - - def need_rd_protect(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[4] == "need_rd_protect" - - def get(self, from_read=True): - for p in self.KEY_PURPOSES: - if p[1] == self.get_raw(from_read): - return p[0] - return "FORBIDDEN_STATE" - - def save(self, new_value): - raw_val = int(self.check_format(str(new_value))) - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py deleted file mode 100644 index 1d9e8c4cf..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/mem_definition.py +++ /dev/null @@ -1,310 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x3f41A000 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1c8 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1cc - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1d0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1d4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x194 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x198 - EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C - EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 - EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 - EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 - EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - BLOCK_ERRORS = [ - # error_reg, err_num_mask, err_num_offs, fail_bit - (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 - ] - - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1e8 - EFUSE_DAC_CLK_DIV_S = 0 - EFUSE_DAC_CLK_DIV_M = 0xFF << EFUSE_DAC_CLK_DIV_S - - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_TSUR_A_S = 16 - EFUSE_TSUR_A_M = 0xFF << EFUSE_TSUR_A_S - EFUSE_TRD_S = 8 - EFUSE_TRD_M = 0xFF << EFUSE_TRD_S - EFUSE_THR_A_S = 0 - EFUSE_THR_A_M = 0xFF << EFUSE_THR_A_S - - EFUSE_WR_TIM_CONF0_REG = DR_REG_EFUSE_BASE + 0x1F0 - EFUSE_TPGM_S = 16 - EFUSE_TPGM_M = 0xFFFF << EFUSE_TPGM_S - EFUSE_TPGM_INACTIVE_S = 8 - EFUSE_TPGM_INACTIVE_M = 0xFF << EFUSE_TPGM_INACTIVE_S - EFUSE_THP_A_S = 0 - EFUSE_THP_A_M = 0xFF << EFUSE_THP_A_S - - # EFUSE_WR_TIM_CONF1_REG - EFUSE_PWR_ON_NUM_S = 8 - EFUSE_PWR_ON_NUM_M = 0xFFFF << EFUSE_PWR_ON_NUM_S - EFUSE_TSUP_A_S = 0 - EFUSE_TSUP_A_M = 0xFF << EFUSE_TSUP_A_S - - # EFUSE_WR_TIM_CONF2_REG - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - # Configure clock - EFUSE_PROGRAMMING_TIMING_PARAMETERS = { - # APB Frequency: ( EFUSE_TSUP_A, EFUSE_TPGM, EFUSE_THP_A, EFUSE_TPGM_INACTIVE ) - # Taken from TRM chapter "eFuse Controller": eFuse-Programming Timing - 80: (0x2, 0x320, 0x2, 0x4), - 40: (0x1, 0x190, 0x1, 0x2), - 20: (0x1, 0xC8, 0x1, 0x1), - } - - VDDQ_TIMING_PARAMETERS = { - # APB Frequency: ( EFUSE_DAC_CLK_DIV, EFUSE_PWR_ON_NUM, EFUSE_PWR_OFF_NUM ) - # Taken from TRM chapter "eFuse Controller": eFuse VDDQ Timing Setting - 80: (0xA0, 0xA200, 0x100), - 40: (0x50, 0x5100, 0x80), - 20: (0x28, 0x2880, 0x40), - } - - EFUSE_READING_PARAMETERS = { - # APB Frequency: ( EFUSE_TSUR_A, EFUSE_TRD, EFUSE_THR_A ) - # Taken from TRM chapter "eFuse Controller": eFuse-Read Timing - 80: (0x2, 0x4, 0x2), - 40: (0x1, 0x2, 0x1), - 20: (0x1, 0x1, 0x1), - } - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), - ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), - ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), - ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), - ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), - ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), - ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), - ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), - ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), - ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), - ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Table 51: Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), - ("DIS_RTC_RAM_BOOT", "config", 0, 1, 7, "bool", 1, None, None, "Disables boot from RTC RAM", None), - ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), - ("DIS_DCACHE", "config", 0, 1, 9, "bool", 2, None, None, "Disables DCache", None), - ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), - ("DIS_DOWNLOAD_DCACHE", "config", 0, 1, 11, "bool", 2, None, None, "Disables Dcache when SoC is in Download mode", None), - ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), - ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), - ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), - ("DIS_BOOT_REMAP", "config", 0, 1, 15, "bool", 2, None, None, "Disables capability to Remap RAM to ROM address space", - None), - ("SOFT_DIS_JTAG", "security", 0, 1, 17, "bool", 2, None, None, "Software disables JTAG. When software disabled, " - "JTAG can be activated temporarily by HMAC peripheral", - None), - ("HARD_DIS_JTAG", "security", 0, 1, 18, "bool", 2, None, None, "Hardware disables JTAG permanently", None), - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 19, "bool", 2, None, None, "Disables flash encryption when in download boot modes", - None), - ("USB_EXCHG_PINS", "usb config", 0, 1, 24, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), - ("EXT_PHY_ENABLE", "usb config", 0, 1, 25, "bool", 30, None, None, "Enables external USB PHY", None), - ("USB_FORCE_NOPERSIST", "usb config", 0, 1, 26, "bool", 30, None, None, "Forces to set USB BVALID to 1", None), - ("BLOCK0_VERSION", "identity", 0, 1, 27, "uint:2", 30, None, None, "BLOCK0 efuse version", None), - ("VDD_SPI_FORCE", "VDD_SPI config", 0, 2, 6, "bool", 3, None, None, "Force using VDD_SPI_XPD and VDD_SPI_TIEH " - "to configure VDD_SPI LDO", None), - ("VDD_SPI_XPD", "VDD_SPI config", 0, 2, 4, "bool", 3, None, None, "The VDD_SPI regulator is powered on", None), - ("VDD_SPI_TIEH", "VDD_SPI config", 0, 2, 5, "bool", 3, None, None, "The VDD_SPI power supply voltage at reset", - {0: "Connect to 1.8V LDO", - 1: "Connect to VDD3P3_RTC_IO"}), - ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "uint:2", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "If set, revokes use of secure boot key digest 0", None), - ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "If set, revokes use of secure boot key digest 1", None), - ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "If set, revokes use of secure boot key digest 2", None), - ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), - ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), - ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), - ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), - ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), - ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), - ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), - ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", - None), - ("FLASH_TPUW", "config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), - ("DIS_LEGACY_SPI_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables Legacy SPI boot mode", None), - ("UART_PRINT_CHANNEL", "config", 0, 4, 2, "bool", 18, None, None, "Selects the default UART for printing boot msg", - - {0: "UART0", - 1: "UART1"}), - ("DIS_USB_DOWNLOAD_MODE", "config", 0, 4, 4, "bool", 18, None, None, "Disables use of USB in UART download boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", - - {0: "Enabled", - 1: "Enable when GPIO 46 is low at reset", - 2: "Enable when GPIO 46 is high at rest", - 3: "Disabled"}), - ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "Sets default power supply for GPIO33..37, " - "set when SPI flash is initialized", - - {0: "VDD3P3_CPU", - 1: "VDD_SPI"}), - ("FLASH_TYPE", "config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", - - {0: "4 data lines", - 1: "8 data lines"}), - ("FORCE_SEND_RESUME", "config", 0, 4, 10, "bool", 18, None, None, "Forces ROM code to send an SPI flash resume command " - "during SPI boot", None), - ("SECURE_VERSION", "identity", 0, 4, 11, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", - None), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), - # - # Table 53: Parameters in BLOCK1-10 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), - ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), - ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), - ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), - ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), - ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), - ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), - ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), - ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), - ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), - ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), - ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), - - ("WAFER_VERSION_MAJOR", "identity", 1, 3, 18, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), - ("WAFER_VERSION_MINOR_HI", "identity", 1, 3, 20, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), - ("FLASH_VERSION", "identity", 1, 3, 21, "uint:4", 20, None, None, "Flash version", - {0: "No Embedded Flash", - 1: "Embedded Flash 2MB", - 2: "Embedded Flash 4MB"}), - ("BLK_VERSION_MAJOR", "identity", 1, 3, 25, "uint:2", 20, None, None, "BLOCK version major", None), - ("PSRAM_VERSION", "identity", 1, 3, 28, "uint:4", 20, None, None, "PSRAM version", - {0: "No Embedded PSRAM", - 1: "Embedded PSRAM 2MB", - 2: "Embedded PSRAM 4MB"}), - ("PKG_VERSION", "identity", 1, 4, 0, "uint:4", 20, None, None, "Package version", None), - ("WAFER_VERSION_MINOR_LO", "identity", 1, 4, 4, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), - - ('OPTIONAL_UNIQUE_ID', "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), - ("BLK_VERSION_MINOR", "identity", 2, 4, 4, "uint:3", 21, None, None, "BLOCK version minor", - {0: "No calibration", - 1: "With ADC calibration V1", - 2: "With ADC calibration V2"}), - ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), - ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), - ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), - ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), - ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), - ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), - ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), - ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, None, "System data (part 2)", None), - ] - - # if BLK_VERSION_MINOR is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "Temperature calibration", None), - ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "ADC1 calibration 1", None), - ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "ADC1 calibration 2", None), - ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "ADC1 calibration 3", None), - ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "ADC1 calibration 4", None), - ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "ADC2 calibration 5", None), - ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "ADC2 calibration 6", None), - ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "ADC2 calibration 7", None), - ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "ADC2 calibration 8", None), - ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "ADC1 calibration 9", None), - ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "ADC1 calibration 10", None), - ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "ADC1 calibration 11", None), - ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "ADC1 calibration 12", None), - ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "ADC2 calibration 13", None), - ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "ADC2 calibration 14", None), - ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "ADC2 calibration 15", None), - ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "ADC2 calibration 16", None), - ] - - CALC = [ - ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py deleted file mode 100644 index 7c4148a81..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s2/operations.py +++ /dev/null @@ -1,525 +0,0 @@ -#!/usr/bin/env python -# -# This file includes the operations with eFuses for ESP32S2 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import io -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support post-write " - "data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software." - "The key with keypurpose[USER, RESERVED and *_DIGEST] " - "will remain readable anyway. For the rest keypurposes the read-protection " - "will be defined the option (Read-protect by default).", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest to key efuse block", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - action="append", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key_digest.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. " - "This means GPIO45 can be high or low at reset without " - "changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format with bytes " - "separated by colons (e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - sdio_force = efuses["VDD_SPI_FORCE"] - sdio_tieh = efuses["VDD_SPI_TIEH"] - sdio_reg = efuses["VDD_SPI_XPD"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - ) - - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" - ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SPI). SPI flash will " - "need to be powered from an external source.\n" - "The following efuse is burned: VDD_SPI_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" - ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse VDD_SPI_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." - print(msg) - - sdio_force.save(1) # Disable GPIO45 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SPI setting complete.") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MINOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) - - print("") - print("ADC1 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) - - print("") - print("ADC2 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) - else: - print("BLK_VERSION_MINOR = {}".format(efuses["BLK_VERSION_MINOR"].get_meaning())) - # fmt: on - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) - ) - - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) - - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) - - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and keypurpose (%d) " - "should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list - ): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - if digest is None: - data = datafile.read() - else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. Key file must be %d bytes (%d bits) " - "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) - ) - - if efuses[block.key_purpose_name].need_rd_protect(keypurpose): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage of - # checking it as the whole field. - efuse.save(data) - - disable_wr_protect_key_purpose = False - if efuses[block.key_purpose_name].get() != keypurpose: - if efuses[block.key_purpose_name].is_writeable(): - print( - "\t'%s': '%s' -> '%s'." - % ( - block.key_purpose_name, - efuses[block.key_purpose_name].get(), - keypurpose, - ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: - raise esptool.FatalError( - "It is not possible to change '%s' to '%s' because " - "write protection bit is set." - % (block.key_purpose_name, keypurpose) - ) - else: - print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) - if efuses[block.key_purpose_name].is_writeable(): - disable_wr_protect_key_purpose = True - - if disable_wr_protect_key_purpose: - print("\tDisabling write to '%s'." % block.key_purpose_name) - efuses[block.key_purpose_name].disable_write() - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - digest_list = [] - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - block_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - for block_name, datafile in zip(block_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - digest = espsecure._digest_sbv2_public_key(datafile) - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. Digest must be %d bytes (%d bits) of raw " - "binary key data." % (len(digest), num_bytes, num_bytes * 8) - ) - digest_list.append(digest) - burn_key(esp, efuses, args, digest=digest_list) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py deleted file mode 100644 index 762f2c522..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/emulate_efuse_controller.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-S3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-S3" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 0 - - def get_minor_chip_version(self): - return 2 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": 0, - "api_version": 0, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py deleted file mode 100644 index 06a9d18a0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/fields.py +++ /dev/null @@ -1,478 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32-S3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-S3": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-S3 chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLK_VERSION_MAJOR"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CALC - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) - ) - ) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - apb_freq = self.get_crystal_freq() - if apb_freq != 40: - raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq - ) - - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - old_addr_reg = 0 - reg_value = 0 - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) - for offs in range(5) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ - block.id - ] - if err_num_mask is None or err_num_offs is None or fail_bit is None: - continue - if addr_reg != old_addr_reg: - old_addr_reg = addr_reg - reg_value = self.read_reg(addr_reg) - block.fail = reg_value & (1 << fail_bit) != 0 - block.num_errors = (reg_value >> err_num_offs) & err_num_mask - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - if self["VDD_SPI_FORCE"].get() == 0: - output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " - "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" - output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " - "VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V)." - elif self["VDD_SPI_XPD"].get() == 0: - output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." - elif self["VDD_SPI_TIEH"].get() == 0: - output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." - else: - output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." - return output - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseWafer(EfuseField): - def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - return (hi_bits << 3) + lo_bits - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not sensible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) - ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) - ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) - ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode - ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) - ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) - ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode - ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) - ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - ] -# fmt: on - - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] - - def check_format(self, new_value_str): - # str convert to int: "XTS_AES_128_KEY" - > str(4) - # if int: 4 -> str(4) - raw_val = new_value_str - for purpose_name in self.KEY_PURPOSES: - if purpose_name[0] == new_value_str: - raw_val = str(purpose_name[1]) - break - if raw_val.isdigit(): - if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: - raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) - else: - raise esptool.FatalError("'%s' unknown name" % raw_val) - return raw_val - - def need_reverse(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[3] == "Reverse" - - def need_rd_protect(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[4] == "need_rd_protect" - - def get(self, from_read=True): - for p in self.KEY_PURPOSES: - if p[1] == self.get_raw(from_read): - return p[0] - return "FORBIDDEN_STATE" - - def save(self, new_value): - raw_val = int(self.check_format(str(new_value))) - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py deleted file mode 100644 index 35d6a2f8e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/mem_definition.py +++ /dev/null @@ -1,256 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32-S3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_ADDR_MASK = 0x00000FFF - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x60007000 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 - EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C - EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 - EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 - EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 - EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - BLOCK_ERRORS = [ - # error_reg, err_num_mask, err_num_offs, fail_bit - (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 - ] - - # EFUSE_WR_TIM_CONF2_REG - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), - ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), - ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), - ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), - ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), - ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), - ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), - ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), - ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), - ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), - ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Table 51: Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), - ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), - ("DIS_DCACHE", "config", 0, 1, 9, "bool", 2, None, None, "Disables DCache", None), - ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), - ("DIS_DOWNLOAD_DCACHE", "config", 0, 1, 11, "bool", 2, None, None, "Disables Dcache when SoC is in Download mode", None), - ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), - ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), - ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), - ("DIS_APP_CPU", "config", 0, 1, 15, "bool", 2, None, None, "Disables APP CPU", None), - ("SOFT_DIS_JTAG", "security", 0, 1, 16, "uint:3", 31, None, None, "Software disables JTAG by programming " - "odd number of 1 bit(s). " - "JTAG can be re-enabled via HMAC peripheral", - None), - ("HARD_DIS_JTAG", "security", 0, 1, 19, "bool", 2, None, None, "Hardware disables JTAG permanently", None), - - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", - None), - ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), - ("EXT_PHY_ENABLE", "usb config", 0, 1, 26, "bool", 30, None, None, "Enables external USB PHY", None), - ("BTLC_GPIO_ENABLE", "usb config", 0, 1, 27, "uint:2", 30, None, None, "Enables BTLC GPIO", None), - ("VDD_SPI_XPD", "VDD_SPI config", 0, 2, 4, "bool", 3, None, None, "The VDD_SPI regulator is powered on", None), - ("VDD_SPI_TIEH", "VDD_SPI config", 0, 2, 5, "bool", 3, None, None, "The VDD_SPI power supply voltage at reset", - {0: "Connect to 1.8V LDO", - 1: "Connect to VDD_RTC_IO"}), - ("VDD_SPI_FORCE", "VDD_SPI config", 0, 2, 6, "bool", 3, None, None, "Force using VDD_SPI_XPD and VDD_SPI_TIEH " - "to configure VDD_SPI LDO", None), - ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "uint:2", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "Revokes use of secure boot key digest 0", None), - ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "Revokes use of secure boot key digest 1", None), - ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "Revokes use of secure boot key digest 2", None), - ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), - ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), - ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), - ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), - ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), - ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), - ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), - ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", - None), - ("DIS_USB_JTAG", "usb config", 0, 3, 22, "bool", 2, None, None, "Disable usb_serial_jtag-to-jtag function", None), - ("DIS_USB_SERIAL_JTAG", "usb config", 0, 3, 23, "bool", 2, None, None, "Disable usb_serial_jtag module", None), - ("STRAP_JTAG_SEL", "security", 0, 3, 24, "bool", 2, None, None, "Enable selection between usb_to_jtag" - "or pad_to_jtag through GPIO3", None), - ("USB_PHY_SEL", "usb config", 0, 3, 25, "bool", 2, None, None, "Select internal/external PHY for USB OTG" - "and usb_serial_jtag", None), - ("FLASH_TPUW", "config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), - ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), - ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), - ("FLASH_ECC_MODE", "config", 0, 4, 3, "bool", 18, None, None, "Configures the ECC mode for SPI flash", - {0: "16-byte to 18-byte mode", - 1: "16-byte to 17-byte mode"}), - ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " - "UART download boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", - {0: "Enabled", - 1: "Enable when GPIO 46 is low at reset", - 2: "Enable when GPIO 46 is high at rest", - 3: "Disabled"}), - ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "Sets default power supply for GPIO33..37", - {0: "VDD3P3_CPU", - 1: "VDD_SPI"}), - ("FLASH_TYPE", "config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", - {0: "4 data lines", - 1: "8 data lines"}), - ("FLASH_PAGE_SIZE", "config", 0, 4, 10, "uint:2", 18, None, None, "Sets the size of flash page", None), - ("FLASH_ECC_EN", "config", 0, 4, 12, "bool", 18, None, None, "Enables ECC in Flash boot mode", None), - ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Forces ROM code to send an SPI flash resume command " - "during SPI boot", None), - ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", - None), - ("DIS_USB_OTG_DOWNLOAD_MODE", "config", 0, 4, 31, "bool", 19, None, None, "Disables USB-OTG download feature in " - "UART download boot mode", None), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), - # - # Table 53: Parameters in BLOCK1-10 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), - ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), - ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), - ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), - ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), - ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), - ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), - ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), - ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), - ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), - ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), - ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), - - ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), - ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), - ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), - ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), - ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), - - ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), - ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", - {0: "No calibration", - 1: "With calibration"}), - ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), - ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), - ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), - ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), - ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), - ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), - ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), - ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, None, "System data (part 2)", None), - ] - - # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "??? Temperature calibration", None), - ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 1", None), - ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 2", None), - ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 3", None), - ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 4", None), - ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 5", None), - ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 6", None), - ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 7", None), - ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 8", None), - ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 9", None), - ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 10", None), - ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 11", None), - ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 12", None), - ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 13", None), - ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 14", None), - ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 15", None), - ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 16", None), - ] - - CALC = [ - ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py deleted file mode 100644 index 6b2d5a72a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3/operations.py +++ /dev/null @@ -1,523 +0,0 @@ -#!/usr/bin/env python -# This file includes the operations with eFuses for ESP32-S3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import io -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support post-write " - "data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software." - "The key with keypurpose[USER, RESERVED and *_DIGEST] " - "will remain readable anyway. " - "For the rest keypurposes the read-protection will be defined the option " - "(Read-protect by default).", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest to key efuse block", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - action="append", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key_digest.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " - "without changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format with " - "bytes separated by colons (e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - sdio_force = efuses["VDD_SPI_FORCE"] - sdio_tieh = efuses["VDD_SPI_TIEH"] - sdio_reg = efuses["VDD_SPI_XPD"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - ) - - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" - ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SPI). " - "SPI flash will need to be powered from an external source.\n" - "The following efuse is burned: VDD_SPI_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" - ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse VDD_SPI_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." - print(msg) - - sdio_force.save(1) # Disable GPIO45 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SPI setting complete.") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) - - print("") - print("ADC1 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) - - print("") - print("ADC2 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) - else: - print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) - # fmt: on - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) - ) - - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) - - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) - - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and keypurpose (%d) " - "should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list - ): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - if digest is None: - data = datafile.read() - else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. Key file must be %d bytes (%d bits) " - "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) - ) - - if efuses[block.key_purpose_name].need_rd_protect(keypurpose): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage of - # checking it as the whole field. - efuse.save(data) - - disable_wr_protect_key_purpose = False - if efuses[block.key_purpose_name].get() != keypurpose: - if efuses[block.key_purpose_name].is_writeable(): - print( - "\t'%s': '%s' -> '%s'." - % ( - block.key_purpose_name, - efuses[block.key_purpose_name].get(), - keypurpose, - ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: - raise esptool.FatalError( - "It is not possible to change '%s' to '%s' because " - "write protection bit is set." - % (block.key_purpose_name, keypurpose) - ) - else: - print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) - if efuses[block.key_purpose_name].is_writeable(): - disable_wr_protect_key_purpose = True - - if disable_wr_protect_key_purpose: - print("\tDisabling write to '%s'." % block.key_purpose_name) - efuses[block.key_purpose_name].disable_write() - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - digest_list = [] - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - block_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - for block_name, datafile in zip(block_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - digest = espsecure._digest_sbv2_public_key(datafile) - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. Digest must be %d bytes (%d bits) " - "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) - ) - digest_list.append(digest) - burn_key(esp, efuses, args, digest=digest_list) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py deleted file mode 100644 index a3b55a802..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import operations -from .emulate_efuse_controller import EmulateEfuseController -from .fields import EspEfuses diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py deleted file mode 100644 index 01d7c9750..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses controller for ESP32-S3(beta2) chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from ..emulate_efuse_controller_base import EmulateEfuseControllerBase, FatalError - - -class EmulateEfuseController(EmulateEfuseControllerBase): - """The class for virtual efuse operation. Using for HOST_TEST.""" - - CHIP_NAME = "ESP32-S3(beta2)" - mem = None - debug = False - Blocks = EfuseDefineBlocks - Fields = EfuseDefineFields - REGS = EfuseDefineRegisters - - def __init__(self, efuse_file=None, debug=False): - super(EmulateEfuseController, self).__init__(efuse_file, debug) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - - """ esptool method start >>""" - - def get_major_chip_version(self): - return 0 - - def get_minor_chip_version(self): - return 2 - - def get_crystal_freq(self): - return 40 # MHz (common for all chips) - - def get_security_info(self): - return { - "flags": 0, - "flash_crypt_cnt": 0, - "key_purposes": 0, - "chip_id": 0, - "api_version": 0, - } - - """ << esptool method end """ - - def handle_writing_event(self, addr, value): - if addr == self.REGS.EFUSE_CMD_REG: - if value & self.REGS.EFUSE_PGM_CMD: - self.copy_blocks_wr_regs_to_rd_regs(updated_block=(value >> 2) & 0xF) - self.clean_blocks_wr_regs() - self.check_rd_protection_area() - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - elif value == self.REGS.EFUSE_READ_CMD: - self.write_reg(addr, 0) - self.write_reg(self.REGS.EFUSE_STATUS_REG, 1) - self.save_to_file() - - def get_bitlen_of_block(self, blk, wr=False): - if blk.id == 0: - if wr: - return 32 * 8 - else: - return 32 * blk.len - else: - if wr: - rs_coding = 32 * 3 - return 32 * 8 + rs_coding - else: - return 32 * blk.len - - def handle_coding_scheme(self, blk, data): - if blk.id != 0: - # CODING_SCHEME RS applied only for all blocks except BLK0. - coded_bytes = 12 - data.pos = coded_bytes * 8 - plain_data = data.readlist("32*uint:8")[::-1] - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(coded_bytes) - # 32 byte of data + 12 bytes RS - calc_encoded_data = list(rs.encode([x for x in plain_data])) - data.pos = 0 - if calc_encoded_data != data.readlist("44*uint:8")[::-1]: - raise FatalError("Error in coding scheme data") - data = data[coded_bytes * 8 :] - if blk.len < 8: - data = data[(8 - blk.len) * 32 :] - return data diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py deleted file mode 100644 index 7eaed8523..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/fields.py +++ /dev/null @@ -1,478 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses for ESP32-S3 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import struct -import time - -from bitstring import BitArray - -import esptool - -import reedsolo - -from .mem_definition import EfuseDefineBlocks, EfuseDefineFields, EfuseDefineRegisters -from .. import base_fields -from .. import util - - -class EfuseBlock(base_fields.EfuseBlockBase): - def len_of_burn_unit(self): - # The writing register window is 8 registers for any blocks. - # len in bytes - return 8 * 4 - - def __init__(self, parent, param, skip_read=False): - parent.read_coding_scheme() - super(EfuseBlock, self).__init__(parent, param, skip_read=skip_read) - - def apply_coding_scheme(self): - data = self.get_raw(from_read=False)[::-1] - if len(data) < self.len_of_burn_unit(): - add_empty_bytes = self.len_of_burn_unit() - len(data) - data = data + (b"\x00" * add_empty_bytes) - if self.get_coding_scheme() == self.parent.REGS.CODING_SCHEME_RS: - # takes 32 bytes - # apply RS encoding - rs = reedsolo.RSCodec(12) - # 32 byte of data + 12 bytes RS - encoded_data = rs.encode([x for x in data]) - words = struct.unpack("<" + "I" * 11, encoded_data) - # returns 11 words (8 words of data + 3 words of RS coding) - else: - # takes 32 bytes - words = struct.unpack("<" + ("I" * (len(data) // 4)), data) - # returns 8 words - return words - - -class EspEfuses(base_fields.EspEfusesBase): - """ - Wrapper object to manage the efuse fields in a connected ESP bootloader - """ - - Blocks = EfuseDefineBlocks() - Fields = EfuseDefineFields() - REGS = EfuseDefineRegisters - BURN_BLOCK_DATA_NAMES = Blocks.get_burn_block_data_names() - BLOCKS_FOR_KEYS = Blocks.get_blocks_for_keys() - - debug = False - do_not_confirm = False - - def __init__(self, esp, skip_connect=False, debug=False, do_not_confirm=False): - self._esp = esp - self.debug = debug - self.do_not_confirm = do_not_confirm - if esp.CHIP_NAME != "ESP32-S3(beta2)": - raise esptool.FatalError( - "Expected the 'esp' param for ESP32-S3(beta2) chip but got for '%s'." - % (esp.CHIP_NAME) - ) - if not skip_connect: - flags = self._esp.get_security_info()["flags"] - GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE = 1 << 2 - if flags & GET_SECURITY_INFO_FLAG_SECURE_DOWNLOAD_ENABLE: - raise esptool.FatalError( - "Secure Download Mode is enabled. The tool can not read eFuses." - ) - self.blocks = [ - EfuseBlock(self, self.Blocks.get(block), skip_read=skip_connect) - for block in self.Blocks.BLOCKS - ] - if not skip_connect: - self.get_coding_scheme_warnings() - self.efuses = [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.KEYBLOCKS - ] - if skip_connect: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - else: - if self["BLK_VERSION_MAJOR"].get() == 1: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.CALC - ] - - def __getitem__(self, efuse_name): - """Return the efuse field with the given name""" - for e in self.efuses: - if efuse_name == e.name: - return e - new_fields = False - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES: - e = self.Fields.get(efuse) - if e.name == efuse_name: - self.efuses += [ - EfuseField.from_tuple( - self, self.Fields.get(efuse), self.Fields.get(efuse).class_type - ) - for efuse in self.Fields.BLOCK2_CALIBRATION_EFUSES - ] - new_fields = True - if new_fields: - for e in self.efuses: - if efuse_name == e.name: - return e - raise KeyError - - def read_coding_scheme(self): - self.coding_scheme = self.REGS.CODING_SCHEME_RS - - def print_status_regs(self): - print("") - self.blocks[0].print_block(self.blocks[0].err_bitarray, "err__regs", debug=True) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR0_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR0_REG) - ) - ) - print( - "{:27} 0x{:08x}".format( - "EFUSE_RD_RS_ERR1_REG", self.read_reg(self.REGS.EFUSE_RD_RS_ERR1_REG) - ) - ) - - def get_block_errors(self, block_num): - """Returns (error count, failure boolean flag)""" - return self.blocks[block_num].num_errors, self.blocks[block_num].fail - - def efuse_controller_setup(self): - self.set_efuse_timing() - self.clear_pgm_registers() - self.wait_efuse_idle() - - def write_efuses(self, block): - self.efuse_program(block) - return self.get_coding_scheme_warnings(silent=True) - - def clear_pgm_registers(self): - self.wait_efuse_idle() - for r in range( - self.REGS.EFUSE_PGM_DATA0_REG, self.REGS.EFUSE_PGM_DATA0_REG + 32, 4 - ): - self.write_reg(r, 0) - - def wait_efuse_idle(self): - deadline = time.time() + self.REGS.EFUSE_BURN_TIMEOUT - while time.time() < deadline: - # if self.read_reg(self.EFUSE_CMD_REG) == 0: - if self.read_reg(self.REGS.EFUSE_STATUS_REG) & 0x7 == 1: - return - raise esptool.FatalError( - "Timed out waiting for Efuse controller command to complete" - ) - - def efuse_program(self, block): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_WRITE_OP_CODE) - self.write_reg(self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_PGM_CMD | (block << 2)) - self.wait_efuse_idle() - self.clear_pgm_registers() - self.efuse_read() - - def efuse_read(self): - self.wait_efuse_idle() - self.write_reg(self.REGS.EFUSE_CONF_REG, self.REGS.EFUSE_READ_OP_CODE) - # need to add a delay after triggering EFUSE_READ_CMD, as ROM loader checks some - # efuse registers after each command is completed - # if ENABLE_SECURITY_DOWNLOAD or DIS_DOWNLOAD_MODE is enabled by the current cmd, then we need to try to reconnect to the chip. - try: - self.write_reg( - self.REGS.EFUSE_CMD_REG, self.REGS.EFUSE_READ_CMD, delay_after_us=1000 - ) - self.wait_efuse_idle() - except esptool.FatalError: - secure_download_mode_before = self._esp.secure_download_mode - - try: - self._esp = self.reconnect_chip(self._esp) - except esptool.FatalError: - print("Can not re-connect to the chip") - if not self["DIS_DOWNLOAD_MODE"].get() and self[ - "DIS_DOWNLOAD_MODE" - ].get(from_read=False): - print( - "This is the correct behavior as we are actually burning " - "DIS_DOWNLOAD_MODE which disables the connection to the chip" - ) - print("DIS_DOWNLOAD_MODE is enabled") - print("Successful") - exit(0) # finish without errors - raise - - print("Established a connection with the chip") - if self._esp.secure_download_mode and not secure_download_mode_before: - print("Secure download mode is enabled") - if not self["ENABLE_SECURITY_DOWNLOAD"].get() and self[ - "ENABLE_SECURITY_DOWNLOAD" - ].get(from_read=False): - print( - "espefuse tool can not continue to work in Secure download mode" - ) - print("ENABLE_SECURITY_DOWNLOAD is enabled") - print("Successful") - exit(0) # finish without errors - raise - - def set_efuse_timing(self): - """Set timing registers for burning efuses""" - # Configure clock - apb_freq = self.get_crystal_freq() - if apb_freq != 40: - raise esptool.FatalError( - "The eFuse supports only xtal=40M (xtal was %d)" % apb_freq - ) - - self.update_reg( - self.REGS.EFUSE_WR_TIM_CONF2_REG, self.REGS.EFUSE_PWR_OFF_NUM_M, 0x190 - ) - - def get_coding_scheme_warnings(self, silent=False): - """Check if the coding scheme has detected any errors.""" - old_addr_reg = 0 - reg_value = 0 - ret_fail = False - for block in self.blocks: - if block.id == 0: - words = [ - self.read_reg(self.REGS.EFUSE_RD_REPEAT_ERR0_REG + offs * 4) - for offs in range(5) - ] - data = BitArray() - for word in reversed(words): - data.append("uint:32=%d" % word) - # pos=32 because EFUSE_WR_DIS goes first it is 32bit long - # and not under error control - block.err_bitarray.overwrite(data, pos=32) - block.num_errors = block.err_bitarray.count(True) - block.fail = block.num_errors != 0 - else: - addr_reg, err_num_mask, err_num_offs, fail_bit = self.REGS.BLOCK_ERRORS[ - block.id - ] - if err_num_mask is None or err_num_offs is None or fail_bit is None: - continue - if addr_reg != old_addr_reg: - old_addr_reg = addr_reg - reg_value = self.read_reg(addr_reg) - block.fail = reg_value & (1 << fail_bit) != 0 - block.num_errors = (reg_value >> err_num_offs) & err_num_mask - ret_fail |= block.fail - if not silent and (block.fail or block.num_errors): - print( - "Error(s) in BLOCK%d [ERRORS:%d FAIL:%d]" - % (block.id, block.num_errors, block.fail) - ) - if (self.debug or ret_fail) and not silent: - self.print_status_regs() - return ret_fail - - def summary(self): - if self["VDD_SPI_FORCE"].get() == 0: - output = "Flash voltage (VDD_SPI) determined by GPIO45 on reset " - "(GPIO45=High: VDD_SPI pin is powered from internal 1.8V LDO\n" - output += "GPIO45=Low or NC: VDD_SPI pin is powered directly from " - "VDD3P3_RTC_IO via resistor Rspi. Typically this voltage is 3.3 V)." - elif self["VDD_SPI_XPD"].get() == 0: - output = "Flash voltage (VDD_SPI) internal regulator disabled by efuse." - elif self["VDD_SPI_TIEH"].get() == 0: - output = "Flash voltage (VDD_SPI) set to 1.8V by efuse." - else: - output = "Flash voltage (VDD_SPI) set to 3.3V by efuse." - return output - - -class EfuseField(base_fields.EfuseFieldBase): - @staticmethod - def from_tuple(parent, efuse_tuple, type_class): - return { - "mac": EfuseMacField, - "keypurpose": EfuseKeyPurposeField, - "t_sensor": EfuseTempSensor, - "adc_tp": EfuseAdcPointCalibration, - "wafer": EfuseWafer, - }.get(type_class, EfuseField)(parent, efuse_tuple) - - def get_info(self): - output = "%s (BLOCK%d)" % (self.name, self.block) - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output += ( - "[FAIL:%d]" % (fail) - if self.block == 0 - else "[ERRS:%d FAIL:%d]" % (errs, fail) - ) - if self.efuse_class == "keyblock": - name = self.parent.blocks[self.block].key_purpose_name - if name is not None: - output += "\n Purpose: %s\n " % (self.parent[name].get()) - return output - - -class EfuseWafer(EfuseField): - def get(self, from_read=True): - hi_bits = self.parent["WAFER_VERSION_MINOR_HI"].get(from_read) - lo_bits = self.parent["WAFER_VERSION_MINOR_LO"].get(from_read) - return (hi_bits << 3) + lo_bits - - def save(self, new_value): - raise esptool.FatalError("Burning %s is not supported" % self.name) - - -class EfuseTempSensor(EfuseField): - def get(self, from_read=True): - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * 0.1 - - -class EfuseAdcPointCalibration(EfuseField): - def get(self, from_read=True): - STEP_SIZE = 4 - value = self.get_bitstring(from_read) - sig = -1 if value[0] else 1 - return sig * value[1:].uint * STEP_SIZE - - -class EfuseMacField(EfuseField): - def check_format(self, new_value_str): - if new_value_str is None: - raise esptool.FatalError( - "Required MAC Address in AA:CD:EF:01:02:03 format!" - ) - if new_value_str.count(":") != 5: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal format " - "separated by colons (:)!" - ) - hexad = new_value_str.replace(":", "") - if len(hexad) != 12: - raise esptool.FatalError( - "MAC Address needs to be a 6-byte hexadecimal number " - "(12 hexadecimal characters)!" - ) - # order of bytearray = b'\xaa\xcd\xef\x01\x02\x03', - bindata = binascii.unhexlify(hexad) - # unicast address check according to - # https://tools.ietf.org/html/rfc7042#section-2.1 - if esptool.util.byte(bindata, 0) & 0x01: - raise esptool.FatalError("Custom MAC must be a unicast MAC!") - return bindata - - def check(self): - errs, fail = self.parent.get_block_errors(self.block) - if errs != 0 or fail: - output = "Block%d has ERRORS:%d FAIL:%d" % (self.block, errs, fail) - else: - output = "OK" - return "(" + output + ")" - - def get(self, from_read=True): - if self.name == "CUSTOM_MAC": - mac = self.get_raw(from_read)[::-1] - else: - mac = self.get_raw(from_read) - return "%s %s" % (util.hexify(mac, ":"), self.check()) - - def save(self, new_value): - def print_field(e, new_value): - print( - " - '{}' ({}) {} -> {}".format( - e.name, e.description, e.get_bitstring(), new_value - ) - ) - - if self.name == "CUSTOM_MAC": - bitarray_mac = self.convert_to_bitstring(new_value) - print_field(self, bitarray_mac) - super(EfuseMacField, self).save(new_value) - else: - # Writing the BLOCK1 (MAC_SPI_8M_0) default MAC is not possible, - # as it's written in the factory. - raise esptool.FatalError("Writing Factory MAC address is not supported") - - -# fmt: off -class EfuseKeyPurposeField(EfuseField): - KEY_PURPOSES = [ - ("USER", 0, None, None, "no_need_rd_protect"), # User purposes (software-only use) - ("RESERVED", 1, None, None, "no_need_rd_protect"), # Reserved - ("XTS_AES_256_KEY_1", 2, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_1 (flash/PSRAM encryption) - ("XTS_AES_256_KEY_2", 3, None, "Reverse", "need_rd_protect"), # XTS_AES_256_KEY_2 (flash/PSRAM encryption) - ("XTS_AES_128_KEY", 4, None, "Reverse", "need_rd_protect"), # XTS_AES_128_KEY (flash/PSRAM encryption) - ("HMAC_DOWN_ALL", 5, None, None, "need_rd_protect"), # HMAC Downstream mode - ("HMAC_DOWN_JTAG", 6, None, None, "need_rd_protect"), # JTAG soft enable key (uses HMAC Downstream mode) - ("HMAC_DOWN_DIGITAL_SIGNATURE", 7, None, None, "need_rd_protect"), # Digital Signature peripheral key (uses HMAC Downstream mode) - ("HMAC_UP", 8, None, None, "need_rd_protect"), # HMAC Upstream mode - ("SECURE_BOOT_DIGEST0", 9, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST0 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST1", 10, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST1 (Secure Boot key digest) - ("SECURE_BOOT_DIGEST2", 11, "DIGEST", None, "no_need_rd_protect"), # SECURE_BOOT_DIGEST2 (Secure Boot key digest) - ("XTS_AES_256_KEY", -1, "VIRTUAL", None, "no_need_rd_protect"), # Virtual purpose splits to XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - ] -# fmt: on - - KEY_PURPOSES_NAME = [name[0] for name in KEY_PURPOSES] - DIGEST_KEY_PURPOSES = [name[0] for name in KEY_PURPOSES if name[2] == "DIGEST"] - - def check_format(self, new_value_str): - # str convert to int: "XTS_AES_128_KEY" - > str(4) - # if int: 4 -> str(4) - raw_val = new_value_str - for purpose_name in self.KEY_PURPOSES: - if purpose_name[0] == new_value_str: - raw_val = str(purpose_name[1]) - break - if raw_val.isdigit(): - if int(raw_val) not in [p[1] for p in self.KEY_PURPOSES if p[1] > 0]: - raise esptool.FatalError("'%s' can not be set (value out of range)" % raw_val) - else: - raise esptool.FatalError("'%s' unknown name" % raw_val) - return raw_val - - def need_reverse(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[3] == "Reverse" - - def need_rd_protect(self, new_key_purpose): - for key in self.KEY_PURPOSES: - if key[0] == new_key_purpose: - return key[4] == "need_rd_protect" - - def get(self, from_read=True): - for p in self.KEY_PURPOSES: - if p[1] == self.get_raw(from_read): - return p[0] - return "FORBIDDEN_STATE" - - def save(self, new_value): - raw_val = int(self.check_format(str(new_value))) - return super(EfuseKeyPurposeField, self).save(raw_val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py deleted file mode 100644 index 42d7a7cc8..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/mem_definition.py +++ /dev/null @@ -1,250 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32-S3(beta2) chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..mem_definition_base import EfuseBlocksBase, EfuseFieldsBase, EfuseRegistersBase - - -# fmt: off -class EfuseDefineRegisters(EfuseRegistersBase): - - EFUSE_ADDR_MASK = 0x00000FFF - EFUSE_MEM_SIZE = (0x01FC + 4) - - # EFUSE registers & command/conf values - DR_REG_EFUSE_BASE = 0x6001A000 - EFUSE_PGM_DATA0_REG = DR_REG_EFUSE_BASE - EFUSE_CHECK_VALUE0_REG = DR_REG_EFUSE_BASE + 0x020 - EFUSE_CLK_REG = DR_REG_EFUSE_BASE + 0x1C8 - EFUSE_CONF_REG = DR_REG_EFUSE_BASE + 0x1CC - EFUSE_STATUS_REG = DR_REG_EFUSE_BASE + 0x1D0 - EFUSE_CMD_REG = DR_REG_EFUSE_BASE + 0x1D4 - EFUSE_RD_RS_ERR0_REG = DR_REG_EFUSE_BASE + 0x1C0 - EFUSE_RD_RS_ERR1_REG = DR_REG_EFUSE_BASE + 0x1C4 - EFUSE_RD_REPEAT_ERR0_REG = DR_REG_EFUSE_BASE + 0x17C - EFUSE_RD_REPEAT_ERR1_REG = DR_REG_EFUSE_BASE + 0x180 - EFUSE_RD_REPEAT_ERR2_REG = DR_REG_EFUSE_BASE + 0x184 - EFUSE_RD_REPEAT_ERR3_REG = DR_REG_EFUSE_BASE + 0x188 - EFUSE_RD_REPEAT_ERR4_REG = DR_REG_EFUSE_BASE + 0x18C - EFUSE_DAC_CONF_REG = DR_REG_EFUSE_BASE + 0x1E8 - EFUSE_RD_TIM_CONF_REG = DR_REG_EFUSE_BASE + 0x1EC - EFUSE_WR_TIM_CONF1_REG = DR_REG_EFUSE_BASE + 0x1F4 - EFUSE_WR_TIM_CONF2_REG = DR_REG_EFUSE_BASE + 0x1F8 - EFUSE_DATE_REG = DR_REG_EFUSE_BASE + 0x1FC - EFUSE_WRITE_OP_CODE = 0x5A5A - EFUSE_READ_OP_CODE = 0x5AA5 - EFUSE_PGM_CMD_MASK = 0x3 - EFUSE_PGM_CMD = 0x2 - EFUSE_READ_CMD = 0x1 - - BLOCK_ERRORS = [ - # error_reg, err_num_mask, err_num_offs, fail_bit - (EFUSE_RD_REPEAT_ERR0_REG, None, None, None), # BLOCK0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 0, 3), # MAC_SPI_8M_0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 4, 7), # BLOCK_SYS_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 8, 11), # BLOCK_USR_DATA - (EFUSE_RD_RS_ERR0_REG, 0x7, 12, 15), # BLOCK_KEY0 - (EFUSE_RD_RS_ERR0_REG, 0x7, 16, 19), # BLOCK_KEY1 - (EFUSE_RD_RS_ERR0_REG, 0x7, 20, 23), # BLOCK_KEY2 - (EFUSE_RD_RS_ERR0_REG, 0x7, 24, 27), # BLOCK_KEY3 - (EFUSE_RD_RS_ERR0_REG, 0x7, 28, 31), # BLOCK_KEY4 - (EFUSE_RD_RS_ERR1_REG, 0x7, 0, 3), # BLOCK_KEY5 - (EFUSE_RD_RS_ERR1_REG, 0x7, 4, 7), # BLOCK_SYS_DATA2 - ] - - # EFUSE_WR_TIM_CONF2_REG - EFUSE_PWR_OFF_NUM_S = 0 - EFUSE_PWR_OFF_NUM_M = 0xFFFF << EFUSE_PWR_OFF_NUM_S - - -class EfuseDefineBlocks(EfuseBlocksBase): - - __base_rd_regs = EfuseDefineRegisters.DR_REG_EFUSE_BASE - __base_wr_regs = EfuseDefineRegisters.EFUSE_PGM_DATA0_REG - # List of efuse blocks - BLOCKS = [ - # Name, Alias, Index, Read address, Write address, Write protect bit, Read protect bit, Len, key_purpose - ("BLOCK0", [], 0, __base_rd_regs + 0x02C, __base_wr_regs, None, None, 6, None), - ("MAC_SPI_8M_0", ["BLOCK1"], 1, __base_rd_regs + 0x044, __base_wr_regs, 20, None, 6, None), - ("BLOCK_SYS_DATA", ["BLOCK2"], 2, __base_rd_regs + 0x05C, __base_wr_regs, 21, None, 8, None), - ("BLOCK_USR_DATA", ["BLOCK3"], 3, __base_rd_regs + 0x07C, __base_wr_regs, 22, None, 8, None), - ("BLOCK_KEY0", ["BLOCK4"], 4, __base_rd_regs + 0x09C, __base_wr_regs, 23, 0, 8, "KEY_PURPOSE_0"), - ("BLOCK_KEY1", ["BLOCK5"], 5, __base_rd_regs + 0x0BC, __base_wr_regs, 24, 1, 8, "KEY_PURPOSE_1"), - ("BLOCK_KEY2", ["BLOCK6"], 6, __base_rd_regs + 0x0DC, __base_wr_regs, 25, 2, 8, "KEY_PURPOSE_2"), - ("BLOCK_KEY3", ["BLOCK7"], 7, __base_rd_regs + 0x0FC, __base_wr_regs, 26, 3, 8, "KEY_PURPOSE_3"), - ("BLOCK_KEY4", ["BLOCK8"], 8, __base_rd_regs + 0x11C, __base_wr_regs, 27, 4, 8, "KEY_PURPOSE_4"), - ("BLOCK_KEY5", ["BLOCK9"], 9, __base_rd_regs + 0x13C, __base_wr_regs, 28, 5, 8, "KEY_PURPOSE_5"), - ("BLOCK_SYS_DATA2", ["BLOCK10"], 10, __base_rd_regs + 0x15C, __base_wr_regs, 29, 6, 8, None), - ] - - def get_burn_block_data_names(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseDefineFields(EfuseFieldsBase): - - # List of efuse fields from TRM the chapter eFuse Controller. - EFUSES = [ - # - # Table 51: Parameters in BLOCK0 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("WR_DIS", "efuse", 0, 0, 0, "uint:32", None, None, None, "Disables programming of individual eFuses", None), - ("RD_DIS", "efuse", 0, 1, 0, "uint:7", 0, None, None, "Disables software reading from BLOCK4-10", None), - ("DIS_ICACHE", "config", 0, 1, 8, "bool", 2, None, None, "Disables ICache", None), - ("DIS_DCACHE", "config", 0, 1, 9, "bool", 2, None, None, "Disables DCache", None), - ("DIS_DOWNLOAD_ICACHE", "config", 0, 1, 10, "bool", 2, None, None, "Disables Icache when SoC is in Download mode", None), - ("DIS_DOWNLOAD_DCACHE", "config", 0, 1, 11, "bool", 2, None, None, "Disables Dcache when SoC is in Download mode", None), - ("DIS_FORCE_DOWNLOAD", "config", 0, 1, 12, "bool", 2, None, None, "Disables forcing chip into Download mode", None), - ("DIS_USB", "usb config", 0, 1, 13, "bool", 2, None, None, "Disables the USB OTG hardware", None), - ("DIS_CAN", "config", 0, 1, 14, "bool", 2, None, None, "Disables the TWAI Controller hardware", None), - ("DIS_APP_CPU", "config", 0, 1, 15, "bool", 2, None, None, "Disables APP CPU", None), - ("SOFT_DIS_JTAG", "security", 0, 1, 16, "uint:3", 31, None, None, "Software disables JTAG by programming " - "odd number of 1 bit(s). " - "JTAG can be re-enabled via HMAC peripheral", - None), - ("HARD_DIS_JTAG", "security", 0, 1, 19, "bool", 2, None, None, "Hardware disables JTAG permanently", None), - - ("DIS_DOWNLOAD_MANUAL_ENCRYPT", "security", 0, 1, 20, "bool", 2, None, None, "Disables flash encryption when in download boot modes", - None), - ("USB_EXCHG_PINS", "usb config", 0, 1, 25, "bool", 30, None, None, "Exchanges USB D+ and D- pins", None), - ("EXT_PHY_ENABLE", "usb config", 0, 1, 26, "bool", 30, None, None, "Enables external USB PHY", None), - ("BTLC_GPIO_ENABLE", "usb config", 0, 1, 27, "uint:2", 30, None, None, "Enables BTLC GPIO", None), - ("VDD_SPI_XPD", "VDD_SPI config", 0, 2, 4, "bool", 3, None, None, "The VDD_SPI regulator is powered on", None), - ("VDD_SPI_TIEH", "VDD_SPI config", 0, 2, 5, "bool", 3, None, None, "The VDD_SPI power supply voltage at reset", - {0: "Connect to 1.8V LDO", - 1: "Connect to VDD_RTC_IO"}), - ("VDD_SPI_FORCE", "VDD_SPI config", 0, 2, 6, "bool", 3, None, None, "Force using VDD_SPI_XPD and VDD_SPI_TIEH " - "to configure VDD_SPI LDO", None), - ("WDT_DELAY_SEL", "WDT config", 0, 2, 16, "uint:2", 3, None, None, "Selects RTC WDT timeout threshold at startup", None), - ("SPI_BOOT_CRYPT_CNT", "security", 0, 2, 18, "uint:3", 4, None, "bitcount", "Enables encryption and decryption, when an SPI boot " - "mode is set. Enabled when 1 or 3 bits are set," - "disabled otherwise", - {0: "Disable", - 1: "Enable", - 3: "Disable", - 7: "Enable"}), - ("SECURE_BOOT_KEY_REVOKE0", "security", 0, 2, 21, "bool", 5, None, None, "Revokes use of secure boot key digest 0", None), - ("SECURE_BOOT_KEY_REVOKE1", "security", 0, 2, 22, "bool", 6, None, None, "Revokes use of secure boot key digest 1", None), - ("SECURE_BOOT_KEY_REVOKE2", "security", 0, 2, 23, "bool", 7, None, None, "Revokes use of secure boot key digest 2", None), - ("KEY_PURPOSE_0", "security", 0, 2, 24, "uint:4", 8, None, "keypurpose", "KEY0 purpose", None), - ("KEY_PURPOSE_1", "security", 0, 2, 28, "uint:4", 9, None, "keypurpose", "KEY1 purpose", None), - ("KEY_PURPOSE_2", "security", 0, 3, 0, "uint:4", 10, None, "keypurpose", "KEY2 purpose", None), - ("KEY_PURPOSE_3", "security", 0, 3, 4, "uint:4", 11, None, "keypurpose", "KEY3 purpose", None), - ("KEY_PURPOSE_4", "security", 0, 3, 8, "uint:4", 12, None, "keypurpose", "KEY4 purpose", None), - ("KEY_PURPOSE_5", "security", 0, 3, 12, "uint:4", 13, None, "keypurpose", "KEY5 purpose", None), - ("SECURE_BOOT_EN", "security", 0, 3, 20, "bool", 15, None, None, "Enables secure boot", None), - ("SECURE_BOOT_AGGRESSIVE_REVOKE", "security", 0, 3, 21, "bool", 16, None, None, "Enables aggressive secure boot key revocation mode", - None), - ("FLASH_TPUW", "config", 0, 3, 28, "uint:4", 18, None, None, "Configures flash startup delay after SoC power-up, " - "unit is (ms/2). When the value is 15, delay is 7.5 ms", - None), - ("DIS_DOWNLOAD_MODE", "security", 0, 4, 0, "bool", 18, None, None, "Disables all Download boot modes", None), - ("DIS_DIRECT_BOOT", "config", 0, 4, 1, "bool", 18, None, None, "Disables direct boot mode", None), - ("DIS_USB_SERIAL_JTAG_ROM_PRINT", "config", 0, 4, 2, "bool", 18, None, None, "Disables USB-Serial-JTAG ROM printing", None), - ("FLASH_ECC_MODE", "config", 0, 4, 3, "bool", 18, None, None, "Configures the ECC mode for SPI flash", - {0: "16-byte to 18-byte mode", - 1: "16-byte to 17-byte mode"}), - ("DIS_USB_SERIAL_JTAG_DOWNLOAD_MODE", "config", 0, 4, 4, "bool", 18, None, None, "Disables USB-Serial-JTAG download feature in " - "UART download boot mode", None), - ("ENABLE_SECURITY_DOWNLOAD", "security", 0, 4, 5, "bool", 18, None, None, "Enables secure UART download mode " - "(read/write flash only)", None), - ("UART_PRINT_CONTROL", "config", 0, 4, 6, "uint:2", 18, None, None, "Sets the default UART boot message output mode", - {0: "Enabled", - 1: "Enable when GPIO 46 is low at reset", - 2: "Enable when GPIO 46 is high at rest", - 3: "Disabled"}), - ("PIN_POWER_SELECTION", "VDD_SPI config", 0, 4, 8, "bool", 18, None, None, "Sets default power supply for GPIO33..37", - {0: "VDD3P3_CPU", - 1: "VDD_SPI"}), - ("FLASH_TYPE", "config", 0, 4, 9, "bool", 18, None, None, "Selects SPI flash type", - {0: "4 data lines", - 1: "8 data lines"}), - ("FLASH_PAGE_SIZE", "config", 0, 4, 10, "uint:2", 18, None, None, "Sets the size of flash page", None), - ("FLASH_ECC_EN", "config", 0, 4, 12, "bool", 18, None, None, "Enables ECC in Flash boot mode", None), - ("FORCE_SEND_RESUME", "config", 0, 4, 13, "bool", 18, None, None, "Forces ROM code to send an SPI flash resume command " - "during SPI boot", None), - ("SECURE_VERSION", "identity", 0, 4, 14, "uint:16", 18, None, "bitcount", "Secure version (used by ESP-IDF anti-rollback feature)", - None), - ("DIS_USB_OTG_DOWNLOAD_MODE", "config", 0, 4, 31, "bool", 19, None, None, "Disables USB-OTG download feature in " - "UART download boot mode", None), - ("DISABLE_WAFER_VERSION_MAJOR", "config", 0, 5, 0, "bool", 19, None, None, "Disables check of wafer version major", None), - ("DISABLE_BLK_VERSION_MAJOR", "config", 0, 5, 1, "bool", 19, None, None, "Disables check of blk version major", None), - # - # Table 53: Parameters in BLOCK1-10 - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ("MAC", "identity", 1, 0, 0, "bytes:6", 20, None, "mac", "Factory MAC Address", None), - ("SPI_PAD_CONFIG_CLK", "spi_pad_config", 1, 1, 16, "uint:6", 20, None, None, "SPI CLK pad", None), - ("SPI_PAD_CONFIG_Q", "spi_pad_config", 1, 1, 22, "uint:6", 20, None, None, "SPI Q (D1) pad", None), - ("SPI_PAD_CONFIG_D", "spi_pad_config", 1, 1, 28, "uint:6", 20, None, None, "SPI D (D0) pad", None), - ("SPI_PAD_CONFIG_CS", "spi_pad_config", 1, 2, 2, "uint:6", 20, None, None, "SPI CS pad", None), - ("SPI_PAD_CONFIG_HD", "spi_pad_config", 1, 2, 8, "uint:6", 20, None, None, "SPI HD (D3) pad", None), - ("SPI_PAD_CONFIG_WP", "spi_pad_config", 1, 2, 14, "uint:6", 20, None, None, "SPI WP (D2) pad", None), - ("SPI_PAD_CONFIG_DQS", "spi_pad_config", 1, 2, 20, "uint:6", 20, None, None, "SPI DQS pad", None), - ("SPI_PAD_CONFIG_D4", "spi_pad_config", 1, 2, 26, "uint:6", 20, None, None, "SPI D4 pad", None), - ("SPI_PAD_CONFIG_D5", "spi_pad_config", 1, 3, 0, "uint:6", 20, None, None, "SPI D5 pad", None), - ("SPI_PAD_CONFIG_D6", "spi_pad_config", 1, 3, 6, "uint:6", 20, None, None, "SPI D6 pad", None), - ("SPI_PAD_CONFIG_D7", "spi_pad_config", 1, 3, 12, "uint:6", 20, None, None, "SPI D7 pad", None), - - ("WAFER_VERSION_MINOR_LO", "identity", 1, 3, 18, "uint:3", 20, None, None, "WAFER_VERSION_MINOR least significant bits", None), - ("PKG_VERSION", "identity", 1, 3, 21, "uint:3", 20, None, None, "Package version", None), - ("BLK_VERSION_MINOR", "identity", 1, 3, 24, "uint:3", 20, None, None, "BLOCK version minor", None), - ("WAFER_VERSION_MINOR_HI", "identity", 1, 5, 23, "uint:1", 20, None, None, "WAFER_VERSION_MINOR most significant bits", None), - ("WAFER_VERSION_MAJOR", "identity", 1, 5, 24, "uint:2", 20, None, None, "WAFER_VERSION_MAJOR", None), - - ("OPTIONAL_UNIQUE_ID", "identity", 2, 0, 0, "bytes:16", 21, None, "keyblock", "Optional unique 128-bit ID", None), - ("BLK_VERSION_MAJOR", "identity", 2, 4, 0, "uint:2", 21, None, None, "BLOCK version major", - {0: "No calibration", - 1: "With calibration"}), - ("CUSTOM_MAC", "identity", 3, 6, 8, "bytes:6", 22, None, "mac", "Custom MAC Address", None), - ] - - KEYBLOCKS = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('BLOCK_USR_DATA', "config", 3, 0, 0, "bytes:32", 22, None, None, "User data", None), - ('BLOCK_KEY0', "security", 4, 0, 0, "bytes:32", 23, 0, "keyblock", "Encryption key0 or user data", None), - ('BLOCK_KEY1', "security", 5, 0, 0, "bytes:32", 24, 1, "keyblock", "Encryption key1 or user data", None), - ('BLOCK_KEY2', "security", 6, 0, 0, "bytes:32", 25, 2, "keyblock", "Encryption key2 or user data", None), - ('BLOCK_KEY3', "security", 7, 0, 0, "bytes:32", 26, 3, "keyblock", "Encryption key3 or user data", None), - ('BLOCK_KEY4', "security", 8, 0, 0, "bytes:32", 27, 4, "keyblock", "Encryption key4 or user data", None), - ('BLOCK_KEY5', "security", 9, 0, 0, "bytes:32", 28, 5, "keyblock", "Encryption key5 or user data", None), - ('BLOCK_SYS_DATA2', "security", 10, 0, 0, "bytes:32", 29, 6, None, "System data (part 2)", None), - ] - - # if BLK_VERSION_MAJOR is 1, these efuse fields are in BLOCK2 - BLOCK2_CALIBRATION_EFUSES = [ - # Name Category Block Word Pos Type:len WR_DIS RD_DIS Class Description Dictionary - ('TEMP_SENSOR_CAL', "calibration", 2, 4, 7, "uint:9", 21, None, "t_sensor", "??? Temperature calibration", None), - ('ADC1_MODE0_D2', "calibration", 2, 4, 16, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 1", None), - ('ADC1_MODE1_D2', "calibration", 2, 4, 24, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 2", None), - ('ADC1_MODE2_D2', "calibration", 2, 5, 0, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 3", None), - ('ADC1_MODE3_D2', "calibration", 2, 5, 8, "uint:8", 21, None, "adc_tp", "??? ADC1 calibration 4", None), - ('ADC2_MODE0_D2', "calibration", 2, 5, 16, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 5", None), - ('ADC2_MODE1_D2', "calibration", 2, 5, 24, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 6", None), - ('ADC2_MODE2_D2', "calibration", 2, 6, 0, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 7", None), - ('ADC2_MODE3_D2', "calibration", 2, 6, 8, "uint:8", 21, None, "adc_tp", "??? ADC2 calibration 8", None), - ('ADC1_MODE0_D1', "calibration", 2, 6, 16, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 9", None), - ('ADC1_MODE1_D1', "calibration", 2, 6, 22, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 10", None), - ('ADC1_MODE2_D1', "calibration", 2, 6, 28, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 11", None), - ('ADC1_MODE3_D1', "calibration", 2, 7, 2, "uint:6", 21, None, "adc_tp", "??? ADC1 calibration 12", None), - ('ADC2_MODE0_D1', "calibration", 2, 7, 8, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 13", None), - ('ADC2_MODE1_D1', "calibration", 2, 7, 14, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 14", None), - ('ADC2_MODE2_D1', "calibration", 2, 7, 20, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 15", None), - ('ADC2_MODE3_D1', "calibration", 2, 7, 26, "uint:6", 21, None, "adc_tp", "??? ADC2 calibration 16", None), - ] - - CALC = [ - ("WAFER_VERSION_MINOR", "identity", 0, None, None, "uint:4", None, None, "wafer", "calc WAFER VERSION MINOR = WAFER_VERSION_MINOR_HI << 3 + WAFER_VERSION_MINOR_LO (read only)", None), - ] -# fmt: on diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py deleted file mode 100644 index 009e55fce..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/esp32s3beta2/operations.py +++ /dev/null @@ -1,523 +0,0 @@ -#!/usr/bin/env python -# This file includes the operations with eFuses for ESP32-S3(beta2) chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import io -import os # noqa: F401. It is used in IDF scripts -import traceback - -import espsecure - -import esptool - -from . import fields -from .. import util -from ..base_operations import ( - add_common_commands, - add_force_write_always, - burn_bit, - burn_block_data, - burn_efuse, - check_error, - dump, - read_protect_efuse, - summary, - write_protect_efuse, -) - - -def protect_options(p): - p.add_argument( - "--no-write-protect", - help="Disable write-protecting of the key. The key remains writable. " - "(The keys use the RS coding scheme that does not support post-write " - "data changes. Forced write can damage RS encoding bits.) " - "The write-protecting of keypurposes does not depend on the option, " - "it will be set anyway.", - action="store_true", - ) - p.add_argument( - "--no-read-protect", - help="Disable read-protecting of the key. The key remains readable software." - "The key with keypurpose[USER, RESERVED and *_DIGEST] " - "will remain readable anyway. " - "For the rest keypurposes the read-protection will be defined the option " - "(Read-protect by default).", - action="store_true", - ) - - -def add_commands(subparsers, efuses): - add_common_commands(subparsers, efuses) - burn_key = subparsers.add_parser( - "burn_key", help="Burn the key block with the specified name" - ) - protect_options(burn_key) - add_force_write_always(burn_key) - burn_key.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - action="append", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key.add_argument( - "keyfile", - help="File containing 256 bits of binary key data", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.KEY_PURPOSES_NAME, - ) - - burn_key_digest = subparsers.add_parser( - "burn_key_digest", - help="Parse a RSA public key and burn the digest to key efuse block", - ) - protect_options(burn_key_digest) - add_force_write_always(burn_key_digest) - burn_key_digest.add_argument( - "block", - help="Key block to burn", - action="append", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - action="append", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - action="append", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - for _ in efuses.BLOCKS_FOR_KEYS: - burn_key_digest.add_argument( - "block", - help="Key block to burn", - nargs="?", - action="append", - metavar="BLOCK", - choices=efuses.BLOCKS_FOR_KEYS, - ) - burn_key_digest.add_argument( - "keyfile", - help="Key file to digest (PEM format)", - nargs="?", - action="append", - metavar="KEYFILE", - type=argparse.FileType("rb"), - ) - burn_key_digest.add_argument( - "keypurpose", - help="Purpose to set.", - nargs="?", - action="append", - metavar="KEYPURPOSE", - choices=fields.EfuseKeyPurposeField.DIGEST_KEY_PURPOSES, - ) - - p = subparsers.add_parser( - "set_flash_voltage", - help="Permanently set the internal flash voltage regulator " - "to either 1.8V, 3.3V or OFF. This means GPIO45 can be high or low at reset " - "without changing the flash voltage.", - ) - p.add_argument("voltage", help="Voltage selection", choices=["1.8V", "3.3V", "OFF"]) - - p = subparsers.add_parser( - "burn_custom_mac", help="Burn a 48-bit Custom MAC Address to EFUSE BLOCK3." - ) - p.add_argument( - "mac", - help="Custom MAC Address to burn given in hexadecimal format with bytes " - "separated by colons (e.g. AA:CD:EF:01:02:03).", - type=fields.base_fields.CheckArgValue(efuses, "CUSTOM_MAC"), - ) - add_force_write_always(p) - - p = subparsers.add_parser("get_custom_mac", help="Prints the Custom MAC Address.") - - -def burn_custom_mac(esp, efuses, args): - efuses["CUSTOM_MAC"].save(args.mac) - if not efuses.burn_all(check_batch_mode=True): - return - get_custom_mac(esp, efuses, args) - print("Successful") - - -def get_custom_mac(esp, efuses, args): - print("Custom MAC Address: {}".format(efuses["CUSTOM_MAC"].get())) - - -def set_flash_voltage(esp, efuses, args): - sdio_force = efuses["VDD_SPI_FORCE"] - sdio_tieh = efuses["VDD_SPI_TIEH"] - sdio_reg = efuses["VDD_SPI_XPD"] - - # check efuses aren't burned in a way which makes this impossible - if args.voltage == "OFF" and sdio_reg.get() != 0: - raise esptool.FatalError( - "Can't set flash regulator to OFF as VDD_SPI_XPD efuse is already burned" - ) - - if args.voltage == "1.8V" and sdio_tieh.get() != 0: - raise esptool.FatalError( - "Can't set regulator to 1.8V is VDD_SPI_TIEH efuse is already burned" - ) - - if args.voltage == "OFF": - msg = "Disable internal flash voltage regulator (VDD_SPI). " - "SPI flash will need to be powered from an external source.\n" - "The following efuse is burned: VDD_SPI_FORCE.\n" - "It is possible to later re-enable the internal regulator (%s) " % ( - "to 3.3V" if sdio_tieh.get() != 0 else "to 1.8V or 3.3V" - ) - "by burning an additional efuse" - elif args.voltage == "1.8V": - msg = "Set internal flash voltage regulator (VDD_SPI) to 1.8V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD.\n" - "It is possible to later increase the voltage to 3.3V (permanently) " - "by burning additional efuse VDD_SPI_TIEH" - elif args.voltage == "3.3V": - msg = "Enable internal flash voltage regulator (VDD_SPI) to 3.3V.\n" - "The following efuses are burned: VDD_SPI_FORCE, VDD_SPI_XPD, VDD_SPI_TIEH." - print(msg) - - sdio_force.save(1) # Disable GPIO45 - if args.voltage != "OFF": - sdio_reg.save(1) # Enable internal regulator - if args.voltage == "3.3V": - sdio_tieh.save(1) - print("VDD_SPI setting complete.") - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def adc_info(esp, efuses, args): - print("") - # fmt: off - if efuses["BLK_VERSION_MAJOR"].get() == 1: - print("Temperature Sensor Calibration = {}C".format(efuses["TEMP_SENSOR_CAL"].get())) - - print("") - print("ADC1 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC1_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC1_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC1_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC1_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC1_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC1_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC1_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC1_MODE3_D2"].get())) - - print("") - print("ADC2 readings stored in efuse BLOCK2:") - print(" MODE0 D1 reading (250mV): {}".format(efuses["ADC2_MODE0_D1"].get())) - print(" MODE0 D2 reading (600mV): {}".format(efuses["ADC2_MODE0_D2"].get())) - - print(" MODE1 D1 reading (250mV): {}".format(efuses["ADC2_MODE1_D1"].get())) - print(" MODE1 D2 reading (800mV): {}".format(efuses["ADC2_MODE1_D2"].get())) - - print(" MODE2 D1 reading (250mV): {}".format(efuses["ADC2_MODE2_D1"].get())) - print(" MODE2 D2 reading (1000mV): {}".format(efuses["ADC2_MODE2_D2"].get())) - - print(" MODE3 D1 reading (250mV): {}".format(efuses["ADC2_MODE3_D1"].get())) - print(" MODE3 D2 reading (2000mV): {}".format(efuses["ADC2_MODE3_D2"].get())) - else: - print("BLK_VERSION_MAJOR = {}".format(efuses["BLK_VERSION_MAJOR"].get_meaning())) - # fmt: on - - -def key_block_is_unused(block, key_purpose_block): - if not block.is_readable() or not block.is_writeable(): - return False - - if key_purpose_block.get() != "USER" or not key_purpose_block.is_writeable(): - return False - - if not block.get_bitstring().all(False): - return False - - return True - - -def get_next_key_block(efuses, current_key_block, block_name_list): - key_blocks = [b for b in efuses.blocks if b.key_purpose_name] - start = key_blocks.index(current_key_block) - - # Sort key blocks so that we pick the next free block (and loop around if necessary) - key_blocks = key_blocks[start:] + key_blocks[0:start] - - # Exclude any other blocks that will be be burned - key_blocks = [b for b in key_blocks if b.name not in block_name_list] - - for block in key_blocks: - key_purpose_block = efuses[block.key_purpose_name] - if key_block_is_unused(block, key_purpose_block): - return block - - return None - - -def split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list): - i = keypurpose_list.index("XTS_AES_256_KEY") - block_name = block_name_list[i] - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - data = datafile_list[i].read() - if len(data) != 64: - raise esptool.FatalError( - "Incorrect key file size %d, XTS_AES_256_KEY should be 64 bytes" % len(data) - ) - - key_block_2 = get_next_key_block(efuses, block, block_name_list) - if not key_block_2: - raise esptool.FatalError("XTS_AES_256_KEY requires two free keyblocks") - - keypurpose_list.append("XTS_AES_256_KEY_1") - datafile_list.append(io.BytesIO(data[:32])) - block_name_list.append(block_name) - - keypurpose_list.append("XTS_AES_256_KEY_2") - datafile_list.append(io.BytesIO(data[32:])) - block_name_list.append(key_block_2.name) - - keypurpose_list.pop(i) - datafile_list.pop(i) - block_name_list.pop(i) - - -def burn_key(esp, efuses, args, digest=None): - if digest is None: - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - else: - datafile_list = digest[0 : len([name for name in digest if name is not None]) :] - efuses.force_write_always = args.force_write_always - block_name_list = args.block[ - 0 : len([name for name in args.block if name is not None]) : - ] - keypurpose_list = args.keypurpose[ - 0 : len([name for name in args.keypurpose if name is not None]) : - ] - - if "XTS_AES_256_KEY" in keypurpose_list: - # XTS_AES_256_KEY is not an actual HW key purpose, needs to be split into - # XTS_AES_256_KEY_1 and XTS_AES_256_KEY_2 - split_512_bit_key(efuses, block_name_list, datafile_list, keypurpose_list) - - util.check_duplicate_name_in_list(block_name_list) - if len(block_name_list) != len(datafile_list) or len(block_name_list) != len( - keypurpose_list - ): - raise esptool.FatalError( - "The number of blocks (%d), datafile (%d) and keypurpose (%d) " - "should be the same." - % (len(block_name_list), len(datafile_list), len(keypurpose_list)) - ) - - print("Burn keys to blocks:") - for block_name, datafile, keypurpose in zip( - block_name_list, datafile_list, keypurpose_list - ): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - - block_num = efuses.get_index_block_by_name(block_name) - block = efuses.blocks[block_num] - - if digest is None: - data = datafile.read() - else: - data = datafile - - print(" - %s" % (efuse.name), end=" ") - revers_msg = None - if efuses[block.key_purpose_name].need_reverse(keypurpose): - revers_msg = "\tReversing byte order for AES-XTS hardware peripheral" - data = data[::-1] - print("-> [%s]" % (util.hexify(data, " "))) - if revers_msg: - print(revers_msg) - if len(data) != num_bytes: - raise esptool.FatalError( - "Incorrect key file size %d. Key file must be %d bytes (%d bits) " - "of raw binary key data." % (len(data), num_bytes, num_bytes * 8) - ) - - if efuses[block.key_purpose_name].need_rd_protect(keypurpose): - read_protect = False if args.no_read_protect else True - else: - read_protect = False - write_protect = not args.no_write_protect - - # using efuse instead of a block gives the advantage of - # checking it as the whole field. - efuse.save(data) - - disable_wr_protect_key_purpose = False - if efuses[block.key_purpose_name].get() != keypurpose: - if efuses[block.key_purpose_name].is_writeable(): - print( - "\t'%s': '%s' -> '%s'." - % ( - block.key_purpose_name, - efuses[block.key_purpose_name].get(), - keypurpose, - ) - ) - efuses[block.key_purpose_name].save(keypurpose) - disable_wr_protect_key_purpose = True - else: - raise esptool.FatalError( - "It is not possible to change '%s' to '%s' because " - "write protection bit is set." - % (block.key_purpose_name, keypurpose) - ) - else: - print("\t'%s' is already '%s'." % (block.key_purpose_name, keypurpose)) - if efuses[block.key_purpose_name].is_writeable(): - disable_wr_protect_key_purpose = True - - if disable_wr_protect_key_purpose: - print("\tDisabling write to '%s'." % block.key_purpose_name) - efuses[block.key_purpose_name].disable_write() - - if read_protect: - print("\tDisabling read to key block") - efuse.disable_read() - - if write_protect: - print("\tDisabling write to key block") - efuse.disable_write() - print("") - - if not write_protect: - print("Keys will remain writeable (due to --no-write-protect)") - if args.no_read_protect: - print("Keys will remain readable (due to --no-read-protect)") - - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") - - -def burn_key_digest(esp, efuses, args): - digest_list = [] - datafile_list = args.keyfile[ - 0 : len([name for name in args.keyfile if name is not None]) : - ] - block_list = args.block[ - 0 : len([block for block in args.block if block is not None]) : - ] - for block_name, datafile in zip(block_list, datafile_list): - efuse = None - for block in efuses.blocks: - if block_name == block.name or block_name in block.alias: - efuse = efuses[block.name] - if efuse is None: - raise esptool.FatalError("Unknown block name - %s" % (block_name)) - num_bytes = efuse.bit_len // 8 - digest = espsecure._digest_sbv2_public_key(datafile) - if len(digest) != num_bytes: - raise esptool.FatalError( - "Incorrect digest size %d. Digest must be %d bytes (%d bits) " - "of raw binary key data." % (len(digest), num_bytes, num_bytes * 8) - ) - digest_list.append(digest) - burn_key(esp, efuses, args, digest=digest_list) - - -def espefuse(esp, efuses, args, command): - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers(dest="operation") - add_commands(subparsers, efuses) - try: - cmd_line_args = parser.parse_args(command.split()) - except SystemExit: - traceback.print_stack() - raise esptool.FatalError('"{}" - incorrect command'.format(command)) - if cmd_line_args.operation == "execute_scripts": - configfiles = cmd_line_args.configfiles - index = cmd_line_args.index - # copy arguments from args to cmd_line_args - vars(cmd_line_args).update(vars(args)) - if cmd_line_args.operation == "execute_scripts": - cmd_line_args.configfiles = configfiles - cmd_line_args.index = index - if cmd_line_args.operation is None: - parser.print_help() - parser.exit(1) - operation_func = globals()[cmd_line_args.operation] - # each 'operation' is a module-level function of the same name - operation_func(esp, efuses, cmd_line_args) - - -def execute_scripts(esp, efuses, args): - efuses.batch_mode_cnt += 1 - del args.operation - scripts = args.scripts - del args.scripts - - for file in scripts: - with open(file.name, "r") as file: - exec(compile(file.read(), file.name, "exec")) - - if args.debug: - for block in efuses.blocks: - data = block.get_bitstring(from_read=False) - block.print_block(data, "regs_for_burn", args.debug) - - efuses.batch_mode_cnt -= 1 - if not efuses.burn_all(check_batch_mode=True): - return - print("Successful") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py deleted file mode 100644 index 3db5694ee..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/mem_definition_base.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -# -# This file describes eFuses fields and registers for ESP32 chip -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from collections import namedtuple - - -class EfuseRegistersBase(object): - # Coding Scheme values - CODING_SCHEME_NONE = 0 - CODING_SCHEME_34 = 1 - CODING_SCHEME_REPEAT = 2 - CODING_SCHEME_NONE_RECOVERY = 3 - CODING_SCHEME_RS = 4 - - EFUSE_BURN_TIMEOUT = 0.250 # seconds - - -class EfuseBlocksBase(object): - - BLOCKS = None - NamedtupleBlock = namedtuple( - "Block", - "name alias id rd_addr wr_addr write_disable_bit " - "read_disable_bit len key_purpose", - ) - - @staticmethod - def get(tuple_block): - return EfuseBlocksBase.NamedtupleBlock._make(tuple_block) - - def get_blocks_for_keys(self): - list_of_names = [] - for block in self.BLOCKS: - blk = self.get(block) - if blk.id > 0: - if blk.name: - list_of_names.append(blk.name) - if blk.alias: - for alias in blk.alias: - list_of_names.append(alias) - return list_of_names - - -class EfuseFieldsBase(object): - - NamedtupleField = namedtuple( - "Efuse", - "name category block word pos type write_disable_bit " - "read_disable_bit class_type description dictionary", - ) - - @staticmethod - def get(tuple_field): - return EfuseFieldsBase.NamedtupleField._make(tuple_field) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py b/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py deleted file mode 100644 index 7c4bee287..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espefuse/efuse/util.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python -# -# This file consists of the common useful functions for eFuse -# -# SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import esptool - - -def hexify(bitstring, separator=""): - as_bytes = tuple(b for b in bitstring) - return separator.join(("%02x" % b) for b in as_bytes) - - -def popcnt(b): - """Return number of "1" bits set in 'b'""" - return len([x for x in bin(b) if x == "1"]) - - -def check_duplicate_name_in_list(name_list): - duples_name = [name for i, name in enumerate(name_list) if name in name_list[:i]] - if duples_name != []: - raise esptool.FatalError( - "Found repeated {} in the name list".format(duples_name) - ) - - -class SdkConfig(object): - def __init__(self, path_to_file): - self.sdkconfig = dict() - if path_to_file is None: - return - with open(path_to_file, "r") as file: - for line in file.readlines(): - if line.startswith("#"): - continue - config = line.strip().split("=", 1) - if len(config) == 2: - self.sdkconfig[config[0]] = ( - True if config[1] == "y" else config[1].strip('"') - ) - - def __getitem__(self, config_name): - try: - return self.sdkconfig[config_name] - except KeyError: - return False diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py deleted file mode 100644 index 33d08da63..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__init__.py +++ /dev/null @@ -1,1558 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import argparse -import hashlib -import operator -import os -import struct -import sys -import zlib -from collections import namedtuple - -from cryptography import exceptions -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives import serialization -from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa, utils -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.utils import int_to_bytes - -import ecdsa - -import esptool - -SIG_BLOCK_MAGIC = 0xE7 - -# Scheme used in Secure Boot V2 -SIG_BLOCK_VERSION_RSA = 0x02 -SIG_BLOCK_VERSION_ECDSA = 0x03 - -# Curve IDs used in Secure Boot V2 ECDSA signature blocks -CURVE_ID_P192 = 1 -CURVE_ID_P256 = 2 - - -def get_chunks(source, chunk_len): - """Returns an iterator over 'chunk_len' chunks of 'source'""" - return (source[i : i + chunk_len] for i in range(0, len(source), chunk_len)) - - -def endian_swap_words(source): - """Endian-swap each word in 'source' bitstring""" - assert len(source) % 4 == 0 - words = "I" * (len(source) // 4) - return struct.pack("<" + words, *struct.unpack(">" + words, source)) - - -def swap_word_order(source): - """Swap the order of the words in 'source' bitstring""" - assert len(source) % 4 == 0 - words = "I" * (len(source) // 4) - return struct.pack(words, *reversed(struct.unpack(words, source))) - - -def _load_hardware_key(keyfile): - """Load a 128/256/512-bit key, similar to stored in efuse, from a file - - 128-bit keys will be extended to 256-bit using the SHA256 of the key - 192-bit keys will be extended to 256-bit using the same algorithm used - by hardware if 3/4 Coding Scheme is set. - """ - key = keyfile.read() - if len(key) not in [16, 24, 32, 64]: - raise esptool.FatalError( - "Key file contains wrong length (%d bytes), 16, 24, 32 or 64 expected." - % len(key) - ) - if len(key) == 16: - key = _sha256_digest(key) - print("Using 128-bit key (extended)") - elif len(key) == 24: - key = key + key[8:16] - assert len(key) == 32 - print("Using 192-bit key (extended)") - elif len(key) == 32: - print("Using 256-bit key") - else: - print("Using 512-bit key") - return key - - -def digest_secure_bootloader(args): - """Calculate the digest of a bootloader image, in the same way the hardware - secure boot engine would do so. Can be used with a pre-loaded key to update a - secure bootloader.""" - _check_output_is_not_input(args.keyfile, args.output) - _check_output_is_not_input(args.image, args.output) - _check_output_is_not_input(args.iv, args.output) - if args.iv is not None: - print("WARNING: --iv argument is for TESTING PURPOSES ONLY") - iv = args.iv.read(128) - else: - iv = os.urandom(128) - plaintext_image = args.image.read() - args.image.seek(0) - - # secure boot engine reads in 128 byte blocks (ie SHA512 block - # size), but also doesn't look for any appended SHA-256 digest - fw_image = esptool.bin_image.ESP32FirmwareImage(args.image) - if fw_image.append_digest: - if len(plaintext_image) % 128 <= 32: - # ROM bootloader will read to the end of the 128 byte block, but not - # to the end of the SHA-256 digest at the end - new_len = len(plaintext_image) - (len(plaintext_image) % 128) - plaintext_image = plaintext_image[:new_len] - - # if image isn't 128 byte multiple then pad with 0xFF (ie unwritten flash) - # as this is what the secure boot engine will see - if len(plaintext_image) % 128 != 0: - plaintext_image += b"\xFF" * (128 - (len(plaintext_image) % 128)) - - plaintext = iv + plaintext_image - - # Secure Boot digest algorithm in hardware uses AES256 ECB to - # produce a ciphertext, then feeds output through SHA-512 to - # produce the digest. Each block in/out of ECB is reordered - # (due to hardware quirks not for security.) - - key = _load_hardware_key(args.keyfile) - backend = default_backend() - cipher = Cipher(algorithms.AES(key), modes.ECB(), backend=backend) - encryptor = cipher.encryptor() - digest = hashlib.sha512() - - for block in get_chunks(plaintext, 16): - block = block[::-1] # reverse each input block - - cipher_block = encryptor.update(block) - # reverse and then byte swap each word in the output block - cipher_block = cipher_block[::-1] - for block in get_chunks(cipher_block, 4): - # Python hashlib can build each SHA block internally - digest.update(block[::-1]) - - if args.output is None: - args.output = os.path.splitext(args.image.name)[0] + "-digest-0x0000.bin" - with open(args.output, "wb") as f: - f.write(iv) - digest = digest.digest() - for word in get_chunks(digest, 4): - f.write(word[::-1]) # swap word order in the result - f.write(b"\xFF" * (0x1000 - f.tell())) # pad to 0x1000 - f.write(plaintext_image) - print("digest+image written to %s" % args.output) - - -def _generate_ecdsa_signing_key(curve_id, keyfile): - sk = ecdsa.SigningKey.generate(curve=curve_id) - with open(keyfile, "wb") as f: - f.write(sk.to_pem()) - - -def generate_signing_key(args): - if os.path.exists(args.keyfile): - raise esptool.FatalError("ERROR: Key file %s already exists" % args.keyfile) - if args.version == "1": - if hasattr(args, "scheme"): - if args.scheme != "ecdsa256" and args.scheme is not None: - raise esptool.FatalError("ERROR: V1 only supports ECDSA256") - """ - Generate an ECDSA signing key for signing secure boot images (post-bootloader) - """ - _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) - print("ECDSA NIST256p private key in PEM format written to %s" % args.keyfile) - elif args.version == "2": - if args.scheme == "rsa3072" or args.scheme is None: - """Generate a RSA 3072 signing key for signing secure boot images""" - private_key = rsa.generate_private_key( - public_exponent=65537, key_size=3072, backend=default_backend() - ).private_bytes( - encoding=serialization.Encoding.PEM, - format=serialization.PrivateFormat.TraditionalOpenSSL, - encryption_algorithm=serialization.NoEncryption(), - ) - with open(args.keyfile, "wb") as f: - f.write(private_key) - print("RSA 3072 private key in PEM format written to %s" % args.keyfile) - elif args.scheme == "ecdsa192": - """Generate a ECDSA 192 signing key for signing secure boot images""" - _generate_ecdsa_signing_key(ecdsa.NIST192p, args.keyfile) - print( - "ECDSA NIST192p private key in PEM format written to %s" % args.keyfile - ) - elif args.scheme == "ecdsa256": - """Generate a ECDSA 256 signing key for signing secure boot images""" - _generate_ecdsa_signing_key(ecdsa.NIST256p, args.keyfile) - print( - "ECDSA NIST256p private key in PEM format written to %s" % args.keyfile - ) - else: - raise esptool.FatalError( - "ERROR: Unsupported signing scheme (%s)" % args.scheme - ) - - -def _load_ecdsa_signing_key(keyfile): - """Load ECDSA signing key for Secure Boot V1 only""" - sk = ecdsa.SigningKey.from_pem(keyfile.read()) - if sk.curve != ecdsa.NIST256p: - raise esptool.FatalError( - "Signing key uses incorrect curve. ESP32 Secure Boot only supports " - "NIST256p (openssl calls this curve 'prime256v1" - ) - return sk - - -def _load_sbv2_signing_key(keydata): - """ - Load Secure Boot V2 signing key - - can be rsa.RSAPrivateKey or ec.EllipticCurvePrivateKey - """ - sk = serialization.load_pem_private_key( - keydata, password=None, backend=default_backend() - ) - if isinstance(sk, rsa.RSAPrivateKey): - if sk.key_size != 3072: - raise esptool.FatalError( - "Key file has length %d bits. Secure boot v2 only supports RSA-3072." - % sk.key_size - ) - return sk - if isinstance(sk, ec.EllipticCurvePrivateKey): - if not ( - isinstance(sk.curve, ec.SECP192R1) or isinstance(sk.curve, ec.SECP256R1) - ): - raise esptool.FatalError( - "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " - "NIST192p, NIST256p (aka prime192v1, prime256v1)" - ) - return sk - - raise esptool.FatalError("Unsupported signing key for Secure Boot V2") - - -def _load_sbv2_pub_key(keydata): - """ - Load Secure Boot V2 public key, can be rsa.RSAPublicKey or ec.EllipticCurvePublicKey - """ - vk = serialization.load_pem_public_key(keydata, backend=default_backend()) - if isinstance(vk, rsa.RSAPublicKey): - if vk.key_size != 3072: - raise esptool.FatalError( - "Key file has length %d bits. Secure boot v2 only supports RSA-3072." - % vk.key_size - ) - return vk - if isinstance(vk, ec.EllipticCurvePublicKey): - if not ( - isinstance(vk.curve, ec.SECP192R1) or isinstance(vk.curve, ec.SECP256R1) - ): - raise esptool.FatalError( - "Key file uses incorrect curve. Secure Boot V2 + ECDSA only supports " - "NIST192p, NIST256p (aka prime192v1, prime256v1)" - ) - return vk - - raise esptool.FatalError("Unsupported public key for Secure Boot V2") - - -def _get_sbv2_pub_key(keyfile): - key_data = keyfile.read() - if b"-BEGIN RSA PRIVATE KEY" in key_data or b"-BEGIN EC PRIVATE KEY" in key_data: - return _load_sbv2_signing_key(key_data).public_key() - elif b"-BEGIN PUBLIC KEY" in key_data: - vk = _load_sbv2_pub_key(key_data) - else: - raise esptool.FatalError( - "Verification key does not appear to be an RSA Private or " - "Public key in PEM format. Unsupported" - ) - return vk - - -def _get_sbv2_rsa_primitives(public_key): - primitives = namedtuple("primitives", ["n", "e", "m", "rinv"]) - numbers = public_key.public_numbers() - primitives.n = numbers.n # - primitives.e = numbers.e # two public key components - - # Note: this cheats and calls a private 'rsa' method to get the modular - # inverse calculation. - primitives.m = -rsa._modinv(primitives.n, 1 << 32) - - rr = 1 << (public_key.key_size * 2) - primitives.rinv = rr % primitives.n - return primitives - - -def _microecc_format(a, b, curve_len): - """ - Given two numbers (curve coordinates or (r,s) signature), write them out as a - little-endian byte sequence suitable for micro-ecc - "native little endian" mode - """ - byte_len = int(curve_len / 8) - ab = int_to_bytes(a, byte_len)[::-1] + int_to_bytes(b, byte_len)[::-1] - assert len(ab) == 48 or len(ab) == 64 - return ab - - -def sign_data(args): - _check_output_is_not_input(args.keyfile, args.output) - _check_output_is_not_input(args.datafile, args.output) - if args.version == "1": - return sign_secure_boot_v1(args) - elif args.version == "2": - return sign_secure_boot_v2(args) - - -def sign_secure_boot_v1(args): - """ - Sign a data file with a ECDSA private key, append binary signature to file contents - """ - if len(args.keyfile) > 1: - raise esptool.FatalError("Secure Boot V1 only supports one signing key") - sk = _load_ecdsa_signing_key(args.keyfile[0]) - - # calculate signature of binary data - binary_content = args.datafile.read() - signature = sk.sign_deterministic(binary_content, hashlib.sha256) - - # back-verify signature - vk = sk.get_verifying_key() - vk.verify(signature, binary_content, hashlib.sha256) # throws exception on failure - - if args.output is None or os.path.abspath(args.output) == os.path.abspath( - args.datafile.name - ): # append signature to input file - args.datafile.close() - outfile = open(args.datafile.name, "ab") - else: # write file & signature to new file - outfile = open(args.output, "wb") - outfile.write(binary_content) - outfile.write( - struct.pack("I", 0) - ) # Version indicator, allow for different curves/formats later - outfile.write(signature) - outfile.close() - print( - "Signed %d bytes of data from %s with key %s" - % (len(binary_content), args.datafile.name, args.keyfile[0].name) - ) - - -def sign_secure_boot_v2(args): - """ - Sign a firmware app image with an RSA private key using RSA-PSS, - or ECDSA private key using P192 or P256. - - Write output file with a Secure Boot V2 header appended. - """ - SECTOR_SIZE = 4096 - SIG_BLOCK_SIZE = 1216 - SIG_BLOCK_MAX_COUNT = 3 - - signature_sector = b"" - key_count = len(args.keyfile) - contents = args.datafile.read() - - if key_count > SIG_BLOCK_MAX_COUNT: - print( - "WARNING: Upto %d signing keys are supported for ESP32-S2. " - "For ESP32-ECO3 only 1 signing key is supported", - SIG_BLOCK_MAX_COUNT, - ) - - if len(contents) % SECTOR_SIZE != 0: - pad_by = SECTOR_SIZE - (len(contents) % SECTOR_SIZE) - print( - "Padding data contents by %d bytes " - "so signature sector aligns at sector boundary" % pad_by - ) - contents += b"\xff" * pad_by - elif args.append_signatures: - sig_block_num = 0 - - while sig_block_num < SIG_BLOCK_MAX_COUNT: - sig_block = validate_signature_block(contents, sig_block_num) - if sig_block is None: - break - signature_sector += ( - sig_block # Signature sector is populated with already valid blocks - ) - sig_block_num += 1 - - assert len(signature_sector) % SIG_BLOCK_SIZE == 0 - - if sig_block_num == 0: - print( - "No valid signature blocks found. " - "Discarding --append-signature and proceeding to sign the image afresh." - ) - else: - print( - "%d valid signature block(s) already present in the signature sector." - % sig_block_num - ) - - empty_signature_blocks = SIG_BLOCK_MAX_COUNT - sig_block_num - if key_count > empty_signature_blocks: - raise esptool.FatalError( - "Number of keys(%d) more than the empty signature blocks.(%d)" - % (key_count, empty_signature_blocks) - ) - # Signature stripped off the content - # (the legitimate blocks are included in signature_sector) - contents = contents[: len(contents) - SECTOR_SIZE] - - print("%d signing key(s) found." % key_count) - # Calculate digest of data file - digest = hashlib.sha256() - digest.update(contents) - digest = digest.digest() - - for keyfile in args.keyfile: - private_key = _load_sbv2_signing_key(keyfile.read()) - - # Sign - if isinstance(private_key, rsa.RSAPrivateKey): - # RSA signature - signature = private_key.sign( - digest, - padding.PSS( - mgf=padding.MGF1(hashes.SHA256()), - salt_length=32, - ), - utils.Prehashed(hashes.SHA256()), - ) - rsa_primitives = _get_sbv2_rsa_primitives(private_key.public_key()) - - # Encode in signature block format - # - # Note: the [::-1] is to byte swap all of the bignum - # values (signatures, coefficients) to little endian - # for use with the RSA peripheral, rather than big endian - # which is conventionally used for RSA. - signature_block = struct.pack( - " 0 - and len(signature_sector) <= SIG_BLOCK_SIZE * 3 - and len(signature_sector) % SIG_BLOCK_SIZE == 0 - ) - total_sig_blocks = len(signature_sector) // SIG_BLOCK_SIZE - - # Pad signature_sector to sector - signature_sector = signature_sector + ( - b"\xff" * (SECTOR_SIZE - len(signature_sector)) - ) - assert len(signature_sector) == SECTOR_SIZE - - # Write to output file, or append to existing file - if args.output is None: - args.datafile.close() - args.output = args.datafile.name - with open(args.output, "wb") as f: - f.write(contents + signature_sector) - print( - "Signed %d bytes of data from %s. Signature sector now has %d signature blocks." - % (len(contents), args.datafile.name, total_sig_blocks) - ) - - -def verify_signature(args): - if args.version == "1": - return verify_signature_v1(args) - elif args.version == "2": - return verify_signature_v2(args) - - -def verify_signature_v1(args): - """Verify a previously signed binary image, using the ECDSA public key""" - key_data = args.keyfile.read() - if b"-BEGIN EC PRIVATE KEY" in key_data: - sk = ecdsa.SigningKey.from_pem(key_data) - vk = sk.get_verifying_key() - elif b"-BEGIN PUBLIC KEY" in key_data: - vk = ecdsa.VerifyingKey.from_pem(key_data) - elif len(key_data) == 64: - vk = ecdsa.VerifyingKey.from_string(key_data, curve=ecdsa.NIST256p) - else: - raise esptool.FatalError( - "Verification key does not appear to be an EC key in PEM format " - "or binary EC public key data. Unsupported" - ) - - if vk.curve != ecdsa.NIST256p: - raise esptool.FatalError( - "Public key uses incorrect curve. ESP32 Secure Boot only supports " - "NIST256p (openssl calls this curve 'prime256v1" - ) - - binary_content = args.datafile.read() - data = binary_content[0:-68] - sig_version, signature = struct.unpack("I64s", binary_content[-68:]) - if sig_version != 0: - raise esptool.FatalError( - "Signature block has version %d. This version of espsecure " - "only supports version 0." % sig_version - ) - print("Verifying %d bytes of data" % len(data)) - try: - if vk.verify(signature, data, hashlib.sha256): - print("Signature is valid") - else: - raise esptool.FatalError("Signature is not valid") - except ecdsa.keys.BadSignatureError: - raise esptool.FatalError("Signature is not valid") - - -def validate_signature_block(image_content, sig_blk_num): - SECTOR_SIZE = 4096 - SIG_BLOCK_SIZE = ( - 1216 # Refer to secure boot v2 signature block format for more details. - ) - - offset = -SECTOR_SIZE + sig_blk_num * SIG_BLOCK_SIZE - sig_blk = image_content[offset : offset + SIG_BLOCK_SIZE] - assert len(sig_blk) == SIG_BLOCK_SIZE - - # note: in case of ECDSA key, the exact fields in the middle are wrong - # (but unused here) - magic, version, _, _, _, _, _, _, blk_crc = struct.unpack( - "> 5 - key ^= ((mul1 * addr) | ((mul2 * addr) & mul2_mask)) & tweak_range - return int.to_bytes(key, length=32, byteorder="big", signed=False) - - -def generate_flash_encryption_key(args): - print("Writing %d random bits to key file %s" % (args.keylen, args.key_file.name)) - args.key_file.write(os.urandom(args.keylen // 8)) - - -def _flash_encryption_operation_esp32( - output_file, input_file, flash_address, keyfile, flash_crypt_conf, do_decrypt -): - key = _load_hardware_key(keyfile) - - if flash_address % 16 != 0: - raise esptool.FatalError( - "Starting flash address 0x%x must be a multiple of 16" % flash_address - ) - - if flash_crypt_conf == 0: - print("WARNING: Setting FLASH_CRYPT_CONF to zero is not recommended") - - tweak_range = _flash_encryption_tweak_range_bits(flash_crypt_conf) - key = int.from_bytes(key, byteorder="big", signed=False) - - backend = default_backend() - - cipher = None - block_offs = flash_address - while True: - block = input_file.read(16) - if len(block) == 0: - break - elif len(block) < 16: - if do_decrypt: - raise esptool.FatalError("Data length is not a multiple of 16 bytes") - pad = 16 - len(block) - block = block + os.urandom(pad) - print( - "Note: Padding with %d bytes of random data " - "(encrypted data must be multiple of 16 bytes long)" % pad - ) - - if block_offs % 32 == 0 or cipher is None: - # each bit of the flash encryption key is XORed with tweak bits - # derived from the offset of 32 byte block of flash - block_key = _flash_encryption_tweak_key(key, block_offs, tweak_range) - - if cipher is None: # first pass - cipher = Cipher(algorithms.AES(block_key), modes.ECB(), backend=backend) - - # note AES is used inverted for flash encryption, so - # "decrypting" flash uses AES encrypt algorithm and vice - # versa. (This does not weaken AES.) - actor = cipher.encryptor() if do_decrypt else cipher.decryptor() - else: - # performance hack: changing the key using pyca-cryptography API - # requires recreating'actor'. - # With openssl backend, this re-initializes the openssl cipher context. - # To save some time, manually call EVP_CipherInit_ex() in the openssl - # backend to update the key. - # If it fails, fall back to recreating the entire context via public API - try: - backend = actor._ctx._backend - res = backend._lib.EVP_CipherInit_ex( - actor._ctx._ctx, - backend._ffi.NULL, - backend._ffi.NULL, - backend._ffi.from_buffer(block_key), - backend._ffi.NULL, - actor._ctx._operation, - ) - backend.openssl_assert(res != 0) - except AttributeError: - # backend is not an openssl backend, or implementation has changed: - # fall back to the slow safe version - cipher.algorithm.key = block_key - actor = cipher.encryptor() if do_decrypt else cipher.decryptor() - - block = block[::-1] # reverse input block byte order - block = actor.update(block) - - output_file.write(block[::-1]) # reverse output block byte order - block_offs += 16 - - -def _flash_encryption_operation_aes_xts( - output_file, input_file, flash_address, keyfile, do_decrypt -): - """ - Apply the AES-XTS algorithm with the hardware addressing scheme used by Espressif - - key = AES-XTS key (32 or 64 bytes) - flash_address = address in flash to encrypt at. Must be multiple of 16 bytes. - indata = Data to encrypt/decrypt. Must be multiple of 16 bytes. - encrypt = True to Encrypt indata, False to decrypt indata. - - Returns a bitstring of the ciphertext or plaintext result. - """ - - backend = default_backend() - key = _load_hardware_key(keyfile) - indata = input_file.read() - - if flash_address % 16 != 0: - raise esptool.FatalError( - "Starting flash address 0x%x must be a multiple of 16" % flash_address - ) - - if len(indata) % 16 != 0: - raise esptool.FatalError( - "Input data length (%d) must be a multiple of 16" % len(indata) - ) - - if len(indata) == 0: - raise esptool.FatalError("Input data must be longer than 0") - - # left pad for a 1024-bit aligned address - pad_left = flash_address % 0x80 - indata = (b"\x00" * pad_left) + indata - - # right pad for full 1024-bit blocks - pad_right = len(indata) % 0x80 - if pad_right > 0: - pad_right = 0x80 - pad_right - indata = indata + (b"\x00" * pad_right) - - inblocks = _split_blocks(indata, 0x80) # split into 1024 bit blocks - - output = [] - for inblock in inblocks: # for each block - tweak = struct.pack(" 0: - if not self.file_obj: - self.file_obj = open(self.path, "wb") - self.file_obj.write(payload) - - def close(self): - if self.file_obj: - self.file_obj.close() - self.file_obj = None - - @property - def name(self): - return self.path - - -def main(custom_commandline=None): - """ - Main function for espsecure - - custom_commandline - Optional override for default arguments parsing - (that uses sys.argv), can be a list of custom arguments as strings. - Arguments and their values need to be added as individual items to the list - e.g. "--port /dev/ttyUSB1" thus becomes ['--port', '/dev/ttyUSB1']. - """ - parser = argparse.ArgumentParser( - description="espsecure.py v%s - ESP32 Secure Boot & Flash Encryption tool" - % esptool.__version__, - prog="espsecure", - ) - - subparsers = parser.add_subparsers( - dest="operation", help="Run espsecure.py {command} -h for additional help" - ) - - p = subparsers.add_parser( - "digest_secure_bootloader", - help="Take a bootloader binary image and a secure boot key, " - "and output a combined digest+binary suitable for flashing along " - "with the precalculated secure boot key.", - ) - p.add_argument( - "--keyfile", - "-k", - help="256 bit key for secure boot digest.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument("--output", "-o", help="Output file for signed digest image.") - p.add_argument( - "--iv", - help="128 byte IV file. Supply a file for testing purposes only, " - "if not supplied an IV will be randomly generated.", - type=argparse.FileType("rb"), - ) - p.add_argument( - "image", - help="Bootloader image file to calculate digest from", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "generate_signing_key", - help="Generate a private key for signing secure boot images " - "as per the secure boot version. " - "Key file is generated in PEM format, " - "Secure Boot V1 - ECDSA NIST256p private key. " - "Secure Boot V2 - RSA 3072, ECDSA NIST256p, ECDSA NIST192p private key.", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot signing scheme to use.", - choices=["1", "2"], - default="1", - ) - p.add_argument( - "--scheme", - "-s", - help="Scheme of secure boot signing.", - choices=["rsa3072", "ecdsa192", "ecdsa256"], - required=False, - ) - p.add_argument( - "keyfile", help="Filename for private key file (embedded public key)" - ) - - p = subparsers.add_parser( - "sign_data", - help="Sign a data file for use with secure boot. " - "Signing algorithm is deterministic ECDSA w/ SHA-512 (V1) " - "or either RSA-PSS or ECDSA w/ SHA-256 (V2).", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot signing scheme to use.", - choices=["1", "2"], - required=True, - ) - p.add_argument( - "--keyfile", - "-k", - help="Private key file for signing. Key is in PEM format.", - type=argparse.FileType("rb"), - required=True, - nargs="+", - ) - p.add_argument( - "--append_signatures", - "-a", - help="Append signature block(s) to already signed image" - "Valid only for ESP32-S2.", - action="store_true", - ) - p.add_argument( - "--output", - "-o", - help="Output file for signed digest image. Default is to sign the input file.", - ) - p.add_argument( - "datafile", - help="File to sign. For version 1, this can be any file. " - "For version 2, this must be a valid app image.", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "verify_signature", - help='Verify a data file previously signed by "sign_data", ' - "using the public key.", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot scheme to use.", - choices=["1", "2"], - required=True, - ) - p.add_argument( - "--keyfile", - "-k", - help="Public key file for verification. " - "Can be private or public key in PEM format.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "datafile", - help="Signed data file to verify signature.", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "extract_public_key", - help="Extract the public verification key for signatures, " - "save it as a raw binary file.", - ) - p.add_argument( - "--version", - "-v", - help="Version of the secure boot signing scheme to use.", - choices=["1", "2"], - default="1", - ) - p.add_argument( - "--keyfile", - "-k", - help="Private key file (PEM format) to extract the " - "public verification key from.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "public_keyfile", help="File to save new public key into", type=OutFileType() - ) - - # Kept for compatibility purpose. We can deprecate this in a future release - p = subparsers.add_parser( - "digest_rsa_public_key", - help="Generate an SHA-256 digest of the RSA public key. " - "This digest is burned into the eFuse and asserts the legitimacy " - "of the public key for Secure boot v2.", - ) - p.add_argument( - "--keyfile", - "-k", - help="Public key file for verification. " - "Can be private or public key in PEM format.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument("--output", "-o", help="Output file for the digest.", required=True) - - p = subparsers.add_parser( - "digest_sbv2_public_key", - help="Generate an SHA-256 digest of the public key. " - "This digest is burned into the eFuse and asserts the legitimacy " - "of the public key for Secure boot v2.", - ) - p.add_argument( - "--keyfile", - "-k", - help="Public key file for verification. " - "Can be private or public key in PEM format.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument("--output", "-o", help="Output file for the digest.", required=True) - - p = subparsers.add_parser( - "signature_info_v2", - help="Reads the signature block and provides the signature block information.", - ) - p.add_argument( - "datafile", - help="Secure boot v2 signed data file.", - type=argparse.FileType("rb"), - ) - - p = subparsers.add_parser( - "digest_private_key", - help="Generate an SHA-256 digest of the private signing key. " - "This can be used as a reproducible secure bootloader or flash encryption key.", - ) - p.add_argument( - "--keyfile", - "-k", - help="Private key file (PEM format) to generate a digest from.", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "--keylen", - "-l", - help="Length of private key digest file to generate (in bits). " - "3/4 Coding Scheme requires 192 bit key.", - choices=[192, 256], - default=256, - type=int, - ) - p.add_argument( - "digest_file", help="File to write 32 byte digest into", type=OutFileType() - ) - - p = subparsers.add_parser( - "generate_flash_encryption_key", - help="Generate a development-use flash encryption key with random data.", - ) - p.add_argument( - "--keylen", - "-l", - help="Length of private key digest file to generate (in bits). " - "3/4 Coding Scheme requires 192 bit key.", - choices=[128, 192, 256, 512], - default=256, - type=int, - ) - p.add_argument( - "key_file", - help="File to write 16, 24, 32 or 64 byte key into", - type=OutFileType(), - ) - - p = subparsers.add_parser( - "decrypt_flash_data", - help="Decrypt some data read from encrypted flash (using known key)", - ) - p.add_argument( - "encrypted_file", - help="File with encrypted flash contents", - type=argparse.FileType("rb"), - ) - p.add_argument( - "--aes_xts", - "-x", - help="Decrypt data using AES-XTS as used on " - "ESP32-S2, ESP32-C2, ESP32-C3 and ESP32-C6", - action="store_true", - ) - p.add_argument( - "--keyfile", - "-k", - help="File with flash encryption key", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "--output", - "-o", - help="Output file for plaintext data.", - type=OutFileType(), - required=True, - ) - p.add_argument( - "--address", - "-a", - help="Address offset in flash that file was read from.", - required=True, - type=esptool.arg_auto_int, - ) - p.add_argument( - "--flash_crypt_conf", - help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", - required=False, - default=0xF, - type=esptool.arg_auto_int, - ) - - p = subparsers.add_parser( - "encrypt_flash_data", - help="Encrypt some data suitable for encrypted flash (using known key)", - ) - p.add_argument( - "--aes_xts", - "-x", - help="Encrypt data using AES-XTS as used on " - "ESP32-S2, ESP32-C2, ESP32-C3 and ESP32-C6", - action="store_true", - ) - p.add_argument( - "--keyfile", - "-k", - help="File with flash encryption key", - type=argparse.FileType("rb"), - required=True, - ) - p.add_argument( - "--output", - "-o", - help="Output file for encrypted data.", - type=OutFileType(), - required=True, - ) - p.add_argument( - "--address", - "-a", - help="Address offset in flash where file will be flashed.", - required=True, - type=esptool.arg_auto_int, - ) - p.add_argument( - "--flash_crypt_conf", - help="Override FLASH_CRYPT_CONF efuse value (default is 0XF).", - required=False, - default=0xF, - type=esptool.arg_auto_int, - ) - p.add_argument( - "plaintext_file", - help="File with plaintext content for encrypting", - type=argparse.FileType("rb"), - ) - - args = parser.parse_args(custom_commandline) - print("espsecure.py v%s" % esptool.__version__) - if args.operation is None: - parser.print_help() - parser.exit(1) - - try: - # each 'operation' is a module-level function of the same name - operation_func = globals()[args.operation] - operation_func(args) - finally: - for arg_name in vars(args): - obj = getattr(args, arg_name) - if isinstance(obj, OutFileType): - obj.close() - - -def _main(): - try: - main() - except esptool.FatalError as e: - print("\nA fatal error occurred: %s" % e) - sys.exit(2) - - -if __name__ == "__main__": - _main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py deleted file mode 100644 index 8c8bce68f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/espsecure/__main__.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import espsecure - -if __name__ == "__main__": - espsecure._main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE deleted file mode 100644 index d159169d1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA deleted file mode 100644 index 4b98bc87b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/METADATA +++ /dev/null @@ -1,57 +0,0 @@ -Metadata-Version: 2.1 -Name: esptool -Version: 4.4 -Summary: A serial utility to communicate & flash code to Espressif chips. -Home-page: https://github.com/espressif/esptool/ -Author: Fredrik Ahlberg (themadinventor) & Angus Gratton (projectgus) & Espressif Systems -Author-email: -License: GPLv2+ -Project-URL: Documentation, https://docs.espressif.com/projects/esptool/ -Project-URL: Source, https://github.com/espressif/esptool/ -Project-URL: Tracker, https://github.com/espressif/esptool/issues/ -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Natural Language :: English -Classifier: Operating System :: POSIX -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Topic :: Software Development :: Embedded Systems -Classifier: Environment :: Console -Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Requires-Python: >=3.7 -License-File: LICENSE -Requires-Dist: bitstring (<4,>=3.1.6) -Requires-Dist: cryptography (>=2.1.4) -Requires-Dist: ecdsa (>=0.16.0) -Requires-Dist: pyserial (>=3.0) -Requires-Dist: reedsolo (<=1.5.4,>=1.5.3) -Provides-Extra: dev -Requires-Dist: flake8 (>=3.2.0) ; extra == 'dev' -Requires-Dist: flake8-import-order ; extra == 'dev' -Requires-Dist: flake8-gl-codeclimate ; extra == 'dev' -Requires-Dist: pyelftools ; extra == 'dev' -Requires-Dist: coverage (~=6.0) ; extra == 'dev' -Requires-Dist: black ; extra == 'dev' -Requires-Dist: pre-commit ; extra == 'dev' -Requires-Dist: pytest ; extra == 'dev' -Requires-Dist: pytest-rerunfailures ; extra == 'dev' - - -========== -esptool.py -========== -A Python-based, open-source, platform-independent utility to communicate with the ROM bootloader in Espressif chips. - -The esptool.py project is `hosted on github `_. - -Documentation -------------- -Visit online `esptool documentation `_ or run ``esptool.py -h``. - -Contributing ------------- -Please see the `contributions guide `_. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD deleted file mode 100644 index 833bb6279..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/RECORD +++ /dev/null @@ -1,158 +0,0 @@ -../../Scripts/espefuse.exe,sha256=dkn2als4JPVDDMaP0OTrNeLDak-Zkn3lxsv00iYU6ck,108448 -../../Scripts/espsecure.exe,sha256=fZ2fj9NAep5RwjYl49P6P-0i1GqTwek9uzwMrKsrsqI,108449 -../../Scripts/esptool.exe,sha256=jdWpeMW-4tgSEwF3CwbxmHIYrIhY3DMUX_lreFiGRyA,108447 -espefuse/__init__.py,sha256=IxmhijQR_moobpjCSXFLV4wA9hFFNCWbeGHTztinLQU,8950 -espefuse/__main__.py,sha256=pP8CMGPe1rCVGDJqgZqYLPnCgD5gLQTf1Z4s2vW4gyU,208 -espefuse/__pycache__/__init__.cpython-310.pyc,, -espefuse/__pycache__/__main__.cpython-310.pyc,, -espefuse/efuse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -espefuse/efuse/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/__pycache__/base_fields.cpython-310.pyc,, -espefuse/efuse/__pycache__/base_operations.cpython-310.pyc,, -espefuse/efuse/__pycache__/emulate_efuse_controller_base.cpython-310.pyc,, -espefuse/efuse/__pycache__/mem_definition_base.cpython-310.pyc,, -espefuse/efuse/__pycache__/util.cpython-310.pyc,, -espefuse/efuse/base_fields.py,sha256=StmMxfERIDRE8Z9FaNctTkR2ztVOJiKy6TPiuxv0KPA,29506 -espefuse/efuse/base_operations.py,sha256=IqkJ_PgPHdVxTIa20o9qfUULjZ8vi8njG9Ax0WEONxk,24905 -espefuse/efuse/emulate_efuse_controller_base.py,sha256=oylxiJ7twW9OWwJ1WbC881g3W-UcXKLHH_xY2aztJyU,8717 -espefuse/efuse/esp32/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32/emulate_efuse_controller.py,sha256=AyJMxiEtNiI2v5EjGC4GdVD-thWkj1-oq_-yaf3yiK0,5274 -espefuse/efuse/esp32/fields.py,sha256=Guq9Civ35x6Z0W6b59DL691HYGzbw82Ns59Fqx8TOlM,18056 -espefuse/efuse/esp32/mem_definition.py,sha256=vn-gklCueJA0mbefbBxTWQFXmI8c6Zwj7A_Pgnk4qRk,10858 -espefuse/efuse/esp32/operations.py,sha256=KvgZ-Lj-fVDcr9X82u-sLFqWCo6_LeizrYZXMTyJNWY,12085 -espefuse/efuse/esp32c2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32c2/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32c2/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32c2/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32c2/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32c2/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32c2/emulate_efuse_controller.py,sha256=SSjvDnObh8vNY8WeMgTKHda7TMjF2kjjS4qsHGNdUk4,5094 -espefuse/efuse/esp32c2/fields.py,sha256=STyN-hwvV5bGn_dkvfjiFfY0ZNbhTEFO9xdTsAWjLps,15510 -espefuse/efuse/esp32c2/mem_definition.py,sha256=TO9kTOpvofOQmu7qchwWLZOKpLvHoAb_D1B0VXyJFUI,11581 -espefuse/efuse/esp32c2/operations.py,sha256=bxpzMAhLlqmC0jEJpoBuZeqcsz4bu9WG2QvO7RVH9jQ,12028 -espefuse/efuse/esp32c3/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32c3/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32c3/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32c3/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32c3/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32c3/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32c3/emulate_efuse_controller.py,sha256=rWP_e27_zajC8TRqVk3H4c1pyORkWi4myRW7yeTMYQo,3067 -espefuse/efuse/esp32c3/fields.py,sha256=pimQHVxIHRCqrouiSo5DVjlWzYRgl-xE24hFs5Q1XW8,18328 -espefuse/efuse/esp32c3/mem_definition.py,sha256=o65aXtvHCA4SWiqt-ZZygb7uQQcHVyeMODBFbC5hI-k,20804 -espefuse/efuse/esp32c3/operations.py,sha256=VqqkrU_YAmZF_YUCY43rhu0Mn4r07mR7gJ-cGSlTqw8,14701 -espefuse/efuse/esp32c6/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32c6/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32c6/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32c6/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32c6/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32c6/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32c6/emulate_efuse_controller.py,sha256=KjLSV_Z4nVsZOkbKQwLYwrBfUQA7wcHGiHjepcDKrm0,3062 -espefuse/efuse/esp32c6/fields.py,sha256=HoPUSP2sTvutdQAf_0Y_CvziMHq3r5g9Ppda4NhHh9Q,18323 -espefuse/efuse/esp32c6/mem_definition.py,sha256=WzoM0qkrq9Cnfn6VoMsXs6tYGwsngIIF8Ad_ffgdL6c,22138 -espefuse/efuse/esp32c6/operations.py,sha256=-7KbxPgsSzgpStfxfdnBSLPgXwyLv-hmjOPKp_VC9hY,14696 -espefuse/efuse/esp32h2beta1/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32h2beta1/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32h2beta1/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32h2beta1/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32h2beta1/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32h2beta1/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32h2beta1/emulate_efuse_controller.py,sha256=t02Qm5HVoXuHM5UHa4nU92zW4puUPi14dJhALvUbBLk,3074 -espefuse/efuse/esp32h2beta1/fields.py,sha256=94R3ub-9hBO9pzm5Y75KnURA7xOtJCWJHm6bmWVrtPs,18173 -espefuse/efuse/esp32h2beta1/mem_definition.py,sha256=7SLk03UWvcwYjCLpMB966vxwoBkieWqppkXlnQ38e4E,20226 -espefuse/efuse/esp32h2beta1/operations.py,sha256=zPF9a6xFywgCcmUVEUnLfRQlyvXWx9GNdwE-22r73wQ,14720 -espefuse/efuse/esp32s2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32s2/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32s2/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32s2/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32s2/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32s2/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32s2/emulate_efuse_controller.py,sha256=HR0pAp039_nFt8nd3GvfZkcosyMpvl9NxldtnAdc7wU,3073 -espefuse/efuse/esp32s2/fields.py,sha256=xbGHTfjqI7OenMt4AAWGz-r5g2RBhgFHVc-YCDD8Lug,20875 -espefuse/efuse/esp32s2/mem_definition.py,sha256=mn7zdqYPwFKB2TpsyVDhrJNYvYV1KEQvdnVswyuH9hY,23447 -espefuse/efuse/esp32s2/operations.py,sha256=FmyW79r1YKR11Po_CTx9gCoswKNTxBUYHplbbKM-TH8,18687 -espefuse/efuse/esp32s3/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32s3/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32s3/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32s3/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32s3/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32s3/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32s3/emulate_efuse_controller.py,sha256=o1mOn_kyoW6WOA6kAtofVh5LnuSohcajs51XVb8IM0Y,3067 -espefuse/efuse/esp32s3/fields.py,sha256=oI2k8XrHX2NgyIzyGZGXNxPut_hCbs6wLMt4VYrOKkE,19364 -espefuse/efuse/esp32s3/mem_definition.py,sha256=vaWKaEWvODSNeNDu88nw7ilErH5EA-sz8dVxR5jwDeQ,22532 -espefuse/efuse/esp32s3/operations.py,sha256=ZInCDIBVoYIkmFAnVJayonUzKbiLfqsdVgArTkNJlaY,18685 -espefuse/efuse/esp32s3beta2/__init__.py,sha256=lKWQYRS5r0GFNZ8lkXNvHRQ1F9_07OaR8dS1_tCdDzc,116 -espefuse/efuse/esp32s3beta2/__pycache__/__init__.cpython-310.pyc,, -espefuse/efuse/esp32s3beta2/__pycache__/emulate_efuse_controller.cpython-310.pyc,, -espefuse/efuse/esp32s3beta2/__pycache__/fields.cpython-310.pyc,, -espefuse/efuse/esp32s3beta2/__pycache__/mem_definition.cpython-310.pyc,, -espefuse/efuse/esp32s3beta2/__pycache__/operations.cpython-310.pyc,, -espefuse/efuse/esp32s3beta2/emulate_efuse_controller.py,sha256=A775IK9X2c5d3-YPMo9UG_5JKoIWlvwQRQ3dt2d18Fc,3081 -espefuse/efuse/esp32s3beta2/fields.py,sha256=BJwQ-mzNRli2NPODeVqMEhOeUR-dD0VgeR5NAWCZna8,19378 -espefuse/efuse/esp32s3beta2/mem_definition.py,sha256=7_iyeXl55xP8MAylvmhWyBcfS8V3I5pFo34JyWbJslQ,21678 -espefuse/efuse/esp32s3beta2/operations.py,sha256=j97k5WN5qKCRI-tJ2DNXndItiJJstlsFQN6aqU85A9E,18692 -espefuse/efuse/mem_definition_base.py,sha256=hp20Xh18fztQ-z3jHEItG0yy30RiiuCQuThos7fQUww,1536 -espefuse/efuse/util.py,sha256=EaiTdZNMB4bIOJ_RacDKY2QsCruiwh4x9ezYgvr_GAU,1440 -espsecure/__init__.py,sha256=DgHXFbkrtrKpfh7imwWnPtnUK0N6BkzAg5mJnbVVh3o,53574 -espsecure/__main__.py,sha256=mKzSjejVZ7TjYNhTvXt_ew0zojFAhzdzgNo2BJjec1A,210 -espsecure/__pycache__/__init__.cpython-310.pyc,, -espsecure/__pycache__/__main__.cpython-310.pyc,, -esptool-4.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -esptool-4.4.dist-info/LICENSE,sha256=gXf5dRMhNSbfLPYYTY_5hsZ1r7UU1OaKQEAQUhuIBkM,18092 -esptool-4.4.dist-info/METADATA,sha256=vUCjsNfMZNhi53efzY226aUHjCGFQS41ojxvOXK4u_A,2400 -esptool-4.4.dist-info/RECORD,, -esptool-4.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -esptool-4.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 -esptool-4.4.dist-info/entry_points.txt,sha256=-hWHXAe1Iw9pcrfRGaz6tKWIwFZRm3ymAHNXUHzb4rA,132 -esptool-4.4.dist-info/top_level.txt,sha256=e2gykZuzaB-2vJ5_XCe55aAhEDxjeeS6MvOWLu8z6JM,27 -esptool/__init__.py,sha256=vyvoQTlq_hpcQfzIBc7V0JvV8CiC1ZSLKo9SRIMvrhU,34821 -esptool/__main__.py,sha256=S0uflKbalJG1-suKoRZmxaUSE7Pld-rrIr9bYYwHN9U,270 -esptool/__pycache__/__init__.cpython-310.pyc,, -esptool/__pycache__/__main__.cpython-310.pyc,, -esptool/__pycache__/bin_image.cpython-310.pyc,, -esptool/__pycache__/cmds.cpython-310.pyc,, -esptool/__pycache__/loader.cpython-310.pyc,, -esptool/__pycache__/util.cpython-310.pyc,, -esptool/bin_image.py,sha256=BRmqb4S81pM-zf1uNST7d8JRLmBgOMN5QJNCt6SWJ1s,45923 -esptool/cmds.py,sha256=MTDNVE6-VJBalYjqpY4_IpgZSaeZYmYeKK9F0fuXnUw,43708 -esptool/loader.py,sha256=Y5J5eKjxdnOsK-3VWAifCGHHwhcb0rjQgxXj6O54jCI,59612 -esptool/targets/__init__.py,sha256=X0Z-3YoofE4ol5foeHVfl4z3L_npAYBDsY79weXZUVY,807 -esptool/targets/__pycache__/__init__.cpython-310.pyc,, -esptool/targets/__pycache__/esp32.cpython-310.pyc,, -esptool/targets/__pycache__/esp32c2.cpython-310.pyc,, -esptool/targets/__pycache__/esp32c3.cpython-310.pyc,, -esptool/targets/__pycache__/esp32c6.cpython-310.pyc,, -esptool/targets/__pycache__/esp32c6beta.cpython-310.pyc,, -esptool/targets/__pycache__/esp32h2beta1.cpython-310.pyc,, -esptool/targets/__pycache__/esp32h2beta2.cpython-310.pyc,, -esptool/targets/__pycache__/esp32s2.cpython-310.pyc,, -esptool/targets/__pycache__/esp32s3.cpython-310.pyc,, -esptool/targets/__pycache__/esp32s3beta2.cpython-310.pyc,, -esptool/targets/__pycache__/esp8266.cpython-310.pyc,, -esptool/targets/esp32.py,sha256=N1GYWUCC-5gwQrJGDzHkQpW-4hFi0Ixuo_4yeRyA774,12188 -esptool/targets/esp32c2.py,sha256=evZw-tr4g7TkaoucsQcTvAOFCzGodLkcxEftIaWXp4Q,5697 -esptool/targets/esp32c3.py,sha256=sSVJhhC1URtOgfaRLC49-t6T-VBBoOIRKeTMmbnaTTg,5871 -esptool/targets/esp32c6.py,sha256=EwnFcnjj3eAqiDLOQ6q2YK4W8zsE3dy7VA5C8hV1zj8,5787 -esptool/targets/esp32c6beta.py,sha256=9zyZiyoMzP2BGVpVaLiuEciWbrL4svZ1UiNF_Satkmo,697 -esptool/targets/esp32h2beta1.py,sha256=aFnRdkG4lhkWF9npZC4GK-V_8bZq0yEvDpEVWy8oeU8,4970 -esptool/targets/esp32h2beta2.py,sha256=d1DE8b5XE4aZ0XLSxbB1mc1u00Nw8dDcz9JPjjY9zDk,1391 -esptool/targets/esp32s2.py,sha256=9NHzIv_vTLqQ62lOFcDzIh6_BABi-1ONY-sjAhHMfYk,10523 -esptool/targets/esp32s3.py,sha256=JZnI3VKjXGjwGjYS43DM-IqrQSO7e2Atr57MFPxd4TU,9592 -esptool/targets/esp32s3beta2.py,sha256=EfdXmk3zminZH6wpYvNnfQttwjBbMqXIYJrUdFXfi-g,1318 -esptool/targets/esp8266.py,sha256=xWRgokg_1rHv6-8hwXhdARkQ2gBEZI9mvUBvBSvx4RY,5932 -esptool/targets/stub_flasher/stub_flasher_32.json,sha256=Etgqi1hficCSH8ZOyT-gKP5743C8pEneflSnnM2OHxg,4703 -esptool/targets/stub_flasher/stub_flasher_32c2.json,sha256=h3bi_IHBEMSsBrKyDpqlftjyHVX0APbp2H5GzgOJFhU,4695 -esptool/targets/stub_flasher/stub_flasher_32c3.json,sha256=pzSkChVRPqgyM7zBtj0JsCwdgZCS6TR_o_efnmrjrp0,5131 -esptool/targets/stub_flasher/stub_flasher_32c6.json,sha256=qQ2rUv_uSOuEaUeQa7fLcEbACr57lnS0cShFcZ1Gq4I,4695 -esptool/targets/stub_flasher/stub_flasher_32c6beta.json,sha256=TQATNG269T70dsUf6sxUkRTSTcfwgIpakhCcGHYhBjI,4711 -esptool/targets/stub_flasher/stub_flasher_32h2beta1.json,sha256=9sfin8uYjzRE-D20V1aclwim2d9JWpuE7BEqY0A0cww,4711 -esptool/targets/stub_flasher/stub_flasher_32h2beta2.json,sha256=6QZT9ZVvERWWS5RKCdyiogvO___FVJiEdwHk-WpaARA,4711 -esptool/targets/stub_flasher/stub_flasher_32s2.json,sha256=joAAtknusB38uoEF4NS2W55Lezpb0EgljbZLXtjreUs,5883 -esptool/targets/stub_flasher/stub_flasher_32s3.json,sha256=LQTAc_NrIwhXLr8_vpQVl2lcI1OqrWD-be-EeVL5KYc,6863 -esptool/targets/stub_flasher/stub_flasher_32s3beta2.json,sha256=iMaFloTtK2EONnoGIPev__MNbe-CPl_dnRiuie9C9CA,6863 -esptool/targets/stub_flasher/stub_flasher_8266.json,sha256=pkkCxKtOuqSiM2VrTqLvDDGahqPB6tUxHM6xhz0Yqx4,12131 -esptool/util.py,sha256=eTDp7gWzdtbDilqJ9d9-tFqvMHgLuBEEgMXWuILPu5w,4829 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt deleted file mode 100644 index 6dd569c0f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/entry_points.txt +++ /dev/null @@ -1,4 +0,0 @@ -[console_scripts] -espefuse.py = espefuse.__init__:_main -espsecure.py = espsecure.__init__:_main -esptool.py = esptool.__init__:_main diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt deleted file mode 100644 index 34928a1fc..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/top_level.txt +++ /dev/null @@ -1,3 +0,0 @@ -espefuse -espsecure -esptool diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py deleted file mode 100644 index 809ccc983..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/__init__.py +++ /dev/null @@ -1,1045 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -__all__ = [ - "chip_id", - "detect_chip", - "dump_mem", - "elf2image", - "erase_flash", - "erase_region", - "flash_id", - "get_security_info", - "image_info", - "load_ram", - "make_image", - "merge_bin", - "read_flash", - "read_flash_status", - "read_mac", - "read_mem", - "run", - "verify_flash", - "version", - "write_flash", - "write_flash_status", - "write_mem", -] - -__version__ = "4.4" - -import argparse -import inspect -import os -import shlex -import sys -import time - -from esptool.cmds import ( - chip_id, - detect_chip, - detect_flash_size, - dump_mem, - elf2image, - erase_flash, - erase_region, - flash_id, - get_security_info, - image_info, - load_ram, - make_image, - merge_bin, - read_flash, - read_flash_status, - read_mac, - read_mem, - run, - verify_flash, - version, - write_flash, - write_flash_status, - write_mem, -) -from esptool.loader import DEFAULT_CONNECT_ATTEMPTS, ESPLoader, list_ports -from esptool.targets import CHIP_DEFS, CHIP_LIST, ESP32ROM -from esptool.util import ( - FatalError, - NotImplementedInROMError, - flash_size_bytes, -) - -import serial - - -def main(argv=None, esp=None): - """ - Main function for esptool - - argv - Optional override for default arguments parsing (that uses sys.argv), - can be a list of custom arguments as strings. Arguments and their values - need to be added as individual items to the list - e.g. "-b 115200" thus becomes ['-b', '115200']. - - esp - Optional override of the connected device previously - returned by get_default_connected_device() - """ - - external_esp = esp is not None - - parser = argparse.ArgumentParser( - description="esptool.py v%s - Espressif chips ROM Bootloader Utility" - % __version__, - prog="esptool", - ) - - parser.add_argument( - "--chip", - "-c", - help="Target chip type", - type=lambda c: c.lower().replace("-", ""), # support ESP32-S2, etc. - choices=["auto"] + CHIP_LIST, - default=os.environ.get("ESPTOOL_CHIP", "auto"), - ) - - parser.add_argument( - "--port", - "-p", - help="Serial port device", - default=os.environ.get("ESPTOOL_PORT", None), - ) - - parser.add_argument( - "--baud", - "-b", - help="Serial port baud rate used when flashing/reading", - type=arg_auto_int, - default=os.environ.get("ESPTOOL_BAUD", ESPLoader.ESP_ROM_BAUD), - ) - - parser.add_argument( - "--before", - help="What to do before connecting to the chip", - choices=["default_reset", "usb_reset", "no_reset", "no_reset_no_sync"], - default=os.environ.get("ESPTOOL_BEFORE", "default_reset"), - ) - - parser.add_argument( - "--after", - "-a", - help="What to do after esptool.py is finished", - choices=["hard_reset", "soft_reset", "no_reset", "no_reset_stub"], - default=os.environ.get("ESPTOOL_AFTER", "hard_reset"), - ) - - parser.add_argument( - "--no-stub", - help="Disable launching the flasher stub, only talk to ROM bootloader. " - "Some features will not be available.", - action="store_true", - ) - - parser.add_argument( - "--trace", - "-t", - help="Enable trace-level output of esptool.py interactions.", - action="store_true", - ) - - parser.add_argument( - "--override-vddsdio", - help="Override ESP32 VDDSDIO internal voltage regulator (use with care)", - choices=ESP32ROM.OVERRIDE_VDDSDIO_CHOICES, - nargs="?", - ) - - parser.add_argument( - "--connect-attempts", - help=( - "Number of attempts to connect, negative or 0 for infinite. " - "Default: %d." % DEFAULT_CONNECT_ATTEMPTS - ), - type=int, - default=os.environ.get("ESPTOOL_CONNECT_ATTEMPTS", DEFAULT_CONNECT_ATTEMPTS), - ) - - subparsers = parser.add_subparsers( - dest="operation", help="Run esptool.py {command} -h for additional help" - ) - - def add_spi_connection_arg(parent): - parent.add_argument( - "--spi-connection", - "-sc", - help="ESP32-only argument. Override default SPI Flash connection. " - "Value can be SPI, HSPI or a comma-separated list of 5 I/O numbers " - "to use for SPI flash (CLK,Q,D,HD,CS).", - action=SpiConnectionAction, - ) - - parser_load_ram = subparsers.add_parser( - "load_ram", help="Download an image to RAM and execute" - ) - parser_load_ram.add_argument("filename", help="Firmware image") - - parser_dump_mem = subparsers.add_parser( - "dump_mem", help="Dump arbitrary memory to disk" - ) - parser_dump_mem.add_argument("address", help="Base address", type=arg_auto_int) - parser_dump_mem.add_argument( - "size", help="Size of region to dump", type=arg_auto_int - ) - parser_dump_mem.add_argument("filename", help="Name of binary dump") - - parser_read_mem = subparsers.add_parser( - "read_mem", help="Read arbitrary memory location" - ) - parser_read_mem.add_argument("address", help="Address to read", type=arg_auto_int) - - parser_write_mem = subparsers.add_parser( - "write_mem", help="Read-modify-write to arbitrary memory location" - ) - parser_write_mem.add_argument("address", help="Address to write", type=arg_auto_int) - parser_write_mem.add_argument("value", help="Value", type=arg_auto_int) - parser_write_mem.add_argument( - "mask", - help="Mask of bits to write", - type=arg_auto_int, - nargs="?", - default="0xFFFFFFFF", - ) - - def add_spi_flash_subparsers(parent, allow_keep, auto_detect): - """Add common parser arguments for SPI flash properties""" - extra_keep_args = ["keep"] if allow_keep else [] - - if auto_detect and allow_keep: - extra_fs_message = ", detect, or keep" - flash_sizes = ["detect", "keep"] - elif auto_detect: - extra_fs_message = ", or detect" - flash_sizes = ["detect"] - elif allow_keep: - extra_fs_message = ", or keep" - flash_sizes = ["keep"] - else: - extra_fs_message = "" - flash_sizes = [] - - parent.add_argument( - "--flash_freq", - "-ff", - help="SPI Flash frequency", - choices=extra_keep_args - + [ - "80m", - "60m", - "48m", - "40m", - "30m", - "26m", - "24m", - "20m", - "16m", - "15m", - "12m", - ], - default=os.environ.get("ESPTOOL_FF", "keep" if allow_keep else None), - ) - parent.add_argument( - "--flash_mode", - "-fm", - help="SPI Flash mode", - choices=extra_keep_args + ["qio", "qout", "dio", "dout"], - default=os.environ.get("ESPTOOL_FM", "keep" if allow_keep else "qio"), - ) - parent.add_argument( - "--flash_size", - "-fs", - help="SPI Flash size in MegaBytes " - "(1MB, 2MB, 4MB, 8MB, 16MB, 32MB, 64MB, 128MB) " - "plus ESP8266-only (256KB, 512KB, 2MB-c1, 4MB-c1)" + extra_fs_message, - choices=flash_sizes - + [ - "256KB", - "512KB", - "1MB", - "2MB", - "2MB-c1", - "4MB", - "4MB-c1", - "8MB", - "16MB", - "32MB", - "64MB", - "128MB", - ], - default=os.environ.get("ESPTOOL_FS", "keep" if allow_keep else "1MB"), - ) - add_spi_connection_arg(parent) - - parser_write_flash = subparsers.add_parser( - "write_flash", help="Write a binary blob to flash" - ) - - parser_write_flash.add_argument( - "addr_filename", - metavar="
      ", - help="Address followed by binary filename, separated by space", - action=AddrFilenamePairAction, - ) - parser_write_flash.add_argument( - "--erase-all", - "-e", - help="Erase all regions of flash (not just write areas) before programming", - action="store_true", - ) - - add_spi_flash_subparsers(parser_write_flash, allow_keep=True, auto_detect=True) - parser_write_flash.add_argument( - "--no-progress", "-p", help="Suppress progress output", action="store_true" - ) - parser_write_flash.add_argument( - "--verify", - help="Verify just-written data on flash " - "(mostly superfluous, data is read back during flashing)", - action="store_true", - ) - parser_write_flash.add_argument( - "--encrypt", - help="Apply flash encryption when writing data " - "(required correct efuse settings)", - action="store_true", - ) - # In order to not break backward compatibility, - # our list of encrypted files to flash is a new parameter - parser_write_flash.add_argument( - "--encrypt-files", - metavar="
      ", - help="Files to be encrypted on the flash. " - "Address followed by binary filename, separated by space.", - action=AddrFilenamePairAction, - ) - parser_write_flash.add_argument( - "--ignore-flash-encryption-efuse-setting", - help="Ignore flash encryption efuse settings ", - action="store_true", - ) - parser_write_flash.add_argument( - "--force", - help="Force write, skip security and compatibility checks. Use with caution!", - action="store_true", - ) - - compress_args = parser_write_flash.add_mutually_exclusive_group(required=False) - compress_args.add_argument( - "--compress", - "-z", - help="Compress data in transfer (default unless --no-stub is specified)", - action="store_true", - default=None, - ) - compress_args.add_argument( - "--no-compress", - "-u", - help="Disable data compression during transfer " - "(default if --no-stub is specified)", - action="store_true", - ) - - subparsers.add_parser("run", help="Run application code in flash") - - parser_image_info = subparsers.add_parser( - "image_info", help="Dump headers from an application image" - ) - parser_image_info.add_argument("filename", help="Image file to parse") - parser_image_info.add_argument( - "--version", - "-v", - help="Output format version (1 - legacy, 2 - extended)", - choices=["1", "2"], - default="1", - ) - - parser_make_image = subparsers.add_parser( - "make_image", help="Create an application image from binary files" - ) - parser_make_image.add_argument("output", help="Output image file") - parser_make_image.add_argument( - "--segfile", "-f", action="append", help="Segment input file" - ) - parser_make_image.add_argument( - "--segaddr", - "-a", - action="append", - help="Segment base address", - type=arg_auto_int, - ) - parser_make_image.add_argument( - "--entrypoint", - "-e", - help="Address of entry point", - type=arg_auto_int, - default=0, - ) - - parser_elf2image = subparsers.add_parser( - "elf2image", help="Create an application image from ELF file" - ) - parser_elf2image.add_argument("input", help="Input ELF file") - parser_elf2image.add_argument( - "--output", - "-o", - help="Output filename prefix (for version 1 image), " - "or filename (for version 2 single image)", - type=str, - ) - parser_elf2image.add_argument( - "--version", - "-e", - help="Output image version", - choices=["1", "2", "3"], - default="1", - ) - parser_elf2image.add_argument( - # it kept for compatibility - # Minimum chip revision (deprecated, consider using --min-rev-full) - "--min-rev", - "-r", - help=argparse.SUPPRESS, - type=int, - choices=range(256), - metavar="{0, ... 255}", - default=0, - ) - parser_elf2image.add_argument( - "--min-rev-full", - help="Minimal chip revision (in format: major * 100 + minor)", - type=int, - choices=range(65536), - metavar="{0, ... 65535}", - default=0, - ) - parser_elf2image.add_argument( - "--max-rev-full", - help="Maximal chip revision (in format: major * 100 + minor)", - type=int, - choices=range(65536), - metavar="{0, ... 65535}", - default=65535, - ) - parser_elf2image.add_argument( - "--secure-pad", - action="store_true", - help="Pad image so once signed it will end on a 64KB boundary. " - "For Secure Boot v1 images only.", - ) - parser_elf2image.add_argument( - "--secure-pad-v2", - action="store_true", - help="Pad image to 64KB, so once signed its signature sector will" - "start at the next 64K block. For Secure Boot v2 images only.", - ) - parser_elf2image.add_argument( - "--elf-sha256-offset", - help="If set, insert SHA256 hash (32 bytes) of the input ELF file " - "at specified offset in the binary.", - type=arg_auto_int, - default=None, - ) - parser_elf2image.add_argument( - "--dont-append-digest", - dest="append_digest", - help="Don't append a SHA256 digest of the entire image after the checksum. " - "This argument is not supported and ignored for ESP8266.", - action="store_false", - default=True, - ) - parser_elf2image.add_argument( - "--use_segments", - help="If set, ELF segments will be used instead of ELF sections " - "to genereate the image.", - action="store_true", - ) - parser_elf2image.add_argument( - "--flash-mmu-page-size", - help="Change flash MMU page size.", - choices=["64KB", "32KB", "16KB", "8KB"], - ) - - add_spi_flash_subparsers(parser_elf2image, allow_keep=False, auto_detect=False) - - subparsers.add_parser("read_mac", help="Read MAC address from OTP ROM") - - subparsers.add_parser("chip_id", help="Read Chip ID from OTP ROM") - - parser_flash_id = subparsers.add_parser( - "flash_id", help="Read SPI flash manufacturer and device ID" - ) - add_spi_connection_arg(parser_flash_id) - - parser_read_status = subparsers.add_parser( - "read_flash_status", help="Read SPI flash status register" - ) - - add_spi_connection_arg(parser_read_status) - parser_read_status.add_argument( - "--bytes", - help="Number of bytes to read (1-3)", - type=int, - choices=[1, 2, 3], - default=2, - ) - - parser_write_status = subparsers.add_parser( - "write_flash_status", help="Write SPI flash status register" - ) - - add_spi_connection_arg(parser_write_status) - parser_write_status.add_argument( - "--non-volatile", - help="Write non-volatile bits (use with caution)", - action="store_true", - ) - parser_write_status.add_argument( - "--bytes", - help="Number of status bytes to write (1-3)", - type=int, - choices=[1, 2, 3], - default=2, - ) - parser_write_status.add_argument("value", help="New value", type=arg_auto_int) - - parser_read_flash = subparsers.add_parser( - "read_flash", help="Read SPI flash content" - ) - add_spi_connection_arg(parser_read_flash) - parser_read_flash.add_argument("address", help="Start address", type=arg_auto_int) - parser_read_flash.add_argument( - "size", help="Size of region to dump", type=arg_auto_int - ) - parser_read_flash.add_argument("filename", help="Name of binary dump") - parser_read_flash.add_argument( - "--no-progress", "-p", help="Suppress progress output", action="store_true" - ) - - parser_verify_flash = subparsers.add_parser( - "verify_flash", help="Verify a binary blob against flash" - ) - parser_verify_flash.add_argument( - "addr_filename", - help="Address and binary file to verify there, separated by space", - action=AddrFilenamePairAction, - ) - parser_verify_flash.add_argument( - "--diff", "-d", help="Show differences", choices=["no", "yes"], default="no" - ) - add_spi_flash_subparsers(parser_verify_flash, allow_keep=True, auto_detect=True) - - parser_erase_flash = subparsers.add_parser( - "erase_flash", help="Perform Chip Erase on SPI flash" - ) - parser_erase_flash.add_argument( - "--force", - help="Erase flash even if security features are enabled. Use with caution!", - action="store_true", - ) - add_spi_connection_arg(parser_erase_flash) - - parser_erase_region = subparsers.add_parser( - "erase_region", help="Erase a region of the flash" - ) - parser_erase_region.add_argument( - "--force", - help="Erase region even if security features are enabled. Use with caution!", - action="store_true", - ) - add_spi_connection_arg(parser_erase_region) - parser_erase_region.add_argument( - "address", help="Start address (must be multiple of 4096)", type=arg_auto_int - ) - parser_erase_region.add_argument( - "size", - help="Size of region to erase (must be multiple of 4096)", - type=arg_auto_int, - ) - - parser_merge_bin = subparsers.add_parser( - "merge_bin", - help="Merge multiple raw binary files into a single file for later flashing", - ) - - parser_merge_bin.add_argument( - "--output", "-o", help="Output filename", type=str, required=True - ) - parser_merge_bin.add_argument( - "--format", "-f", help="Format of the output file", choices="raw", default="raw" - ) # for future expansion - add_spi_flash_subparsers(parser_merge_bin, allow_keep=True, auto_detect=False) - - parser_merge_bin.add_argument( - "--target-offset", - "-t", - help="Target offset where the output file will be flashed", - type=arg_auto_int, - default=0, - ) - parser_merge_bin.add_argument( - "--fill-flash-size", - help="If set, the final binary file will be padded with FF " - "bytes up to this flash size.", - choices=[ - "256KB", - "512KB", - "1MB", - "2MB", - "4MB", - "8MB", - "16MB", - "32MB", - "64MB", - "128MB", - ], - ) - parser_merge_bin.add_argument( - "addr_filename", - metavar="
      ", - help="Address followed by binary filename, separated by space", - action=AddrFilenamePairAction, - ) - - subparsers.add_parser("get_security_info", help="Get some security-related data") - - subparsers.add_parser("version", help="Print esptool version") - - # internal sanity check - every operation matches a module function of the same name - for operation in subparsers.choices.keys(): - assert operation in globals(), "%s should be a module function" % operation - - argv = expand_file_arguments(argv or sys.argv[1:]) - - args = parser.parse_args(argv) - print("esptool.py v%s" % __version__) - - # operation function can take 1 arg (args), 2 args (esp, arg) - # or be a member function of the ESPLoader class. - - if args.operation is None: - parser.print_help() - sys.exit(1) - - # Forbid the usage of both --encrypt, which means encrypt all the given files, - # and --encrypt-files, which represents the list of files to encrypt. - # The reason is that allowing both at the same time increases the chances of - # having contradictory lists (e.g. one file not available in one of list). - if ( - args.operation == "write_flash" - and args.encrypt - and args.encrypt_files is not None - ): - raise FatalError( - "Options --encrypt and --encrypt-files " - "must not be specified at the same time." - ) - - operation_func = globals()[args.operation] - operation_args = inspect.getfullargspec(operation_func).args - - if ( - operation_args[0] == "esp" - ): # operation function takes an ESPLoader connection object - if args.before != "no_reset_no_sync": - initial_baud = min( - ESPLoader.ESP_ROM_BAUD, args.baud - ) # don't sync faster than the default baud rate - else: - initial_baud = args.baud - - if args.port is None: - ser_list = get_port_list() - print("Found %d serial ports" % len(ser_list)) - else: - ser_list = [args.port] - esp = esp or get_default_connected_device( - ser_list, - port=args.port, - connect_attempts=args.connect_attempts, - initial_baud=initial_baud, - chip=args.chip, - trace=args.trace, - before=args.before, - ) - - if esp is None: - raise FatalError( - "Could not connect to an Espressif device " - "on any of the %d available serial ports." % len(ser_list) - ) - - if esp.secure_download_mode: - print("Chip is %s in Secure Download Mode" % esp.CHIP_NAME) - else: - print("Chip is %s" % (esp.get_chip_description())) - print("Features: %s" % ", ".join(esp.get_chip_features())) - print("Crystal is %dMHz" % esp.get_crystal_freq()) - read_mac(esp, args) - - if not args.no_stub: - if esp.secure_download_mode: - print( - "WARNING: Stub loader is not supported in Secure Download Mode, " - "setting --no-stub" - ) - args.no_stub = True - elif not esp.IS_STUB and esp.stub_is_disabled: - print( - "WARNING: Stub loader has been disabled for compatibility, " - "setting --no-stub" - ) - args.no_stub = True - else: - esp = esp.run_stub() - - if args.override_vddsdio: - esp.override_vddsdio(args.override_vddsdio) - - if args.baud > initial_baud: - try: - esp.change_baud(args.baud) - except NotImplementedInROMError: - print( - "WARNING: ROM doesn't support changing baud rate. " - "Keeping initial baud rate %d" % initial_baud - ) - - # override common SPI flash parameter stuff if configured to do so - if hasattr(args, "spi_connection") and args.spi_connection is not None: - if esp.CHIP_NAME != "ESP32": - raise FatalError( - "Chip %s does not support --spi-connection option." % esp.CHIP_NAME - ) - print("Configuring SPI flash mode...") - esp.flash_spi_attach(args.spi_connection) - elif args.no_stub: - print("Enabling default SPI flash mode...") - # ROM loader doesn't enable flash unless we explicitly do it - esp.flash_spi_attach(0) - - # XMC chip startup sequence - XMC_VENDOR_ID = 0x20 - - def is_xmc_chip_strict(): - id = esp.flash_id() - rdid = ((id & 0xFF) << 16) | ((id >> 16) & 0xFF) | (id & 0xFF00) - - vendor_id = (rdid >> 16) & 0xFF - mfid = (rdid >> 8) & 0xFF - cpid = rdid & 0xFF - - if vendor_id != XMC_VENDOR_ID: - return False - - matched = False - if mfid == 0x40: - if cpid >= 0x13 and cpid <= 0x20: - matched = True - elif mfid == 0x41: - if cpid >= 0x17 and cpid <= 0x20: - matched = True - elif mfid == 0x50: - if cpid >= 0x15 and cpid <= 0x16: - matched = True - return matched - - def flash_xmc_startup(): - # If the RDID value is a valid XMC one, may skip the flow - fast_check = True - if fast_check and is_xmc_chip_strict(): - return # Successful XMC flash chip boot-up detected by RDID, skipping. - - sfdp_mfid_addr = 0x10 - mf_id = esp.read_spiflash_sfdp(sfdp_mfid_addr, 8) - if mf_id != XMC_VENDOR_ID: # Non-XMC chip detected by SFDP Read, skipping. - return - - print( - "WARNING: XMC flash chip boot-up failure detected! " - "Running XMC25QHxxC startup flow" - ) - esp.run_spiflash_command(0xB9) # Enter DPD - esp.run_spiflash_command(0x79) # Enter UDPD - esp.run_spiflash_command(0xFF) # Exit UDPD - time.sleep(0.002) # Delay tXUDPD - esp.run_spiflash_command(0xAB) # Release Power-Down - time.sleep(0.00002) - # Check for success - if not is_xmc_chip_strict(): - print("WARNING: XMC flash boot-up fix failed.") - print("XMC flash chip boot-up fix successful!") - - # Check flash chip connection - if not esp.secure_download_mode: - try: - flash_id = esp.flash_id() - if flash_id in (0xFFFFFF, 0x000000): - print( - "WARNING: Failed to communicate with the flash chip, " - "read/write operations will fail. " - "Try checking the chip connections or removing " - "any other hardware connected to IOs." - ) - except Exception as e: - esp.trace("Unable to verify flash chip connection ({}).".format(e)) - - # Check if XMC SPI flash chip booted-up successfully, fix if not - if not esp.secure_download_mode: - try: - flash_xmc_startup() - except Exception as e: - esp.trace( - "Unable to perform XMC flash chip startup sequence ({}).".format(e) - ) - - if hasattr(args, "flash_size"): - print("Configuring flash size...") - detect_flash_size(esp, args) - if args.flash_size != "keep": # TODO: should set this even with 'keep' - esp.flash_set_parameters(flash_size_bytes(args.flash_size)) - # Check if stub supports chosen flash size - if esp.IS_STUB and args.flash_size in ("32MB", "64MB", "128MB"): - print( - "WARNING: Flasher stub doesn't fully support flash size larger " - "than 16MB, in case of failure use --no-stub." - ) - - if esp.IS_STUB and hasattr(args, "address") and hasattr(args, "size"): - if args.address + args.size > 0x1000000: - print( - "WARNING: Flasher stub doesn't fully support flash size larger " - "than 16MB, in case of failure use --no-stub." - ) - - try: - operation_func(esp, args) - finally: - try: # Clean up AddrFilenamePairAction files - for address, argfile in args.addr_filename: - argfile.close() - except AttributeError: - pass - - # Handle post-operation behaviour (reset or other) - if operation_func == load_ram: - # the ESP is now running the loaded image, so let it run - print("Exiting immediately.") - elif args.after == "hard_reset": - esp.hard_reset() - elif args.after == "soft_reset": - print("Soft resetting...") - # flash_finish will trigger a soft reset - esp.soft_reset(False) - elif args.after == "no_reset_stub": - print("Staying in flasher stub.") - else: # args.after == 'no_reset' - print("Staying in bootloader.") - if esp.IS_STUB: - esp.soft_reset(True) # exit stub back to ROM loader - - if not external_esp: - esp._port.close() - - else: - operation_func(args) - - -def arg_auto_int(x): - return int(x, 0) - - -def get_port_list(): - if list_ports is None: - raise FatalError( - "Listing all serial ports is currently not available. " - "Please try to specify the port when running esptool.py or update " - "the pyserial package to the latest version" - ) - return sorted(ports.device for ports in list_ports.comports()) - - -def expand_file_arguments(argv): - """ - Any argument starting with "@" gets replaced with all values read from a text file. - Text file arguments can be split by newline or by space. - Values are added "as-is", as if they were specified in this order - on the command line. - """ - new_args = [] - expanded = False - for arg in argv: - if arg.startswith("@"): - expanded = True - with open(arg[1:], "r") as f: - for line in f.readlines(): - new_args += shlex.split(line) - else: - new_args.append(arg) - if expanded: - print("esptool %s" % (" ".join(new_args[1:]))) - return new_args - return argv - - -def get_default_connected_device( - serial_list, - port, - connect_attempts, - initial_baud, - chip="auto", - trace=False, - before="default_reset", -): - _esp = None - for each_port in reversed(serial_list): - print("Serial port %s" % each_port) - try: - if chip == "auto": - _esp = detect_chip( - each_port, initial_baud, before, trace, connect_attempts - ) - else: - chip_class = CHIP_DEFS[chip] - _esp = chip_class(each_port, initial_baud, trace) - _esp.connect(before, connect_attempts) - break - except (FatalError, OSError) as err: - if port is not None: - raise - print("%s failed to connect: %s" % (each_port, err)) - if _esp and _esp._port: - _esp._port.close() - _esp = None - return _esp - - -class SpiConnectionAction(argparse.Action): - """ - Custom action to parse 'spi connection' override. - Values are SPI, HSPI, or a sequence of 5 pin numbers separated by commas. - """ - - def __call__(self, parser, namespace, value, option_string=None): - if value.upper() == "SPI": - value = 0 - elif value.upper() == "HSPI": - value = 1 - elif "," in value: - values = value.split(",") - if len(values) != 5: - raise argparse.ArgumentError( - self, - "%s is not a valid list of comma-separate pin numbers. " - "Must be 5 numbers - CLK,Q,D,HD,CS." % value, - ) - try: - values = tuple(int(v, 0) for v in values) - except ValueError: - raise argparse.ArgumentError( - self, - "%s is not a valid argument. All pins must be numeric values" - % values, - ) - if any([v for v in values if v > 33 or v < 0]): - raise argparse.ArgumentError( - self, "Pin numbers must be in the range 0-33." - ) - # encode the pin numbers as a 32-bit integer with packed 6-bit values, - # the same way ESP32 ROM takes them - # TODO: make this less ESP32 ROM specific somehow... - clk, q, d, hd, cs = values - value = (hd << 24) | (cs << 18) | (d << 12) | (q << 6) | clk - else: - raise argparse.ArgumentError( - self, - "%s is not a valid spi-connection value. " - "Values are SPI, HSPI, or a sequence of 5 pin numbers CLK,Q,D,HD,CS)." - % value, - ) - setattr(namespace, self.dest, value) - - -class AddrFilenamePairAction(argparse.Action): - """Custom parser class for the address/filename pairs passed as arguments""" - - def __init__(self, option_strings, dest, nargs="+", **kwargs): - super(AddrFilenamePairAction, self).__init__( - option_strings, dest, nargs, **kwargs - ) - - def __call__(self, parser, namespace, values, option_string=None): - # validate pair arguments - pairs = [] - for i in range(0, len(values), 2): - try: - address = int(values[i], 0) - except ValueError: - raise argparse.ArgumentError( - self, 'Address "%s" must be a number' % values[i] - ) - try: - argfile = open(values[i + 1], "rb") - except IOError as e: - raise argparse.ArgumentError(self, e) - except IndexError: - raise argparse.ArgumentError( - self, - "Must be pairs of an address " - "and the binary filename to write there", - ) - pairs.append((address, argfile)) - - # Sort the addresses and check for overlapping - end = 0 - for address, argfile in sorted(pairs, key=lambda x: x[0]): - argfile.seek(0, 2) # seek to end - size = argfile.tell() - argfile.seek(0) - sector_start = address & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) - sector_end = ( - (address + size + ESPLoader.FLASH_SECTOR_SIZE - 1) - & ~(ESPLoader.FLASH_SECTOR_SIZE - 1) - ) - 1 - if sector_start < end: - message = "Detected overlap at address: 0x%x for file: %s" % ( - address, - argfile.name, - ) - raise argparse.ArgumentError(self, message) - end = sector_end - setattr(namespace, self.dest, pairs) - - -def _main(): - try: - main() - except FatalError as e: - print(f"\nA fatal error occurred: {e}") - sys.exit(2) - except serial.serialutil.SerialException as e: - print(f"\nA serial exception error occurred: {e}") - print( - "Note: This error originates from pySerial. " - "It is likely not a problem with esptool, " - "but with the hardware connection or drivers." - ) - print( - "For troubleshooting steps visit: " - "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html" - ) - sys.exit(1) - - -if __name__ == "__main__": - _main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py deleted file mode 100644 index e5b9358f6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/__main__.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -# -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import esptool - -if __name__ == "__main__": - esptool._main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py deleted file mode 100644 index 9c441d625..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/bin_image.py +++ /dev/null @@ -1,1221 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import binascii -import copy -import hashlib -import io -import os -import re -import struct - -from .loader import ESPLoader -from .targets import ( - ESP32C2ROM, - ESP32C3ROM, - ESP32C6BETAROM, - ESP32C6ROM, - ESP32H2BETA1ROM, - ESP32H2BETA2ROM, - ESP32ROM, - ESP32S2ROM, - ESP32S3BETA2ROM, - ESP32S3ROM, - ESP8266ROM, -) -from .util import FatalError, byte, pad_to - - -def align_file_position(f, size): - """Align the position in the file to the next block of specified size""" - align = (size - 1) - (f.tell() % size) - f.seek(align, 1) - - -def LoadFirmwareImage(chip, image_file): - """ - Load a firmware image. Can be for any supported SoC. - - ESP8266 images will be examined to determine if they are original ROM firmware - images (ESP8266ROMFirmwareImage) or "v2" OTA bootloader images. - - Returns a BaseFirmwareImage subclass, either ESP8266ROMFirmwareImage (v1) - or ESP8266V2FirmwareImage (v2). - """ - - def select_image_class(f, chip): - chip = re.sub(r"[-()]", "", chip.lower()) - if chip != "esp8266": - return { - "esp32": ESP32FirmwareImage, - "esp32s2": ESP32S2FirmwareImage, - "esp32s3beta2": ESP32S3BETA2FirmwareImage, - "esp32s3": ESP32S3FirmwareImage, - "esp32c3": ESP32C3FirmwareImage, - "esp32c6beta": ESP32C6BETAFirmwareImage, - "esp32h2beta1": ESP32H2BETA1FirmwareImage, - "esp32h2beta2": ESP32H2BETA2FirmwareImage, - "esp32c2": ESP32C2FirmwareImage, - "esp32c6": ESP32C6FirmwareImage, - }[chip](f) - else: # Otherwise, ESP8266 so look at magic to determine the image type - magic = ord(f.read(1)) - f.seek(0) - if magic == ESPLoader.ESP_IMAGE_MAGIC: - return ESP8266ROMFirmwareImage(f) - elif magic == ESP8266V2FirmwareImage.IMAGE_V2_MAGIC: - return ESP8266V2FirmwareImage(f) - else: - raise FatalError("Invalid image magic number: %d" % magic) - - if isinstance(image_file, str): - with open(image_file, "rb") as f: - return select_image_class(f, chip) - return select_image_class(image_file, chip) - - -class ImageSegment(object): - """Wrapper class for a segment in an ESP image - (very similar to a section in an ELFImage also)""" - - def __init__(self, addr, data, file_offs=None): - self.addr = addr - self.data = data - self.file_offs = file_offs - self.include_in_checksum = True - if self.addr != 0: - self.pad_to_alignment( - 4 - ) # pad all "real" ImageSegments 4 byte aligned length - - def copy_with_new_addr(self, new_addr): - """Return a new ImageSegment with same data, but mapped at - a new address.""" - return ImageSegment(new_addr, self.data, 0) - - def split_image(self, split_len): - """Return a new ImageSegment which splits "split_len" bytes - from the beginning of the data. Remaining bytes are kept in - this segment object (and the start address is adjusted to match.)""" - result = copy.copy(self) - result.data = self.data[:split_len] - self.data = self.data[split_len:] - self.addr += split_len - self.file_offs = None - result.file_offs = None - return result - - def __repr__(self): - r = "len 0x%05x load 0x%08x" % (len(self.data), self.addr) - if self.file_offs is not None: - r += " file_offs 0x%08x" % (self.file_offs) - return r - - def get_memory_type(self, image): - """ - Return a list describing the memory type(s) that is covered by this - segment's start address. - """ - return [ - map_range[2] - for map_range in image.ROM_LOADER.MEMORY_MAP - if map_range[0] <= self.addr < map_range[1] - ] - - def pad_to_alignment(self, alignment): - self.data = pad_to(self.data, alignment, b"\x00") - - -class ELFSection(ImageSegment): - """Wrapper class for a section in an ELF image, has a section - name as well as the common properties of an ImageSegment.""" - - def __init__(self, name, addr, data): - super(ELFSection, self).__init__(addr, data) - self.name = name.decode("utf-8") - - def __repr__(self): - return "%s %s" % (self.name, super(ELFSection, self).__repr__()) - - -class BaseFirmwareImage(object): - SEG_HEADER_LEN = 8 - SHA256_DIGEST_LEN = 32 - - """ Base class with common firmware image functions """ - - def __init__(self): - self.segments = [] - self.entrypoint = 0 - self.elf_sha256 = None - self.elf_sha256_offset = 0 - - def load_common_header(self, load_file, expected_magic): - ( - magic, - segments, - self.flash_mode, - self.flash_size_freq, - self.entrypoint, - ) = struct.unpack(" 16: - raise FatalError( - "Invalid segment count %d (max 16). " - "Usually this indicates a linker script problem." % len(self.segments) - ) - - def load_segment(self, f, is_irom_segment=False): - """Load the next segment from the image file""" - file_offs = f.tell() - (offset, size) = struct.unpack(" 0x40200000 or offset < 0x3FFE0000 or size > 65536: - print("WARNING: Suspicious segment 0x%x, length %d" % (offset, size)) - - def maybe_patch_segment_data(self, f, segment_data): - """ - If SHA256 digest of the ELF file needs to be inserted into this segment, do so. - Returns segment data. - """ - segment_len = len(segment_data) - file_pos = f.tell() # file_pos is position in the .bin file - if ( - self.elf_sha256_offset >= file_pos - and self.elf_sha256_offset < file_pos + segment_len - ): - # SHA256 digest needs to be patched into this binary segment, - # calculate offset of the digest inside the binary segment. - patch_offset = self.elf_sha256_offset - file_pos - # Sanity checks - if ( - patch_offset < self.SEG_HEADER_LEN - or patch_offset + self.SHA256_DIGEST_LEN > segment_len - ): - raise FatalError( - "Cannot place SHA256 digest on segment boundary" - "(elf_sha256_offset=%d, file_pos=%d, segment_size=%d)" - % (self.elf_sha256_offset, file_pos, segment_len) - ) - # offset relative to the data part - patch_offset -= self.SEG_HEADER_LEN - if ( - segment_data[patch_offset : patch_offset + self.SHA256_DIGEST_LEN] - != b"\x00" * self.SHA256_DIGEST_LEN - ): - raise FatalError( - "Contents of segment at SHA256 digest offset 0x%x are not all zero." - " Refusing to overwrite." % self.elf_sha256_offset - ) - assert len(self.elf_sha256) == self.SHA256_DIGEST_LEN - segment_data = ( - segment_data[0:patch_offset] - + self.elf_sha256 - + segment_data[patch_offset + self.SHA256_DIGEST_LEN :] - ) - return segment_data - - def save_segment(self, f, segment, checksum=None): - """ - Save the next segment to the image file, - return next checksum value if provided - """ - segment_data = self.maybe_patch_segment_data(f, segment.data) - f.write(struct.pack(" 0: - if len(irom_segments) != 1: - raise FatalError( - "Found %d segments that could be irom0. Bad ELF file?" - % len(irom_segments) - ) - return irom_segments[0] - return None - - def get_non_irom_segments(self): - irom_segment = self.get_irom_segment() - return [s for s in self.segments if s != irom_segment] - - def merge_adjacent_segments(self): - if not self.segments: - return # nothing to merge - - segments = [] - # The easiest way to merge the sections is the browse them backward. - for i in range(len(self.segments) - 1, 0, -1): - # elem is the previous section, the one `next_elem` may need to be - # merged in - elem = self.segments[i - 1] - next_elem = self.segments[i] - if all( - ( - elem.get_memory_type(self) == next_elem.get_memory_type(self), - elem.include_in_checksum == next_elem.include_in_checksum, - next_elem.addr == elem.addr + len(elem.data), - ) - ): - # Merge any segment that ends where the next one starts, - # without spanning memory types - # - # (don't 'pad' any gaps here as they may be excluded from the image - # due to 'noinit' or other reasons.) - elem.data += next_elem.data - else: - # The section next_elem cannot be merged into the previous one, - # which means it needs to be part of the final segments. - # As we are browsing the list backward, the elements need to be - # inserted at the beginning of the final list. - segments.insert(0, next_elem) - - # The first segment will always be here as it cannot be merged into any - # "previous" section. - segments.insert(0, self.segments[0]) - - # note: we could sort segments here as well, but the ordering of segments is - # sometimes important for other reasons (like embedded ELF SHA-256), - # so we assume that the linker script will have produced any adjacent sections - # in linear order in the ELF, anyhow. - self.segments = segments - - def set_mmu_page_size(self, size): - """ - If supported, this should be overridden by the chip-specific class. - Gets called in elf2image. - """ - print( - "WARNING: Changing MMU page size is not supported on {}! " - "Defaulting to 64KB.".format(self.ROM_LOADER.CHIP_NAME) - ) - - -class ESP8266ROMFirmwareImage(BaseFirmwareImage): - """'Version 1' firmware image, segments loaded directly by the ROM bootloader.""" - - ROM_LOADER = ESP8266ROM - - def __init__(self, load_file=None): - super(ESP8266ROMFirmwareImage, self).__init__() - self.flash_mode = 0 - self.flash_size_freq = 0 - self.version = 1 - - if load_file is not None: - segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) - - for _ in range(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - self.verify() - - def default_output_name(self, input_file): - """Derive a default output name from the ELF name.""" - return input_file + "-" - - def save(self, basename): - """Save a set of V1 images for flashing. Parameter is a base filename.""" - # IROM data goes in its own plain binary file - irom_segment = self.get_irom_segment() - if irom_segment is not None: - with open( - "%s0x%05x.bin" - % (basename, irom_segment.addr - ESP8266ROM.IROM_MAP_START), - "wb", - ) as f: - f.write(irom_segment.data) - - # everything but IROM goes at 0x00000 in an image file - normal_segments = self.get_non_irom_segments() - with open("%s0x00000.bin" % basename, "wb") as f: - self.write_common_header(f, normal_segments) - checksum = ESPLoader.ESP_CHECKSUM_MAGIC - for segment in normal_segments: - checksum = self.save_segment(f, segment, checksum) - self.append_checksum(f, checksum) - - -ESP8266ROM.BOOTLOADER_IMAGE = ESP8266ROMFirmwareImage - - -class ESP8266V2FirmwareImage(BaseFirmwareImage): - """'Version 2' firmware image, segments loaded by software bootloader stub - (ie Espressif bootloader or rboot) - """ - - ROM_LOADER = ESP8266ROM - # First byte of the "v2" application image - IMAGE_V2_MAGIC = 0xEA - - # First 'segment' value in a "v2" application image, - # appears to be a constant version value? - IMAGE_V2_SEGMENT = 4 - - def __init__(self, load_file=None): - super(ESP8266V2FirmwareImage, self).__init__() - self.version = 2 - if load_file is not None: - segments = self.load_common_header(load_file, self.IMAGE_V2_MAGIC) - if segments != self.IMAGE_V2_SEGMENT: - # segment count is not really segment count here, - # but we expect to see '4' - print( - 'Warning: V2 header has unexpected "segment" count %d (usually 4)' - % segments - ) - - # irom segment comes before the second header - # - # the file is saved in the image with a zero load address - # in the header, so we need to calculate a load address - irom_segment = self.load_segment(load_file, True) - # for actual mapped addr, add ESP8266ROM.IROM_MAP_START + flashing_addr + 8 - irom_segment.addr = 0 - irom_segment.include_in_checksum = False - - first_flash_mode = self.flash_mode - first_flash_size_freq = self.flash_size_freq - first_entrypoint = self.entrypoint - # load the second header - - segments = self.load_common_header(load_file, ESPLoader.ESP_IMAGE_MAGIC) - - if first_flash_mode != self.flash_mode: - print( - "WARNING: Flash mode value in first header (0x%02x) disagrees " - "with second (0x%02x). Using second value." - % (first_flash_mode, self.flash_mode) - ) - if first_flash_size_freq != self.flash_size_freq: - print( - "WARNING: Flash size/freq value in first header (0x%02x) disagrees " - "with second (0x%02x). Using second value." - % (first_flash_size_freq, self.flash_size_freq) - ) - if first_entrypoint != self.entrypoint: - print( - "WARNING: Entrypoint address in first header (0x%08x) disagrees " - "with second header (0x%08x). Using second value." - % (first_entrypoint, self.entrypoint) - ) - - # load all the usual segments - for _ in range(segments): - self.load_segment(load_file) - self.checksum = self.read_checksum(load_file) - - self.verify() - - def default_output_name(self, input_file): - """Derive a default output name from the ELF name.""" - irom_segment = self.get_irom_segment() - if irom_segment is not None: - irom_offs = irom_segment.addr - ESP8266ROM.IROM_MAP_START - else: - irom_offs = 0 - return "%s-0x%05x.bin" % ( - os.path.splitext(input_file)[0], - irom_offs & ~(ESPLoader.FLASH_SECTOR_SIZE - 1), - ) - - def save(self, filename): - with open(filename, "wb") as f: - # Save first header for irom0 segment - f.write( - struct.pack( - b" 0: - last_addr = flash_segments[0].addr - for segment in flash_segments[1:]: - if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: - raise FatalError( - "Segment loaded at 0x%08x lands in same 64KB flash mapping " - "as segment loaded at 0x%08x. Can't generate binary. " - "Suggest changing linker script or ELF to merge sections." - % (segment.addr, last_addr) - ) - last_addr = segment.addr - - def get_alignment_data_needed(segment): - # Actual alignment (in data bytes) required for a segment header: - # positioned so that after we write the next 8 byte header, - # file_offs % IROM_ALIGN == segment.addr % IROM_ALIGN - # - # (this is because the segment's vaddr may not be IROM_ALIGNed, - # more likely is aligned IROM_ALIGN+0x18 - # to account for the binary file header - align_past = (segment.addr % self.IROM_ALIGN) - self.SEG_HEADER_LEN - pad_len = (self.IROM_ALIGN - (f.tell() % self.IROM_ALIGN)) + align_past - if pad_len == 0 or pad_len == self.IROM_ALIGN: - return 0 # already aligned - - # subtract SEG_HEADER_LEN a second time, - # as the padding block has a header as well - pad_len -= self.SEG_HEADER_LEN - if pad_len < 0: - pad_len += self.IROM_ALIGN - return pad_len - - # try to fit each flash segment on a 64kB aligned boundary - # by padding with parts of the non-flash segments... - while len(flash_segments) > 0: - segment = flash_segments[0] - pad_len = get_alignment_data_needed(segment) - if pad_len > 0: # need to pad - if len(ram_segments) > 0 and pad_len > self.SEG_HEADER_LEN: - pad_segment = ram_segments[0].split_image(pad_len) - if len(ram_segments[0].data) == 0: - ram_segments.pop(0) - else: - pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) - checksum = self.save_segment(f, pad_segment, checksum) - total_segments += 1 - else: - # write the flash segment - assert ( - f.tell() + 8 - ) % self.IROM_ALIGN == segment.addr % self.IROM_ALIGN - checksum = self.save_flash_segment(f, segment, checksum) - flash_segments.pop(0) - total_segments += 1 - - # flash segments all written, so write any remaining RAM segments - for segment in ram_segments: - checksum = self.save_segment(f, segment, checksum) - total_segments += 1 - - if self.secure_pad: - # pad the image so that after signing it will end on a a 64KB boundary. - # This ensures all mapped flash content will be verified. - if not self.append_digest: - raise FatalError( - "secure_pad only applies if a SHA-256 digest " - "is also appended to the image" - ) - align_past = (f.tell() + self.SEG_HEADER_LEN) % self.IROM_ALIGN - # 16 byte aligned checksum - # (force the alignment to simplify calculations) - checksum_space = 16 - if self.secure_pad == "1": - # after checksum: SHA-256 digest + - # (to be added by signing process) version, - # signature + 12 trailing bytes due to alignment - space_after_checksum = 32 + 4 + 64 + 12 - elif self.secure_pad == "2": # Secure Boot V2 - # after checksum: SHA-256 digest + - # signature sector, - # but we place signature sector after the 64KB boundary - space_after_checksum = 32 - pad_len = ( - self.IROM_ALIGN - align_past - checksum_space - space_after_checksum - ) % self.IROM_ALIGN - pad_segment = ImageSegment(0, b"\x00" * pad_len, f.tell()) - - checksum = self.save_segment(f, pad_segment, checksum) - total_segments += 1 - - # done writing segments - self.append_checksum(f, checksum) - image_length = f.tell() - - if self.secure_pad: - assert ((image_length + space_after_checksum) % self.IROM_ALIGN) == 0 - - # kinda hacky: go back to the initial header and write the new segment count - # that includes padding segments. This header is not checksummed - f.seek(1) - f.write(bytes([total_segments])) - - if self.append_digest: - # calculate the SHA256 of the whole file and append it - f.seek(0) - digest = hashlib.sha256() - digest.update(f.read(image_length)) - f.write(digest.digest()) - - with open(filename, "wb") as real_file: - real_file.write(f.getvalue()) - - def save_flash_segment(self, f, segment, checksum=None): - """ - Save the next segment to the image file, return next checksum value if provided - """ - segment_end_pos = f.tell() + len(segment.data) + self.SEG_HEADER_LEN - segment_len_remainder = segment_end_pos % self.IROM_ALIGN - if segment_len_remainder < 0x24: - # Work around a bug in ESP-IDF 2nd stage bootloader, that it didn't map the - # last MMU page, if an IROM/DROM segment was < 0x24 bytes - # over the page boundary. - segment.data += b"\x00" * (0x24 - segment_len_remainder) - return self.save_segment(f, segment, checksum) - - def load_extended_header(self, load_file): - def split_byte(n): - return (n & 0x0F, (n >> 4) & 0x0F) - - fields = list( - struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) - ) - - self.wp_pin = fields[0] - - # SPI pin drive stengths are two per byte - self.clk_drv, self.q_drv = split_byte(fields[1]) - self.d_drv, self.cs_drv = split_byte(fields[2]) - self.hd_drv, self.wp_drv = split_byte(fields[3]) - - self.chip_id = fields[4] - if self.chip_id != self.ROM_LOADER.IMAGE_CHIP_ID: - print( - ( - "Unexpected chip id in image. Expected %d but value was %d. " - "Is this image for a different chip model?" - ) - % (self.ROM_LOADER.IMAGE_CHIP_ID, self.chip_id) - ) - - self.min_rev = fields[5] - self.min_rev_full = fields[6] - self.max_rev_full = fields[7] - - # reserved fields in the middle should all be zero - if any(f for f in fields[8:-1] if f != 0): - print( - "Warning: some reserved header fields have non-zero values. " - "This image may be from a newer esptool.py?" - ) - - append_digest = fields[-1] # last byte is append_digest - if append_digest in [0, 1]: - self.append_digest = append_digest == 1 - else: - raise RuntimeError( - "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", - append_digest, - ) - - def save_extended_header(self, save_file): - def join_byte(ln, hn): - return (ln & 0x0F) + ((hn & 0x0F) << 4) - - append_digest = 1 if self.append_digest else 0 - - fields = [ - self.wp_pin, - join_byte(self.clk_drv, self.q_drv), - join_byte(self.d_drv, self.cs_drv), - join_byte(self.hd_drv, self.wp_drv), - self.ROM_LOADER.IMAGE_CHIP_ID, - self.min_rev, - self.min_rev_full, - self.max_rev_full, - ] - fields += [0] * 4 # padding - fields += [append_digest] - - packed = struct.pack(self.EXTENDED_HEADER_STRUCT_FMT, *fields) - save_file.write(packed) - - -class ESP8266V3FirmwareImage(ESP32FirmwareImage): - """ESP8266 V3 firmware image is very similar to ESP32 image""" - - EXTENDED_HEADER_STRUCT_FMT = "B" * 16 - - def is_flash_addr(self, addr): - return addr > ESP8266ROM.IROM_MAP_START - - def save(self, filename): - total_segments = 0 - with io.BytesIO() as f: # write file to memory first - self.write_common_header(f, self.segments) - - checksum = ESPLoader.ESP_CHECKSUM_MAGIC - - # split segments into flash-mapped vs ram-loaded, - # and take copies so we can mutate them - flash_segments = [ - copy.deepcopy(s) - for s in sorted(self.segments, key=lambda s: s.addr) - if self.is_flash_addr(s.addr) and len(s.data) - ] - ram_segments = [ - copy.deepcopy(s) - for s in sorted(self.segments, key=lambda s: s.addr) - if not self.is_flash_addr(s.addr) and len(s.data) - ] - - # check for multiple ELF sections that are mapped in the same - # flash mapping region. This is usually a sign of a broken linker script, - # but if you have a legitimate use case then let us know - if len(flash_segments) > 0: - last_addr = flash_segments[0].addr - for segment in flash_segments[1:]: - if segment.addr // self.IROM_ALIGN == last_addr // self.IROM_ALIGN: - raise FatalError( - "Segment loaded at 0x%08x lands in same 64KB flash mapping " - "as segment loaded at 0x%08x. Can't generate binary. " - "Suggest changing linker script or ELF to merge sections." - % (segment.addr, last_addr) - ) - last_addr = segment.addr - - # try to fit each flash segment on a 64kB aligned boundary - # by padding with parts of the non-flash segments... - while len(flash_segments) > 0: - segment = flash_segments[0] - # remove 8 bytes empty data for insert segment header - if segment.name == ".flash.rodata": - segment.data = segment.data[8:] - # write the flash segment - checksum = self.save_segment(f, segment, checksum) - flash_segments.pop(0) - total_segments += 1 - - # flash segments all written, so write any remaining RAM segments - for segment in ram_segments: - checksum = self.save_segment(f, segment, checksum) - total_segments += 1 - - # done writing segments - self.append_checksum(f, checksum) - image_length = f.tell() - - # kinda hacky: go back to the initial header and write the new segment count - # that includes padding segments. This header is not checksummed - f.seek(1) - f.write(bytes([total_segments])) - - if self.append_digest: - # calculate the SHA256 of the whole file and append it - f.seek(0) - digest = hashlib.sha256() - digest.update(f.read(image_length)) - f.write(digest.digest()) - - with open(filename, "wb") as real_file: - real_file.write(f.getvalue()) - - def load_extended_header(self, load_file): - def split_byte(n): - return (n & 0x0F, (n >> 4) & 0x0F) - - fields = list( - struct.unpack(self.EXTENDED_HEADER_STRUCT_FMT, load_file.read(16)) - ) - - self.wp_pin = fields[0] - - # SPI pin drive stengths are two per byte - self.clk_drv, self.q_drv = split_byte(fields[1]) - self.d_drv, self.cs_drv = split_byte(fields[2]) - self.hd_drv, self.wp_drv = split_byte(fields[3]) - - if fields[15] in [0, 1]: - self.append_digest = fields[15] == 1 - else: - raise RuntimeError( - "Invalid value for append_digest field (0x%02x). Should be 0 or 1.", - fields[15], - ) - - # remaining fields in the middle should all be zero - if any(f for f in fields[4:15] if f != 0): - print( - "Warning: some reserved header fields have non-zero values. " - "This image may be from a newer esptool.py?" - ) - - -ESP32ROM.BOOTLOADER_IMAGE = ESP32FirmwareImage - - -class ESP32S2FirmwareImage(ESP32FirmwareImage): - """ESP32S2 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32S2ROM - - -ESP32S2ROM.BOOTLOADER_IMAGE = ESP32S2FirmwareImage - - -class ESP32S3BETA2FirmwareImage(ESP32FirmwareImage): - """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32S3BETA2ROM - - -ESP32S3BETA2ROM.BOOTLOADER_IMAGE = ESP32S3BETA2FirmwareImage - - -class ESP32S3FirmwareImage(ESP32FirmwareImage): - """ESP32S3 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32S3ROM - - -ESP32S3ROM.BOOTLOADER_IMAGE = ESP32S3FirmwareImage - - -class ESP32C3FirmwareImage(ESP32FirmwareImage): - """ESP32C3 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32C3ROM - - -ESP32C3ROM.BOOTLOADER_IMAGE = ESP32C3FirmwareImage - - -class ESP32C6BETAFirmwareImage(ESP32FirmwareImage): - """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32C6BETAROM - - -ESP32C6BETAROM.BOOTLOADER_IMAGE = ESP32C6BETAFirmwareImage - - -class ESP32H2BETA1FirmwareImage(ESP32FirmwareImage): - """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32H2BETA1ROM - - -ESP32H2BETA1ROM.BOOTLOADER_IMAGE = ESP32H2BETA1FirmwareImage - - -class ESP32H2BETA2FirmwareImage(ESP32FirmwareImage): - """ESP32H2 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32H2BETA2ROM - - -ESP32H2BETA2ROM.BOOTLOADER_IMAGE = ESP32H2BETA2FirmwareImage - - -class ESP32C2FirmwareImage(ESP32FirmwareImage): - """ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32C2ROM - - def set_mmu_page_size(self, size): - if size not in [16384, 32768, 65536]: - raise FatalError( - "{} bytes is not a valid ESP32-C2 page size, " - "select from 64KB, 32KB, 16KB.".format(size) - ) - self.IROM_ALIGN = size - - -ESP32C2ROM.BOOTLOADER_IMAGE = ESP32C2FirmwareImage - - -class ESP32C6FirmwareImage(ESP32FirmwareImage): - """ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage""" - - ROM_LOADER = ESP32C6ROM - - def set_mmu_page_size(self, size): - if size not in [8192, 16384, 32768, 65536]: - raise FatalError( - "{} bytes is not a valid ESP32-C6 page size, " - "select from 64KB, 32KB, 16KB, 8KB.".format(size) - ) - self.IROM_ALIGN = size - - -ESP32C6ROM.BOOTLOADER_IMAGE = ESP32C6FirmwareImage - - -class ELFFile(object): - SEC_TYPE_PROGBITS = 0x01 - SEC_TYPE_STRTAB = 0x03 - SEC_TYPE_INITARRAY = 0x0E - SEC_TYPE_FINIARRAY = 0x0F - - PROG_SEC_TYPES = (SEC_TYPE_PROGBITS, SEC_TYPE_INITARRAY, SEC_TYPE_FINIARRAY) - - LEN_SEC_HEADER = 0x28 - - SEG_TYPE_LOAD = 0x01 - LEN_SEG_HEADER = 0x20 - - def __init__(self, name): - # Load sections from the ELF file - self.name = name - with open(self.name, "rb") as f: - self._read_elf_file(f) - - def get_section(self, section_name): - for s in self.sections: - if s.name == section_name: - return s - raise ValueError("No section %s in ELF file" % section_name) - - def _read_elf_file(self, f): - # read the ELF file header - LEN_FILE_HEADER = 0x34 - try: - ( - ident, - _type, - machine, - _version, - self.entrypoint, - _phoff, - shoff, - _flags, - _ehsize, - _phentsize, - _phnum, - shentsize, - shnum, - shstrndx, - ) = struct.unpack("<16sHHLLLLLHHHHHH", f.read(LEN_FILE_HEADER)) - except struct.error as e: - raise FatalError( - "Failed to read a valid ELF header from %s: %s" % (self.name, e) - ) - - if byte(ident, 0) != 0x7F or ident[1:4] != b"ELF": - raise FatalError("%s has invalid ELF magic header" % self.name) - if machine not in [0x5E, 0xF3]: - raise FatalError( - "%s does not appear to be an Xtensa or an RISCV ELF file. " - "e_machine=%04x" % (self.name, machine) - ) - if shentsize != self.LEN_SEC_HEADER: - raise FatalError( - "%s has unexpected section header entry size 0x%x (not 0x%x)" - % (self.name, shentsize, self.LEN_SEC_HEADER) - ) - if shnum == 0: - raise FatalError("%s has 0 section headers" % (self.name)) - self._read_sections(f, shoff, shnum, shstrndx) - self._read_segments(f, _phoff, _phnum, shstrndx) - - def _read_sections(self, f, section_header_offs, section_header_count, shstrndx): - f.seek(section_header_offs) - len_bytes = section_header_count * self.LEN_SEC_HEADER - section_header = f.read(len_bytes) - if len(section_header) == 0: - raise FatalError( - "No section header found at offset %04x in ELF file." - % section_header_offs - ) - if len(section_header) != (len_bytes): - raise FatalError( - "Only read 0x%x bytes from section header (expected 0x%x.) " - "Truncated ELF file?" % (len(section_header), len_bytes) - ) - - # walk through the section header and extract all sections - section_header_offsets = range(0, len(section_header), self.LEN_SEC_HEADER) - - def read_section_header(offs): - name_offs, sec_type, _flags, lma, sec_offs, size = struct.unpack_from( - " 0 - ] - self.sections = prog_sections - - def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx): - f.seek(segment_header_offs) - len_bytes = segment_header_count * self.LEN_SEG_HEADER - segment_header = f.read(len_bytes) - if len(segment_header) == 0: - raise FatalError( - "No segment header found at offset %04x in ELF file." - % segment_header_offs - ) - if len(segment_header) != (len_bytes): - raise FatalError( - "Only read 0x%x bytes from segment header (expected 0x%x.) " - "Truncated ELF file?" % (len(segment_header), len_bytes) - ) - - # walk through the segment header and extract all segments - segment_header_offsets = range(0, len(segment_header), self.LEN_SEG_HEADER) - - def read_segment_header(offs): - ( - seg_type, - seg_offs, - _vaddr, - lma, - size, - _memsize, - _flags, - _align, - ) = struct.unpack_from(" 0 - ] - self.segments = prog_segments - - def sha256(self): - # return SHA256 hash of the input ELF file - sha256 = hashlib.sha256() - with open(self.name, "rb") as f: - sha256.update(f.read()) - return sha256.digest() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py deleted file mode 100644 index d1d92fa7f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/cmds.py +++ /dev/null @@ -1,1160 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import hashlib -import io -import os -import struct -import sys -import time -import zlib - -from .bin_image import ELFFile, ImageSegment, LoadFirmwareImage -from .bin_image import ( - ESP8266ROMFirmwareImage, - ESP8266V2FirmwareImage, - ESP8266V3FirmwareImage, -) -from .loader import ( - DEFAULT_CONNECT_ATTEMPTS, - DEFAULT_TIMEOUT, - ERASE_WRITE_TIMEOUT_PER_MB, - ESPLoader, - timeout_per_mb, -) -from .targets import CHIP_DEFS, CHIP_LIST, ESP8266ROM, ROM_LIST -from .util import ( - FatalError, - NotImplementedInROMError, - NotSupportedError, - UnsupportedCommandError, -) -from .util import ( - div_roundup, - flash_size_bytes, - hexify, - pad_to, - print_overwrite, -) - -DETECTED_FLASH_SIZES = { - 0x12: "256KB", - 0x13: "512KB", - 0x14: "1MB", - 0x15: "2MB", - 0x16: "4MB", - 0x17: "8MB", - 0x18: "16MB", - 0x19: "32MB", - 0x1A: "64MB", - 0x1B: "128MB", - 0x1C: "256MB", - 0x20: "64MB", - 0x21: "128MB", - 0x22: "256MB", - 0x32: "256KB", - 0x33: "512KB", - 0x34: "1MB", - 0x35: "2MB", - 0x36: "4MB", - 0x37: "8MB", - 0x38: "16MB", - 0x39: "32MB", - 0x3A: "64MB", -} - -FLASH_MODES = {"qio": 0, "qout": 1, "dio": 2, "dout": 3} - - -def detect_chip( - port=ESPLoader.DEFAULT_PORT, - baud=ESPLoader.ESP_ROM_BAUD, - connect_mode="default_reset", - trace_enabled=False, - connect_attempts=DEFAULT_CONNECT_ATTEMPTS, -): - """Use serial access to detect the chip type. - - First, get_security_info command is sent to detect the ID of the chip - (supported only by ESP32-C3 and later, works even in the Secure Download Mode). - If this fails, we reconnect and fall-back to reading the magic number. - It's mapped at a specific ROM address and has a different value on each chip model. - This way we use one memory read and compare it to the magic number for each chip. - - This routine automatically performs ESPLoader.connect() (passing - connect_mode parameter) as part of querying the chip. - """ - inst = None - detect_port = ESPLoader(port, baud, trace_enabled=trace_enabled) - if detect_port.serial_port.startswith("rfc2217:"): - detect_port.USES_RFC2217 = True - detect_port.connect(connect_mode, connect_attempts, detecting=True) - try: - print("Detecting chip type...", end="") - res = detect_port.check_command( - "get security info", ESPLoader.ESP_GET_SECURITY_INFO, b"" - ) - # 4b flags, 1b flash_crypt_cnt, 7*1b key_purposes, 4b chip_id - res = struct.unpack(", ) or a single -# argument. - - -def load_ram(esp, args): - image = LoadFirmwareImage(esp.CHIP_NAME, args.filename) - - print("RAM boot...") - for seg in image.segments: - size = len(seg.data) - print("Downloading %d bytes at %08x..." % (size, seg.addr), end=" ") - sys.stdout.flush() - esp.mem_begin( - size, div_roundup(size, esp.ESP_RAM_BLOCK), esp.ESP_RAM_BLOCK, seg.addr - ) - - seq = 0 - while len(seg.data) > 0: - esp.mem_block(seg.data[0 : esp.ESP_RAM_BLOCK], seq) - seg.data = seg.data[esp.ESP_RAM_BLOCK :] - seq += 1 - print("done!") - - print("All segments done, executing at %08x" % image.entrypoint) - esp.mem_finish(image.entrypoint) - - -def read_mem(esp, args): - print("0x%08x = 0x%08x" % (args.address, esp.read_reg(args.address))) - - -def write_mem(esp, args): - esp.write_reg(args.address, args.value, args.mask, 0) - print("Wrote %08x, mask %08x to %08x" % (args.value, args.mask, args.address)) - - -def dump_mem(esp, args): - with open(args.filename, "wb") as f: - for i in range(args.size // 4): - d = esp.read_reg(args.address + (i * 4)) - f.write(struct.pack(b"> 16 - args.flash_size = DETECTED_FLASH_SIZES.get(size_id) - if args.flash_size is None: - print( - "Warning: Could not auto-detect Flash size (FlashID=0x%x, SizeID=0x%x)," - " defaulting to 4MB" % (flash_id, size_id) - ) - args.flash_size = "4MB" - else: - print("Auto-detected Flash size:", args.flash_size) - - -def _update_image_flash_params(esp, address, args, image): - """ - Modify the flash mode & size bytes if this looks like an executable bootloader image - """ - if len(image) < 8: - return image # not long enough to be a bootloader image - - # unpack the (potential) image header - magic, _, flash_mode, flash_size_freq = struct.unpack("BBBB", image[:4]) - if address != esp.BOOTLOADER_FLASH_OFFSET: - return image # not flashing bootloader offset, so don't modify this - - if (args.flash_mode, args.flash_freq, args.flash_size) == ("keep",) * 3: - return image # all settings are 'keep', not modifying anything - - # easy check if this is an image: does it start with a magic byte? - if magic != esp.ESP_IMAGE_MAGIC: - print( - "Warning: Image file at 0x%x doesn't look like an image file, " - "so not changing any flash settings." % address - ) - return image - - # make sure this really is an image, and not just data that - # starts with esp.ESP_IMAGE_MAGIC (mostly a problem for encrypted - # images that happen to start with a magic byte - try: - test_image = esp.BOOTLOADER_IMAGE(io.BytesIO(image)) - test_image.verify() - except Exception: - print( - "Warning: Image file at 0x%x is not a valid %s image, " - "so not changing any flash settings." % (address, esp.CHIP_NAME) - ) - return image - - # After the 8-byte header comes the extended header for chips others than ESP8266. - # The 15th byte of the extended header indicates if the image is protected by - # a SHA256 checksum. In that case we should not modify the header because - # the checksum check would fail. - sha_implies_keep = args.chip != "esp8266" and image[8 + 15] == 1 - - def print_keep_warning(arg_to_keep, arg_used): - print( - "Warning: Image file at {addr} is protected with a hash checksum, " - "so not changing the flash {arg} setting. " - "Use the --flash_{arg}=keep option instead of --flash_{arg}={arg_orig} " - "in order to remove this warning, or use the --dont-append-digest option " - "for the elf2image command in order to generate an image file " - "without a hash checksum".format( - addr=hex(address), arg=arg_to_keep, arg_orig=arg_used - ) - ) - - if args.flash_mode != "keep": - new_flash_mode = FLASH_MODES[args.flash_mode] - if flash_mode != new_flash_mode and sha_implies_keep: - print_keep_warning("mode", args.flash_mode) - else: - flash_mode = new_flash_mode - - flash_freq = flash_size_freq & 0x0F - if args.flash_freq != "keep": - new_flash_freq = esp.parse_flash_freq_arg(args.flash_freq) - if flash_freq != new_flash_freq and sha_implies_keep: - print_keep_warning("frequency", args.flash_freq) - else: - flash_freq = new_flash_freq - - flash_size = flash_size_freq & 0xF0 - if args.flash_size != "keep": - new_flash_size = esp.parse_flash_size_arg(args.flash_size) - if flash_size != new_flash_size and sha_implies_keep: - print_keep_warning("size", args.flash_size) - else: - flash_size = new_flash_size - - flash_params = struct.pack(b"BB", flash_mode, flash_size + flash_freq) - if flash_params != image[2:4]: - print("Flash params set to 0x%04x" % struct.unpack(">H", flash_params)) - image = image[0:2] + flash_params + image[4:] - return image - - -def write_flash(esp, args): - # set args.compress based on default behaviour: - # -> if either --compress or --no-compress is set, honour that - # -> otherwise, set --compress unless --no-stub is set - if args.compress is None and not args.no_compress: - args.compress = not args.no_stub - - if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: - # Check if secure boot is active - if esp.get_secure_boot_enabled(): - for address, _ in args.addr_filename: - if address < 0x8000: - raise FatalError( - "Secure Boot detected, writing to flash regions < 0x8000 " - "is disabled to protect the bootloader. " - "Use --force to override, " - "please use with caution, otherwise it may brick your device!" - ) - # Check if chip_id and min_rev in image are valid for the target in use - for _, argfile in args.addr_filename: - try: - image = LoadFirmwareImage(esp.CHIP_NAME, argfile) - except (FatalError, struct.error, RuntimeError): - continue - if image.chip_id != esp.IMAGE_CHIP_ID: - raise FatalError( - f"{argfile.name} is not an {esp.CHIP_NAME} image. " - "Use --force to flash anyway." - ) - - # this logic below decides which min_rev to use, min_rev or min/max_rev_full - if image.max_rev_full == 0: # image does not have max/min_rev_full fields - use_rev_full_fields = False - elif image.max_rev_full == 65535: # image has default value of max_rev_full - if ( - image.min_rev_full == 0 and image.min_rev != 0 - ): # min_rev_full is not set, min_rev is used - use_rev_full_fields = False - use_rev_full_fields = True - else: # max_rev_full set to a version - use_rev_full_fields = True - - if use_rev_full_fields: - rev = esp.get_chip_revision() - if rev < image.min_rev_full or rev > image.max_rev_full: - error_str = f"{argfile.name} requires chip revision in range " - error_str += ( - f"[v{image.min_rev_full // 100}.{image.min_rev_full % 100} - " - ) - if image.max_rev_full == 65535: - error_str += "max rev not set] " - else: - error_str += ( - f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}] " - ) - error_str += f"(this chip is revision v{rev // 100}.{rev % 100})" - raise FatalError(f"{error_str}. Use --force to flash anyway.") - else: - # In IDF, image.min_rev is set based on Kconfig option. - # For C3 chip, image.min_rev is the Minor revision - # while for the rest chips it is the Major revision. - if esp.CHIP_NAME == "ESP32-C3": - rev = esp.get_minor_chip_version() - else: - rev = esp.get_major_chip_version() - if rev < image.min_rev: - raise FatalError( - f"{argfile.name} requires chip revision " - f"{image.min_rev} or higher (this chip is revision {rev}). " - "Use --force to flash anyway." - ) - - # In case we have encrypted files to write, - # we first do few sanity checks before actual flash - if args.encrypt or args.encrypt_files is not None: - do_write = True - - if not esp.secure_download_mode: - if esp.get_encrypted_download_disabled(): - raise FatalError( - "This chip has encrypt functionality " - "in UART download mode disabled. " - "This is the Flash Encryption configuration for Production mode " - "instead of Development mode." - ) - - crypt_cfg_efuse = esp.get_flash_crypt_config() - - if crypt_cfg_efuse is not None and crypt_cfg_efuse != 0xF: - print("Unexpected FLASH_CRYPT_CONFIG value: 0x%x" % (crypt_cfg_efuse)) - do_write = False - - enc_key_valid = esp.is_flash_encryption_key_valid() - - if not enc_key_valid: - print("Flash encryption key is not programmed") - do_write = False - - # Determine which files list contain the ones to encrypt - files_to_encrypt = args.addr_filename if args.encrypt else args.encrypt_files - - for address, argfile in files_to_encrypt: - if address % esp.FLASH_ENCRYPTED_WRITE_ALIGN: - print( - "File %s address 0x%x is not %d byte aligned, can't flash encrypted" - % (argfile.name, address, esp.FLASH_ENCRYPTED_WRITE_ALIGN) - ) - do_write = False - - if not do_write and not args.ignore_flash_encryption_efuse_setting: - raise FatalError( - "Can't perform encrypted flash write, " - "consult Flash Encryption documentation for more information" - ) - - # verify file sizes fit in flash - if args.flash_size != "keep": # TODO: check this even with 'keep' - flash_end = flash_size_bytes(args.flash_size) - for address, argfile in args.addr_filename: - argfile.seek(0, os.SEEK_END) - if address + argfile.tell() > flash_end: - raise FatalError( - "File %s (length %d) at offset %d " - "will not fit in %d bytes of flash. " - "Use --flash_size argument, or change flashing address." - % (argfile.name, argfile.tell(), address, flash_end) - ) - argfile.seek(0) - - if args.erase_all: - erase_flash(esp, args) - else: - for address, argfile in args.addr_filename: - argfile.seek(0, os.SEEK_END) - write_end = address + argfile.tell() - argfile.seek(0) - bytes_over = address % esp.FLASH_SECTOR_SIZE - if bytes_over != 0: - print( - "WARNING: Flash address {:#010x} is not aligned " - "to a {:#x} byte flash sector. " - "{:#x} bytes before this address will be erased.".format( - address, esp.FLASH_SECTOR_SIZE, bytes_over - ) - ) - # Print the address range of to-be-erased flash memory region - print( - "Flash will be erased from {:#010x} to {:#010x}...".format( - address - bytes_over, - div_roundup(write_end, esp.FLASH_SECTOR_SIZE) - * esp.FLASH_SECTOR_SIZE - - 1, - ) - ) - - """ Create a list describing all the files we have to flash. - Each entry holds an "encrypt" flag marking whether the file needs encryption or not. - This list needs to be sorted. - - First, append to each entry of our addr_filename list the flag args.encrypt - E.g., if addr_filename is [(0x1000, "partition.bin"), (0x8000, "bootloader")], - all_files will be [ - (0x1000, "partition.bin", args.encrypt), - (0x8000, "bootloader", args.encrypt) - ], - where, of course, args.encrypt is either True or False - """ - all_files = [ - (offs, filename, args.encrypt) for (offs, filename) in args.addr_filename - ] - - """ - Now do the same with encrypt_files list, if defined. - In this case, the flag is True - """ - if args.encrypt_files is not None: - encrypted_files_flag = [ - (offs, filename, True) for (offs, filename) in args.encrypt_files - ] - - # Concatenate both lists and sort them. - # As both list are already sorted, we could simply do a merge instead, - # but for the sake of simplicity and because the lists are very small, - # let's use sorted. - all_files = sorted(all_files + encrypted_files_flag, key=lambda x: x[0]) - - for address, argfile, encrypted in all_files: - compress = args.compress - - # Check whether we can compress the current file before flashing - if compress and encrypted: - print("\nWARNING: - compress and encrypt options are mutually exclusive ") - print("Will flash %s uncompressed" % argfile.name) - compress = False - - if args.no_stub: - print("Erasing flash...") - image = pad_to( - argfile.read(), esp.FLASH_ENCRYPTED_WRITE_ALIGN if encrypted else 4 - ) - if len(image) == 0: - print("WARNING: File %s is empty" % argfile.name) - continue - image = _update_image_flash_params(esp, address, args, image) - calcmd5 = hashlib.md5(image).hexdigest() - uncsize = len(image) - if compress: - uncimage = image - image = zlib.compress(uncimage, 9) - # Decompress the compressed binary a block at a time, - # to dynamically calculate the timeout based on the real write size - decompress = zlib.decompressobj() - blocks = esp.flash_defl_begin(uncsize, len(image), address) - else: - blocks = esp.flash_begin(uncsize, address, begin_rom_encrypted=encrypted) - argfile.seek(0) # in case we need it again - seq = 0 - bytes_sent = 0 # bytes sent on wire - bytes_written = 0 # bytes written to flash - t = time.time() - - timeout = DEFAULT_TIMEOUT - - while len(image) > 0: - print_overwrite( - "Writing at 0x%08x... (%d %%)" - % (address + bytes_written, 100 * (seq + 1) // blocks) - ) - sys.stdout.flush() - block = image[0 : esp.FLASH_WRITE_SIZE] - if compress: - # feeding each compressed block into the decompressor lets us - # see block-by-block how much will be written - block_uncompressed = len(decompress.decompress(block)) - bytes_written += block_uncompressed - block_timeout = max( - DEFAULT_TIMEOUT, - timeout_per_mb(ERASE_WRITE_TIMEOUT_PER_MB, block_uncompressed), - ) - if not esp.IS_STUB: - timeout = ( - block_timeout # ROM code writes block to flash before ACKing - ) - esp.flash_defl_block(block, seq, timeout=timeout) - if esp.IS_STUB: - # Stub ACKs when block is received, - # then writes to flash while receiving the block after it - timeout = block_timeout - else: - # Pad the last block - block = block + b"\xff" * (esp.FLASH_WRITE_SIZE - len(block)) - if encrypted: - esp.flash_encrypt_block(block, seq) - else: - esp.flash_block(block, seq) - bytes_written += len(block) - bytes_sent += len(block) - image = image[esp.FLASH_WRITE_SIZE :] - seq += 1 - - if esp.IS_STUB: - # Stub only writes each block to flash after 'ack'ing the receive, - # so do a final dummy operation which will not be 'ack'ed - # until the last block has actually been written out to flash - esp.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR, timeout=timeout) - - t = time.time() - t - speed_msg = "" - if compress: - if t > 0.0: - speed_msg = " (effective %.1f kbit/s)" % (uncsize / t * 8 / 1000) - print_overwrite( - "Wrote %d bytes (%d compressed) at 0x%08x in %.1f seconds%s..." - % (uncsize, bytes_sent, address, t, speed_msg), - last_line=True, - ) - else: - if t > 0.0: - speed_msg = " (%.1f kbit/s)" % (bytes_written / t * 8 / 1000) - print_overwrite( - "Wrote %d bytes at 0x%08x in %.1f seconds%s..." - % (bytes_written, address, t, speed_msg), - last_line=True, - ) - - if not encrypted and not esp.secure_download_mode: - try: - res = esp.flash_md5sum(address, uncsize) - if res != calcmd5: - print("File md5: %s" % calcmd5) - print("Flash md5: %s" % res) - print( - "MD5 of 0xFF is %s" - % (hashlib.md5(b"\xFF" * uncsize).hexdigest()) - ) - raise FatalError("MD5 of file does not match data in flash!") - else: - print("Hash of data verified.") - except NotImplementedInROMError: - pass - - print("\nLeaving...") - - if esp.IS_STUB: - # skip sending flash_finish to ROM loader here, - # as it causes the loader to exit and run user code - esp.flash_begin(0, 0) - - # Get the "encrypted" flag for the last file flashed - # Note: all_files list contains triplets like: - # (address: Integer, filename: String, encrypted: Boolean) - last_file_encrypted = all_files[-1][2] - - # Check whether the last file flashed was compressed or not - if args.compress and not last_file_encrypted: - esp.flash_defl_finish(False) - else: - esp.flash_finish(False) - - if args.verify: - print("Verifying just-written flash...") - print( - "(This option is deprecated, " - "flash contents are now always read back after flashing.)" - ) - # If some encrypted files have been flashed, - # print a warning saying that we won't check them - if args.encrypt or args.encrypt_files is not None: - print("WARNING: - cannot verify encrypted files, they will be ignored") - # Call verify_flash function only if there is at least - # one non-encrypted file flashed - if not args.encrypt: - verify_flash(esp, args) - - -def image_info(args): - def v2(): - def get_key_from_value(dict, val): - """Get key from value in dictionary""" - for key, value in dict.items(): - if value == val: - return key - return None - - print() - title = "{} image header".format(args.chip.upper()) - print(title) - print("=" * len(title)) - print("Image version: {}".format(image.version)) - print( - "Entry point: {:#8x}".format(image.entrypoint) - if image.entrypoint != 0 - else "Entry point not set" - ) - - print("Segments: {}".format(len(image.segments))) - - # Flash size - flash_s_bits = image.flash_size_freq & 0xF0 # high four bits - flash_s = get_key_from_value(image.ROM_LOADER.FLASH_SIZES, flash_s_bits) - print( - "Flash size: {}".format(flash_s) - if flash_s is not None - else "WARNING: Invalid flash size ({:#02x})".format(flash_s_bits) - ) - - # Flash frequency - flash_fr_bits = image.flash_size_freq & 0x0F # low four bits - flash_fr = get_key_from_value(image.ROM_LOADER.FLASH_FREQUENCY, flash_fr_bits) - print( - "Flash freq: {}".format(flash_fr) - if flash_fr is not None - else "WARNING: Invalid flash frequency ({:#02x})".format(flash_fr_bits) - ) - - # Flash mode - flash_mode = get_key_from_value(FLASH_MODES, image.flash_mode) - print( - "Flash mode: {}".format(flash_mode.upper()) - if flash_mode is not None - else "WARNING: Invalid flash mode ({})".format(image.flash_mode) - ) - - # Extended header (ESP32 and later only) - if args.chip != "esp8266": - print() - title = "{} extended image header".format(args.chip.upper()) - print(title) - print("=" * len(title)) - print("WP pin: {:#02x}".format(image.wp_pin)) - print( - "Flash pins drive settings: " - "clk_drv: {:#02x}, q_drv: {:#02x}, d_drv: {:#02x}, " - "cs0_drv: {:#02x}, hd_drv: {:#02x}, wp_drv: {:#02x}".format( - image.clk_drv, - image.q_drv, - image.d_drv, - image.cs_drv, - image.hd_drv, - image.wp_drv, - ) - ) - print("Chip ID: {}".format(image.chip_id)) - print( - "Minimal chip revision: " - f"v{image.min_rev_full // 100}.{image.min_rev_full % 100}, " - f"(legacy min_rev = {image.min_rev})" - ) - print( - "Maximal chip revision: " - f"v{image.max_rev_full // 100}.{image.max_rev_full % 100}" - ) - print() - - # Segments overview - title = "Segments information" - print(title) - print("=" * len(title)) - headers_str = "{:>7} {:>7} {:>10} {:>10} {:10}" - print( - headers_str.format( - "Segment", "Length", "Load addr", "File offs", "Memory types" - ) - ) - print( - "{} {} {} {} {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12) - ) - format_str = "{:7} {:#07x} {:#010x} {:#010x} {}" - app_desc = None - for idx, seg in enumerate(image.segments, start=1): - segs = seg.get_memory_type(image) - seg_name = ", ".join(segs) - if "DROM" in segs: # The DROM segment starts with the esp_app_desc_t struct - app_desc = seg.data[:256] - print( - format_str.format(idx, len(seg.data), seg.addr, seg.file_offs, seg_name) - ) - print() - - # Footer - title = f"{args.chip.upper()} image footer" - print(title) - print("=" * len(title)) - calc_checksum = image.calculate_checksum() - print( - "Checksum: {:#02x} ({})".format( - image.checksum, - "valid" - if image.checksum == calc_checksum - else "invalid - calculated {:02x}".format(calc_checksum), - ) - ) - try: - digest_msg = "Not appended" - if image.append_digest: - is_valid = image.stored_digest == image.calc_digest - digest_msg = "{} ({})".format( - hexify(image.calc_digest, uppercase=False), - "valid" if is_valid else "invalid", - ) - print("Validation hash: {}".format(digest_msg)) - except AttributeError: - pass # ESP8266 image has no append_digest field - - if app_desc: - APP_DESC_STRUCT_FMT = " 1 else "")) - - image.verify() - - if args.output is None: - args.output = image.default_output_name(args.input) - image.save(args.output) - - print("Successfully created {} image.".format(args.chip)) - - -def read_mac(esp, args): - mac = esp.read_mac() - - def print_mac(label, mac): - print("%s: %s" % (label, ":".join(map(lambda x: "%02x" % x, mac)))) - - print_mac("MAC", mac) - - -def chip_id(esp, args): - try: - chipid = esp.chip_id() - print("Chip ID: 0x%08x" % chipid) - except NotSupportedError: - print("Warning: %s has no Chip ID. Reading MAC instead." % esp.CHIP_NAME) - read_mac(esp, args) - - -def erase_flash(esp, args): - if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: - if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled(): - raise FatalError( - "Active security features detected, " - "erasing flash is disabled as a safety measure. " - "Use --force to override, " - "please use with caution, otherwise it may brick your device!" - ) - print("Erasing flash (this may take a while)...") - t = time.time() - esp.erase_flash() - print("Chip erase completed successfully in %.1fs" % (time.time() - t)) - - -def erase_region(esp, args): - if not args.force and esp.CHIP_NAME != "ESP8266" and not esp.secure_download_mode: - if esp.get_flash_encryption_enabled() or esp.get_secure_boot_enabled(): - raise FatalError( - "Active security features detected, " - "erasing flash is disabled as a safety measure. " - "Use --force to override, " - "please use with caution, otherwise it may brick your device!" - ) - print("Erasing region (may be slow depending on size)...") - t = time.time() - esp.erase_region(args.address, args.size) - print("Erase completed successfully in %.1f seconds." % (time.time() - t)) - - -def run(esp, args): - esp.run() - - -def flash_id(esp, args): - flash_id = esp.flash_id() - print("Manufacturer: %02x" % (flash_id & 0xFF)) - flid_lowbyte = (flash_id >> 16) & 0xFF - print("Device: %02x%02x" % ((flash_id >> 8) & 0xFF, flid_lowbyte)) - print( - "Detected flash size: %s" % (DETECTED_FLASH_SIZES.get(flid_lowbyte, "Unknown")) - ) - - -def read_flash(esp, args): - if args.no_progress: - flash_progress = None - else: - - def flash_progress(progress, length): - msg = "%d (%d %%)" % (progress, progress * 100.0 / length) - padding = "\b" * len(msg) - if progress == length: - padding = "\n" - sys.stdout.write(msg + padding) - sys.stdout.flush() - - t = time.time() - data = esp.read_flash(args.address, args.size, flash_progress) - t = time.time() - t - speed_msg = " ({:.1f} kbit/s)".format(len(data) / t * 8 / 1000) if t > 0.0 else "" - print_overwrite( - "Read {:d} bytes at {:#010x} in {:.1f} seconds{}...".format( - len(data), args.address, t, speed_msg - ), - last_line=True, - ) - with open(args.filename, "wb") as f: - f.write(data) - - -def verify_flash(esp, args): - differences = False - - for address, argfile in args.addr_filename: - image = pad_to(argfile.read(), 4) - argfile.seek(0) # rewind in case we need it again - - image = _update_image_flash_params(esp, address, args, image) - - image_size = len(image) - print( - "Verifying 0x%x (%d) bytes @ 0x%08x in flash against %s..." - % (image_size, image_size, address, argfile.name) - ) - # Try digest first, only read if there are differences. - digest = esp.flash_md5sum(address, image_size) - expected_digest = hashlib.md5(image).hexdigest() - if digest == expected_digest: - print("-- verify OK (digest matched)") - continue - else: - differences = True - if getattr(args, "diff", "no") != "yes": - print("-- verify FAILED (digest mismatch)") - continue - - flash = esp.read_flash(address, image_size) - assert flash != image - diff = [i for i in range(image_size) if flash[i] != image[i]] - print( - "-- verify FAILED: %d differences, first @ 0x%08x" - % (len(diff), address + diff[0]) - ) - for d in diff: - flash_byte = flash[d] - image_byte = image[d] - print(" %08x %02x %02x" % (address + d, flash_byte, image_byte)) - if differences: - raise FatalError("Verify failed.") - - -def read_flash_status(esp, args): - print("Status value: 0x%04x" % esp.read_status(args.bytes)) - - -def write_flash_status(esp, args): - fmt = "0x%%0%dx" % (args.bytes * 2) - args.value = args.value & ((1 << (args.bytes * 8)) - 1) - print(("Initial flash status: " + fmt) % esp.read_status(args.bytes)) - print(("Setting flash status: " + fmt) % args.value) - esp.write_status(args.value, args.bytes, args.non_volatile) - print(("After flash status: " + fmt) % esp.read_status(args.bytes)) - - -def get_security_info(esp, args): - si = esp.get_security_info() - # TODO: better display and tests - print("Flags: {:#010x} ({})".format(si["flags"], bin(si["flags"]))) - print("Flash_Crypt_Cnt: {:#x}".format(si["flash_crypt_cnt"])) - print("Key_Purposes: {}".format(si["key_purposes"])) - if si["chip_id"] is not None and si["api_version"] is not None: - print("Chip_ID: {}".format(si["chip_id"])) - print("Api_Version: {}".format(si["api_version"])) - - -def merge_bin(args): - try: - chip_class = CHIP_DEFS[args.chip] - except KeyError: - msg = ( - "Please specify the chip argument" - if args.chip == "auto" - else "Invalid chip choice: '{}'".format(args.chip) - ) - msg = msg + " (choose from {})".format(", ".join(CHIP_LIST)) - raise FatalError(msg) - - # sort the files by offset. - # The AddrFilenamePairAction has already checked for overlap - input_files = sorted(args.addr_filename, key=lambda x: x[0]) - if not input_files: - raise FatalError("No input files specified") - first_addr = input_files[0][0] - if first_addr < args.target_offset: - raise FatalError( - "Output file target offset is 0x%x. Input file offset 0x%x is before this." - % (args.target_offset, first_addr) - ) - - if args.format != "raw": - raise FatalError( - "This version of esptool only supports the 'raw' output format" - ) - - with open(args.output, "wb") as of: - - def pad_to(flash_offs): - # account for output file offset if there is any - of.write(b"\xFF" * (flash_offs - args.target_offset - of.tell())) - - for addr, argfile in input_files: - pad_to(addr) - image = argfile.read() - image = _update_image_flash_params(chip_class, addr, args, image) - of.write(image) - if args.fill_flash_size: - pad_to(flash_size_bytes(args.fill_flash_size)) - print( - "Wrote 0x%x bytes to file %s, ready to flash to offset 0x%x" - % (of.tell(), args.output, args.target_offset) - ) - - -def version(args): - from . import __version__ - - print(__version__) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py deleted file mode 100644 index d32312386..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/loader.py +++ /dev/null @@ -1,1573 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import base64 -import hashlib -import itertools -import json -import os -import re -import string -import struct -import sys -import time - -from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError -from .util import byte, hexify, mask_to_shift, pad_to - -try: - import serial -except ImportError: - print( - "Pyserial is not installed for %s. " - "Check the README for installation instructions." % (sys.executable) - ) - raise - -# check 'serial' is 'pyserial' and not 'serial' -# ref. https://github.com/espressif/esptool/issues/269 -try: - if "serialization" in serial.__doc__ and "deserialization" in serial.__doc__: - raise ImportError( - "esptool.py depends on pyserial, but there is a conflict with a currently " - "installed package named 'serial'.\n" - "You may work around this by 'pip uninstall serial; pip install pyserial' " - "but this may break other installed Python software " - "that depends on 'serial'.\n" - "There is no good fix for this right now, " - "apart from configuring virtualenvs. " - "See https://github.com/espressif/esptool/issues/269#issuecomment-385298196" - " for discussion of the underlying issue(s)." - ) -except TypeError: - pass # __doc__ returns None for pyserial - -try: - import serial.tools.list_ports as list_ports -except ImportError: - print( - "The installed version (%s) of pyserial appears to be too old for esptool.py " - "(Python interpreter %s). Check the README for installation instructions." - % (sys.VERSION, sys.executable) - ) - raise -except Exception: - if sys.platform == "darwin": - # swallow the exception, this is a known issue in pyserial+macOS Big Sur preview - # ref https://github.com/espressif/esptool/issues/540 - list_ports = None - else: - raise - - -DEFAULT_TIMEOUT = 3 # timeout for most flash operations -START_FLASH_TIMEOUT = 20 # timeout for starting flash (may perform erase) -CHIP_ERASE_TIMEOUT = 120 # timeout for full chip erase -MAX_TIMEOUT = CHIP_ERASE_TIMEOUT * 2 # longest any command can run -SYNC_TIMEOUT = 0.1 # timeout for syncing with bootloader -MD5_TIMEOUT_PER_MB = 8 # timeout (per megabyte) for calculating md5sum -ERASE_REGION_TIMEOUT_PER_MB = 30 # timeout (per megabyte) for erasing a region -ERASE_WRITE_TIMEOUT_PER_MB = 40 # timeout (per megabyte) for erasing and writing data -MEM_END_ROM_TIMEOUT = 0.05 # short timeout for ESP_MEM_END, as it may never respond -DEFAULT_SERIAL_WRITE_TIMEOUT = 10 # timeout for serial port write -DEFAULT_CONNECT_ATTEMPTS = 7 # default number of times to try connection -WRITE_BLOCK_ATTEMPTS = 3 # number of times to try writing a data block - -STUBS_DIR = os.path.join(os.path.dirname(__file__), "./targets/stub_flasher/") - - -def get_stub_json_path(chip_name): - chip_name = re.sub(r"[-()]", "", chip_name.lower()) - chip_name = chip_name.replace("esp", "") - return STUBS_DIR + "stub_flasher_" + chip_name + ".json" - - -def timeout_per_mb(seconds_per_mb, size_bytes): - """Scales timeouts which are size-specific""" - result = seconds_per_mb * (size_bytes / 1e6) - if result < DEFAULT_TIMEOUT: - return DEFAULT_TIMEOUT - return result - - -def check_supported_function(func, check_func): - """ - Decorator implementation that wraps a check around an ESPLoader - bootloader function to check if it's supported. - - This is used to capture the multidimensional differences in - functionality between the ESP8266 & ESP32 (and later chips) ROM loaders, and the - software stub that runs on these. Not possible to do this cleanly - via inheritance alone. - """ - - def inner(*args, **kwargs): - obj = args[0] - if check_func(obj): - return func(*args, **kwargs) - else: - raise NotImplementedInROMError(obj, func) - - return inner - - -def stub_function_only(func): - """Attribute for a function only supported in the software stub loader""" - return check_supported_function(func, lambda o: o.IS_STUB) - - -def stub_and_esp32_function_only(func): - """Attribute for a function only supported by stubs or ESP32 and later chips ROM""" - return check_supported_function( - func, lambda o: o.IS_STUB or o.CHIP_NAME not in ["ESP8266"] - ) - - -def esp32s3_or_newer_function_only(func): - """Attribute for a function only supported by ESP32S3 and later chips ROM""" - return check_supported_function( - func, lambda o: o.CHIP_NAME not in ["ESP8266", "ESP32", "ESP32-S2"] - ) - - -class StubFlasher: - def __init__(self, json_path): - with open(json_path) as json_file: - stub = json.load(json_file) - - self.text = base64.b64decode(stub["text"]) - self.text_start = stub["text_start"] - self.entry = stub["entry"] - - try: - self.data = base64.b64decode(stub["data"]) - self.data_start = stub["data_start"] - except KeyError: - self.data = None - self.data_start = None - - -class ESPLoader(object): - """Base class providing access to ESP ROM & software stub bootloaders. - Subclasses provide ESP8266 & ESP32 Family specific functionality. - - Don't instantiate this base class directly, either instantiate a subclass or - call cmds.detect_chip() which will interrogate the chip and return the - appropriate subclass instance. - - """ - - CHIP_NAME = "Espressif device" - IS_STUB = False - - FPGA_SLOW_BOOT = False - - DEFAULT_PORT = "/dev/ttyUSB0" - - USES_RFC2217 = False - - # Commands supported by ESP8266 ROM bootloader - ESP_FLASH_BEGIN = 0x02 - ESP_FLASH_DATA = 0x03 - ESP_FLASH_END = 0x04 - ESP_MEM_BEGIN = 0x05 - ESP_MEM_END = 0x06 - ESP_MEM_DATA = 0x07 - ESP_SYNC = 0x08 - ESP_WRITE_REG = 0x09 - ESP_READ_REG = 0x0A - - # Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/ stub) - ESP_SPI_SET_PARAMS = 0x0B - ESP_SPI_ATTACH = 0x0D - ESP_READ_FLASH_SLOW = 0x0E # ROM only, much slower than the stub flash read - ESP_CHANGE_BAUDRATE = 0x0F - ESP_FLASH_DEFL_BEGIN = 0x10 - ESP_FLASH_DEFL_DATA = 0x11 - ESP_FLASH_DEFL_END = 0x12 - ESP_SPI_FLASH_MD5 = 0x13 - - # Commands supported by ESP32-S2 and later chips ROM bootloader only - ESP_GET_SECURITY_INFO = 0x14 - - # Some commands supported by stub only - ESP_ERASE_FLASH = 0xD0 - ESP_ERASE_REGION = 0xD1 - ESP_READ_FLASH = 0xD2 - ESP_RUN_USER_CODE = 0xD3 - - # Flash encryption encrypted data command - ESP_FLASH_ENCRYPT_DATA = 0xD4 - - # Response code(s) sent by ROM - ROM_INVALID_RECV_MSG = 0x05 # response if an invalid message is received - - # Maximum block sized for RAM and Flash writes, respectively. - ESP_RAM_BLOCK = 0x1800 - - FLASH_WRITE_SIZE = 0x400 - - # Default baudrate. The ROM auto-bauds, so we can use more or less whatever we want. - ESP_ROM_BAUD = 115200 - - # First byte of the application image - ESP_IMAGE_MAGIC = 0xE9 - - # Initial state for the checksum routine - ESP_CHECKSUM_MAGIC = 0xEF - - # Flash sector size, minimum unit of erase. - FLASH_SECTOR_SIZE = 0x1000 - - UART_DATE_REG_ADDR = 0x60000078 - - # This ROM address has a different value on each chip model - CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000 - - UART_CLKDIV_MASK = 0xFFFFF - - # Memory addresses - IROM_MAP_START = 0x40200000 - IROM_MAP_END = 0x40300000 - - # The number of bytes in the UART response that signify command status - STATUS_BYTES_LENGTH = 2 - - # Bootloader flashing offset - BOOTLOADER_FLASH_OFFSET = 0x0 - - # ROM supports an encrypted flashing mode - SUPPORTS_ENCRYPTED_FLASH = False - - # Response to ESP_SYNC might indicate that flasher stub is running - # instead of the ROM bootloader - sync_stub_detected = False - - # Device PIDs - USB_JTAG_SERIAL_PID = 0x1001 - - # Chip IDs that are no longer supported by esptool - UNSUPPORTED_CHIPS = {6: "ESP32-S3(beta 3)"} - - def __init__(self, port=DEFAULT_PORT, baud=ESP_ROM_BAUD, trace_enabled=False): - """Base constructor for ESPLoader bootloader interaction - - Don't call this constructor, either instantiate a specific - ROM class directly, or use cmds.detect_chip(). - - This base class has all of the instance methods for bootloader - functionality supported across various chips & stub - loaders. Subclasses replace the functions they don't support - with ones which throw NotImplementedInROMError(). - - """ - # True if esptool detects the ROM is in Secure Download Mode - self.secure_download_mode = False - # True if esptool detects conditions which require the stub to be disabled - self.stub_is_disabled = False - - if isinstance(port, str): - try: - self._port = serial.serial_for_url(port) - except serial.serialutil.SerialException: - raise FatalError(f"Could not open {port}, the port doesn't exist") - else: - self._port = port - self._slip_reader = slip_reader(self._port, self.trace) - # setting baud rate in a separate step is a workaround for - # CH341 driver on some Linux versions (this opens at 9600 then - # sets), shouldn't matter for other platforms/drivers. See - # https://github.com/espressif/esptool/issues/44#issuecomment-107094446 - self._set_port_baudrate(baud) - self._trace_enabled = trace_enabled - # set write timeout, to prevent esptool blocked at write forever. - try: - self._port.write_timeout = DEFAULT_SERIAL_WRITE_TIMEOUT - except NotImplementedError: - # no write timeout for RFC2217 ports - # need to set the property back to None or it will continue to fail - self._port.write_timeout = None - - @property - def serial_port(self): - return self._port.port - - def _set_port_baudrate(self, baud): - try: - self._port.baudrate = baud - except IOError: - raise FatalError( - "Failed to set baud rate %d. The driver may not support this rate." - % baud - ) - - def read(self): - """Read a SLIP packet from the serial port""" - return next(self._slip_reader) - - def write(self, packet): - """Write bytes to the serial port while performing SLIP escaping""" - buf = ( - b"\xc0" - + (packet.replace(b"\xdb", b"\xdb\xdd").replace(b"\xc0", b"\xdb\xdc")) - + b"\xc0" - ) - self.trace("Write %d bytes: %s", len(buf), HexFormatter(buf)) - self._port.write(buf) - - def trace(self, message, *format_args): - if self._trace_enabled: - now = time.time() - try: - - delta = now - self._last_trace - except AttributeError: - delta = 0.0 - self._last_trace = now - prefix = "TRACE +%.3f " % delta - print(prefix + (message % format_args)) - - @staticmethod - def checksum(data, state=ESP_CHECKSUM_MAGIC): - """Calculate checksum of a blob, as it is defined by the ROM""" - for b in data: - state ^= b - - return state - - def command( - self, - op=None, - data=b"", - chk=0, - wait_response=True, - timeout=DEFAULT_TIMEOUT, - ): - """Send a request and read the response""" - saved_timeout = self._port.timeout - new_timeout = min(timeout, MAX_TIMEOUT) - if new_timeout != saved_timeout: - self._port.timeout = new_timeout - - try: - if op is not None: - self.trace( - "command op=0x%02x data len=%s wait_response=%d " - "timeout=%.3f data=%s", - op, - len(data), - 1 if wait_response else 0, - timeout, - HexFormatter(data), - ) - pkt = struct.pack(b" self.STATUS_BYTES_LENGTH: - return data[: -self.STATUS_BYTES_LENGTH] - else: - # otherwise, just return the 'val' field which comes from the reply header - # (this is used by read_reg) - return val - - def flush_input(self): - self._port.flushInput() - self._slip_reader = slip_reader(self._port, self.trace) - - def sync(self): - val, _ = self.command( - self.ESP_SYNC, b"\x07\x07\x12\x20" + 32 * b"\x55", timeout=SYNC_TIMEOUT - ) - - # ROM bootloaders send some non-zero "val" response. The flasher stub sends 0. - # If we receive 0 then it probably indicates that the chip wasn't or couldn't be - # reseted properly and esptool is talking to the flasher stub. - self.sync_stub_detected = val == 0 - - for _ in range(7): - val, _ = self.command() - self.sync_stub_detected &= val == 0 - - def _setDTR(self, state): - self._port.setDTR(state) - - def _setRTS(self, state): - self._port.setRTS(state) - # Work-around for adapters on Windows using the usbser.sys driver: - # generate a dummy change to DTR so that the set-control-line-state - # request is sent with the updated RTS state and the same DTR state - self._port.setDTR(self._port.dtr) - - def _get_pid(self): - if list_ports is None: - print( - "\nListing all serial ports is currently not available. " - "Can't get device PID." - ) - return - active_port = self._port.port - - # Pyserial only identifies regular ports, URL handlers are not supported - if not active_port.lower().startswith(("com", "/dev/")): - print( - "\nDevice PID identification is only supported on " - "COM and /dev/ serial ports." - ) - return - # Return the real path if the active port is a symlink - if active_port.startswith("/dev/") and os.path.islink(active_port): - active_port = os.path.realpath(active_port) - - # The "cu" (call-up) device has to be used for outgoing communication on MacOS - if sys.platform == "darwin" and "tty" in active_port: - active_port = [active_port, active_port.replace("tty", "cu")] - ports = list_ports.comports() - for p in ports: - if p.device in active_port: - return p.pid - print( - "\nFailed to get PID of a device on {}, " - "using standard reset sequence.".format(active_port) - ) - - def bootloader_reset(self, usb_jtag_serial=False, extra_delay=False): - """ - Issue a reset-to-bootloader, with USB-JTAG-Serial custom reset sequence option - """ - # RTS = either CH_PD/EN or nRESET (both active low = chip in reset) - # DTR = GPIO0 (active low = boot to flasher) - # - # DTR & RTS are active low signals, - # ie True = pin @ 0V, False = pin @ VCC. - if usb_jtag_serial: - # Custom reset sequence, which is required when the device - # is connecting via its USB-JTAG-Serial peripheral - self._setRTS(False) - self._setDTR(False) # Idle - time.sleep(0.1) - self._setDTR(True) # Set IO0 - self._setRTS(False) - time.sleep(0.1) - self._setRTS( - True - ) # Reset. Note dtr/rts calls inverted to go through (1,1) instead of (0,0) - self._setDTR(False) - self._setRTS( - True - ) # Extra RTS set for RTS as Windows only propagates DTR on RTS setting - time.sleep(0.1) - self._setDTR(False) - self._setRTS(False) - else: - # This fpga delay is for Espressif internal use - fpga_delay = ( - True - if self.FPGA_SLOW_BOOT - and os.environ.get("ESPTOOL_ENV_FPGA", "").strip() == "1" - else False - ) - delay = ( - 7 if fpga_delay else 0.5 if extra_delay else 0.05 - ) # 0.5 needed for ESP32 rev0 and rev1 - - self._setDTR(False) # IO0=HIGH - self._setRTS(True) # EN=LOW, chip in reset - time.sleep(0.1) - self._setDTR(True) # IO0=LOW - self._setRTS(False) # EN=HIGH, chip out of reset - time.sleep(delay) - self._setDTR(False) # IO0=HIGH, done - - def _connect_attempt( - self, mode="default_reset", usb_jtag_serial=False, extra_delay=False - ): - """A single connection attempt""" - last_error = None - boot_log_detected = False - download_mode = False - - # If we're doing no_sync, we're likely communicating as a pass through - # with an intermediate device to the ESP32 - if mode == "no_reset_no_sync": - return last_error - - if mode != "no_reset": - if not self.USES_RFC2217: # Might block on rfc2217 ports - # Empty serial buffer to isolate boot log - self._port.reset_input_buffer() - self.bootloader_reset(usb_jtag_serial, extra_delay) - - # Detect the ROM boot log and check actual boot mode (ESP32 and later only) - waiting = self._port.inWaiting() - read_bytes = self._port.read(waiting) - data = re.search( - b"boot:(0x[0-9a-fA-F]+)(.*waiting for download)?", read_bytes, re.DOTALL - ) - if data is not None: - boot_log_detected = True - boot_mode = data.group(1) - download_mode = data.group(2) is not None - - for _ in range(5): - try: - self.flush_input() - self._port.flushOutput() - self.sync() - return None - except FatalError as e: - print(".", end="") - sys.stdout.flush() - time.sleep(0.05) - last_error = e - - if boot_log_detected: - last_error = FatalError( - "Wrong boot mode detected ({})! " - "The chip needs to be in download mode.".format( - boot_mode.decode("utf-8") - ) - ) - if download_mode: - last_error = FatalError( - "Download mode successfully detected, but getting no sync reply: " - "The serial TX path seems to be down." - ) - return last_error - - def get_memory_region(self, name): - """ - Returns a tuple of (start, end) for the memory map entry with the given name, - or None if it doesn't exist - """ - try: - return [(start, end) for (start, end, n) in self.MEMORY_MAP if n == name][0] - except IndexError: - return None - - def connect( - self, - mode="default_reset", - attempts=DEFAULT_CONNECT_ATTEMPTS, - detecting=False, - warnings=True, - ): - """Try connecting repeatedly until successful, or giving up""" - if warnings and mode in ["no_reset", "no_reset_no_sync"]: - print( - 'WARNING: Pre-connection option "{}" was selected.'.format(mode), - "Connection may fail if the chip is not in bootloader " - "or flasher stub mode.", - ) - print("Connecting...", end="") - sys.stdout.flush() - last_error = None - - usb_jtag_serial = (mode == "usb_reset") or ( - self._get_pid() == self.USB_JTAG_SERIAL_PID - ) - - try: - for _, extra_delay in zip( - range(attempts) if attempts > 0 else itertools.count(), - itertools.cycle((False, True)), - ): - last_error = self._connect_attempt( - mode=mode, usb_jtag_serial=usb_jtag_serial, extra_delay=extra_delay - ) - if last_error is None: - break - finally: - print("") # end 'Connecting...' line - - if last_error is not None: - raise FatalError( - "Failed to connect to {}: {}" - "\nFor troubleshooting steps visit: " - "https://docs.espressif.com/projects/esptool/en/latest/troubleshooting.html".format( # noqa E501 - self.CHIP_NAME, last_error - ) - ) - - if not detecting: - try: - from .targets import ROM_LIST - - # check the date code registers match what we expect to see - chip_magic_value = self.read_reg(ESPLoader.CHIP_DETECT_MAGIC_REG_ADDR) - if chip_magic_value not in self.CHIP_DETECT_MAGIC_VALUE: - actually = None - for cls in ROM_LIST: - if chip_magic_value in cls.CHIP_DETECT_MAGIC_VALUE: - actually = cls - break - if warnings and actually is None: - print( - "WARNING: This chip doesn't appear to be a %s " - "(chip magic value 0x%08x). " - "Probably it is unsupported by this version of esptool." - % (self.CHIP_NAME, chip_magic_value) - ) - else: - raise FatalError( - "This chip is %s not %s. Wrong --chip argument?" - % (actually.CHIP_NAME, self.CHIP_NAME) - ) - except UnsupportedCommandError: - self.secure_download_mode = True - self._post_connect() - self.check_chip_id() - - def _post_connect(self): - """ - Additional initialization hook, may be overridden by the chip-specific class. - Gets called after connect, and after auto-detection. - """ - pass - - def read_reg(self, addr, timeout=DEFAULT_TIMEOUT): - """Read memory address in target""" - # we don't call check_command here because read_reg() function is called - # when detecting chip type, and the way we check for success - # (STATUS_BYTES_LENGTH) is different for different chip types (!) - val, data = self.command( - self.ESP_READ_REG, struct.pack(" 0: - # add a dummy write to a date register as an excuse to have a delay - command += struct.pack( - " start: - raise FatalError( - "Software loader is resident at 0x%08x-0x%08x. " - "Can't load binary at overlapping address range 0x%08x-0x%08x. " - "Either change binary loading address, or use the --no-stub " - "option to disable the software loader." - % (start, end, load_start, load_end) - ) - - return self.check_command( - "enter RAM download mode", - self.ESP_MEM_BEGIN, - struct.pack(" length: - raise FatalError("Read more than expected") - - digest_frame = self.read() - if len(digest_frame) != 16: - raise FatalError("Expected digest, got: %s" % hexify(digest_frame)) - expected_digest = hexify(digest_frame).upper() - digest = hashlib.md5(data).hexdigest().upper() - if digest != expected_digest: - raise FatalError( - "Digest mismatch: expected %s, got %s" % (expected_digest, digest) - ) - return data - - def flash_spi_attach(self, hspi_arg): - """Send SPI attach command to enable the SPI flash pins - - ESP8266 ROM does this when you send flash_begin, ESP32 ROM - has it as a SPI command. - """ - # last 3 bytes in ESP_SPI_ATTACH argument are reserved values - arg = struct.pack(" 0: - self.write_reg(SPI_MOSI_DLEN_REG, mosi_bits - 1) - if miso_bits > 0: - self.write_reg(SPI_MISO_DLEN_REG, miso_bits - 1) - flags = 0 - if dummy_len > 0: - flags |= dummy_len - 1 - if addr_len > 0: - flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT - if flags: - self.write_reg(SPI_USR1_REG, flags) - - else: - - def set_data_lengths(mosi_bits, miso_bits): - SPI_DATA_LEN_REG = SPI_USR1_REG - SPI_MOSI_BITLEN_S = 17 - SPI_MISO_BITLEN_S = 8 - mosi_mask = 0 if (mosi_bits == 0) else (mosi_bits - 1) - miso_mask = 0 if (miso_bits == 0) else (miso_bits - 1) - flags = (miso_mask << SPI_MISO_BITLEN_S) | ( - mosi_mask << SPI_MOSI_BITLEN_S - ) - if dummy_len > 0: - flags |= dummy_len - 1 - if addr_len > 0: - flags |= (addr_len - 1) << SPI_USR_ADDR_LEN_SHIFT - self.write_reg(SPI_DATA_LEN_REG, flags) - - # SPI peripheral "command" bitmasks for SPI_CMD_REG - SPI_CMD_USR = 1 << 18 - - # shift values - SPI_USR2_COMMAND_LEN_SHIFT = 28 - SPI_USR_ADDR_LEN_SHIFT = 26 - - if read_bits > 32: - raise FatalError( - "Reading more than 32 bits back from a SPI flash " - "operation is unsupported" - ) - if len(data) > 64: - raise FatalError( - "Writing more than 64 bytes of data with one SPI " - "command is unsupported" - ) - - data_bits = len(data) * 8 - old_spi_usr = self.read_reg(SPI_USR_REG) - old_spi_usr2 = self.read_reg(SPI_USR2_REG) - flags = SPI_USR_COMMAND - if read_bits > 0: - flags |= SPI_USR_MISO - if data_bits > 0: - flags |= SPI_USR_MOSI - if addr_len > 0: - flags |= SPI_USR_ADDR - if dummy_len > 0: - flags |= SPI_USR_DUMMY - set_data_lengths(data_bits, read_bits) - self.write_reg(SPI_USR_REG, flags) - self.write_reg( - SPI_USR2_REG, (7 << SPI_USR2_COMMAND_LEN_SHIFT) | spiflash_command - ) - if addr and addr_len > 0: - self.write_reg(SPI_ADDR_REG, addr) - if data_bits == 0: - self.write_reg(SPI_W0_REG, 0) # clear data register before we read it - else: - data = pad_to(data, 4, b"\00") # pad to 32-bit multiple - words = struct.unpack("I" * (len(data) // 4), data) - next_reg = SPI_W0_REG - for word in words: - self.write_reg(next_reg, word) - next_reg += 4 - self.write_reg(SPI_CMD_REG, SPI_CMD_USR) - - def wait_done(): - for _ in range(10): - if (self.read_reg(SPI_CMD_REG) & SPI_CMD_USR) == 0: - return - raise FatalError("SPI command did not complete in time") - - wait_done() - - status = self.read_reg(SPI_W0_REG) - # restore some SPI controller registers - self.write_reg(SPI_USR_REG, old_spi_usr) - self.write_reg(SPI_USR2_REG, old_spi_usr2) - return status - - def read_spiflash_sfdp(self, addr, read_bits): - CMD_RDSFDP = 0x5A - return self.run_spiflash_command( - CMD_RDSFDP, read_bits=read_bits, addr=addr, addr_len=24, dummy_len=8 - ) - - def read_status(self, num_bytes=2): - """Read up to 24 bits (num_bytes) of SPI flash status register contents - via RDSR, RDSR2, RDSR3 commands - - Not all SPI flash supports all three commands. The upper 1 or 2 - bytes may be 0xFF. - """ - SPIFLASH_RDSR = 0x05 - SPIFLASH_RDSR2 = 0x35 - SPIFLASH_RDSR3 = 0x15 - - status = 0 - shift = 0 - for cmd in [SPIFLASH_RDSR, SPIFLASH_RDSR2, SPIFLASH_RDSR3][0:num_bytes]: - status += self.run_spiflash_command(cmd, read_bits=8) << shift - shift += 8 - return status - - def write_status(self, new_status, num_bytes=2, set_non_volatile=False): - """Write up to 24 bits (num_bytes) of new status register - - num_bytes can be 1, 2 or 3. - - Not all flash supports the additional commands to write the - second and third byte of the status register. When writing 2 - bytes, esptool also sends a 16-byte WRSR command (as some - flash types use this instead of WRSR2.) - - If the set_non_volatile flag is set, non-volatile bits will - be set as well as volatile ones (WREN used instead of WEVSR). - - """ - SPIFLASH_WRSR = 0x01 - SPIFLASH_WRSR2 = 0x31 - SPIFLASH_WRSR3 = 0x11 - SPIFLASH_WEVSR = 0x50 - SPIFLASH_WREN = 0x06 - SPIFLASH_WRDI = 0x04 - - enable_cmd = SPIFLASH_WREN if set_non_volatile else SPIFLASH_WEVSR - - # try using a 16-bit WRSR (not supported by all chips) - # this may be redundant, but shouldn't hurt - if num_bytes == 2: - self.run_spiflash_command(enable_cmd) - self.run_spiflash_command(SPIFLASH_WRSR, struct.pack(">= 8 - - self.run_spiflash_command(SPIFLASH_WRDI) - - def get_crystal_freq(self): - """ - Figure out the crystal frequency from the UART clock divider - - Returns a normalized value in integer MHz (only values 40 or 26 are supported) - """ - # The logic here is: - # - We know that our baud rate and the ESP UART baud rate are roughly the same, - # or we couldn't communicate - # - We can read the UART clock divider register to know how the ESP derives this - # from the APB bus frequency - # - Multiplying these two together gives us the bus frequency which is either - # the crystal frequency (ESP32) or double the crystal frequency (ESP8266). - # See the self.XTAL_CLK_DIVIDER parameter for this factor. - uart_div = self.read_reg(self.UART_CLKDIV_REG) & self.UART_CLKDIV_MASK - est_xtal = (self._port.baudrate * uart_div) / 1e6 / self.XTAL_CLK_DIVIDER - norm_xtal = 40 if est_xtal > 33 else 26 - if abs(norm_xtal - est_xtal) > 1: - print( - "WARNING: Detected crystal freq %.2fMHz is quite different to " - "normalized freq %dMHz. Unsupported crystal in use?" - % (est_xtal, norm_xtal) - ) - return norm_xtal - - def hard_reset(self): - print("Hard resetting via RTS pin...") - self._setRTS(True) # EN->LOW - time.sleep(0.1) - self._setRTS(False) - - def soft_reset(self, stay_in_bootloader): - if not self.IS_STUB: - if stay_in_bootloader: - return # ROM bootloader is already in bootloader! - else: - # 'run user code' is as close to a soft reset as we can do - self.flash_begin(0, 0) - self.flash_finish(False) - else: - if stay_in_bootloader: - # soft resetting from the stub loader - # will re-load the ROM bootloader - self.flash_begin(0, 0) - self.flash_finish(True) - elif self.CHIP_NAME != "ESP8266": - raise FatalError( - "Soft resetting is currently only supported on ESP8266" - ) - else: - # running user code from stub loader requires some hacks - # in the stub loader - self.command(self.ESP_RUN_USER_CODE, wait_response=False) - - def check_chip_id(self): - try: - chip_id = self.get_chip_id() - if chip_id != self.IMAGE_CHIP_ID: - print( - "WARNING: Chip ID {} ({}) doesn't match expected Chip ID {}. " - "esptool may not work correctly.".format( - chip_id, - self.UNSUPPORTED_CHIPS.get(chip_id, "Unknown"), - self.IMAGE_CHIP_ID, - ) - ) - # Try to flash anyways by disabling stub - self.stub_is_disabled = True - except NotImplementedInROMError: - pass - - -def slip_reader(port, trace_function): - """Generator to read SLIP packets from a serial port. - Yields one full SLIP packet at a time, raises exception on timeout or invalid data. - - Designed to avoid too many calls to serial.read(1), which can bog - down on slow systems. - """ - - def detect_panic_handler(input): - """ - Checks the input bytes for panic handler messages. - Raises a FatalError if Guru Meditation or Fatal Exception is found, as both - of these are used between different ROM versions. - Tries to also parse the error cause (e.g. IllegalInstruction). - """ - - guru_meditation = ( - rb"G?uru Meditation Error: (?:Core \d panic'ed \(([a-zA-Z]*)\))?" - ) - fatal_exception = rb"F?atal exception \(\d+\): (?:([a-zA-Z]*)?.*epc)?" - - # Search either for Guru Meditation or Fatal Exception - data = re.search( - rb"".join([rb"(?:", guru_meditation, rb"|", fatal_exception, rb")"]), - input, - re.DOTALL, - ) - if data is not None: - msg = "Guru Meditation Error detected {}".format( - " ".join( - [ - "({})".format(i.decode("utf-8")) - for i in [data.group(1), data.group(2)] - if i is not None - ] - ) - ) - raise FatalError(msg) - - partial_packet = None - in_escape = False - successful_slip = False - while True: - waiting = port.inWaiting() - read_bytes = port.read(1 if waiting == 0 else waiting) - if read_bytes == b"": - if partial_packet is None: # fail due to no data - msg = ( - "Serial data stream stopped: Possible serial noise or corruption." - if successful_slip - else "No serial data received." - ) - else: # fail during packet transfer - msg = "Packet content transfer stopped (received {} bytes)".format( - len(partial_packet) - ) - trace_function(msg) - raise FatalError(msg) - trace_function("Read %d bytes: %s", len(read_bytes), HexFormatter(read_bytes)) - for b in read_bytes: - b = bytes([b]) - if partial_packet is None: # waiting for packet header - if b == b"\xc0": - partial_packet = b"" - else: - trace_function("Read invalid data: %s", HexFormatter(read_bytes)) - remaining_data = port.read(port.inWaiting()) - trace_function( - "Remaining data in serial buffer: %s", - HexFormatter(remaining_data), - ) - detect_panic_handler(read_bytes + remaining_data) - raise FatalError( - "Invalid head of packet (0x%s): " - "Possible serial noise or corruption." % hexify(b) - ) - elif in_escape: # part-way through escape sequence - in_escape = False - if b == b"\xdc": - partial_packet += b"\xc0" - elif b == b"\xdd": - partial_packet += b"\xdb" - else: - trace_function("Read invalid data: %s", HexFormatter(read_bytes)) - remaining_data = port.read(port.inWaiting()) - trace_function( - "Remaining data in serial buffer: %s", - HexFormatter(remaining_data), - ) - detect_panic_handler(read_bytes + remaining_data) - raise FatalError("Invalid SLIP escape (0xdb, 0x%s)" % (hexify(b))) - elif b == b"\xdb": # start of escape sequence - in_escape = True - elif b == b"\xc0": # end of packet - trace_function("Received full packet: %s", HexFormatter(partial_packet)) - yield partial_packet - partial_packet = None - successful_slip = True - else: # normal byte in packet - partial_packet += b - - -class HexFormatter(object): - """ - Wrapper class which takes binary data in its constructor - and returns a hex string as it's __str__ method. - - This is intended for "lazy formatting" of trace() output - in hex format. Avoids overhead (significant on slow computers) - of generating long hex strings even if tracing is disabled. - - Note that this doesn't save any overhead if passed as an - argument to "%", only when passed to trace() - - If auto_split is set (default), any long line (> 16 bytes) will be - printed as separately indented lines, with ASCII decoding at the end - of each line. - """ - - def __init__(self, binary_string, auto_split=True): - self._s = binary_string - self._auto_split = auto_split - - def __str__(self): - if self._auto_split and len(self._s) > 16: - result = "" - s = self._s - while len(s) > 0: - line = s[:16] - ascii_line = "".join( - c - if ( - c == " " - or (c in string.printable and c not in string.whitespace) - ) - else "." - for c in line.decode("ascii", "replace") - ) - s = s[16:] - result += "\n %-16s %-16s | %s" % ( - hexify(line[:8], False), - hexify(line[8:], False), - ascii_line, - ) - return result - else: - return hexify(self._s, False) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py deleted file mode 100644 index aae27f328..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -from .esp32 import ESP32ROM -from .esp32c2 import ESP32C2ROM -from .esp32c3 import ESP32C3ROM -from .esp32c6 import ESP32C6ROM -from .esp32c6beta import ESP32C6BETAROM -from .esp32h2beta1 import ESP32H2BETA1ROM -from .esp32h2beta2 import ESP32H2BETA2ROM -from .esp32s2 import ESP32S2ROM -from .esp32s3 import ESP32S3ROM -from .esp32s3beta2 import ESP32S3BETA2ROM -from .esp8266 import ESP8266ROM - - -CHIP_DEFS = { - "esp8266": ESP8266ROM, - "esp32": ESP32ROM, - "esp32s2": ESP32S2ROM, - "esp32s3beta2": ESP32S3BETA2ROM, - "esp32s3": ESP32S3ROM, - "esp32c3": ESP32C3ROM, - "esp32c6beta": ESP32C6BETAROM, - "esp32h2beta1": ESP32H2BETA1ROM, - "esp32h2beta2": ESP32H2BETA2ROM, - "esp32c2": ESP32C2ROM, - "esp32c6": ESP32C6ROM, -} - -CHIP_LIST = list(CHIP_DEFS.keys()) -ROM_LIST = list(CHIP_DEFS.values()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py deleted file mode 100644 index ea7deb5ad..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32.py +++ /dev/null @@ -1,360 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import struct - -from ..loader import ESPLoader -from ..util import FatalError, NotSupportedError - - -class ESP32ROM(ESPLoader): - """Access class for ESP32 ROM bootloader""" - - CHIP_NAME = "ESP32" - IMAGE_CHIP_ID = 0 - IS_STUB = False - - FPGA_SLOW_BOOT = True - - CHIP_DETECT_MAGIC_VALUE = [0x00F01D83] - - IROM_MAP_START = 0x400D0000 - IROM_MAP_END = 0x40400000 - - DROM_MAP_START = 0x3F400000 - DROM_MAP_END = 0x3F800000 - - # ESP32 uses a 4 byte status reply - STATUS_BYTES_LENGTH = 4 - - SPI_REG_BASE = 0x3FF42000 - SPI_USR_OFFS = 0x1C - SPI_USR1_OFFS = 0x20 - SPI_USR2_OFFS = 0x24 - SPI_MOSI_DLEN_OFFS = 0x28 - SPI_MISO_DLEN_OFFS = 0x2C - EFUSE_RD_REG_BASE = 0x3FF5A000 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE + 0x18 - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 7 # EFUSE_RD_DISABLE_DL_ENCRYPT - - EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_RD_REG_BASE # EFUSE_BLK0_WDATA0_REG - EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7F << 20 # EFUSE_FLASH_CRYPT_CNT - - EFUSE_RD_ABS_DONE_REG = EFUSE_RD_REG_BASE + 0x018 - EFUSE_RD_ABS_DONE_0_MASK = 1 << 4 - EFUSE_RD_ABS_DONE_1_MASK = 1 << 5 - - DR_REG_SYSCON_BASE = 0x3FF66000 - APB_CTL_DATE_ADDR = DR_REG_SYSCON_BASE + 0x7C - APB_CTL_DATE_V = 0x1 - APB_CTL_DATE_S = 31 - - SPI_W0_OFFS = 0x80 - - UART_CLKDIV_REG = 0x3FF40014 - - XTAL_CLK_DIVIDER = 1 - - FLASH_SIZES = { - "1MB": 0x00, - "2MB": 0x10, - "4MB": 0x20, - "8MB": 0x30, - "16MB": 0x40, - "32MB": 0x50, - "64MB": 0x60, - "128MB": 0x70, - } - - FLASH_FREQUENCY = { - "80m": 0xF, - "40m": 0x0, - "26m": 0x1, - "20m": 0x2, - } - - BOOTLOADER_FLASH_OFFSET = 0x1000 - - OVERRIDE_VDDSDIO_CHOICES = ["1.8V", "1.9V", "OFF"] - - MEMORY_MAP = [ - [0x00000000, 0x00010000, "PADDING"], - [0x3F400000, 0x3F800000, "DROM"], - [0x3F800000, 0x3FC00000, "EXTRAM_DATA"], - [0x3FF80000, 0x3FF82000, "RTC_DRAM"], - [0x3FF90000, 0x40000000, "BYTE_ACCESSIBLE"], - [0x3FFAE000, 0x40000000, "DRAM"], - [0x3FFE0000, 0x3FFFFFFC, "DIRAM_DRAM"], - [0x40000000, 0x40070000, "IROM"], - [0x40070000, 0x40078000, "CACHE_PRO"], - [0x40078000, 0x40080000, "CACHE_APP"], - [0x40080000, 0x400A0000, "IRAM"], - [0x400A0000, 0x400BFFFC, "DIRAM_IRAM"], - [0x400C0000, 0x400C2000, "RTC_IRAM"], - [0x400D0000, 0x40400000, "IROM"], - [0x50000000, 0x50002000, "RTC_DATA"], - ] - - FLASH_ENCRYPTED_WRITE_ALIGN = 32 - - """ Try to read the BLOCK1 (encryption key) and check if it is valid """ - - def is_flash_encryption_key_valid(self): - """Bit 0 of efuse_rd_disable[3:0] is mapped to BLOCK1 - this bit is at position 16 in EFUSE_BLK0_RDATA0_REG""" - word0 = self.read_efuse(0) - rd_disable = (word0 >> 16) & 0x1 - - # reading of BLOCK1 is NOT ALLOWED so we assume valid key is programmed - if rd_disable: - return True - else: - # reading of BLOCK1 is ALLOWED so we will read and verify for non-zero. - # When ESP32 has not generated AES/encryption key in BLOCK1, - # the contents will be readable and 0. - # If the flash encryption is enabled it is expected to have a valid - # non-zero key. We break out on first occurance of non-zero value - key_word = [0] * 7 - for i in range(len(key_word)): - key_word[i] = self.read_efuse(14 + i) - # key is non-zero so break & return - if key_word[i] != 0: - return True - return False - - def get_flash_crypt_config(self): - """For flash encryption related commands we need to make sure - user has programmed all the relevant efuse correctly so before - writing encrypted write_flash_encrypt esptool will verify the values - of flash_crypt_config to be non zero if they are not read - protected. If the values are zero a warning will be printed - - bit 3 in efuse_rd_disable[3:0] is mapped to flash_crypt_config - this bit is at position 19 in EFUSE_BLK0_RDATA0_REG""" - word0 = self.read_efuse(0) - rd_disable = (word0 >> 19) & 0x1 - - if rd_disable == 0: - """we can read the flash_crypt_config efuse value - so go & read it (EFUSE_BLK0_RDATA5_REG[31:28])""" - word5 = self.read_efuse(5) - word5 = (word5 >> 28) & 0xF - return word5 - else: - # if read of the efuse is disabled we assume it is set correctly - return 0xF - - def get_encrypted_download_disabled(self): - if ( - self.read_reg(self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG) - & self.EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT - ): - return True - else: - return False - - def get_flash_encryption_enabled(self): - flash_crypt_cnt = ( - self.read_reg(self.EFUSE_SPI_BOOT_CRYPT_CNT_REG) - & self.EFUSE_SPI_BOOT_CRYPT_CNT_MASK - ) - # Flash encryption enabled when odd number of bits are set - return bin(flash_crypt_cnt).count("1") & 1 != 0 - - def get_secure_boot_enabled(self): - efuses = self.read_reg(self.EFUSE_RD_ABS_DONE_REG) - rev = self.get_chip_revision() - return efuses & self.EFUSE_RD_ABS_DONE_0_MASK or ( - rev >= 300 and efuses & self.EFUSE_RD_ABS_DONE_1_MASK - ) - - def get_pkg_version(self): - word3 = self.read_efuse(3) - pkg_version = (word3 >> 9) & 0x07 - pkg_version += ((word3 >> 2) & 0x1) << 3 - return pkg_version - - def get_chip_revision(self): - return self.get_major_chip_version() * 100 + self.get_minor_chip_version() - - def get_minor_chip_version(self): - return (self.read_efuse(5) >> 24) & 0x3 - - def get_major_chip_version(self): - rev_bit0 = (self.read_efuse(3) >> 15) & 0x1 - rev_bit1 = (self.read_efuse(5) >> 20) & 0x1 - apb_ctl_date = self.read_reg(self.APB_CTL_DATE_ADDR) - rev_bit2 = (apb_ctl_date >> self.APB_CTL_DATE_S) & self.APB_CTL_DATE_V - combine_value = (rev_bit2 << 2) | (rev_bit1 << 1) | rev_bit0 - - revision = { - 0: 0, - 1: 1, - 3: 2, - 7: 3, - }.get(combine_value, 0) - return revision - - def get_chip_description(self): - pkg_version = self.get_pkg_version() - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - rev3 = major_rev == 3 - single_core = self.read_efuse(3) & (1 << 0) # CHIP_VER DIS_APP_CPU - - chip_name = { - 0: "ESP32-S0WDQ6" if single_core else "ESP32-D0WDQ6", - 1: "ESP32-S0WD" if single_core else "ESP32-D0WD", - 2: "ESP32-D2WD", - 4: "ESP32-U4WDH", - 5: "ESP32-PICO-V3" if rev3 else "ESP32-PICO-D4", - 6: "ESP32-PICO-V3-02", - 7: "ESP32-D0WDR2-V3", - }.get(pkg_version, "unknown ESP32") - - # ESP32-D0WD-V3, ESP32-D0WDQ6-V3 - if chip_name.startswith("ESP32-D0WD") and rev3: - chip_name += "-V3" - - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - def get_chip_features(self): - features = ["WiFi"] - word3 = self.read_efuse(3) - - # names of variables in this section are lowercase - # versions of EFUSE names as documented in TRM and - # ESP-IDF efuse_reg.h - - chip_ver_dis_bt = word3 & (1 << 1) - if chip_ver_dis_bt == 0: - features += ["BT"] - - chip_ver_dis_app_cpu = word3 & (1 << 0) - if chip_ver_dis_app_cpu: - features += ["Single Core"] - else: - features += ["Dual Core"] - - chip_cpu_freq_rated = word3 & (1 << 13) - if chip_cpu_freq_rated: - chip_cpu_freq_low = word3 & (1 << 12) - if chip_cpu_freq_low: - features += ["160MHz"] - else: - features += ["240MHz"] - - pkg_version = self.get_pkg_version() - if pkg_version in [2, 4, 5, 6]: - features += ["Embedded Flash"] - - if pkg_version == 6: - features += ["Embedded PSRAM"] - - word4 = self.read_efuse(4) - adc_vref = (word4 >> 8) & 0x1F - if adc_vref: - features += ["VRef calibration in efuse"] - - blk3_part_res = word3 >> 14 & 0x1 - if blk3_part_res: - features += ["BLK3 partially reserved"] - - word6 = self.read_efuse(6) - coding_scheme = word6 & 0x3 - features += [ - "Coding Scheme %s" - % {0: "None", 1: "3/4", 2: "Repeat (UNSUPPORTED)", 3: "Invalid"}[ - coding_scheme - ] - ] - - return features - - def read_efuse(self, n): - """Read the nth word of the ESP3x EFUSE region.""" - return self.read_reg(self.EFUSE_RD_REG_BASE + (4 * n)) - - def chip_id(self): - raise NotSupportedError(self, "chip_id") - - def read_mac(self): - """Read MAC from EFUSE region""" - words = [self.read_efuse(2), self.read_efuse(1)] - bitstring = struct.pack(">II", *words) - bitstring = bitstring[2:8] # trim the 2 byte CRC - return tuple(bitstring) - - def get_erase_size(self, offset, size): - return size - - def override_vddsdio(self, new_voltage): - new_voltage = new_voltage.upper() - if new_voltage not in self.OVERRIDE_VDDSDIO_CHOICES: - raise FatalError( - "The only accepted VDDSDIO overrides are '1.8V', '1.9V' and 'OFF'" - ) - RTC_CNTL_SDIO_CONF_REG = 0x3FF48074 - RTC_CNTL_XPD_SDIO_REG = 1 << 31 - RTC_CNTL_DREFH_SDIO_M = 3 << 29 - RTC_CNTL_DREFM_SDIO_M = 3 << 27 - RTC_CNTL_DREFL_SDIO_M = 3 << 25 - # RTC_CNTL_SDIO_TIEH = (1 << 23) - # not used here, setting TIEH=1 would set 3.3V output, - # not safe for esptool.py to do - RTC_CNTL_SDIO_FORCE = 1 << 22 - RTC_CNTL_SDIO_PD_EN = 1 << 21 - - reg_val = RTC_CNTL_SDIO_FORCE # override efuse setting - reg_val |= RTC_CNTL_SDIO_PD_EN - if new_voltage != "OFF": - reg_val |= RTC_CNTL_XPD_SDIO_REG # enable internal LDO - if new_voltage == "1.9V": - reg_val |= ( - RTC_CNTL_DREFH_SDIO_M | RTC_CNTL_DREFM_SDIO_M | RTC_CNTL_DREFL_SDIO_M - ) # boost voltage - self.write_reg(RTC_CNTL_SDIO_CONF_REG, reg_val) - print("VDDSDIO regulator set to %s" % new_voltage) - - def read_flash_slow(self, offset, length, progress_fn): - BLOCK_LEN = 64 # ROM read limit per command (this limit is why it's so slow) - - data = b"" - while len(data) < length: - block_len = min(BLOCK_LEN, length - len(data)) - r = self.check_command( - "read flash block", - self.ESP_READ_FLASH_SLOW, - struct.pack("> 22) & 0x07 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C2", - 1: "ESP32-C2", - }.get(self.get_pkg_version(), "unknown ESP32-C2") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - def get_minor_chip_version(self): - num_word = 1 - return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 16) & 0xF - - def get_major_chip_version(self): - num_word = 1 - return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 20) & 0x3 - - def get_crystal_freq(self): - # The crystal detection algorithm of ESP32/ESP8266 works for ESP32-C2 as well. - return ESPLoader.get_crystal_freq(self) - - def change_baud(self, baud): - rom_with_26M_XTAL = not self.IS_STUB and self.get_crystal_freq() == 26 - if rom_with_26M_XTAL: - # The code is copied over from ESPLoader.change_baud(). - # Probably this is just a temporary solution until the next chip revision. - - # The ROM code thinks it uses a 40 MHz XTAL. Recompute the baud rate - # in order to trick the ROM code to set the correct baud rate for - # a 26 MHz XTAL. - false_rom_baud = baud * 40 // 26 - - print(f"Changing baud rate to {baud}") - self.command( - self.ESP_CHANGE_BAUDRATE, struct.pack("> 21) & 0x07 - - def get_minor_chip_version(self): - hi_num_word = 5 - hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 - low_num_word = 3 - low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 - return (hi << 3) + low - - def get_major_chip_version(self): - num_word = 5 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C3", - }.get(self.get_pkg_version(), "unknown ESP32-C3") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - def get_chip_features(self): - return ["WiFi", "BLE"] - - def get_crystal_freq(self): - # ESP32C3 XTAL is fixed to 40MHz - return 40 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-C3" - ) - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-C3 - - def get_secure_boot_enabled(self): - return ( - self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) - & self.EFUSE_SECURE_BOOT_EN_MASK - ) - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [ - (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), - ][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see an AES-128 key - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) - - -class ESP32C3StubLoader(ESP32C3ROM): - """Access class for ESP32C3 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32C3ROM.STUB_CLASS = ESP32C3StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py deleted file mode 100644 index 3cc9d4fdd..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6.py +++ /dev/null @@ -1,175 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import struct - -from .esp32 import ESP32ROM -from ..util import FatalError, NotImplementedInROMError - - -class ESP32C6ROM(ESP32ROM): - CHIP_NAME = "ESP32-C6" - IMAGE_CHIP_ID = 13 - - FPGA_SLOW_BOOT = False - - IROM_MAP_START = 0x42000000 - IROM_MAP_END = 0x42800000 - DROM_MAP_START = 0x42800000 - DROM_MAP_END = 0x43000000 - - BOOTLOADER_FLASH_OFFSET = 0x0 - - # Magic value for ESP32C6 - CHIP_DETECT_MAGIC_VALUE = [0x2CE0806F] - - SPI_REG_BASE = 0x60003000 - SPI_USR_OFFS = 0x18 - SPI_USR1_OFFS = 0x1C - SPI_USR2_OFFS = 0x20 - SPI_MOSI_DLEN_OFFS = 0x24 - SPI_MISO_DLEN_OFFS = 0x28 - SPI_W0_OFFS = 0x58 - - UART_DATE_REG_ADDR = 0x60000000 + 0x7C - - EFUSE_BASE = 0x600B0800 - EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 - MAC_EFUSE_REG = EFUSE_BASE + 0x044 - - EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address - - EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY0_SHIFT = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 - - EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 - EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 - - EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 - EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 - - PURPOSE_VAL_XTS_AES128_KEY = 4 - - SUPPORTS_ENCRYPTED_FLASH = True - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - MEMORY_MAP = [ - [0x00000000, 0x00010000, "PADDING"], - [0x42800000, 0x43000000, "DROM"], - [0x40800000, 0x40880000, "DRAM"], - [0x40800000, 0x40880000, "BYTE_ACCESSIBLE"], - [0x4004AC00, 0x40050000, "DROM_MASK"], - [0x40000000, 0x4004AC00, "IROM_MASK"], - [0x42000000, 0x42800000, "IROM"], - [0x40800000, 0x40880000, "IRAM"], - [0x50000000, 0x50004000, "RTC_IRAM"], - [0x50000000, 0x50004000, "RTC_DRAM"], - [0x600FE000, 0x60100000, "MEM_INTERNAL2"], - ] - - def get_pkg_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 - - def get_minor_chip_version(self): - hi_num_word = 5 - hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 - low_num_word = 3 - low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 - return (hi << 3) + low - - def get_major_chip_version(self): - num_word = 5 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C6", - }.get(self.get_pkg_version(), "unknown ESP32-C6") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - def get_chip_features(self): - return ["WiFi 6", "BT 5"] - - def get_crystal_freq(self): - # ESP32C6 XTAL is fixed to 40MHz - return 40 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-C6" - ) - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-C6 - - def get_secure_boot_enabled(self): - return ( - self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) - & self.EFUSE_SECURE_BOOT_EN_MASK - ) - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [ - (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), - ][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see an AES-128 key - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) - - -class ESP32C6StubLoader(ESP32C6ROM): - """Access class for ESP32C6 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32C6ROM.STUB_CLASS = ESP32C6StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py deleted file mode 100644 index d30e7b010..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32c6beta.py +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from .esp32c3 import ESP32C3ROM - - -class ESP32C6BETAROM(ESP32C3ROM): - CHIP_NAME = "ESP32-C6(beta)" - IMAGE_CHIP_ID = 7 - - CHIP_DETECT_MAGIC_VALUE = [0x0DA1806F] - - UART_DATE_REG_ADDR = 0x00000500 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-C6", - }.get(self.get_pkg_version(), "unknown ESP32-C6") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py deleted file mode 100644 index 529ba4c0a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta1.py +++ /dev/null @@ -1,154 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import struct - -from .esp32 import ESP32ROM -from ..util import FatalError, NotImplementedInROMError - - -class ESP32H2BETA1ROM(ESP32ROM): - CHIP_NAME = "ESP32-H2(beta1)" - IMAGE_CHIP_ID = 10 - - IROM_MAP_START = 0x42000000 - IROM_MAP_END = 0x42800000 - DROM_MAP_START = 0x3C000000 - DROM_MAP_END = 0x3C800000 - - SPI_REG_BASE = 0x60002000 - SPI_USR_OFFS = 0x18 - SPI_USR1_OFFS = 0x1C - SPI_USR2_OFFS = 0x20 - SPI_MOSI_DLEN_OFFS = 0x24 - SPI_MISO_DLEN_OFFS = 0x28 - SPI_W0_OFFS = 0x58 - - BOOTLOADER_FLASH_OFFSET = 0x0 - - CHIP_DETECT_MAGIC_VALUE = [0xCA26CC22] - - UART_DATE_REG_ADDR = 0x60000000 + 0x7C - - EFUSE_BASE = 0x6001A000 - EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 - MAC_EFUSE_REG = EFUSE_BASE + 0x044 - - EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address - - EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY0_SHIFT = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 - - PURPOSE_VAL_XTS_AES128_KEY = 4 - - SUPPORTS_ENCRYPTED_FLASH = True - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - MEMORY_MAP = [] - - FLASH_FREQUENCY = { - "48m": 0xF, - "24m": 0x0, - "16m": 0x1, - "12m": 0x2, - } - - def get_pkg_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F - - def get_minor_chip_version(self): - hi_num_word = 5 - hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 - low_num_word = 3 - low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 - return (hi << 3) + low - - def get_major_chip_version(self): - num_word = 5 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-H2", - }.get(self.get_pkg_version(), "unknown ESP32-H2") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - def get_chip_features(self): - return ["BLE/802.15.4"] - - def get_crystal_freq(self): - return 32 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-H2" - ) - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-H2 - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [ - (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), - ][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see an AES-128 key - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - return any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes) - - -class ESP32H2BETA1StubLoader(ESP32H2BETA1ROM): - """Access class for ESP32H2BETA1 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32H2BETA1ROM.STUB_CLASS = ESP32H2BETA1StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py deleted file mode 100644 index 97a66a019..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32h2beta2.py +++ /dev/null @@ -1,42 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from .esp32h2beta1 import ESP32H2BETA1ROM - - -class ESP32H2BETA2ROM(ESP32H2BETA1ROM): - CHIP_NAME = "ESP32-H2(beta2)" - IMAGE_CHIP_ID = 14 - - CHIP_DETECT_MAGIC_VALUE = [0x6881B06F] - - def get_chip_description(self): - chip_name = { - 1: "ESP32-H2(beta2)", - }.get(self.get_pkg_version(), "unknown ESP32-H2") - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - -class ESP32H2BETA2StubLoader(ESP32H2BETA2ROM): - """Access class for ESP32H2BETA2 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32H2BETA2ROM.STUB_CLASS = ESP32H2BETA2StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py deleted file mode 100644 index 5bfefa6cf..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s2.py +++ /dev/null @@ -1,300 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import struct -import time - -from .esp32 import ESP32ROM -from ..util import FatalError, NotImplementedInROMError - - -class ESP32S2ROM(ESP32ROM): - CHIP_NAME = "ESP32-S2" - IMAGE_CHIP_ID = 2 - - FPGA_SLOW_BOOT = False - - IROM_MAP_START = 0x40080000 - IROM_MAP_END = 0x40B80000 - DROM_MAP_START = 0x3F000000 - DROM_MAP_END = 0x3F3F0000 - - CHIP_DETECT_MAGIC_VALUE = [0x000007C6] - - SPI_REG_BASE = 0x3F402000 - SPI_USR_OFFS = 0x18 - SPI_USR1_OFFS = 0x1C - SPI_USR2_OFFS = 0x20 - SPI_MOSI_DLEN_OFFS = 0x24 - SPI_MISO_DLEN_OFFS = 0x28 - SPI_W0_OFFS = 0x58 - - MAC_EFUSE_REG = 0x3F41A044 # ESP32-S2 has special block for MAC efuses - - UART_CLKDIV_REG = 0x3F400014 - - SUPPORTS_ENCRYPTED_FLASH = True - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - # todo: use espefuse APIs to get this info - EFUSE_BASE = 0x3F41A000 - EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address - EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x044 - EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x05C - - EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY0_SHIFT = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 19 - - EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 - EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 - - EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 - EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 - - PURPOSE_VAL_XTS_AES256_KEY_1 = 2 - PURPOSE_VAL_XTS_AES256_KEY_2 = 3 - PURPOSE_VAL_XTS_AES128_KEY = 4 - - UARTDEV_BUF_NO = 0x3FFFFD14 # Variable in ROM .bss which indicates the port in use - UARTDEV_BUF_NO_USB_OTG = 2 # Value of the above indicating that USB-OTG is in use - - USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used - - GPIO_STRAP_REG = 0x3F404038 - GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode - RTC_CNTL_OPTION1_REG = 0x3F408128 - RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? - - MEMORY_MAP = [ - [0x00000000, 0x00010000, "PADDING"], - [0x3F000000, 0x3FF80000, "DROM"], - [0x3F500000, 0x3FF80000, "EXTRAM_DATA"], - [0x3FF9E000, 0x3FFA0000, "RTC_DRAM"], - [0x3FF9E000, 0x40000000, "BYTE_ACCESSIBLE"], - [0x3FF9E000, 0x40072000, "MEM_INTERNAL"], - [0x3FFB0000, 0x40000000, "DRAM"], - [0x40000000, 0x4001A100, "IROM_MASK"], - [0x40020000, 0x40070000, "IRAM"], - [0x40070000, 0x40072000, "RTC_IRAM"], - [0x40080000, 0x40800000, "IROM"], - [0x50000000, 0x50002000, "RTC_DATA"], - ] - - def get_pkg_version(self): - num_word = 4 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 0) & 0x0F - - def get_minor_chip_version(self): - hi_num_word = 3 - hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 20) & 0x01 - low_num_word = 4 - low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 4) & 0x07 - return (hi << 3) + low - - def get_major_chip_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 18) & 0x03 - - def get_flash_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x0F - - def get_psram_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 28) & 0x0F - - def get_block2_version(self): - # BLK_VERSION_MINOR - num_word = 4 - return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 4) & 0x07 - - def get_chip_description(self): - chip_name = { - 0: "ESP32-S2", - 1: "ESP32-S2FH2", - 2: "ESP32-S2FH4", - 102: "ESP32-S2FNR2", - 100: "ESP32-S2R2", - }.get( - self.get_flash_version() + self.get_psram_version() * 100, - "unknown ESP32-S2", - ) - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{chip_name} (revision v{major_rev}.{minor_rev})" - - def get_chip_features(self): - features = ["WiFi"] - - if self.secure_download_mode: - features += ["Secure Download Mode Enabled"] - - flash_version = { - 0: "No Embedded Flash", - 1: "Embedded Flash 2MB", - 2: "Embedded Flash 4MB", - }.get(self.get_flash_version(), "Unknown Embedded Flash") - features += [flash_version] - - psram_version = { - 0: "No Embedded PSRAM", - 1: "Embedded PSRAM 2MB", - 2: "Embedded PSRAM 4MB", - }.get(self.get_psram_version(), "Unknown Embedded PSRAM") - features += [psram_version] - - block2_version = { - 0: "No calibration in BLK2 of efuse", - 1: "ADC and temperature sensor calibration in BLK2 of efuse V1", - 2: "ADC and temperature sensor calibration in BLK2 of efuse V2", - }.get(self.get_block2_version(), "Unknown Calibration in BLK2") - features += [block2_version] - - return features - - def get_crystal_freq(self): - # ESP32-S2 XTAL is fixed to 40MHz - return 40 - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-S2" - ) - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - return tuple(bitstring) - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-S2 - - def get_secure_boot_enabled(self): - return ( - self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) - & self.EFUSE_SECURE_BOOT_EN_MASK - ) - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [ - (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), - ][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see either an AES-128 key or two AES-256 keys - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): - return True - - return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any( - p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes - ) - - def uses_usb_otg(self, _cache=[]): - """ - Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used - """ - if self.secure_download_mode: - return False # can't detect native USB in secure download mode - if not _cache: - buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xFF - _cache.append(buf_no == self.UARTDEV_BUF_NO_USB_OTG) - return _cache[0] - - def _post_connect(self): - if self.uses_usb_otg(): - self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - - def _check_if_can_reset(self): - """ - Check the strapping register to see if we can reset out of download mode. - """ - if os.getenv("ESPTOOL_TESTING") is not None: - print("ESPTOOL_TESTING is set, ignoring strapping mode check") - # Esptool tests over USB-OTG run with GPIO0 strapped low, - # don't complain in this case. - return - strap_reg = self.read_reg(self.GPIO_STRAP_REG) - force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) - if ( - strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 - and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 - ): - print( - "WARNING: {} chip was placed into download mode using GPIO0.\n" - "esptool.py can not exit the download mode over USB. " - "To run the app, reset the chip manually.\n" - "To suppress this note, set --after option to 'no_reset'.".format( - self.get_chip_description() - ) - ) - raise SystemExit(1) - - def hard_reset(self): - if self.uses_usb_otg(): - self._check_if_can_reset() - - print("Hard resetting via RTS pin...") - self._setRTS(True) # EN->LOW - if self.uses_usb_otg(): - # Give the chip some time to come out of reset, - # to be able to handle further DTR/RTS transitions - time.sleep(0.2) - self._setRTS(False) - time.sleep(0.2) - else: - time.sleep(0.1) - self._setRTS(False) - - -class ESP32S2StubLoader(ESP32S2ROM): - """Access class for ESP32-S2 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - if rom_loader.uses_usb_otg(): - self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK - - -ESP32S2ROM.STUB_CLASS = ESP32S2StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py deleted file mode 100644 index 80469918e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3.py +++ /dev/null @@ -1,275 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import struct -import time - -from .esp32 import ESP32ROM -from ..util import FatalError, NotImplementedInROMError - - -class ESP32S3ROM(ESP32ROM): - CHIP_NAME = "ESP32-S3" - - IMAGE_CHIP_ID = 9 - - CHIP_DETECT_MAGIC_VALUE = [0x9] - - FPGA_SLOW_BOOT = False - - IROM_MAP_START = 0x42000000 - IROM_MAP_END = 0x44000000 - DROM_MAP_START = 0x3C000000 - DROM_MAP_END = 0x3E000000 - - UART_DATE_REG_ADDR = 0x60000080 - - SPI_REG_BASE = 0x60002000 - SPI_USR_OFFS = 0x18 - SPI_USR1_OFFS = 0x1C - SPI_USR2_OFFS = 0x20 - SPI_MOSI_DLEN_OFFS = 0x24 - SPI_MISO_DLEN_OFFS = 0x28 - SPI_W0_OFFS = 0x58 - - BOOTLOADER_FLASH_OFFSET = 0x0 - - SUPPORTS_ENCRYPTED_FLASH = True - - FLASH_ENCRYPTED_WRITE_ALIGN = 16 - - # todo: use espefuse APIs to get this info - EFUSE_BASE = 0x60007000 # BLOCK0 read base address - EFUSE_BLOCK1_ADDR = EFUSE_BASE + 0x44 - EFUSE_BLOCK2_ADDR = EFUSE_BASE + 0x5C - MAC_EFUSE_REG = EFUSE_BASE + 0x044 - - EFUSE_RD_REG_BASE = EFUSE_BASE + 0x030 # BLOCK0 read base address - - EFUSE_PURPOSE_KEY0_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY0_SHIFT = 24 - EFUSE_PURPOSE_KEY1_REG = EFUSE_BASE + 0x34 - EFUSE_PURPOSE_KEY1_SHIFT = 28 - EFUSE_PURPOSE_KEY2_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY2_SHIFT = 0 - EFUSE_PURPOSE_KEY3_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY3_SHIFT = 4 - EFUSE_PURPOSE_KEY4_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY4_SHIFT = 8 - EFUSE_PURPOSE_KEY5_REG = EFUSE_BASE + 0x38 - EFUSE_PURPOSE_KEY5_SHIFT = 12 - - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT_REG = EFUSE_RD_REG_BASE - EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT = 1 << 20 - - EFUSE_SPI_BOOT_CRYPT_CNT_REG = EFUSE_BASE + 0x034 - EFUSE_SPI_BOOT_CRYPT_CNT_MASK = 0x7 << 18 - - EFUSE_SECURE_BOOT_EN_REG = EFUSE_BASE + 0x038 - EFUSE_SECURE_BOOT_EN_MASK = 1 << 20 - - PURPOSE_VAL_XTS_AES256_KEY_1 = 2 - PURPOSE_VAL_XTS_AES256_KEY_2 = 3 - PURPOSE_VAL_XTS_AES128_KEY = 4 - - UARTDEV_BUF_NO = 0x3FCEF14C # Variable in ROM .bss which indicates the port in use - UARTDEV_BUF_NO_USB_OTG = 3 # Value of the above indicating that USB-OTG is in use - - USB_RAM_BLOCK = 0x800 # Max block size USB-OTG is used - - GPIO_STRAP_REG = 0x60004038 - GPIO_STRAP_SPI_BOOT_MASK = 0x8 # Not download mode - RTC_CNTL_OPTION1_REG = 0x6000812C - RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK = 0x1 # Is download mode forced over USB? - - UART_CLKDIV_REG = 0x60000014 - - MEMORY_MAP = [ - [0x00000000, 0x00010000, "PADDING"], - [0x3C000000, 0x3D000000, "DROM"], - [0x3D000000, 0x3E000000, "EXTRAM_DATA"], - [0x600FE000, 0x60100000, "RTC_DRAM"], - [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], - [0x3FC88000, 0x403E2000, "MEM_INTERNAL"], - [0x3FC88000, 0x3FD00000, "DRAM"], - [0x40000000, 0x4001A100, "IROM_MASK"], - [0x40370000, 0x403E0000, "IRAM"], - [0x600FE000, 0x60100000, "RTC_IRAM"], - [0x42000000, 0x42800000, "IROM"], - [0x50000000, 0x50002000, "RTC_DATA"], - ] - - def get_pkg_version(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 21) & 0x07 - - def get_minor_chip_version(self): - hi_num_word = 5 - hi = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * hi_num_word)) >> 23) & 0x01 - low_num_word = 3 - low = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * low_num_word)) >> 18) & 0x07 - return (hi << 3) + low - - def get_blk_version_major(self): - num_word = 4 - return (self.read_reg(self.EFUSE_BLOCK2_ADDR + (4 * num_word)) >> 0) & 0x03 - - def get_blk_version_minor(self): - num_word = 3 - return (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x07 - - def get_major_chip_version(self): - num_word = 5 - rev = (self.read_reg(self.EFUSE_BLOCK1_ADDR + (4 * num_word)) >> 24) & 0x03 - - # Workaround: The major version field was allocated to other purposes - # when block version is v1.1. - # Luckily only chip v0.0 have this kind of block version and efuse usage. - if ( - self.get_minor_chip_version() == 0 - and self.get_blk_version_major() == 1 - and self.get_blk_version_minor() == 1 - ): - rev = 0 - return rev - - def get_chip_description(self): - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" - - def get_chip_features(self): - return ["WiFi", "BLE"] - - def get_crystal_freq(self): - # ESP32S3 XTAL is fixed to 40MHz - return 40 - - def get_flash_crypt_config(self): - return None # doesn't exist on ESP32-S3 - - def get_key_block_purpose(self, key_block): - if key_block < 0 or key_block > 5: - raise FatalError("Valid key block numbers must be in range 0-5") - - reg, shift = [ - (self.EFUSE_PURPOSE_KEY0_REG, self.EFUSE_PURPOSE_KEY0_SHIFT), - (self.EFUSE_PURPOSE_KEY1_REG, self.EFUSE_PURPOSE_KEY1_SHIFT), - (self.EFUSE_PURPOSE_KEY2_REG, self.EFUSE_PURPOSE_KEY2_SHIFT), - (self.EFUSE_PURPOSE_KEY3_REG, self.EFUSE_PURPOSE_KEY3_SHIFT), - (self.EFUSE_PURPOSE_KEY4_REG, self.EFUSE_PURPOSE_KEY4_SHIFT), - (self.EFUSE_PURPOSE_KEY5_REG, self.EFUSE_PURPOSE_KEY5_SHIFT), - ][key_block] - return (self.read_reg(reg) >> shift) & 0xF - - def is_flash_encryption_key_valid(self): - # Need to see either an AES-128 key or two AES-256 keys - purposes = [self.get_key_block_purpose(b) for b in range(6)] - - if any(p == self.PURPOSE_VAL_XTS_AES128_KEY for p in purposes): - return True - - return any(p == self.PURPOSE_VAL_XTS_AES256_KEY_1 for p in purposes) and any( - p == self.PURPOSE_VAL_XTS_AES256_KEY_2 for p in purposes - ) - - def get_secure_boot_enabled(self): - return ( - self.read_reg(self.EFUSE_SECURE_BOOT_EN_REG) - & self.EFUSE_SECURE_BOOT_EN_MASK - ) - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "VDD_SDIO overrides are not supported for ESP32-S3" - ) - - def read_mac(self): - mac0 = self.read_reg(self.MAC_EFUSE_REG) - mac1 = self.read_reg(self.MAC_EFUSE_REG + 4) # only bottom 16 bits are MAC - bitstring = struct.pack(">II", mac1, mac0)[2:] - return tuple(bitstring) - - def uses_usb_otg(self, _cache=[]): - """ - Check the UARTDEV_BUF_NO register to see if USB-OTG console is being used - """ - if self.secure_download_mode: - return False # can't detect native USB in secure download mode - if not _cache: - buf_no = self.read_reg(self.UARTDEV_BUF_NO) & 0xFF - _cache.append(buf_no == self.UARTDEV_BUF_NO_USB_OTG) - return _cache[0] - - def _post_connect(self): - if self.uses_usb_otg(): - self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - - def _check_if_can_reset(self): - """ - Check the strapping register to see if we can reset out of download mode. - """ - if os.getenv("ESPTOOL_TESTING") is not None: - print("ESPTOOL_TESTING is set, ignoring strapping mode check") - # Esptool tests over USB-OTG run with GPIO0 strapped low, - # don't complain in this case. - return - strap_reg = self.read_reg(self.GPIO_STRAP_REG) - force_dl_reg = self.read_reg(self.RTC_CNTL_OPTION1_REG) - if ( - strap_reg & self.GPIO_STRAP_SPI_BOOT_MASK == 0 - and force_dl_reg & self.RTC_CNTL_FORCE_DOWNLOAD_BOOT_MASK == 0 - ): - print( - "WARNING: {} chip was placed into download mode using GPIO0.\n" - "esptool.py can not exit the download mode over USB. " - "To run the app, reset the chip manually.\n" - "To suppress this note, set --after option to 'no_reset'.".format( - self.get_chip_description() - ) - ) - raise SystemExit(1) - - def hard_reset(self): - if self.uses_usb_otg(): - self._check_if_can_reset() - - print("Hard resetting via RTS pin...") - self._setRTS(True) # EN->LOW - if self.uses_usb_otg(): - # Give the chip some time to come out of reset, - # to be able to handle further DTR/RTS transitions - time.sleep(0.2) - self._setRTS(False) - time.sleep(0.2) - else: - time.sleep(0.1) - self._setRTS(False) - - -class ESP32S3StubLoader(ESP32S3ROM): - """Access class for ESP32S3 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - if rom_loader.uses_usb_otg(): - self.ESP_RAM_BLOCK = self.USB_RAM_BLOCK - self.FLASH_WRITE_SIZE = self.USB_RAM_BLOCK - - -ESP32S3ROM.STUB_CLASS = ESP32S3StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py deleted file mode 100644 index 87c60dcce..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp32s3beta2.py +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from .esp32s3 import ESP32S3ROM - - -class ESP32S3BETA2ROM(ESP32S3ROM): - CHIP_NAME = "ESP32-S3(beta2)" - IMAGE_CHIP_ID = 4 - - CHIP_DETECT_MAGIC_VALUE = [0xEB004136] - - EFUSE_BASE = 0x6001A000 # BLOCK0 read base address - - def get_chip_description(self): - major_rev = self.get_major_chip_version() - minor_rev = self.get_minor_chip_version() - return f"{self.CHIP_NAME} (revision v{major_rev}.{minor_rev})" - - -class ESP32S3BETA2StubLoader(ESP32S3BETA2ROM): - """Access class for ESP32S3 stub loader, runs on top of ROM. - - (Basically the same as ESP32StubLoader, but different base class. - Can possibly be made into a mixin.) - """ - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - STATUS_BYTES_LENGTH = 2 # same as ESP8266, different to ESP32 ROM - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - -ESP32S3BETA2ROM.STUB_CLASS = ESP32S3BETA2StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py deleted file mode 100644 index cc5ba200f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/esp8266.py +++ /dev/null @@ -1,190 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -from ..loader import ESPLoader -from ..util import FatalError, NotImplementedInROMError - - -class ESP8266ROM(ESPLoader): - """Access class for ESP8266 ROM bootloader""" - - CHIP_NAME = "ESP8266" - IS_STUB = False - - CHIP_DETECT_MAGIC_VALUE = [0xFFF0C101] - - # OTP ROM addresses - ESP_OTP_MAC0 = 0x3FF00050 - ESP_OTP_MAC1 = 0x3FF00054 - ESP_OTP_MAC3 = 0x3FF0005C - - SPI_REG_BASE = 0x60000200 - SPI_USR_OFFS = 0x1C - SPI_USR1_OFFS = 0x20 - SPI_USR2_OFFS = 0x24 - SPI_MOSI_DLEN_OFFS = None - SPI_MISO_DLEN_OFFS = None - SPI_W0_OFFS = 0x40 - - UART_CLKDIV_REG = 0x60000014 - - XTAL_CLK_DIVIDER = 2 - - FLASH_SIZES = { - "512KB": 0x00, - "256KB": 0x10, - "1MB": 0x20, - "2MB": 0x30, - "4MB": 0x40, - "2MB-c1": 0x50, - "4MB-c1": 0x60, - "8MB": 0x80, - "16MB": 0x90, - } - - FLASH_FREQUENCY = { - "80m": 0xF, - "40m": 0x0, - "26m": 0x1, - "20m": 0x2, - } - - BOOTLOADER_FLASH_OFFSET = 0 - - MEMORY_MAP = [ - [0x3FF00000, 0x3FF00010, "DPORT"], - [0x3FFE8000, 0x40000000, "DRAM"], - [0x40100000, 0x40108000, "IRAM"], - [0x40201010, 0x402E1010, "IROM"], - ] - - def get_efuses(self): - # Return the 128 bits of ESP8266 efuse as a single Python integer - result = self.read_reg(0x3FF0005C) << 96 - result |= self.read_reg(0x3FF00058) << 64 - result |= self.read_reg(0x3FF00054) << 32 - result |= self.read_reg(0x3FF00050) - return result - - def _get_flash_size(self, efuses): - # rX_Y = EFUSE_DATA_OUTX[Y] - r0_4 = (efuses & (1 << 4)) != 0 - r3_25 = (efuses & (1 << 121)) != 0 - r3_26 = (efuses & (1 << 122)) != 0 - r3_27 = (efuses & (1 << 123)) != 0 - - if r0_4 and not r3_25: - if not r3_27 and not r3_26: - return 1 - elif not r3_27 and r3_26: - return 2 - if not r0_4 and r3_25: - if not r3_27 and not r3_26: - return 2 - elif not r3_27 and r3_26: - return 4 - return -1 - - def get_chip_description(self): - efuses = self.get_efuses() - is_8285 = ( - efuses & ((1 << 4) | 1 << 80) - ) != 0 # One or the other efuse bit is set for ESP8285 - if is_8285: - flash_size = self._get_flash_size(efuses) - max_temp = ( - efuses & (1 << 5) - ) != 0 # This efuse bit identifies the max flash temperature - chip_name = { - 1: "ESP8285H08" if max_temp else "ESP8285N08", - 2: "ESP8285H16" if max_temp else "ESP8285N16", - }.get(flash_size, "ESP8285") - return chip_name - return "ESP8266EX" - - def get_chip_features(self): - features = ["WiFi"] - if "ESP8285" in self.get_chip_description(): - features += ["Embedded Flash"] - return features - - def flash_spi_attach(self, hspi_arg): - if self.IS_STUB: - super(ESP8266ROM, self).flash_spi_attach(hspi_arg) - else: - # ESP8266 ROM has no flash_spi_attach command in serial protocol, - # but flash_begin will do it - self.flash_begin(0, 0) - - def flash_set_parameters(self, size): - # not implemented in ROM, but OK to silently skip for ROM - if self.IS_STUB: - super(ESP8266ROM, self).flash_set_parameters(size) - - def chip_id(self): - """ - Read Chip ID from efuse - the equivalent of the SDK system_get_chip_id() func - """ - id0 = self.read_reg(self.ESP_OTP_MAC0) - id1 = self.read_reg(self.ESP_OTP_MAC1) - return (id0 >> 24) | ((id1 & 0xFFFFFF) << 8) - - def read_mac(self): - """Read MAC from OTP ROM""" - mac0 = self.read_reg(self.ESP_OTP_MAC0) - mac1 = self.read_reg(self.ESP_OTP_MAC1) - mac3 = self.read_reg(self.ESP_OTP_MAC3) - if mac3 != 0: - oui = ((mac3 >> 16) & 0xFF, (mac3 >> 8) & 0xFF, mac3 & 0xFF) - elif ((mac1 >> 16) & 0xFF) == 0: - oui = (0x18, 0xFE, 0x34) - elif ((mac1 >> 16) & 0xFF) == 1: - oui = (0xAC, 0xD0, 0x74) - else: - raise FatalError("Unknown OUI") - return oui + ((mac1 >> 8) & 0xFF, mac1 & 0xFF, (mac0 >> 24) & 0xFF) - - def get_erase_size(self, offset, size): - """Calculate an erase size given a specific size in bytes. - - Provides a workaround for the bootloader erase bug.""" - - sectors_per_block = 16 - sector_size = self.FLASH_SECTOR_SIZE - num_sectors = (size + sector_size - 1) // sector_size - start_sector = offset // sector_size - - head_sectors = sectors_per_block - (start_sector % sectors_per_block) - if num_sectors < head_sectors: - head_sectors = num_sectors - - if num_sectors < 2 * head_sectors: - return (num_sectors + 1) // 2 * sector_size - else: - return (num_sectors - head_sectors) * sector_size - - def override_vddsdio(self, new_voltage): - raise NotImplementedInROMError( - "Overriding VDDSDIO setting only applies to ESP32" - ) - - -class ESP8266StubLoader(ESP8266ROM): - """Access class for ESP8266 stub loader, runs on top of ROM.""" - - FLASH_WRITE_SIZE = 0x4000 # matches MAX_WRITE_BLOCK in stub_loader.c - IS_STUB = True - - def __init__(self, rom_loader): - self.secure_download_mode = rom_loader.secure_download_mode - self._port = rom_loader._port - self._trace_enabled = rom_loader._trace_enabled - self.flush_input() # resets _slip_reader - - def get_erase_size(self, offset, size): - return size # stub doesn't have same size bug as ROM loader - - -ESP8266ROM.STUB_CLASS = ESP8266StubLoader diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json deleted file mode 100644 index 37b9ae2ff..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1074521516, - "text": "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", - "text_start": 1074520064, - "data": "CMD8Pw==", - "data_start": 1073605544 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json deleted file mode 100644 index 988dea7ce..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077413328, - "text": "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", - "text_start": 1077411840, - "data": "DEDKPw==", - "data_start": 1070295976 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json deleted file mode 100644 index c132a8e8e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077413554, - "text": "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", - "text_start": 1077411840, - "data": "FEDIPw==", - "data_start": 1070164912 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json deleted file mode 100644 index 66a5e5b4f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1082131920, - "text": "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", - "text_start": 1082130432, - "data": "DACEQA==", - "data_start": 1082469288 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json deleted file mode 100644 index 730bd23f3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32c6beta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077413328, - "text": "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", - "text_start": 1077411840, - "data": "DEDIPw==", - "data_start": 1070164904 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json deleted file mode 100644 index bfb62c033..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta1.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077413328, - "text": "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", - "text_start": 1077411840, - "data": "DEDIPw==", - "data_start": 1070164904 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json deleted file mode 100644 index 4ab15211f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32h2beta2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077413328, - "text": "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", - "text_start": 1077411840, - "data": "DEDIPw==", - "data_start": 1070164904 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json deleted file mode 100644 index 7f804348f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1073907652, - "text": "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", - "text_start": 1073905664, - "data": "EAD9Pw==", - "data_start": 1073622012 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json deleted file mode 100644 index 94ff2b1fc..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077381644, - "text": "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", - "text_start": 1077379072, - "data": "EADKPw==", - "data_start": 1070279676 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json deleted file mode 100644 index 1c71662c9..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_32s3beta2.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1077381644, - "text": "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", - "text_start": 1077379072, - "data": "EADKPw==", - "data_start": 1070279676 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json b/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json deleted file mode 100644 index 98223b800..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/targets/stub_flasher/stub_flasher_8266.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "entry": 1074843652, - "text": "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", - "text_start": 1074843648, - "data": "CIH+PwUFBAACAwcAAwMLALnXEEDv1xBAHdgQQLrYEEBo5xBAHtkQQHTZEEDA2RBAaOcQQILaEED/2hBAwNsQQGjnEEBo5xBAWNwQQGjnEEA33xBAAOAQQDvgEEBo5xBAaOcQQNfgEEBo5xBAv+EQQGXiEECj4xBAY+QQQDTlEEBo5xBAaOcQQGjnEEBo5xBAYuYQQGjnEEBX5xBAkN0QQI/YEECm5RBAq9oQQPzZEEBo5xBA7OYQQDHnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQGjnEEBo5xBAaOcQQCLaEEBf2hBAvuUQQAEAAAACAAAAAwAAAAQAAAAFAAAABwAAAAkAAAANAAAAEQAAABkAAAAhAAAAMQAAAEEAAABhAAAAgQAAAMEAAAABAQAAgQEAAAECAAABAwAAAQQAAAEGAAABCAAAAQwAAAEQAAABGAAAASAAAAEwAAABQAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABAAAAAgAAAAIAAAADAAAAAwAAAAQAAAAEAAAABQAAAAUAAAAGAAAABgAAAAcAAAAHAAAACAAAAAgAAAAJAAAACQAAAAoAAAAKAAAACwAAAAsAAAAMAAAADAAAAA0AAAANAAAAAAAAAAAAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAANAAAADwAAABEAAAATAAAAFwAAABsAAAAfAAAAIwAAACsAAAAzAAAAOwAAAEMAAABTAAAAYwAAAHMAAACDAAAAowAAAMMAAADjAAAAAgEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAABAAAAAgAAAAIAAAACAAAAAgAAAAMAAAADAAAAAwAAAAMAAAAEAAAABAAAAAQAAAAEAAAABQAAAAUAAAAFAAAABQAAAAAAAAAAAAAAAAAAABAREgAIBwkGCgULBAwDDQIOAQ8AAQEAAAEAAAAEAAAA", - "data_start": 1073720488 -} \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py b/dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py deleted file mode 100644 index 91637caf4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/esptool/util.py +++ /dev/null @@ -1,157 +0,0 @@ -# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, -# Espressif Systems (Shanghai) CO LTD, other contributors as noted. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import struct -import sys - - -def byte(bitstr, index): - return bitstr[index] - - -def mask_to_shift(mask): - """Return the index of the least significant bit in the mask""" - shift = 0 - while mask & 0x1 == 0: - shift += 1 - mask >>= 1 - return shift - - -def div_roundup(a, b): - """Return a/b rounded up to nearest integer, - equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only - without possible floating point accuracy errors. - """ - return (int(a) + int(b) - 1) // int(b) - - -def flash_size_bytes(size): - """Given a flash size of the type passed in args.flash_size - (ie 512KB or 1MB) then return the size in bytes. - """ - if "MB" in size: - return int(size[: size.index("MB")]) * 1024 * 1024 - elif "KB" in size: - return int(size[: size.index("KB")]) * 1024 - else: - raise FatalError("Unknown size %s" % size) - - -def hexify(s, uppercase=True): - format_str = "%02X" if uppercase else "%02x" - return "".join(format_str % c for c in s) - - -def pad_to(data, alignment, pad_character=b"\xFF"): - """Pad to the next alignment boundary""" - pad_mod = len(data) % alignment - if pad_mod != 0: - data += pad_character * (alignment - pad_mod) - return data - - -def print_overwrite(message, last_line=False): - """Print a message, overwriting the currently printed line. - - If last_line is False, don't append a newline at the end - (expecting another subsequent call will overwrite this one.) - - After a sequence of calls with last_line=False, call once with last_line=True. - - If output is not a TTY (for example redirected a pipe), - no overwriting happens and this function is the same as print(). - """ - if sys.stdout.isatty(): - print("\r%s" % message, end="\n" if last_line else "") - else: - print(message) - - -class FatalError(RuntimeError): - """ - Wrapper class for runtime errors that aren't caused by internal bugs, but by - ESP ROM responses or input content. - """ - - def __init__(self, message): - RuntimeError.__init__(self, message) - - @staticmethod - def WithResult(message, result): - """ - Return a fatal error object that appends the hex values of - 'result' and its meaning as a string formatted argument. - """ - - err_defs = { - # ROM error codes - 0x101: "Out of memory", - 0x102: "Invalid argument", - 0x103: "Invalid state", - 0x104: "Invalid size", - 0x105: "Requested resource not found", - 0x106: "Operation or feature not supported", - 0x107: "Operation timed out", - 0x108: "Received response was invalid", - 0x109: "CRC or checksum was invalid", - 0x10A: "Version was invalid", - 0x10B: "MAC address was invalid", - # Flasher stub error codes - 0xC000: "Bad data length", - 0xC100: "Bad data checksum", - 0xC200: "Bad blocksize", - 0xC300: "Invalid command", - 0xC400: "Failed SPI operation", - 0xC500: "Failed SPI unlock", - 0xC600: "Not in flash mode", - 0xC700: "Inflate error", - 0xC800: "Not enough data", - 0xC900: "Too much data", - 0xFF00: "Command not implemented", - } - - err_code = struct.unpack(">H", result[:2]) - message += " (result was {}: {})".format( - hexify(result), err_defs.get(err_code[0], "Unknown result") - ) - return FatalError(message) - - -class NotImplementedInROMError(FatalError): - """ - Wrapper class for the error thrown when a particular ESP bootloader function - is not implemented in the ROM bootloader. - """ - - def __init__(self, bootloader, func): - FatalError.__init__( - self, - "%s ROM does not support function %s." - % (bootloader.CHIP_NAME, func.__name__), - ) - - -class NotSupportedError(FatalError): - def __init__(self, esp, function_name): - FatalError.__init__( - self, - "Function %s is not supported for %s." % (function_name, esp.CHIP_NAME), - ) - - -class UnsupportedCommandError(RuntimeError): - """ - Wrapper class for when ROM loader returns an invalid command response. - - Usually this indicates the loader is running in Secure Download Mode. - """ - - def __init__(self, esp, op): - if esp.secure_download_mode: - msg = "This command (0x%x) is not supported in Secure Download Mode" % op - else: - msg = "Invalid (unsupported) command 0x%x" % op - RuntimeError.__init__(self, msg) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/INSTALLER similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/INSTALLER rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/INSTALLER diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/LICENSE.txt b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/LICENSE.txt similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/LICENSE.txt rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/LICENSE.txt diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/METADATA similarity index 99% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/METADATA rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/METADATA index e935e1a56..984f9ad3f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/METADATA +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pip -Version: 22.3.1 +Version: 23.0.1 Summary: The PyPA recommended tool for installing Python packages. Home-page: https://pip.pypa.io/ Author: The pip developers diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/RECORD similarity index 81% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/RECORD index 33e08b099..8672b17e9 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/RECORD +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/RECORD @@ -1,15 +1,15 @@ -../../Scripts/pip.exe,sha256=C5XUgadRfynCyunDMP41Mc5O2O6AKV7DFTUBMae_n-s,108451 -../../Scripts/pip3.10.exe,sha256=C5XUgadRfynCyunDMP41Mc5O2O6AKV7DFTUBMae_n-s,108451 -../../Scripts/pip3.exe,sha256=C5XUgadRfynCyunDMP41Mc5O2O6AKV7DFTUBMae_n-s,108451 -pip-22.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip-22.3.1.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093 -pip-22.3.1.dist-info/METADATA,sha256=a9COYc5qzklDgbGlrKYkypMXon4A6IDgpeUTWLr7zzY,4072 -pip-22.3.1.dist-info/RECORD,, -pip-22.3.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -pip-22.3.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92 -pip-22.3.1.dist-info/entry_points.txt,sha256=ynZN1_707_L23Oa8_O5LOxEoccj1nDa4xHT5galfN7o,125 -pip-22.3.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pip/__init__.py,sha256=Z2hXGRMvmdhpmmqr0OW1fA2Jje8tnmU0uzibRoUF-w8,357 +../../Scripts/pip.exe,sha256=RNPZp9tbzP5xuG6yNb41E0flClucNFXbxRCfUSh_0F4,108451 +../../Scripts/pip3.10.exe,sha256=RNPZp9tbzP5xuG6yNb41E0flClucNFXbxRCfUSh_0F4,108451 +../../Scripts/pip3.exe,sha256=RNPZp9tbzP5xuG6yNb41E0flClucNFXbxRCfUSh_0F4,108451 +pip-23.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip-23.0.1.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093 +pip-23.0.1.dist-info/METADATA,sha256=POh89utz-H1e0K-xDY9CL9gs-x0MjH-AWxbhJG3aaVE,4072 +pip-23.0.1.dist-info/RECORD,, +pip-23.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pip-23.0.1.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 +pip-23.0.1.dist-info/entry_points.txt,sha256=w694mjHYSfmSoUVVSaHoQ9UkOBBdtKKIJbyDRLdKju8,124 +pip-23.0.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +pip/__init__.py,sha256=5yroedzc2dKKbcynDrHX8vBoLxqU27KmFvvHmdqQN9w,357 pip/__main__.py,sha256=mXwWDftNLMKfwVqKFWGE_uuBZvGSIiUELhLkeysIuZc,1198 pip/__pip-runner__.py,sha256=EnrfKmKMzWAdqg_JicLCOP9Y95Ux7zHh4ObvqLtQcjo,1444 pip/__pycache__/__init__.cpython-310.pyc,, @@ -25,7 +25,7 @@ pip/_internal/__pycache__/main.cpython-310.pyc,, pip/_internal/__pycache__/pyproject.cpython-310.pyc,, pip/_internal/__pycache__/self_outdated_check.cpython-310.pyc,, pip/_internal/__pycache__/wheel_builder.cpython-310.pyc,, -pip/_internal/build_env.py,sha256=gEAT8R6SuWbg2mcrsmOTKWMw_x5pedMzvSTxQS57JZs,10234 +pip/_internal/build_env.py,sha256=1ESpqw0iupS_K7phZK5zshVE5Czy9BtGLFU4W6Enva8,10243 pip/_internal/cache.py,sha256=C3n78VnBga9rjPXZqht_4A4d-T25poC7K0qBM7FHDhU,10734 pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132 pip/_internal/cli/__pycache__/__init__.cpython-310.pyc,, @@ -42,7 +42,7 @@ pip/_internal/cli/__pycache__/spinners.cpython-310.pyc,, pip/_internal/cli/__pycache__/status_codes.cpython-310.pyc,, pip/_internal/cli/autocompletion.py,sha256=wY2JPZY2Eji1vhR7bVo-yCBPJ9LCy6P80iOAhZD1Vi8,6676 pip/_internal/cli/base_command.py,sha256=t1D5x40Hfn9HnPnMt-iSxvqL14nht2olBCacW74pc-k,7842 -pip/_internal/cli/cmdoptions.py,sha256=Jlarlzz9qv9tC_tCaEbcc_jVvrPreFLBBUnDgoyWflw,29381 +pip/_internal/cli/cmdoptions.py,sha256=0OHXkgnppCtC4QyF28ZL8FBosVUXG5pWj2uzO1CgWhM,29497 pip/_internal/cli/command_context.py,sha256=RHgIPwtObh5KhMrd3YZTkl8zbVG-6Okml7YbFX4Ehg0,774 pip/_internal/cli/main.py,sha256=ioJ8IVlb2K1qLOxR-tXkee9lURhYV89CDM71MKag7YY,2472 pip/_internal/cli/main_parser.py,sha256=laDpsuBDl6kyfywp9eMMA9s84jfH2TJJn-vmL0GG90w,4338 @@ -74,18 +74,18 @@ pip/_internal/commands/cache.py,sha256=muaT0mbL-ZUpn6AaushVAipzTiMwE4nV2BLbJBwt_ pip/_internal/commands/check.py,sha256=0gjXR7j36xJT5cs2heYU_dfOfpnFfzX8OoPNNoKhqdM,1685 pip/_internal/commands/completion.py,sha256=H0TJvGrdsoleuIyQKzJbicLFppYx2OZA0BLNpQDeFjI,4129 pip/_internal/commands/configuration.py,sha256=NB5uf8HIX8-li95YLoZO09nALIWlLCHDF5aifSKcBn8,9815 -pip/_internal/commands/debug.py,sha256=kVjn-O1ixLk0webD0w9vfFFq_GCTUTd2hmLOnYtDCig,6573 +pip/_internal/commands/debug.py,sha256=AesEID-4gPFDWTwPiPaGZuD4twdT-imaGuMR5ZfSn8s,6591 pip/_internal/commands/download.py,sha256=LwKEyYMG2L67nQRyGo8hQdNEeMU2bmGWqJfcB8JDXas,5289 pip/_internal/commands/freeze.py,sha256=gCjoD6foBZPBAAYx5t8zZLkJhsF_ZRtnb3dPuD7beO8,2951 pip/_internal/commands/hash.py,sha256=EVVOuvGtoPEdFi8SNnmdqlCQrhCxV-kJsdwtdcCnXGQ,1703 pip/_internal/commands/help.py,sha256=gcc6QDkcgHMOuAn5UxaZwAStsRBrnGSn_yxjS57JIoM,1132 -pip/_internal/commands/index.py,sha256=1VVXXj5MsI2qH-N7uniQQyVkg-KCn_RdjiyiUmkUS5U,4762 -pip/_internal/commands/inspect.py,sha256=mRJ9aIkBQN0IJ7Um8pzaxAzVPIgL8KfWHx1fWKJgUAQ,3374 -pip/_internal/commands/install.py,sha256=_XbW0PyxtZCMMNqo8mDaOq3TBRiJNFM-94CR27mburc,31726 +pip/_internal/commands/index.py,sha256=cGQVSA5dAs7caQ9sz4kllYvaI4ZpGiq1WhCgaImXNSA,4793 +pip/_internal/commands/inspect.py,sha256=2wSPt9yfr3r6g-s2S5L6PvRtaHNVyb4TuodMStJ39cw,3188 +pip/_internal/commands/install.py,sha256=3vT9tnHOV-p6dPMaKDqzivqmcq_kPAI-jVkxOEwN5C4,32389 pip/_internal/commands/list.py,sha256=Fk1TSxB33NlRS4qlLQ0xwnytnF9-zkQJbKQYv2xc4Q4,12343 pip/_internal/commands/search.py,sha256=sbBZiARRc050QquOKcCvOr2K3XLsoYebLKZGRi__iUI,5697 -pip/_internal/commands/show.py,sha256=CJI8q4SSY0X346K1hi4Th8Nbyhl4nxPTBJUuzOlTaYE,6129 -pip/_internal/commands/uninstall.py,sha256=0JQhifYxecNrJAwoILFwjm9V1V3liXzNT-y4bgRXXPw,3680 +pip/_internal/commands/show.py,sha256=t5jia4zcYJRJZy4U_Von7zMl03hJmmcofj6oDNTnj7Y,6419 +pip/_internal/commands/uninstall.py,sha256=OIqO9tqadY8kM4HwhFf1Q62fUIp7v8KDrTRo8yWMz7Y,3886 pip/_internal/commands/wheel.py,sha256=mbFJd4dmUfrVFJkQbK8n2zHyRcD3AI91f7EUo9l3KYg,7396 pip/_internal/configuration.py,sha256=uBKTus43pDIO6IzT2mLWQeROmHhtnoabhniKNjPYvD0,13529 pip/_internal/distributions/__init__.py,sha256=Hq6kt6gXBgjNit5hTTWLAzeCNOKoB-N0pGYSqehrli8,858 @@ -98,23 +98,23 @@ pip/_internal/distributions/base.py,sha256=jrF1Vi7eGyqFqMHrieh1PIOrGU7KeCxhYPZnb pip/_internal/distributions/installed.py,sha256=NI2OgsgH9iBq9l5vB-56vOg5YsybOy-AU4VE5CSCO2I,729 pip/_internal/distributions/sdist.py,sha256=SQBdkatXSigKGG_SaD0U0p1Jwdfrg26UCNcHgkXZfdA,6494 pip/_internal/distributions/wheel.py,sha256=m-J4XO-gvFerlYsFzzSXYDvrx8tLZlJFTCgDxctn8ig,1164 -pip/_internal/exceptions.py,sha256=BfvcyN2iEv3Sf00SVmSk59lEeZEBHELqkuoN2KeIWKc,20942 +pip/_internal/exceptions.py,sha256=cU4dz7x-1uFGrf2A1_Np9tKcy599bRJKRJkikgARxW4,24244 pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30 pip/_internal/index/__pycache__/__init__.cpython-310.pyc,, pip/_internal/index/__pycache__/collector.cpython-310.pyc,, pip/_internal/index/__pycache__/package_finder.cpython-310.pyc,, pip/_internal/index/__pycache__/sources.cpython-310.pyc,, -pip/_internal/index/collector.py,sha256=Pb9FW9STH2lwaApCIdMCivsbPP5pSYQp5bh3nLQBkDU,16503 -pip/_internal/index/package_finder.py,sha256=kmcMu5_i-BP6v3NQGY0_am1ezxM2Gk4t00arZMmm4sc,37596 +pip/_internal/index/collector.py,sha256=3OmYZ3tCoRPGOrELSgQWG-03M-bQHa2-VCA3R_nJAaU,16504 +pip/_internal/index/package_finder.py,sha256=rrUw4vj7QE_eMt022jw--wQiKznMaUgVBkJ1UCrVUxo,37873 pip/_internal/index/sources.py,sha256=SVyPitv08-Qalh2_Bk5diAJ9GAA_d-a93koouQodAG0,6557 -pip/_internal/locations/__init__.py,sha256=QhB-Y6TNyaU010cimm2T4wM5loe8oRdjLwJ6xmsGc-k,17552 +pip/_internal/locations/__init__.py,sha256=Dh8LJWG8LRlDK4JIj9sfRF96TREzE--N_AIlx7Tqoe4,15365 pip/_internal/locations/__pycache__/__init__.cpython-310.pyc,, pip/_internal/locations/__pycache__/_distutils.cpython-310.pyc,, pip/_internal/locations/__pycache__/_sysconfig.cpython-310.pyc,, pip/_internal/locations/__pycache__/base.cpython-310.pyc,, -pip/_internal/locations/_distutils.py,sha256=wgHDvHGNZHtlcHkQjYovHzkEUBzisR0iOh7OqCIkB5g,6302 -pip/_internal/locations/_sysconfig.py,sha256=nM-DiVHXWTxippdmN0MGVl5r7OIfIMy3vgDMlo8c_oo,7867 -pip/_internal/locations/base.py,sha256=ufyDqPwZ4jLbScD44u8AwTVI-3ft8O78UGrroQI5f68,2573 +pip/_internal/locations/_distutils.py,sha256=cmi6h63xYNXhQe7KEWEMaANjHFy5yQOPt_1_RCWyXMY,6100 +pip/_internal/locations/_sysconfig.py,sha256=jyNVtUfMIf0mtyY-Xp1m9yQ8iwECozSVVFmjkN9a2yw,7680 +pip/_internal/locations/base.py,sha256=RQiPi1d4FVM2Bxk04dQhXZ2PqkeljEL2fZZ9SYqIQ78,2556 pip/_internal/main.py,sha256=r-UnUe8HLo5XFJz8inTcOOTiu_sxNhgHb6VwlGUllOI,340 pip/_internal/metadata/__init__.py,sha256=84j1dPJaIoz5Q2ZTPi0uB1iaDAHiUNfKtYSGQCfFKpo,4280 pip/_internal/metadata/__pycache__/__init__.cpython-310.pyc,, @@ -146,11 +146,11 @@ pip/_internal/models/__pycache__/selection_prefs.cpython-310.pyc,, pip/_internal/models/__pycache__/target_python.cpython-310.pyc,, pip/_internal/models/__pycache__/wheel.cpython-310.pyc,, pip/_internal/models/candidate.py,sha256=6pcABsaR7CfIHlbJbr2_kMkVJFL_yrYjTx6SVWUnCPQ,990 -pip/_internal/models/direct_url.py,sha256=HLO0sL2aYB6n45bwmd72TDN05sLHJlOQI8M01l2SH3I,5877 +pip/_internal/models/direct_url.py,sha256=f3WiKUwWPdBkT1xm7DlolS32ZAMYh3jbkkVH-BUON5A,6626 pip/_internal/models/format_control.py,sha256=DJpMYjxeYKKQdwNcML2_F0vtAh-qnKTYe-CpTxQe-4g,2520 pip/_internal/models/index.py,sha256=tYnL8oxGi4aSNWur0mG8DAP7rC6yuha_MwJO8xw0crI,1030 -pip/_internal/models/installation_report.py,sha256=ad1arqtxrSFBvWnm6mRqmG12HLV3pZZcZcHrlTFIiqU,2617 -pip/_internal/models/link.py,sha256=9HWL14UQTMxRCnY6dmAz09rGElJrMAcHn2OJZCBx0tk,18083 +pip/_internal/models/installation_report.py,sha256=Hymmzv9-e3WhtewYm2NIOeMyAB6lXp736mpYqb9scZ0,2617 +pip/_internal/models/link.py,sha256=nfybVSpXgVHeU0MkC8hMkN2IgMup8Pdaudg74_sQEC8,18602 pip/_internal/models/scheme.py,sha256=3EFQp_ICu_shH1-TBqhl0QAusKCPDFOlgHFeN4XowWs,738 pip/_internal/models/search_scope.py,sha256=iGPQQ6a4Lau8oGQ_FWj8aRLik8A21o03SMO5KnSt-Cg,4644 pip/_internal/models/selection_prefs.py,sha256=KZdi66gsR-_RUXUr9uejssk3rmTHrQVJWeNA2sV-VSY,1907 @@ -165,7 +165,7 @@ pip/_internal/network/__pycache__/lazy_wheel.cpython-310.pyc,, pip/_internal/network/__pycache__/session.cpython-310.pyc,, pip/_internal/network/__pycache__/utils.cpython-310.pyc,, pip/_internal/network/__pycache__/xmlrpc.cpython-310.pyc,, -pip/_internal/network/auth.py,sha256=a3C7Xaa8kTJjXkdi_wrUjqaySc8Z9Yz7U6QIbXfzMyc,12190 +pip/_internal/network/auth.py,sha256=MQVP0k4hUXk8ReYEfsGQ5t7_TS7cNHQuaHJuBlJLHxU,16507 pip/_internal/network/cache.py,sha256=hgXftU-eau4MWxHSLquTMzepYq5BPC2zhCkhN3glBy8,2145 pip/_internal/network/download.py,sha256=HvDDq9bVqaN3jcS3DyVJHP7uTqFzbShdkf7NFSoHfkw,6096 pip/_internal/network/lazy_wheel.py,sha256=PbPyuleNhtEq6b2S7rufoGXZWMD15FAGL4XeiAQ8FxA,7638 @@ -187,13 +187,13 @@ pip/_internal/operations/build/__pycache__/wheel.cpython-310.pyc,, pip/_internal/operations/build/__pycache__/wheel_editable.cpython-310.pyc,, pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-310.pyc,, pip/_internal/operations/build/build_tracker.py,sha256=vf81EwomN3xe9G8qRJED0VGqNikmRQRQoobNsxi5Xrs,4133 -pip/_internal/operations/build/metadata.py,sha256=ES_uRmAvhrNm_nDTpZxshBfUsvnXtkj-g_4rZrH9Rww,1404 -pip/_internal/operations/build/metadata_editable.py,sha256=_Rai0VZjxoeJUkjkuICrq45LtjwFoDOveosMYH43rKc,1456 +pip/_internal/operations/build/metadata.py,sha256=9S0CUD8U3QqZeXp-Zyt8HxwU90lE4QrnYDgrqZDzBnc,1422 +pip/_internal/operations/build/metadata_editable.py,sha256=VLL7LvntKE8qxdhUdEJhcotFzUsOSI8NNS043xULKew,1474 pip/_internal/operations/build/metadata_legacy.py,sha256=o-eU21As175hDC7dluM1fJJ_FqokTIShyWpjKaIpHZw,2198 -pip/_internal/operations/build/wheel.py,sha256=AO9XnTGhTgHtZmU8Dkbfo1OGr41rBuSDjIgAa4zUKgE,1063 -pip/_internal/operations/build/wheel_editable.py,sha256=TVETY-L_M_dSEKBhTIcQOP75zKVXw8tuq1U354Mm30A,1405 +pip/_internal/operations/build/wheel.py,sha256=sT12FBLAxDC6wyrDorh8kvcZ1jG5qInCRWzzP-UkJiQ,1075 +pip/_internal/operations/build/wheel_editable.py,sha256=yOtoH6zpAkoKYEUtr8FhzrYnkNHQaQBjWQ2HYae1MQg,1417 pip/_internal/operations/build/wheel_legacy.py,sha256=C9j6rukgQI1n_JeQLoZGuDdfUwzCXShyIdPTp6edbMQ,3064 -pip/_internal/operations/check.py,sha256=ca4O9CkPt9Em9sLCf3H0iVt1GIcW7M8C0U5XooaBuT4,5109 +pip/_internal/operations/check.py,sha256=WsN7z0_QSgJjw0JsWWcqOHj4wWTaFv0J7mxgUByDCOg,5122 pip/_internal/operations/freeze.py,sha256=mwTZ2uML8aQgo3k8MR79a7SZmmmvdAJqdyaknKbavmg,9784 pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51 pip/_internal/operations/install/__pycache__/__init__.cpython-310.pyc,, @@ -204,7 +204,7 @@ pip/_internal/operations/install/editable_legacy.py,sha256=ee4kfJHNuzTdKItbfAsNO pip/_internal/operations/install/legacy.py,sha256=cHdcHebyzf8w7OaOLwcsTNSMSSV8WBoAPFLay_9CjE8,4105 pip/_internal/operations/install/wheel.py,sha256=CxzEg2wTPX4SxNTPIx0ozTqF1X7LhpCyP3iM2FjcKUE,27407 pip/_internal/operations/prepare.py,sha256=BeYXrLFpRoV5XBnRXQHxRA2plyC36kK9Pms5D9wjCo4,25091 -pip/_internal/pyproject.py,sha256=ob0Gb0l12YLZNxjdpZGRfWHgjqhZTnSVv96RuJyNOfs,7074 +pip/_internal/pyproject.py,sha256=QqSZR5AGwtf3HTa8NdbDq2yj9T2r9S2h9gnU4aX2Kvg,6987 pip/_internal/req/__init__.py,sha256=rUQ9d_Sh3E5kNYqX9pkN0D06YL-LrtcbJQ-LiIonq08,2807 pip/_internal/req/__pycache__/__init__.cpython-310.pyc,, pip/_internal/req/__pycache__/constructors.cpython-310.pyc,, @@ -214,7 +214,7 @@ pip/_internal/req/__pycache__/req_set.cpython-310.pyc,, pip/_internal/req/__pycache__/req_uninstall.cpython-310.pyc,, pip/_internal/req/constructors.py,sha256=ypjtq1mOQ3d2mFkFPMf_6Mr8SLKeHQk3tUKHA1ddG0U,16611 pip/_internal/req/req_file.py,sha256=N6lPO3c0to_G73YyGAnk7VUYmed5jV4Qxgmt1xtlXVg,17646 -pip/_internal/req/req_install.py,sha256=4tzyVGPHJ1-GXowm6PBT52BGIlbc4w7fhVqf-55bmRg,35600 +pip/_internal/req/req_install.py,sha256=X4WNQlTtvkeATwWdSiJcNLihwbYI_EnGDgE99p-Aa00,35763 pip/_internal/req/req_set.py,sha256=j3esG0s6SzoVReX9rWn4rpYNtyET_fwxbwJPRimvRxo,2858 pip/_internal/req/req_uninstall.py,sha256=ZFQfgSNz6H1BMsgl87nQNr2iaQCcbFcmXpW8rKVQcic,24045 pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 @@ -243,7 +243,7 @@ pip/_internal/resolution/resolvelib/provider.py,sha256=Vd4jW_NnyifB-HMkPYtZIO70M pip/_internal/resolution/resolvelib/reporter.py,sha256=3ZVVYrs5PqvLFJkGLcuXoMK5mTInFzl31xjUpDBpZZk,2526 pip/_internal/resolution/resolvelib/requirements.py,sha256=B1ndvKPSuyyyTEXt9sKhbwminViSWnBrJa7qO2ln4Z0,5455 pip/_internal/resolution/resolvelib/resolver.py,sha256=nYZ9bTFXj5c1ILKnkSgU7tUCTYyo5V5J-J0sKoA7Wzg,11533 -pip/_internal/self_outdated_check.py,sha256=R3MmjCyUt_lkUNMc6p3xVSx7vX28XiDh3VDs5OrYn6Q,8020 +pip/_internal/self_outdated_check.py,sha256=pnqBuKKZQ8OxKP0MaUUiDHl3AtyoMJHHG4rMQ7YcYXY,8167 pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_internal/utils/__pycache__/__init__.cpython-310.pyc,, pip/_internal/utils/__pycache__/_log.cpython-310.pyc,, @@ -281,7 +281,7 @@ pip/_internal/utils/datetime.py,sha256=m21Y3wAtQc-ji6Veb6k_M5g6A0ZyFI4egchTdnwh- pip/_internal/utils/deprecation.py,sha256=OLc7GzDwPob9y8jscDYCKUNBV-9CWwqFplBOJPLOpBM,5764 pip/_internal/utils/direct_url_helpers.py,sha256=6F1tc2rcKaCZmgfVwsE6ObIe_Pux23mUVYA-2D9wCFc,3206 pip/_internal/utils/distutils_args.py,sha256=bYUt4wfFJRaeGO4VHia6FNaA8HlYXMcKuEq1zYijY5g,1115 -pip/_internal/utils/egg_link.py,sha256=5MVlpz5LirT4iLQq86OYzjXaYF0D4Qk1dprEI7ThST4,2203 +pip/_internal/utils/egg_link.py,sha256=ZryCchR_yQSCsdsMkCpxQjjLbQxObA5GDtLG0RR5mGc,2118 pip/_internal/utils/encoding.py,sha256=qqsXDtiwMIjXMEiIVSaOjwH5YmirCaK-dIzb6-XJsL0,1169 pip/_internal/utils/entrypoints.py,sha256=YlhLTRl2oHBAuqhc-zmL7USS67TPWVHImjeAQHreZTQ,3064 pip/_internal/utils/filesystem.py,sha256=RhMIXUaNVMGjc3rhsDahWQ4MavvEQDdqXqgq-F6fpw8,5122 @@ -290,15 +290,15 @@ pip/_internal/utils/glibc.py,sha256=tDfwVYnJCOC0BNVpItpy8CGLP9BjkxFHdl0mTS0J7fc, pip/_internal/utils/hashes.py,sha256=1WhkVNIHNfuYLafBHThIjVKGplxFJXSlQtuG2mXNlJI,4831 pip/_internal/utils/inject_securetransport.py,sha256=o-QRVMGiENrTJxw3fAhA7uxpdEdw6M41TjHYtSVRrcg,795 pip/_internal/utils/logging.py,sha256=U2q0i1n8hPS2gQh8qcocAg5dovGAa_bR24akmXMzrk4,11632 -pip/_internal/utils/misc.py,sha256=49Rs2NgrD4JGTKFt0farCm7FIAi-rjyoxgioArhCW_0,21617 +pip/_internal/utils/misc.py,sha256=XLtMDOmy8mWiNLuPIhxPdO1bWIleLdN6JnWDZsXfTgE,22253 pip/_internal/utils/models.py,sha256=5GoYU586SrxURMvDn_jBMJInitviJg4O5-iOU-6I0WY,1193 pip/_internal/utils/packaging.py,sha256=5Wm6_x7lKrlqVjPI5MBN_RurcRHwVYoQ7Ksrs84de7s,2108 pip/_internal/utils/setuptools_build.py,sha256=4i3CuS34yNrkePnZ73rR47pyDzpZBo-SX9V5PNDSSHY,5662 -pip/_internal/utils/subprocess.py,sha256=MYySbvY7qBevRxq_RFfOsDqG4vMqrB4vDoL_eyPE6Bo,9197 +pip/_internal/utils/subprocess.py,sha256=0EMhgfPGFk8FZn6Qq7Hp9PN6YHuQNWiVby4DXcTCON4,9200 pip/_internal/utils/temp_dir.py,sha256=aCX489gRa4Nu0dMKRFyGhV6maJr60uEynu5uCbKR4Qg,7702 pip/_internal/utils/unpacking.py,sha256=SBb2iV1crb89MDRTEKY86R4A_UOWApTQn9VQVcMDOlE,8821 pip/_internal/utils/urls.py,sha256=AhaesUGl-9it6uvG6fsFPOr9ynFpGaTMk4t5XTX7Z_Q,1759 -pip/_internal/utils/virtualenv.py,sha256=4_48qMzCwB_F5jIK5BC_ua7uiAMVifmQWU9NdaGUoVA,3459 +pip/_internal/utils/virtualenv.py,sha256=S6f7csYorRpiD6cvn3jISZYc3I8PJC43H5iMFpRAEDU,3456 pip/_internal/utils/wheel.py,sha256=lXOgZyTlOm5HmK8tw5iw0A3_5A6wRzsXHOaQkIvvloU,4549 pip/_internal/vcs/__init__.py,sha256=UAqvzpbi0VbZo3Ub6skEeZAw-ooIZR-zX_WpCbxyCoU,596 pip/_internal/vcs/__pycache__/__init__.cpython-310.pyc,, @@ -307,10 +307,10 @@ pip/_internal/vcs/__pycache__/git.cpython-310.pyc,, pip/_internal/vcs/__pycache__/mercurial.cpython-310.pyc,, pip/_internal/vcs/__pycache__/subversion.cpython-310.pyc,, pip/_internal/vcs/__pycache__/versioncontrol.cpython-310.pyc,, -pip/_internal/vcs/bazaar.py,sha256=zq-Eu2NtJffc6kOsyv2kmRTnKg9qeIXE-KH5JeKck70,3518 +pip/_internal/vcs/bazaar.py,sha256=j0oin0fpGRHcCFCxEcpPCQoFEvA-DMLULKdGP8Nv76o,3519 pip/_internal/vcs/git.py,sha256=mjhwudCx9WlLNkxZ6_kOKmueF0rLoU2i1xeASKF6yiQ,18116 pip/_internal/vcs/mercurial.py,sha256=Bzbd518Jsx-EJI0IhIobiQqiRsUv5TWYnrmRIFWE0Gw,5238 -pip/_internal/vcs/subversion.py,sha256=AeUVE9d9qp-0QSOMiUvuFHy1TK950E3QglN7ipP13sI,11728 +pip/_internal/vcs/subversion.py,sha256=vhZs8L-TNggXqM1bbhl-FpbxE3TrIB6Tgnx8fh3S2HE,11729 pip/_internal/vcs/versioncontrol.py,sha256=KUOc-hN51em9jrqxKwUR3JnkgSE-xSOqMiiJcSaL6B8,22811 pip/_internal/wheel_builder.py,sha256=8cObBCu4mIsMJqZM7xXI9DO3vldiAnRNa1Gt6izPPTs,13079 pip/_vendor/__init__.py,sha256=fNxOSVD0auElsD8fN9tuq5psfgMQ-RFBtD4X5gjlRkg,4966 @@ -343,14 +343,14 @@ pip/_vendor/cachecontrol/filewrapper.py,sha256=X4BAQOO26GNOR7nH_fhTzAfeuct2rBQcx pip/_vendor/cachecontrol/heuristics.py,sha256=8kAyuZLSCyEIgQr6vbUwfhpqg9ows4mM0IV6DWazevI,4154 pip/_vendor/cachecontrol/serialize.py,sha256=_U1NU_C-SDgFzkbAxAsPDgMTHeTWZZaHCQnZN_jh0U8,7105 pip/_vendor/cachecontrol/wrapper.py,sha256=X3-KMZ20Ho3VtqyVaXclpeQpFzokR5NE8tZSfvKVaB8,774 -pip/_vendor/certifi/__init__.py,sha256=luDjIGxDSrQ9O0zthdz5Lnt069Z_7eR1GIEefEaf-Ys,94 +pip/_vendor/certifi/__init__.py,sha256=bK_nm9bLJzNvWZc2oZdiTwg2KWD4HSPBWGaM0zUDvMw,94 pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255 pip/_vendor/certifi/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/certifi/__pycache__/__main__.cpython-310.pyc,, pip/_vendor/certifi/__pycache__/core.cpython-310.pyc,, -pip/_vendor/certifi/cacert.pem,sha256=3l8CcWt_qL42030rGieD3SLufICFX0bYtGhDl_EXVPI,286370 +pip/_vendor/certifi/cacert.pem,sha256=LBHDzgj_xA05AxnHK8ENT5COnGNElNZe0svFUHMf1SQ,275233 pip/_vendor/certifi/core.py,sha256=ZwiOsv-sD_ouU1ft8wy_xZ3LQ7UbcVzyqj2XNyrsZis,4279 -pip/_vendor/chardet/__init__.py,sha256=9-r0i294avRciob2HKVcKf6GJmXPHpgMqIijVrqHBDU,3705 +pip/_vendor/chardet/__init__.py,sha256=57R-HSxj0PWmILMN0GFmUNqEMfrEVSamXyjD-W6_fbs,4797 pip/_vendor/chardet/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/big5freq.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/big5prober.cpython-310.pyc,, @@ -358,6 +358,7 @@ pip/_vendor/chardet/__pycache__/chardistribution.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/charsetprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-310.pyc,, +pip/_vendor/chardet/__pycache__/codingstatemachinedict.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/cp949prober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/enums.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/escprober.cpython-310.pyc,, @@ -382,9 +383,11 @@ pip/_vendor/chardet/__pycache__/langrussianmodel.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/langthaimodel.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/latin1prober.cpython-310.pyc,, +pip/_vendor/chardet/__pycache__/macromanprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/mbcssm.cpython-310.pyc,, +pip/_vendor/chardet/__pycache__/resultdict.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/sjisprober.cpython-310.pyc,, @@ -393,31 +396,32 @@ pip/_vendor/chardet/__pycache__/utf1632prober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/utf8prober.cpython-310.pyc,, pip/_vendor/chardet/__pycache__/version.cpython-310.pyc,, pip/_vendor/chardet/big5freq.py,sha256=ltcfP-3PjlNHCoo5e4a7C4z-2DhBTXRfY6jbMbB7P30,31274 -pip/_vendor/chardet/big5prober.py,sha256=neUXIlq35507yibstiznZWFzyNcMn6EXrqJaUJVPWKg,1741 -pip/_vendor/chardet/chardistribution.py,sha256=M9NTKdM72KieFKy4TT5eml4PP0WaVcXuY5PpWSFD0FA,9608 -pip/_vendor/chardet/charsetgroupprober.py,sha256=CaIBAmNitEsYuSgMvgAsMREN4cLxMj5OYwMhVo6MAxk,3817 -pip/_vendor/chardet/charsetprober.py,sha256=Eo3w8sCmbvnVKOGNW1iy50KATVs8xV-gF7cQ0VG85dQ,4801 +pip/_vendor/chardet/big5prober.py,sha256=lPMfwCX6v2AaPgvFh_cSWZcgLDbWiFCHLZ_p9RQ9uxE,1763 +pip/_vendor/chardet/chardistribution.py,sha256=13B8XUG4oXDuLdXvfbIWwLFeR-ZU21AqTS1zcdON8bU,10032 +pip/_vendor/chardet/charsetgroupprober.py,sha256=UKK3SaIZB2PCdKSIS0gnvMtLR9JJX62M-fZJu3OlWyg,3915 +pip/_vendor/chardet/charsetprober.py,sha256=L3t8_wIOov8em-vZWOcbkdsrwe43N6_gqNh5pH7WPd4,5420 pip/_vendor/chardet/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_vendor/chardet/cli/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-310.pyc,, -pip/_vendor/chardet/cli/chardetect.py,sha256=1qMxT3wrp5vP6ugSf1-Zz3BWwlbCWJ0jzeCuhgX85vw,2406 -pip/_vendor/chardet/codingstatemachine.py,sha256=BiGR9kgTYbS4gJI5qBmE52HMOBOR_roDvXf7aIehdEk,3559 -pip/_vendor/chardet/cp949prober.py,sha256=kCQEaOCzMntqv7pAyXEobWTRgIUxYfoiUr0btXO1nI8,1838 -pip/_vendor/chardet/enums.py,sha256=Rodw4p61Vg9U-oCo6eUuT7uDzKwIbCaA15HwbvCoCNk,1619 -pip/_vendor/chardet/escprober.py,sha256=girD61r3NsQLnMQXsWWBU4hHuRJzTH3V7-VfTUr-nQY,3864 -pip/_vendor/chardet/escsm.py,sha256=0Vs4iPPovberMoSxxnK5pI161Xf-mtKgOl14g5Xc7zg,12021 -pip/_vendor/chardet/eucjpprober.py,sha256=pGgs4lINwCEDV2bxqIZ6hXpaj2j4l2oLsMx6kuOK_zQ,3676 +pip/_vendor/chardet/cli/chardetect.py,sha256=zibMVg5RpKb-ME9_7EYG4ZM2Sf07NHcQzZ12U-rYJho,3242 +pip/_vendor/chardet/codingstatemachine.py,sha256=K7k69sw3jY5DmTXoSJQVsUtFIQKYPQVOSJJhBuGv_yE,3732 +pip/_vendor/chardet/codingstatemachinedict.py,sha256=0GY3Hi2qIZvDrOOJ3AtqppM1RsYxr_66ER4EHjuMiMc,542 +pip/_vendor/chardet/cp949prober.py,sha256=0jKRV7fECuWI16rNnks0ZECKA1iZYCIEaP8A1ZvjUSI,1860 +pip/_vendor/chardet/enums.py,sha256=TzECiZoCKNMqgwU76cPCeKWFBqaWvAdLMev5_bCkhY8,1683 +pip/_vendor/chardet/escprober.py,sha256=Kho48X65xE0scFylIdeJjM2bcbvRvv0h0WUbMWrJD3A,4006 +pip/_vendor/chardet/escsm.py,sha256=AqyXpA2FQFD7k-buBty_7itGEYkhmVa8X09NLRul3QM,12176 +pip/_vendor/chardet/eucjpprober.py,sha256=5KYaM9fsxkRYzw1b5k0fL-j_-ezIw-ij9r97a9MHxLY,3934 pip/_vendor/chardet/euckrfreq.py,sha256=3mHuRvXfsq_QcQysDQFb8qSudvTiol71C6Ic2w57tKM,13566 -pip/_vendor/chardet/euckrprober.py,sha256=qBuSS2zXWaoUmGdzz3owAnD1GNhuKR_8bYzDC3yxe6I,1731 +pip/_vendor/chardet/euckrprober.py,sha256=hiFT6wM174GIwRvqDsIcuOc-dDsq2uPKMKbyV8-1Xnc,1753 pip/_vendor/chardet/euctwfreq.py,sha256=2alILE1Lh5eqiFJZjzRkMQXolNJRHY5oBQd-vmZYFFM,36913 -pip/_vendor/chardet/euctwprober.py,sha256=SLnCoJC94jZL8PJio60Q8PZACJA1rVPtUdWMa1W8Pwk,1731 +pip/_vendor/chardet/euctwprober.py,sha256=NxbpNdBtU0VFI0bKfGfDkpP7S2_8_6FlO87dVH0ogws,1753 pip/_vendor/chardet/gb2312freq.py,sha256=49OrdXzD-HXqwavkqjo8Z7gvs58hONNzDhAyMENNkvY,20735 -pip/_vendor/chardet/gb2312prober.py,sha256=NS_i52jZE0TnWGkKqFduvu9fzW0nMcS2XbYJ8qSX8hY,1737 -pip/_vendor/chardet/hebrewprober.py,sha256=1l1hXF8-2IWDrPkf85UvAO1GVtMfY1r11kDgOqa-gU4,13919 +pip/_vendor/chardet/gb2312prober.py,sha256=KPEBueaSLSvBpFeINMu0D6TgHcR90e5PaQawifzF4o0,1759 +pip/_vendor/chardet/hebrewprober.py,sha256=96T_Lj_OmW-fK7JrSHojYjyG3fsGgbzkoTNleZ3kfYE,14537 pip/_vendor/chardet/jisfreq.py,sha256=mm8tfrwqhpOd3wzZKS4NJqkYBQVcDfTM2JiQ5aW932E,25796 pip/_vendor/chardet/johabfreq.py,sha256=dBpOYG34GRX6SL8k_LbS9rxZPMjLjoMlgZ03Pz5Hmqc,42498 -pip/_vendor/chardet/johabprober.py,sha256=C18osd4vMPfy9facw-Y1Lor_9UrW0PeV-zxM2fu441c,1730 -pip/_vendor/chardet/jpcntx.py,sha256=m1gDpPkRca4EDwym8XSL5YdoILFnFsDbNBYMQV7_-NE,26797 +pip/_vendor/chardet/johabprober.py,sha256=O1Qw9nVzRnun7vZp4UZM7wvJSv9W941mEU9uDMnY3DU,1752 +pip/_vendor/chardet/jpcntx.py,sha256=uhHrYWkLxE_rF5OkHKInm0HUsrjgKHHVQvtt3UcvotA,27055 pip/_vendor/chardet/langbulgarianmodel.py,sha256=vmbvYFP8SZkSxoBvLkFqKiH1sjma5ihk3PTpdy71Rr4,104562 pip/_vendor/chardet/langgreekmodel.py,sha256=JfB7bupjjJH2w3X_mYnQr9cJA_7EuITC2cRW13fUjeI,98484 pip/_vendor/chardet/langhebrewmodel.py,sha256=3HXHaLQPNAGcXnJjkIJfozNZLTvTJmf4W5Awi6zRRKc,98196 @@ -425,22 +429,24 @@ pip/_vendor/chardet/langhungarianmodel.py,sha256=WxbeQIxkv8YtApiNqxQcvj-tMycsoI4 pip/_vendor/chardet/langrussianmodel.py,sha256=s395bTZ87ESTrZCOdgXbEjZ9P1iGPwCl_8xSsac_DLY,128035 pip/_vendor/chardet/langthaimodel.py,sha256=7bJlQitRpTnVGABmbSznHnJwOHDy3InkTvtFUx13WQI,102774 pip/_vendor/chardet/langturkishmodel.py,sha256=XY0eGdTIy4eQ9Xg1LVPZacb-UBhHBR-cq0IpPVHowKc,95372 -pip/_vendor/chardet/latin1prober.py,sha256=u_iGcQMUcZLXvj4B_WXx4caA0C5oaE2Qj1KTpz_RQ1I,5260 -pip/_vendor/chardet/mbcharsetprober.py,sha256=iKKuB6o_FF80NynRLBDT0UtwOnpLqmL_OspRPMib7CM,3367 -pip/_vendor/chardet/mbcsgroupprober.py,sha256=1D_kp9nv2_NQRddq9I2WDvB35OJh7Tfpo-OYTnL3B5o,2056 -pip/_vendor/chardet/mbcssm.py,sha256=EfORNu1WXgnFvpFarU8uJHS8KFif63xmgrHOB4DdDdY,30068 +pip/_vendor/chardet/latin1prober.py,sha256=p15EEmFbmQUwbKLC7lOJVGHEZwcG45ubEZYTGu01J5g,5380 +pip/_vendor/chardet/macromanprober.py,sha256=9anfzmY6TBfUPDyBDOdY07kqmTHpZ1tK0jL-p1JWcOY,6077 +pip/_vendor/chardet/mbcharsetprober.py,sha256=Wr04WNI4F3X_VxEverNG-H25g7u-MDDKlNt-JGj-_uU,3715 +pip/_vendor/chardet/mbcsgroupprober.py,sha256=iRpaNBjV0DNwYPu_z6TiHgRpwYahiM7ztI_4kZ4Uz9A,2131 +pip/_vendor/chardet/mbcssm.py,sha256=hUtPvDYgWDaA2dWdgLsshbwRfm3Q5YRlRogdmeRUNQw,30391 pip/_vendor/chardet/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_vendor/chardet/metadata/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/chardet/metadata/__pycache__/languages.cpython-310.pyc,, -pip/_vendor/chardet/metadata/languages.py,sha256=HcaBygWtZq3gR8prIkJp_etvkhm2V4pUIToqjPZhgrc,13280 -pip/_vendor/chardet/sbcharsetprober.py,sha256=VvtWiNRLbHDZ5xgnofsmP1u8VQIkkaAuw3Ir9m1zDzQ,6199 -pip/_vendor/chardet/sbcsgroupprober.py,sha256=mekr4E3hgT4onmwi8oi1iEGW1CN-Z-BArG6kOtCunJw,4129 -pip/_vendor/chardet/sjisprober.py,sha256=sLfWS25PVFr5cDGhEf6h_s-RJsyeSteA-4ynsTl_UvA,3749 -pip/_vendor/chardet/universaldetector.py,sha256=BHeNWt1kn0yQgnR6xNtLAjiNmEQpSHYlKEvuZ9QyR1k,13288 -pip/_vendor/chardet/utf1632prober.py,sha256=N42YJEOkVDB67c38t5aJhXMG1QvnyWWDMNY5ERzniU0,8289 -pip/_vendor/chardet/utf8prober.py,sha256=mnLaSBV4gg-amt2WmxKFKWy4vVBedMNgjdbvgzBo0Dc,2709 -pip/_vendor/chardet/version.py,sha256=u_QYi-DXU1s7fyC_Rwa0I0-UcxMVmH7Co6c7QGKbe3g,242 -pip/_vendor/colorama/__init__.py,sha256=ihDoWQOkapwF7sqQ99AoDoEF3vGYm40OtmgW211cLZw,239 +pip/_vendor/chardet/metadata/languages.py,sha256=FhvBIdZFxRQ-dTwkb_0madRKgVBCaUMQz9I5xqjE5iQ,13560 +pip/_vendor/chardet/resultdict.py,sha256=ez4FRvN5KaSosJeJ2WzUyKdDdg35HDy_SSLPXKCdt5M,402 +pip/_vendor/chardet/sbcharsetprober.py,sha256=-nd3F90i7GpXLjehLVHqVBE0KlWzGvQUPETLBNn4o6U,6400 +pip/_vendor/chardet/sbcsgroupprober.py,sha256=gcgI0fOfgw_3YTClpbra_MNxwyEyJ3eUXraoLHYb59E,4137 +pip/_vendor/chardet/sjisprober.py,sha256=aqQufMzRw46ZpFlzmYaYeT2-nzmKb-hmcrApppJ862k,4007 +pip/_vendor/chardet/universaldetector.py,sha256=xYBrg4x0dd9WnT8qclfADVD9ondrUNkqPmvte1pa520,14848 +pip/_vendor/chardet/utf1632prober.py,sha256=pw1epGdMj1hDGiCu1AHqqzOEfjX8MVdiW7O1BlT8-eQ,8505 +pip/_vendor/chardet/utf8prober.py,sha256=8m08Ub5490H4jQ6LYXvFysGtgKoKsHUd2zH_i8_TnVw,2812 +pip/_vendor/chardet/version.py,sha256=lGtJcxGM44Qz4Cbk4rbbmrKxnNr1-97U25TameLehZw,244 +pip/_vendor/colorama/__init__.py,sha256=wePQA4U20tKgYARySLEC047ucNX-g8pRLpYBuiHlLb8,266 pip/_vendor/colorama/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/colorama/__pycache__/ansi.cpython-310.pyc,, pip/_vendor/colorama/__pycache__/ansitowin32.cpython-310.pyc,, @@ -448,10 +454,24 @@ pip/_vendor/colorama/__pycache__/initialise.cpython-310.pyc,, pip/_vendor/colorama/__pycache__/win32.cpython-310.pyc,, pip/_vendor/colorama/__pycache__/winterm.cpython-310.pyc,, pip/_vendor/colorama/ansi.py,sha256=Top4EeEuaQdBWdteKMEcGOTeKeF19Q-Wo_6_Cj5kOzQ,2522 -pip/_vendor/colorama/ansitowin32.py,sha256=gGrO7MVtwc-j1Sq3jKfZpERT1JWmYSOsTVDiTnFbZU4,10830 -pip/_vendor/colorama/initialise.py,sha256=PprovDNxMTrvoNHFcL2NZjpH2XzDc8BLxLxiErfUl4k,1915 -pip/_vendor/colorama/win32.py,sha256=bJ8Il9jwaBN5BJ8bmN6FoYZ1QYuMKv2j8fGrXh7TJjw,5404 -pip/_vendor/colorama/winterm.py,sha256=2y_2b7Zsv34feAsP67mLOVc-Bgq51mdYGo571VprlrM,6438 +pip/_vendor/colorama/ansitowin32.py,sha256=vPNYa3OZbxjbuFyaVo0Tmhmy1FZ1lKMWCnT7odXpItk,11128 +pip/_vendor/colorama/initialise.py,sha256=-hIny86ClXo39ixh5iSCfUIa2f_h_bgKRDW7gqs-KLU,3325 +pip/_vendor/colorama/tests/__init__.py,sha256=MkgPAEzGQd-Rq0w0PZXSX2LadRWhUECcisJY8lSrm4Q,75 +pip/_vendor/colorama/tests/__pycache__/__init__.cpython-310.pyc,, +pip/_vendor/colorama/tests/__pycache__/ansi_test.cpython-310.pyc,, +pip/_vendor/colorama/tests/__pycache__/ansitowin32_test.cpython-310.pyc,, +pip/_vendor/colorama/tests/__pycache__/initialise_test.cpython-310.pyc,, +pip/_vendor/colorama/tests/__pycache__/isatty_test.cpython-310.pyc,, +pip/_vendor/colorama/tests/__pycache__/utils.cpython-310.pyc,, +pip/_vendor/colorama/tests/__pycache__/winterm_test.cpython-310.pyc,, +pip/_vendor/colorama/tests/ansi_test.py,sha256=FeViDrUINIZcr505PAxvU4AjXz1asEiALs9GXMhwRaE,2839 +pip/_vendor/colorama/tests/ansitowin32_test.py,sha256=RN7AIhMJ5EqDsYaCjVo-o4u8JzDD4ukJbmevWKS70rY,10678 +pip/_vendor/colorama/tests/initialise_test.py,sha256=BbPy-XfyHwJ6zKozuQOvNvQZzsx9vdb_0bYXn7hsBTc,6741 +pip/_vendor/colorama/tests/isatty_test.py,sha256=Pg26LRpv0yQDB5Ac-sxgVXG7hsA1NYvapFgApZfYzZg,1866 +pip/_vendor/colorama/tests/utils.py,sha256=1IIRylG39z5-dzq09R_ngufxyPZxgldNbrxKxUGwGKE,1079 +pip/_vendor/colorama/tests/winterm_test.py,sha256=qoWFPEjym5gm2RuMwpf3pOis3a5r_PJZFCzK254JL8A,3709 +pip/_vendor/colorama/win32.py,sha256=YQOKwMTwtGBbsY4dL5HYTvwTeP9wIQra5MvPNddpxZs,6181 +pip/_vendor/colorama/winterm.py,sha256=XCQFDHjPi6AHYNdZwy0tA02H-Jh48Jp-HvCjeLeLp3U,7134 pip/_vendor/distlib/__init__.py,sha256=acgfseOC55dNrVAzaBKpUiH3Z6V7Q1CaxsiQ3K7pC-E,581 pip/_vendor/distlib/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/distlib/__pycache__/compat.cpython-310.pyc,, @@ -489,7 +509,7 @@ pip/_vendor/distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96V pip/_vendor/distro/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/distro/__pycache__/__main__.cpython-310.pyc,, pip/_vendor/distro/__pycache__/distro.cpython-310.pyc,, -pip/_vendor/distro/distro.py,sha256=UYQG_9H_iSOt422uasA92HlY7aXeTnWKdV-IhsSAdwQ,48841 +pip/_vendor/distro/distro.py,sha256=UZO1LjIhtFCMdlbiz39gj3raV-Amf3SBwzGzfApiMHw,49330 pip/_vendor/idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849 pip/_vendor/idna/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/idna/__pycache__/codec.cpython-310.pyc,, @@ -536,33 +556,11 @@ pip/_vendor/packaging/specifiers.py,sha256=LRQ0kFsHrl5qfcFNEEJrIFYsnIHQUJXY9fIsa pip/_vendor/packaging/tags.py,sha256=lmsnGNiJ8C4D_Pf9PbM0qgbZvD9kmB9lpZBQUZa3R_Y,15699 pip/_vendor/packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200 pip/_vendor/packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665 -pip/_vendor/pep517/__init__.py,sha256=QJpRfzTpk6YSPgjcxp9-MCAiS5dEdzf9Bh0UXophG6c,130 -pip/_vendor/pep517/__pycache__/__init__.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/_compat.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/build.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/check.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/colorlog.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/dirtools.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/envbuild.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/meta.cpython-310.pyc,, -pip/_vendor/pep517/__pycache__/wrappers.cpython-310.pyc,, -pip/_vendor/pep517/_compat.py,sha256=by6evrYnqkisiM-MQcvOKs5bgDMzlOSgZqRHNqf04zE,138 -pip/_vendor/pep517/build.py,sha256=VLtq0hOvNWCfX0FkdvTKEr-TmyrbaX0UqghpU7bHO1w,3443 -pip/_vendor/pep517/check.py,sha256=o0Mp_PX1yOM2WNq1ZdDph3YA7RObj2UGQUCUF-46RaU,6083 -pip/_vendor/pep517/colorlog.py,sha256=eCV1W52xzBjA-sOlKzUcvabRiFa11Y7hA791u-85_c8,3994 -pip/_vendor/pep517/dirtools.py,sha256=JiZ1Hlt2LNaLZEhNa_pm1YyG3MUoRh7KxY6hJ8ac-w0,607 -pip/_vendor/pep517/envbuild.py,sha256=nkTt1ZY7MXVgYOhPTyTr-VOxQ-q_Qc1touXfQgM56Bs,6081 -pip/_vendor/pep517/in_process/__init__.py,sha256=4yDanGyKTXQtLhqRo9eEZ1CsLFezEAEZMfqEd88xrvY,872 -pip/_vendor/pep517/in_process/__pycache__/__init__.cpython-310.pyc,, -pip/_vendor/pep517/in_process/__pycache__/_in_process.cpython-310.pyc,, -pip/_vendor/pep517/in_process/_in_process.py,sha256=JDpTxlKMDN1QfN_ey4IDtE6ZVSWtzP0_WLSqt1TyGaA,10801 -pip/_vendor/pep517/meta.py,sha256=budDWsV3I2OnnpSvXQ_ycuTqxh8G7DABoazAq-j8OlQ,2520 -pip/_vendor/pep517/wrappers.py,sha256=jcxIy-1Kl8I2xAZgbr6qNjF5b_6Q5gTndf9cxF0p5gM,12721 pip/_vendor/pkg_resources/__init__.py,sha256=NnpQ3g6BCHzpMgOR_OLBmYtniY4oOzdKpwqghfq_6ug,108287 pip/_vendor/pkg_resources/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-310.pyc,, pip/_vendor/pkg_resources/py31compat.py,sha256=CRk8fkiPRDLsbi5pZcKsHI__Pbmh_94L8mr9Qy9Ab2U,562 -pip/_vendor/platformdirs/__init__.py,sha256=x0aUmmovXXuRFVrVQBtwIiovX12B7rUkdV4F9UlLz0Y,12831 +pip/_vendor/platformdirs/__init__.py,sha256=9iY4Z8iJDZB0djln6zHHwrPVWpB54TCygcnh--MujU0,12936 pip/_vendor/platformdirs/__main__.py,sha256=ZmsnTxEOxtTvwa-Y_Vfab_JN3X4XCVeN8X0yyy9-qnc,1176 pip/_vendor/platformdirs/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/platformdirs/__pycache__/__main__.cpython-310.pyc,, @@ -575,9 +573,9 @@ pip/_vendor/platformdirs/__pycache__/windows.cpython-310.pyc,, pip/_vendor/platformdirs/android.py,sha256=GKizhyS7ESRiU67u8UnBJLm46goau9937EchXWbPBlk,4068 pip/_vendor/platformdirs/api.py,sha256=MXKHXOL3eh_-trSok-JUTjAR_zjmmKF3rjREVABjP8s,4910 pip/_vendor/platformdirs/macos.py,sha256=-3UXQewbT0yMhMdkzRXfXGAntmLIH7Qt4a9Hlf8I5_Y,2655 -pip/_vendor/platformdirs/unix.py,sha256=b4aVYTz0qZ50HntwOXo8r6tp82jAa3qTjxw-WlnC2yc,6910 -pip/_vendor/platformdirs/version.py,sha256=tsBKKPDX3LLh39yHXeTYauGRbRd-AmOJr9SwKldlFIU,78 -pip/_vendor/platformdirs/windows.py,sha256=ISruopR5UGBePC0BxCxXevkZYfjJsIZc49YWU5iYfQ4,6439 +pip/_vendor/platformdirs/unix.py,sha256=P-WQjSSieE38DXjMDa1t4XHnKJQ5idEaKT0PyXwm8KQ,6911 +pip/_vendor/platformdirs/version.py,sha256=qaN-fw_htIgKUVXoAuAEVgKxQu3tZ9qE2eiKkWIS7LA,160 +pip/_vendor/platformdirs/windows.py,sha256=LOrXLgI0CjQldDo2zhOZYGYZ6g4e_cJOCB_pF9aMRWQ,6596 pip/_vendor/pygments/__init__.py,sha256=5oLcMLXD0cTG8YcHBPITtK1fS0JBASILEvEnWkTezgE,2999 pip/_vendor/pygments/__main__.py,sha256=p0_rz3JZmNZMNZBOqDojaEx1cr9wmA9FQZX_TYl74lQ,353 pip/_vendor/pygments/__pycache__/__init__.cpython-310.pyc,, @@ -670,7 +668,17 @@ pip/_vendor/pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sG pip/_vendor/pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402 pip/_vendor/pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787 pip/_vendor/pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805 -pip/_vendor/requests/__init__.py,sha256=3XN75ZS4slWy3TQsEGF7-Q6l2R146teU-s2_rXNhxhU,5178 +pip/_vendor/pyproject_hooks/__init__.py,sha256=kCehmy0UaBa9oVMD7ZIZrnswfnP3LXZ5lvnNJAL5JBM,491 +pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-310.pyc,, +pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-310.pyc,, +pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-310.pyc,, +pip/_vendor/pyproject_hooks/_compat.py,sha256=by6evrYnqkisiM-MQcvOKs5bgDMzlOSgZqRHNqf04zE,138 +pip/_vendor/pyproject_hooks/_impl.py,sha256=61GJxzQip0IInhuO69ZI5GbNQ82XEDUB_1Gg5_KtUoc,11920 +pip/_vendor/pyproject_hooks/_in_process/__init__.py,sha256=9gQATptbFkelkIy0OfWFEACzqxXJMQDWCH9rBOAZVwQ,546 +pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-310.pyc,, +pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-310.pyc,, +pip/_vendor/pyproject_hooks/_in_process/_in_process.py,sha256=m2b34c917IW5o-Q_6TYIHlsK9lSUlNiyrITTUH_zwew,10927 +pip/_vendor/requests/__init__.py,sha256=64HgJ8cke-XyNrj1ErwNq0F9SqyAThUTh5lV6m7-YkI,5178 pip/_vendor/requests/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/requests/__pycache__/__version__.cpython-310.pyc,, pip/_vendor/requests/__pycache__/_internal_utils.cpython-310.pyc,, @@ -689,7 +697,7 @@ pip/_vendor/requests/__pycache__/sessions.cpython-310.pyc,, pip/_vendor/requests/__pycache__/status_codes.cpython-310.pyc,, pip/_vendor/requests/__pycache__/structures.cpython-310.pyc,, pip/_vendor/requests/__pycache__/utils.cpython-310.pyc,, -pip/_vendor/requests/__version__.py,sha256=nJVa3ef2yRyeYMhy7yHnRyjjpnNTDykZsE4Sp9irBC4,440 +pip/_vendor/requests/__version__.py,sha256=h48zn-oFukaXrYHocdadp_hIszWyd_PGrS8Eiii6aoc,435 pip/_vendor/requests/_internal_utils.py,sha256=aSPlF4uDhtfKxEayZJJ7KkAxtormeTfpwKSBSwtmAUw,1397 pip/_vendor/requests/adapters.py,sha256=GFEz5koZaMZD86v0SHXKVB5SE9MgslEjkCQzldkNwVM,21443 pip/_vendor/requests/api.py,sha256=dyvkDd5itC9z2g0wHl_YfD1yf6YwpGWLO7__8e21nks,6377 @@ -700,7 +708,7 @@ pip/_vendor/requests/cookies.py,sha256=kD3kNEcCj-mxbtf5fJsSaT86eGoEYpD3X0CSgpzl7 pip/_vendor/requests/exceptions.py,sha256=FA-_kVwBZ2jhXauRctN_ewHVK25b-fj0Azyz1THQ0Kk,3823 pip/_vendor/requests/help.py,sha256=FnAAklv8MGm_qb2UilDQgS6l0cUttiCFKUjx0zn2XNA,3879 pip/_vendor/requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 -pip/_vendor/requests/models.py,sha256=GZRMMrGwDOLVvVfFHLUq0qTfIWDla3NcFHa1f5xs9Q8,35287 +pip/_vendor/requests/models.py,sha256=dDZ-iThotky-Noq9yy97cUEJhr3wnY6mv-xR_ePg_lk,35288 pip/_vendor/requests/packages.py,sha256=njJmVifY4aSctuW3PP5EFRCxjEwMRDO6J_feG2dKWsI,695 pip/_vendor/requests/sessions.py,sha256=KUqJcRRLovNefUs7ScOXSUVCcfSayTFWtbiJ7gOSlTI,30180 pip/_vendor/requests/status_codes.py,sha256=FvHmT5uH-_uimtRz5hH9VCbt7VV-Nei2J9upbej6j8g,4235 @@ -720,8 +728,8 @@ pip/_vendor/resolvelib/providers.py,sha256=roVmFBItQJ0TkhNua65h8LdNny7rmeqVEXZu9 pip/_vendor/resolvelib/reporters.py,sha256=fW91NKf-lK8XN7i6Yd_rczL5QeOT3sc6AKhpaTEnP3E,1583 pip/_vendor/resolvelib/resolvers.py,sha256=2wYzVGBGerbmcIpH8cFmgSKgLSETz8jmwBMGjCBMHG4,17592 pip/_vendor/resolvelib/structs.py,sha256=IVIYof6sA_N4ZEiE1C1UhzTX495brCNnyCdgq6CYq28,4794 -pip/_vendor/rich/__init__.py,sha256=zREyQ22R3zKg8gMdhiikczdVQYtZNeayHNrbBg5scm0,5944 -pip/_vendor/rich/__main__.py,sha256=BmTmBWI93ytq75IEPi1uAAdeRYzFfDbgaAXjsX1ogig,8808 +pip/_vendor/rich/__init__.py,sha256=dRxjIL-SbFVY0q3IjSMrfgBTHrm1LZDgLOygVBwiYZc,6090 +pip/_vendor/rich/__main__.py,sha256=TT8sb9PTnsnKhhrGuHkLN0jdN0dtKhtPkEr9CidDbPM,8478 pip/_vendor/rich/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/rich/__pycache__/__main__.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_cell_widths.cpython-310.pyc,, @@ -732,6 +740,7 @@ pip/_vendor/rich/__pycache__/_extension.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_inspect.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_log_render.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_loop.cpython-310.pyc,, +pip/_vendor/rich/__pycache__/_null_file.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_palettes.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_pick.cpython-310.pyc,, pip/_vendor/rich/__pycache__/_ratio.cpython-310.pyc,, @@ -805,6 +814,7 @@ pip/_vendor/rich/_extension.py,sha256=Xt47QacCKwYruzjDi-gOBq724JReDj9Cm9xUi5fr-3 pip/_vendor/rich/_inspect.py,sha256=oZJGw31e64dwXSCmrDnvZbwVb1ZKhWfU8wI3VWohjJk,9695 pip/_vendor/rich/_log_render.py,sha256=1ByI0PA1ZpxZY3CGJOK54hjlq4X-Bz_boIjIqCd8Kns,3225 pip/_vendor/rich/_loop.py,sha256=hV_6CLdoPm0va22Wpw4zKqM0RYsz3TZxXj0PoS-9eDQ,1236 +pip/_vendor/rich/_null_file.py,sha256=cTaTCU_xuDXGGa9iqK-kZ0uddZCSvM-RgM2aGMuMiHs,1643 pip/_vendor/rich/_palettes.py,sha256=cdev1JQKZ0JvlguV9ipHgznTdnvlIzUFDBb0It2PzjI,7063 pip/_vendor/rich/_pick.py,sha256=evDt8QN4lF5CiwrUIXlOJCntitBCOsI3ZLPEIAVRLJU,423 pip/_vendor/rich/_ratio.py,sha256=2lLSliL025Y-YMfdfGbutkQDevhcyDqc-DtUYW9mU70,5472 @@ -817,14 +827,14 @@ pip/_vendor/rich/_windows_renderer.py,sha256=t74ZL3xuDCP3nmTp9pH1L5LiI2cakJuQRQl pip/_vendor/rich/_wrap.py,sha256=xfV_9t0Sg6rzimmrDru8fCVmUlalYAcHLDfrJZnbbwQ,1840 pip/_vendor/rich/abc.py,sha256=ON-E-ZqSSheZ88VrKX2M3PXpFbGEUUZPMa_Af0l-4f0,890 pip/_vendor/rich/align.py,sha256=FV6_GS-8uhIyViMng3hkIWSFaTgMohK1Oqyjl8I8mGE,10368 -pip/_vendor/rich/ansi.py,sha256=HtaPG7dvgL6_yo0sQmx5CM05DJ4_1goY5SWXXOYNaKs,6820 +pip/_vendor/rich/ansi.py,sha256=THex7-qjc82-ZRtmDPAYlVEObYOEE_ARB1692Fk-JHs,6819 pip/_vendor/rich/bar.py,sha256=a7UD303BccRCrEhGjfMElpv5RFYIinaAhAuqYqhUvmw,3264 -pip/_vendor/rich/box.py,sha256=1Iv1sUWqjtp5XwLwGH-AJ8HgyXZ7dRFUkO0z3M_bRl8,9864 +pip/_vendor/rich/box.py,sha256=FJ6nI3jD7h2XNFU138bJUt2HYmWOlRbltoCEuIAZhew,9842 pip/_vendor/rich/cells.py,sha256=zMjFI15wCpgjLR14lHdfFMVC6qMDi5OsKIB0PYZBBMk,4503 -pip/_vendor/rich/color.py,sha256=kp87L8V4-3qayE6CUxtW_nP8Ujfew_-DAhNwYMXBMOY,17957 +pip/_vendor/rich/color.py,sha256=GTITgffj47On3YK1v_I5T2CPZJGSnyWipPID_YkYXqw,18015 pip/_vendor/rich/color_triplet.py,sha256=3lhQkdJbvWPoLDO-AnYImAWmJvV5dlgYNCVZ97ORaN4,1054 pip/_vendor/rich/columns.py,sha256=HUX0KcMm9dsKNi11fTbiM_h2iDtl8ySCaVcxlalEzq8,7131 -pip/_vendor/rich/console.py,sha256=bTT9DNX03V4cQXefg22d-gLSs_e_ZY2zdCvLIlEyU2Q,95885 +pip/_vendor/rich/console.py,sha256=w3tJfrILZpS359wrNqaldGmyk3PEhEmV8Pg2g2GjXWI,97992 pip/_vendor/rich/constrain.py,sha256=1VIPuC8AgtKWrcncQrjBdYqA3JVWysu6jZo1rrh7c7Q,1288 pip/_vendor/rich/containers.py,sha256=aKgm5UDHn5Nmui6IJaKdsZhbHClh_X7D-_Wg8Ehrr7s,5497 pip/_vendor/rich/control.py,sha256=DSkHTUQLorfSERAKE_oTAEUFefZnZp4bQb4q8rHbKws,6630 @@ -833,42 +843,42 @@ pip/_vendor/rich/diagnose.py,sha256=an6uouwhKPAlvQhYpNNpGq9EJysfMIOvvCbO3oSoR24, pip/_vendor/rich/emoji.py,sha256=omTF9asaAnsM4yLY94eR_9dgRRSm1lHUszX20D1yYCQ,2501 pip/_vendor/rich/errors.py,sha256=5pP3Kc5d4QJ_c0KFsxrfyhjiPVe7J1zOqSFbFAzcV-Y,642 pip/_vendor/rich/file_proxy.py,sha256=4gCbGRXg0rW35Plaf0UVvj3dfENHuzc_n8I_dBqxI7o,1616 -pip/_vendor/rich/filesize.py,sha256=yShoVpARafJBreyZFaAhC4OhnJ6ydC1WXR-Ez4wU_YQ,2507 +pip/_vendor/rich/filesize.py,sha256=9fTLAPCAwHmBXdRv7KZU194jSgNrRb6Wx7RIoBgqeKY,2508 pip/_vendor/rich/highlighter.py,sha256=3WW6PACGlq0e3YDjfqiMBQ0dYZwu7pcoFYUgJy01nb0,9585 -pip/_vendor/rich/json.py,sha256=RCm4lXBXrjvXHpqrWPH8wdGP0jEo4IohLmkddlhRY18,5051 +pip/_vendor/rich/json.py,sha256=TmeFm96Utaov-Ff5miavBPNo51HRooM8S78HEwrYEjA,5053 pip/_vendor/rich/jupyter.py,sha256=QyoKoE_8IdCbrtiSHp9TsTSNyTHY0FO5whE7jOTd9UE,3252 -pip/_vendor/rich/layout.py,sha256=E3xJ4fomizUADwime3VA0lBXoMSPl9blEokIzVBjO0Q,14074 +pip/_vendor/rich/layout.py,sha256=RFYL6HdCFsHf9WRpcvi3w-fpj-8O5dMZ8W96VdKNdbI,14007 pip/_vendor/rich/live.py,sha256=emVaLUua-FKSYqZXmtJJjBIstO99CqMOuA6vMAKVkO0,14172 pip/_vendor/rich/live_render.py,sha256=zElm3PrfSIvjOce28zETHMIUf9pFYSUA5o0AflgUP64,3667 -pip/_vendor/rich/logging.py,sha256=10j13lPr-QuYqEEBz_2aRJp8gNYvSN2wmCUlUqJcPLM,11471 +pip/_vendor/rich/logging.py,sha256=uB-cB-3Q4bmXDLLpbOWkmFviw-Fde39zyMV6tKJ2WHQ,11903 pip/_vendor/rich/markup.py,sha256=xzF4uAafiEeEYDJYt_vUnJOGoTU8RrH-PH7WcWYXjCg,8198 pip/_vendor/rich/measure.py,sha256=HmrIJX8sWRTHbgh8MxEay_83VkqNW_70s8aKP5ZcYI8,5305 pip/_vendor/rich/padding.py,sha256=kTFGsdGe0os7tXLnHKpwTI90CXEvrceeZGCshmJy5zw,4970 pip/_vendor/rich/pager.py,sha256=SO_ETBFKbg3n_AgOzXm41Sv36YxXAyI3_R-KOY2_uSc,828 pip/_vendor/rich/palette.py,sha256=lInvR1ODDT2f3UZMfL1grq7dY_pDdKHw4bdUgOGaM4Y,3396 -pip/_vendor/rich/panel.py,sha256=CzdojkDAjxAKgvDxis47nWzUh1V2NniOqkJJQajosG8,8744 -pip/_vendor/rich/pretty.py,sha256=CalVLVW3mvTn1hvI9Pgi2v-y4S-5zUWBK-PH7SlVs-U,36576 -pip/_vendor/rich/progress.py,sha256=zjQRwd3TmDnAvSjTPsNPHFjmqE9GOEX3bf0Lj56hIL8,59746 -pip/_vendor/rich/progress_bar.py,sha256=zHHaFPEfIhW2fq6Fnl5vBY7AUpP1N0HVGElISUHsnqw,8161 +pip/_vendor/rich/panel.py,sha256=wGMe40J8KCGgQoM0LyjRErmGIkv2bsYA71RCXThD0xE,10574 +pip/_vendor/rich/pretty.py,sha256=dAbLqSF3jJnyfBLJ7QjQ3B2J-WGyBnAdGXeuBVIyMyA,37414 +pip/_vendor/rich/progress.py,sha256=eg-OURdfZW3n3bib1-zP3SZl6cIm2VZup1pr_96CyLk,59836 +pip/_vendor/rich/progress_bar.py,sha256=cEoBfkc3lLwqba4XKsUpy4vSQKDh2QQ5J2J94-ACFoo,8165 pip/_vendor/rich/prompt.py,sha256=x0mW-pIPodJM4ry6grgmmLrl8VZp99kqcmdnBe70YYA,11303 pip/_vendor/rich/protocol.py,sha256=5hHHDDNHckdk8iWH5zEbi-zuIVSF5hbU2jIo47R7lTE,1391 pip/_vendor/rich/region.py,sha256=rNT9xZrVZTYIXZC0NYn41CJQwYNbR-KecPOxTgQvB8Y,166 -pip/_vendor/rich/repr.py,sha256=Je91CIrZN_av9L3FRCKCs5yoX2LvczrCNKqUbVsjUvQ,4449 +pip/_vendor/rich/repr.py,sha256=eJObQe6_c5pUjRM85sZ2rrW47_iF9HT3Z8DrgVjvOl8,4436 pip/_vendor/rich/rule.py,sha256=V6AWI0wCb6DB0rvN967FRMlQrdlG7HoZdfEAHyeG8CM,4773 -pip/_vendor/rich/scope.py,sha256=HX13XsJfqzQHpPfw4Jn9JmJjCsRj9uhHxXQEqjkwyLA,2842 +pip/_vendor/rich/scope.py,sha256=TMUU8qo17thyqQCPqjDLYpg_UU1k5qVd-WwiJvnJVas,2843 pip/_vendor/rich/screen.py,sha256=YoeReESUhx74grqb0mSSb9lghhysWmFHYhsbMVQjXO8,1591 pip/_vendor/rich/segment.py,sha256=6XdX0MfL18tUCaUWDWncIqx0wpq3GiaqzhYP779JvRA,24224 pip/_vendor/rich/spinner.py,sha256=7b8MCleS4fa46HX0AzF98zfu6ZM6fAL0UgYzPOoakF4,4374 pip/_vendor/rich/status.py,sha256=gJsIXIZeSo3urOyxRUjs6VrhX5CZrA0NxIQ-dxhCnwo,4425 -pip/_vendor/rich/style.py,sha256=4WnUEkHNMp9Tfmd8cmbxWGby7QeTk2LUTQzFSs46EQc,26240 +pip/_vendor/rich/style.py,sha256=odBbAlrgdEbAj7pmtPbQtWJNS8upyNhhy--Ks6KwAKk,26332 pip/_vendor/rich/styled.py,sha256=eZNnzGrI4ki_54pgY3Oj0T-x3lxdXTYh4_ryDB24wBU,1258 -pip/_vendor/rich/syntax.py,sha256=_M08KbE11nNWNBPooFLKAA7lWkThPzlGUsuesxQYsuA,34697 -pip/_vendor/rich/table.py,sha256=r_lahmj45cINCWLYaIjq9yEv3gve8E6bkYTP8NDqApE,39515 +pip/_vendor/rich/syntax.py,sha256=W1xtdBA1-EVP-weYofKXusUlV5zghCOv1nWMHHfNmiY,34995 +pip/_vendor/rich/table.py,sha256=-WzesL-VJKsaiDU3uyczpJMHy6VCaSewBYJwx8RudI8,39684 pip/_vendor/rich/terminal_theme.py,sha256=1j5-ufJfnvlAo5Qsi_ACZiXDmwMXzqgmFByObT9-yJY,3370 -pip/_vendor/rich/text.py,sha256=oajdGIeHcLcSdOwbC48_20ylDsHAS5fsPZD_Ih0clyA,44666 +pip/_vendor/rich/text.py,sha256=andXaxWW_wBveMiZZpd5viQwucWo7SPopcM3ZCQeO0c,45686 pip/_vendor/rich/theme.py,sha256=GKNtQhDBZKAzDaY0vQVQQFzbc0uWfFe6CJXA-syT7zQ,3627 pip/_vendor/rich/themes.py,sha256=0xgTLozfabebYtcJtDdC5QkX5IVUEaviqDUJJh4YVFk,102 -pip/_vendor/rich/traceback.py,sha256=MORQpXH7AvhAAThW8oIbtwffXb8M6XRkSkcJ52JuA3g,26060 +pip/_vendor/rich/traceback.py,sha256=6LkGguCEAxKv8v8xmKfMeYPPJ1UXUEHDv4726To6FiQ,26070 pip/_vendor/rich/tree.py,sha256=BMbUYNjS9uodNPfvtY_odmU09GA5QzcMbQ5cJZhllQI,9169 pip/_vendor/six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 pip/_vendor/tenacity/__init__.py,sha256=rjcWJVq5PcNJNC42rt-TAGGskM-RUEkZbDKu1ra7IPo,18364 @@ -915,9 +925,9 @@ pip/_vendor/urllib3/__pycache__/poolmanager.cpython-310.pyc,, pip/_vendor/urllib3/__pycache__/request.cpython-310.pyc,, pip/_vendor/urllib3/__pycache__/response.cpython-310.pyc,, pip/_vendor/urllib3/_collections.py,sha256=Rp1mVyBgc_UlAcp6M3at1skJBXR5J43NawRTvW2g_XY,10811 -pip/_vendor/urllib3/_version.py,sha256=GhuGBUT_MtRxHEHDb-LYs5yLPeYWlCwFBPjGZmVJbVg,64 +pip/_vendor/urllib3/_version.py,sha256=JWE--BUVy7--9FsXILONIpQ43irftKGjT9j2H_fdF2M,64 pip/_vendor/urllib3/connection.py,sha256=8976wL6sGeVMW0JnXvx5mD00yXu87uQjxtB9_VL8dx8,20070 -pip/_vendor/urllib3/connectionpool.py,sha256=vEzk1iJEw1qR2vHBo7m3Y98iDfna6rKkUz3AyK5lJKQ,39093 +pip/_vendor/urllib3/connectionpool.py,sha256=vS4UaHLoR9_5aGLXSQ776y_jTxgqqjx0YsjkYksWGOo,39095 pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-310.pyc,, @@ -933,9 +943,9 @@ pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-310.py pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-310.pyc,, pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=4Xk64qIkPBt09A5q-RIFUuDhNc9mXilVapm7WnYnzRw,17632 pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=B2JBB2_NRP02xK6DCa1Pa9IuxrPwxzDzZbixQkb7U9M,13922 -pip/_vendor/urllib3/contrib/appengine.py,sha256=lfzpHFmJiO82shClLEm3QB62SYgHWnjpZOH_2JhU5Tc,11034 -pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=ej9gGvfAb2Gt00lafFp45SIoRz-QwrQ4WChm6gQmAlM,4538 -pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=rt9NEIP8iMBLxxRhH0jLnmshW-OFP83jEayxMSqu2MU,17182 +pip/_vendor/urllib3/contrib/appengine.py,sha256=VR68eAVE137lxTgjBDwCna5UiBZTOKa01Aj_-5BaCz4,11036 +pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=NlfkW7WMdW8ziqudopjHoW299og1BTWi0IeIibquFwk,4528 +pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=hDJh4MhyY_p-oKlFcYcQaVQRDv6GMmBGuW9yjxyeejM,17081 pip/_vendor/urllib3/contrib/securetransport.py,sha256=yhZdmVjY6PI6EeFbp7qYOp6-vp1Rkv2NMuOGaEj7pmc,34448 pip/_vendor/urllib3/contrib/socks.py,sha256=aRi9eWXo9ZEb95XUxef4Z21CFlnnjbEiAo9HOseoMt4,7097 pip/_vendor/urllib3/exceptions.py,sha256=0Mnno3KHTNfXRfY7638NufOPkUb6mXOm-Lqj-4x2w8A,8217 @@ -951,7 +961,7 @@ pip/_vendor/urllib3/packages/backports/makefile.py,sha256=nbzt3i0agPVP07jqqgjhaY pip/_vendor/urllib3/packages/six.py,sha256=b9LM0wBXv7E7SrbCjAm4wwN-hrH-iNxv18LgWNMMKPo,34665 pip/_vendor/urllib3/poolmanager.py,sha256=0KOOJECoeLYVjUHvv-0h4Oq3FFQQ2yb-Fnjkbj8gJO0,19786 pip/_vendor/urllib3/request.py,sha256=ZFSIqX0C6WizixecChZ3_okyu7BEv0lZu1VT0s6h4SM,5985 -pip/_vendor/urllib3/response.py,sha256=p3VBYPhwBca77wCZfmoXvEDVVC3SdF7yxQ6TXuxy1BI,30109 +pip/_vendor/urllib3/response.py,sha256=fmDJAFkG71uFTn-sVSTh2Iw0WmcXQYqkbRjihvwBjU8,30641 pip/_vendor/urllib3/util/__init__.py,sha256=JEmSmmqqLyaw8P51gUImZh8Gwg9i1zSe-DoqAitn2nc,1155 pip/_vendor/urllib3/util/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/urllib3/util/__pycache__/connection.cpython-310.pyc,, @@ -971,14 +981,14 @@ pip/_vendor/urllib3/util/proxy.py,sha256=zUvPPCJrp6dOF0N4GAVbOcl6o-4uXKSrGiTkkr5 pip/_vendor/urllib3/util/queue.py,sha256=nRgX8_eX-_VkvxoX096QWoz8Ps0QHUAExILCY_7PncM,498 pip/_vendor/urllib3/util/request.py,sha256=C0OUt2tcU6LRiQJ7YYNP9GvPrSvl7ziIBekQ-5nlBZk,3997 pip/_vendor/urllib3/util/response.py,sha256=GJpg3Egi9qaJXRwBh5wv-MNuRWan5BIu40oReoxWP28,3510 -pip/_vendor/urllib3/util/retry.py,sha256=iESg2PvViNdXBRY4MpL4h0kqwOOkHkxmLn1kkhFHPU8,22001 +pip/_vendor/urllib3/util/retry.py,sha256=4laWh0HpwGijLiBmdBIYtbhYekQnNzzhx2W9uys0RHA,22003 pip/_vendor/urllib3/util/ssl_.py,sha256=X4-AqW91aYPhPx6-xbf66yHFQKbqqfC_5Zt4WkLX1Hc,17177 pip/_vendor/urllib3/util/ssl_match_hostname.py,sha256=Ir4cZVEjmAk8gUAIHWSi7wtOO83UCYABY2xFD1Ql_WA,5758 pip/_vendor/urllib3/util/ssltransport.py,sha256=NA-u5rMTrDFDFC8QzRKUEKMG0561hOD4qBTr3Z4pv6E,6895 pip/_vendor/urllib3/util/timeout.py,sha256=QSbBUNOB9yh6AnDn61SrLQ0hg5oz0I9-uXEG91AJuIg,10003 -pip/_vendor/urllib3/util/url.py,sha256=49HwObaTUUjqVe4qvSUvIjZyf3ghgNA6-OLm3kmkFKM,14287 +pip/_vendor/urllib3/util/url.py,sha256=HLCLEKt8D-QMioTNbneZSzGTGyUkns4w_lSJP1UzE2E,14298 pip/_vendor/urllib3/util/wait.py,sha256=fOX0_faozG2P7iVojQoE1mbydweNyTcm-hXEfFrTtLI,5403 -pip/_vendor/vendor.txt,sha256=07gLL_CcEHdl1XM0g4PH2L4gsTTMlJr8WWIC11yEyMo,469 +pip/_vendor/vendor.txt,sha256=3i3Zr7_kRDD9UEva0I8YOMroCZ8xuZ9OWd_Q4jmazqE,476 pip/_vendor/webencodings/__init__.py,sha256=qOBJIuPy_4ByYH6W_bNgJF-qYQ2DoU-dKsDu5yRWCXg,10579 pip/_vendor/webencodings/__pycache__/__init__.cpython-310.pyc,, pip/_vendor/webencodings/__pycache__/labels.cpython-310.pyc,, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/REQUESTED b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/REQUESTED similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/REQUESTED rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/REQUESTED diff --git a/dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/WHEEL similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/esptool-4.4.dist-info/WHEEL rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/WHEEL diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/entry_points.txt similarity index 69% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/entry_points.txt rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/entry_points.txt index 5367846d2..ab909c9b3 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/entry_points.txt +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/entry_points.txt @@ -1,4 +1,4 @@ [console_scripts] pip = pip._internal.cli.main:main pip3 = pip._internal.cli.main:main -pip3.10 = pip._internal.cli.main:main +pip3.9 = pip._internal.cli.main:main diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/top_level.txt similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/top_level.txt rename to dependencies/windows_amd64/python/Lib/site-packages/pip-23.0.1.dist-info/top_level.txt diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/__init__.py index 5563b5d55..42f6c455c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/__init__.py @@ -1,6 +1,6 @@ from typing import List, Optional -__version__ = "22.3.1" +__version__ = "23.0.1" def main(args: Optional[List[str]] = None) -> int: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/build_env.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/build_env.py index cc2b38bab..4f704a354 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/build_env.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/build_env.py @@ -8,9 +8,8 @@ import sys import textwrap from collections import OrderedDict -from sysconfig import get_paths from types import TracebackType -from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type +from typing import TYPE_CHECKING, Iterable, List, Optional, Set, Tuple, Type, Union from pip._vendor.certifi import where from pip._vendor.packaging.requirements import Requirement @@ -18,7 +17,7 @@ from pip import __file__ as pip_location from pip._internal.cli.spinners import open_spinner -from pip._internal.locations import get_platlib, get_prefixed_libs, get_purelib +from pip._internal.locations import get_platlib, get_purelib, get_scheme from pip._internal.metadata import get_default_environment, get_environment from pip._internal.utils.subprocess import call_subprocess from pip._internal.utils.temp_dir import TempDirectory, tempdir_kinds @@ -29,15 +28,17 @@ logger = logging.getLogger(__name__) +def _dedup(a: str, b: str) -> Union[Tuple[str], Tuple[str, str]]: + return (a, b) if a != b else (a,) + + class _Prefix: def __init__(self, path: str) -> None: self.path = path self.setup = False - self.bin_dir = get_paths( - "nt" if os.name == "nt" else "posix_prefix", - vars={"base": path, "platbase": path}, - )["scripts"] - self.lib_dirs = get_prefixed_libs(path) + scheme = get_scheme("", prefix=path) + self.bin_dir = scheme.scripts + self.lib_dirs = _dedup(scheme.purelib, scheme.platlib) def get_runnable_pip() -> str: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/cli/cmdoptions.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/cli/cmdoptions.py index b4e2560de..1f804097e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/cli/cmdoptions.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/cli/cmdoptions.py @@ -164,6 +164,14 @@ class PipOption(Option): ), ) +override_externally_managed: Callable[..., Option] = partial( + Option, + "--break-system-packages", + dest="override_externally_managed", + action="store_true", + help="Allow pip to modify an EXTERNALLY-MANAGED Python installation", +) + python: Callable[..., Option] = partial( Option, "--python", @@ -825,11 +833,9 @@ def _handle_config_settings( dest="install_options", action="append", metavar="options", - help="Extra arguments to be supplied to the setup.py install " - 'command (use like --install-option="--install-scripts=/usr/local/' - 'bin"). Use multiple --install-option options to pass multiple ' - "options to setup.py install. If you are using an option with a " - "directory path, be sure to use absolute path.", + help="This option is deprecated. Using this option with location-changing " + "options may cause unexpected behavior. " + "Use pip-level options like --user, --prefix, --root, and --target.", ) build_options: Callable[..., Option] = partial( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/debug.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/debug.py index 6fad1fe89..2a3e7d298 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/debug.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/debug.py @@ -48,7 +48,7 @@ def create_vendor_txt_map() -> Dict[str, str]: def get_module_from_module_name(module_name: str) -> ModuleType: # Module name can be uppercase in vendor.txt for some reason... - module_name = module_name.lower() + module_name = module_name.lower().replace("-", "_") # PATCH: setuptools is actually only pkg_resources. if module_name == "setuptools": module_name = "pkg_resources" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/index.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/index.py index b4bf0ac06..7267effed 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/index.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/index.py @@ -24,6 +24,7 @@ class IndexCommand(IndexGroupCommand): Inspect information available from package indexes. """ + ignore_require_venv = True usage = """ %prog versions """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/inspect.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/inspect.py index a4e359930..27c8fa3d5 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/inspect.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/inspect.py @@ -46,11 +46,6 @@ def add_options(self) -> None: self.parser.insert_option_group(0, self.cmd_opts) def run(self, options: Values, args: List[str]) -> int: - logger.warning( - "pip inspect is currently an experimental command. " - "The output format may change in a future release without prior warning." - ) - cmdoptions.check_list_path_option(options) dists = get_environment(options.path).iter_installed_distributions( local_only=options.local, @@ -58,7 +53,7 @@ def run(self, options: Values, args: List[str]) -> int: skip=set(stdlib_pkgs), ) output = { - "version": "0", + "version": "1", "pip_version": __version__, "installed": [self._dist_to_dict(dist) for dist in dists], "environment": default_environment(), diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/install.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/install.py index e081c27d2..b20aeddf8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/install.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/install.py @@ -41,6 +41,7 @@ from pip._internal.utils.filesystem import test_writable_dir from pip._internal.utils.logging import getLogger from pip._internal.utils.misc import ( + check_externally_managed, ensure_dir, get_pip_version, protect_pip_from_modification_on_windows, @@ -214,6 +215,7 @@ def add_options(self) -> None: self.cmd_opts.add_option(cmdoptions.use_pep517()) self.cmd_opts.add_option(cmdoptions.no_use_pep517()) self.cmd_opts.add_option(cmdoptions.check_build_deps()) + self.cmd_opts.add_option(cmdoptions.override_externally_managed()) self.cmd_opts.add_option(cmdoptions.config_settings()) self.cmd_opts.add_option(cmdoptions.install_options()) @@ -284,6 +286,23 @@ def run(self, options: Values, args: List[str]) -> int: if options.use_user_site and options.target_dir is not None: raise CommandError("Can not combine '--user' and '--target'") + # Check whether the environment we're installing into is externally + # managed, as specified in PEP 668. Specifying --root, --target, or + # --prefix disables the check, since there's no reliable way to locate + # the EXTERNALLY-MANAGED file for those cases. An exception is also + # made specifically for "--dry-run --report" for convenience. + installing_into_current_environment = ( + not (options.dry_run and options.json_report_file) + and options.root_path is None + and options.target_dir is None + and options.prefix_path is None + ) + if ( + installing_into_current_environment + and not options.override_externally_managed + ): + check_externally_managed() + upgrade_strategy = "to-satisfy-only" if options.upgrade: upgrade_strategy = options.upgrade_strategy @@ -402,12 +421,6 @@ def run(self, options: Values, args: List[str]) -> int: ) if options.json_report_file: - logger.warning( - "--report is currently an experimental option. " - "The output format may change in a future release " - "without prior warning." - ) - report = InstallationReport(requirement_set.requirements_to_install) if options.json_report_file == "-": print_json(data=report.to_dict()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/show.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/show.py index 212167c9d..3f10701f6 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/show.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/show.py @@ -53,6 +53,7 @@ class _PackageInfo(NamedTuple): name: str version: str location: str + editable_project_location: Optional[str] requires: List[str] required_by: List[str] installer: str @@ -120,6 +121,7 @@ def _get_requiring_packages(current_dist: BaseDistribution) -> Iterator[str]: name=dist.raw_name, version=str(dist.version), location=dist.location or "", + editable_project_location=dist.editable_project_location, requires=requires, required_by=required_by, installer=dist.installer, @@ -158,6 +160,10 @@ def print_results( write_output("Author-email: %s", dist.author_email) write_output("License: %s", dist.license) write_output("Location: %s", dist.location) + if dist.editable_project_location is not None: + write_output( + "Editable project location: %s", dist.editable_project_location + ) write_output("Requires: %s", ", ".join(dist.requires)) write_output("Required-by: %s", ", ".join(dist.required_by)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/uninstall.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/uninstall.py index dea8077e7..f198fc313 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/uninstall.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/commands/uninstall.py @@ -14,7 +14,10 @@ install_req_from_line, install_req_from_parsed_requirement, ) -from pip._internal.utils.misc import protect_pip_from_modification_on_windows +from pip._internal.utils.misc import ( + check_externally_managed, + protect_pip_from_modification_on_windows, +) logger = logging.getLogger(__name__) @@ -55,6 +58,7 @@ def add_options(self) -> None: help="Don't ask for confirmation of uninstall deletions.", ) self.cmd_opts.add_option(cmdoptions.root_user_action()) + self.cmd_opts.add_option(cmdoptions.override_externally_managed()) self.parser.insert_option_group(0, self.cmd_opts) def run(self, options: Values, args: List[str]) -> int: @@ -90,6 +94,9 @@ def run(self, options: Values, args: List[str]) -> int: f'"pip help {self.name}")' ) + if not options.override_externally_managed: + check_externally_managed() + protect_pip_from_modification_on_windows( modifying_pip="pip" in reqs_to_uninstall ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/exceptions.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/exceptions.py index 2ab1f591f..d4527295d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/exceptions.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/exceptions.py @@ -6,9 +6,14 @@ """ import configparser +import contextlib +import locale +import logging +import pathlib import re +import sys from itertools import chain, groupby, repeat -from typing import TYPE_CHECKING, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union from pip._vendor.requests.models import Request, Response from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult @@ -22,6 +27,8 @@ from pip._internal.metadata import BaseDistribution from pip._internal.req.req_install import InstallRequirement +logger = logging.getLogger(__name__) + # # Scaffolding @@ -658,3 +665,83 @@ def __str__(self) -> str: assert self.error is not None message_part = f".\n{self.error}\n" return f"Configuration file {self.reason}{message_part}" + + +_DEFAULT_EXTERNALLY_MANAGED_ERROR = f"""\ +The Python environment under {sys.prefix} is managed externally, and may not be +manipulated by the user. Please use specific tooling from the distributor of +the Python installation to interact with this environment instead. +""" + + +class ExternallyManagedEnvironment(DiagnosticPipError): + """The current environment is externally managed. + + This is raised when the current environment is externally managed, as + defined by `PEP 668`_. The ``EXTERNALLY-MANAGED`` configuration is checked + and displayed when the error is bubbled up to the user. + + :param error: The error message read from ``EXTERNALLY-MANAGED``. + """ + + reference = "externally-managed-environment" + + def __init__(self, error: Optional[str]) -> None: + if error is None: + context = Text(_DEFAULT_EXTERNALLY_MANAGED_ERROR) + else: + context = Text(error) + super().__init__( + message="This environment is externally managed", + context=context, + note_stmt=( + "If you believe this is a mistake, please contact your " + "Python installation or OS distribution provider. " + "You can override this, at the risk of breaking your Python " + "installation or OS, by passing --break-system-packages." + ), + hint_stmt=Text("See PEP 668 for the detailed specification."), + ) + + @staticmethod + def _iter_externally_managed_error_keys() -> Iterator[str]: + # LC_MESSAGES is in POSIX, but not the C standard. The most common + # platform that does not implement this category is Windows, where + # using other categories for console message localization is equally + # unreliable, so we fall back to the locale-less vendor message. This + # can always be re-evaluated when a vendor proposes a new alternative. + try: + category = locale.LC_MESSAGES + except AttributeError: + lang: Optional[str] = None + else: + lang, _ = locale.getlocale(category) + if lang is not None: + yield f"Error-{lang}" + for sep in ("-", "_"): + before, found, _ = lang.partition(sep) + if not found: + continue + yield f"Error-{before}" + yield "Error" + + @classmethod + def from_config( + cls, + config: Union[pathlib.Path, str], + ) -> "ExternallyManagedEnvironment": + parser = configparser.ConfigParser(interpolation=None) + try: + parser.read(config, encoding="utf-8") + section = parser["externally-managed"] + for key in cls._iter_externally_managed_error_keys(): + with contextlib.suppress(KeyError): + return cls(section[key]) + except KeyError: + pass + except (OSError, UnicodeDecodeError, configparser.ParsingError): + from pip._internal.utils._log import VERBOSE + + exc_info = logger.isEnabledFor(VERBOSE) + logger.warning("Failed to read %s", config, exc_info=exc_info) + return cls(None) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/collector.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/collector.py index 0120610c7..b3e293ea3 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/collector.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/collector.py @@ -354,7 +354,7 @@ def _get_index_content(link: Link, *, session: PipSession) -> Optional["IndexCon if not url.endswith("/"): url += "/" # TODO: In the future, it would be nice if pip supported PEP 691 - # style respones in the file:// URLs, however there's no + # style responses in the file:// URLs, however there's no # standard file extension for application/vnd.pypi.simple.v1+json # so we'll need to come up with something on our own. url = urllib.parse.urljoin(url, "index.html") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/package_finder.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/package_finder.py index 9bf247f02..b6f8d57e8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/package_finder.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/index/package_finder.py @@ -1,14 +1,11 @@ """Routines related to PyPI, indexes""" -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import enum import functools import itertools import logging import re -from typing import FrozenSet, Iterable, List, Optional, Set, Tuple, Union +from typing import TYPE_CHECKING, FrozenSet, Iterable, List, Optional, Set, Tuple, Union from pip._vendor.packaging import specifiers from pip._vendor.packaging.tags import Tag @@ -39,6 +36,9 @@ from pip._internal.utils.packaging import check_requires_python from pip._internal.utils.unpacking import SUPPORTED_EXTENSIONS +if TYPE_CHECKING: + from pip._vendor.typing_extensions import TypeGuard + __all__ = ["FormatControl", "BestCandidateResult", "PackageFinder"] @@ -251,7 +251,7 @@ def evaluate_link(self, link: Link) -> Tuple[LinkType, str]: def filter_unallowed_hashes( candidates: List[InstallationCandidate], - hashes: Hashes, + hashes: Optional[Hashes], project_name: str, ) -> List[InstallationCandidate]: """ @@ -540,6 +540,7 @@ def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey: binary_preference = 1 if wheel.build_tag is not None: match = re.match(r"^(\d+)(.*)$", wheel.build_tag) + assert match is not None, "guaranteed by filename validation" build_tag_groups = match.groups() build_tag = (int(build_tag_groups[0]), build_tag_groups[1]) else: # sdist @@ -942,43 +943,46 @@ def _format_versions(cand_iter: Iterable[InstallationCandidate]) -> str: "No matching distribution found for {}".format(req) ) - best_installed = False - if installed_version and ( - best_candidate is None or best_candidate.version <= installed_version - ): - best_installed = True + def _should_install_candidate( + candidate: Optional[InstallationCandidate], + ) -> "TypeGuard[InstallationCandidate]": + if installed_version is None: + return True + if best_candidate is None: + return False + return best_candidate.version > installed_version if not upgrade and installed_version is not None: - if best_installed: + if _should_install_candidate(best_candidate): logger.debug( - "Existing installed version (%s) is most up-to-date and " - "satisfies requirement", + "Existing installed version (%s) satisfies requirement " + "(most up-to-date version is %s)", installed_version, + best_candidate.version, ) else: logger.debug( - "Existing installed version (%s) satisfies requirement " - "(most up-to-date version is %s)", + "Existing installed version (%s) is most up-to-date and " + "satisfies requirement", installed_version, - best_candidate.version, ) return None - if best_installed: - # We have an existing version, and its the best version + if _should_install_candidate(best_candidate): logger.debug( - "Installed version (%s) is most up-to-date (past versions: %s)", - installed_version, + "Using version %s (newest of versions: %s)", + best_candidate.version, _format_versions(best_candidate_result.iter_applicable()), ) - raise BestVersionAlreadyInstalled + return best_candidate + # We have an existing version, and its the best version logger.debug( - "Using version %s (newest of versions: %s)", - best_candidate.version, + "Installed version (%s) is most up-to-date (past versions: %s)", + installed_version, _format_versions(best_candidate_result.iter_applicable()), ) - return best_candidate + raise BestVersionAlreadyInstalled def _find_name_version_sep(fragment: str, canonical_name: str) -> int: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/__init__.py index 60afe0a73..d54bc63eb 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/__init__.py @@ -4,7 +4,7 @@ import pathlib import sys import sysconfig -from typing import Any, Dict, Generator, List, Optional, Tuple +from typing import Any, Dict, Generator, Optional, Tuple from pip._internal.models.scheme import SCHEME_KEYS, Scheme from pip._internal.utils.compat import WINDOWS @@ -27,7 +27,6 @@ "get_bin_user", "get_major_minor_version", "get_platlib", - "get_prefixed_libs", "get_purelib", "get_scheme", "get_src_prefix", @@ -466,63 +465,3 @@ def get_platlib() -> str: if _warn_if_mismatch(pathlib.Path(old), pathlib.Path(new), key="platlib"): _log_context() return old - - -def _deduplicated(v1: str, v2: str) -> List[str]: - """Deduplicate values from a list.""" - if v1 == v2: - return [v1] - return [v1, v2] - - -def _looks_like_apple_library(path: str) -> bool: - """Apple patches sysconfig to *always* look under */Library/Python*.""" - if sys.platform[:6] != "darwin": - return False - return path == f"/Library/Python/{get_major_minor_version()}/site-packages" - - -def get_prefixed_libs(prefix: str) -> List[str]: - """Return the lib locations under ``prefix``.""" - new_pure, new_plat = _sysconfig.get_prefixed_libs(prefix) - if _USE_SYSCONFIG: - return _deduplicated(new_pure, new_plat) - - old_pure, old_plat = _distutils.get_prefixed_libs(prefix) - old_lib_paths = _deduplicated(old_pure, old_plat) - - # Apple's Python (shipped with Xcode and Command Line Tools) hard-code - # platlib and purelib to '/Library/Python/X.Y/site-packages'. This will - # cause serious build isolation bugs when Apple starts shipping 3.10 because - # pip will install build backends to the wrong location. This tells users - # who is at fault so Apple may notice it and fix the issue in time. - if all(_looks_like_apple_library(p) for p in old_lib_paths): - deprecated( - reason=( - "Python distributed by Apple's Command Line Tools incorrectly " - "patches sysconfig to always point to '/Library/Python'. This " - "will cause build isolation to operate incorrectly on Python " - "3.10 or later. Please help report this to Apple so they can " - "fix this. https://developer.apple.com/bug-reporting/" - ), - replacement=None, - gone_in=None, - ) - return old_lib_paths - - warned = [ - _warn_if_mismatch( - pathlib.Path(old_pure), - pathlib.Path(new_pure), - key="prefixed-purelib", - ), - _warn_if_mismatch( - pathlib.Path(old_plat), - pathlib.Path(new_plat), - key="prefixed-platlib", - ), - ] - if any(warned): - _log_context(prefix=prefix) - - return old_lib_paths diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_distutils.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_distutils.py index c7712f016..92bd93179 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_distutils.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_distutils.py @@ -21,7 +21,7 @@ from distutils.command.install import SCHEME_KEYS from distutils.command.install import install as distutils_install_command from distutils.sysconfig import get_python_lib -from typing import Dict, List, Optional, Tuple, Union, cast +from typing import Dict, List, Optional, Union, cast from pip._internal.models.scheme import Scheme from pip._internal.utils.compat import WINDOWS @@ -171,10 +171,3 @@ def get_purelib() -> str: def get_platlib() -> str: return get_python_lib(plat_specific=True) - - -def get_prefixed_libs(prefix: str) -> Tuple[str, str]: - return ( - get_python_lib(plat_specific=False, prefix=prefix), - get_python_lib(plat_specific=True, prefix=prefix), - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_sysconfig.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_sysconfig.py index 0bbc9283d..97aef1f1a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_sysconfig.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/_sysconfig.py @@ -211,8 +211,3 @@ def get_purelib() -> str: def get_platlib() -> str: return sysconfig.get_paths()["platlib"] - - -def get_prefixed_libs(prefix: str) -> typing.Tuple[str, str]: - paths = sysconfig.get_paths(vars={"base": prefix, "platbase": prefix}) - return (paths["purelib"], paths["platlib"]) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/base.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/base.py index 3f7de0061..3f9f896e6 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/base.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/locations/base.py @@ -13,7 +13,7 @@ USER_CACHE_DIR = appdirs.user_cache_dir("pip") # FIXME doesn't account for venv linked to global site-packages -site_packages: typing.Optional[str] = sysconfig.get_path("purelib") +site_packages: str = sysconfig.get_path("purelib") def get_major_minor_version() -> str: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/direct_url.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/direct_url.py index e75feda9c..c3de70a74 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/direct_url.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/direct_url.py @@ -103,17 +103,33 @@ class ArchiveInfo: def __init__( self, hash: Optional[str] = None, + hashes: Optional[Dict[str, str]] = None, ) -> None: + if hash is not None: + # Auto-populate the hashes key to upgrade to the new format automatically. + # We don't back-populate the legacy hash key. + try: + hash_name, hash_value = hash.split("=", 1) + except ValueError: + raise DirectUrlValidationError( + f"invalid archive_info.hash format: {hash!r}" + ) + if hashes is None: + hashes = {hash_name: hash_value} + elif hash_name not in hash: + hashes = hashes.copy() + hashes[hash_name] = hash_value self.hash = hash + self.hashes = hashes @classmethod def _from_dict(cls, d: Optional[Dict[str, Any]]) -> Optional["ArchiveInfo"]: if d is None: return None - return cls(hash=_get(d, str, "hash")) + return cls(hash=_get(d, str, "hash"), hashes=_get(d, dict, "hashes")) def _to_dict(self) -> Dict[str, Any]: - return _filter_none(hash=self.hash) + return _filter_none(hash=self.hash, hashes=self.hashes) class DirInfo: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/installation_report.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/installation_report.py index 965f09523..b54afb109 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/installation_report.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/installation_report.py @@ -38,7 +38,7 @@ def _install_req_to_dict(cls, ireq: InstallRequirement) -> Dict[str, Any]: def to_dict(self) -> Dict[str, Any]: return { - "version": "0", + "version": "1", "pip_version": __version__, "install": [ self._install_req_to_dict(ireq) for ireq in self._install_requirements diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/link.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/link.py index c792d128b..a1e4d5a08 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/link.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/models/link.py @@ -18,6 +18,7 @@ Union, ) +from pip._internal.utils.deprecation import deprecated from pip._internal.utils.filetypes import WHEEL_EXTENSION from pip._internal.utils.hashes import Hashes from pip._internal.utils.misc import ( @@ -78,6 +79,9 @@ def split_hash_name_and_value(cls, url: str) -> Optional["LinkHash"]: name, value = match.groups() return cls(name=name, value=value) + def as_dict(self) -> Dict[str, str]: + return {self.name: self.value} + def as_hashes(self) -> Hashes: """Return a Hashes instance which checks only for the current hash.""" return Hashes({self.name: [self.value]}) @@ -164,8 +168,8 @@ class Link(KeyBasedCompareMixin): "requires_python", "yanked_reason", "dist_info_metadata", - "link_hash", "cache_link_parsing", + "egg_fragment", ] def __init__( @@ -175,7 +179,6 @@ def __init__( requires_python: Optional[str] = None, yanked_reason: Optional[str] = None, dist_info_metadata: Optional[str] = None, - link_hash: Optional[LinkHash] = None, cache_link_parsing: bool = True, hashes: Optional[Mapping[str, str]] = None, ) -> None: @@ -198,16 +201,11 @@ def __init__( attribute, if present, in a simple repository HTML link. This may be parsed into its own `Link` by `self.metadata_link()`. See PEP 658 for more information and the specification. - :param link_hash: a checksum for the content the link points to. If not - provided, this will be extracted from the link URL, if the URL has - any checksum. :param cache_link_parsing: A flag that is used elsewhere to determine - whether resources retrieved from this link - should be cached. PyPI index urls should - generally have this set to False, for - example. + whether resources retrieved from this link should be cached. PyPI + URLs should generally have this set to False, for example. :param hashes: A mapping of hash names to digests to allow us to - determine the validity of a download. + determine the validity of a download. """ # url can be a UNC windows share @@ -218,17 +216,23 @@ def __init__( # Store the url as a private attribute to prevent accidentally # trying to set a new value. self._url = url - self._hashes = hashes if hashes is not None else {} + + link_hash = LinkHash.split_hash_name_and_value(url) + hashes_from_link = {} if link_hash is None else link_hash.as_dict() + if hashes is None: + self._hashes = hashes_from_link + else: + self._hashes = {**hashes, **hashes_from_link} self.comes_from = comes_from self.requires_python = requires_python if requires_python else None self.yanked_reason = yanked_reason self.dist_info_metadata = dist_info_metadata - self.link_hash = link_hash or LinkHash.split_hash_name_and_value(self._url) super().__init__(key=url, defining_class=Link) self.cache_link_parsing = cache_link_parsing + self.egg_fragment = self._egg_fragment() @classmethod def from_json( @@ -358,12 +362,28 @@ def url_without_fragment(self) -> str: _egg_fragment_re = re.compile(r"[#&]egg=([^&]*)") - @property - def egg_fragment(self) -> Optional[str]: + # Per PEP 508. + _project_name_re = re.compile( + r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.IGNORECASE + ) + + def _egg_fragment(self) -> Optional[str]: match = self._egg_fragment_re.search(self._url) if not match: return None - return match.group(1) + + # An egg fragment looks like a PEP 508 project name, along with + # an optional extras specifier. Anything else is invalid. + project_name = match.group(1) + if not self._project_name_re.match(project_name): + deprecated( + reason=f"{self} contains an egg fragment with a non-PEP 508 name", + replacement="to use the req @ url syntax, and remove the egg fragment", + gone_in="25.0", + issue=11617, + ) + + return project_name _subdirectory_fragment_re = re.compile(r"[#&]subdirectory=([^&]*)") @@ -382,29 +402,26 @@ def metadata_link(self) -> Optional["Link"]: if self.dist_info_metadata is None: return None metadata_url = f"{self.url_without_fragment}.metadata" - link_hash: Optional[LinkHash] = None # If data-dist-info-metadata="true" is set, then the metadata file exists, # but there is no information about its checksum or anything else. if self.dist_info_metadata != "true": link_hash = LinkHash.split_hash_name_and_value(self.dist_info_metadata) - return Link(metadata_url, link_hash=link_hash) + else: + link_hash = None + if link_hash is None: + return Link(metadata_url) + return Link(metadata_url, hashes=link_hash.as_dict()) - def as_hashes(self) -> Optional[Hashes]: - if self.link_hash is not None: - return self.link_hash.as_hashes() - return None + def as_hashes(self) -> Hashes: + return Hashes({k: [v] for k, v in self._hashes.items()}) @property def hash(self) -> Optional[str]: - if self.link_hash is not None: - return self.link_hash.value - return None + return next(iter(self._hashes.values()), None) @property def hash_name(self) -> Optional[str]: - if self.link_hash is not None: - return self.link_hash.name - return None + return next(iter(self._hashes), None) @property def show_url(self) -> str: @@ -433,15 +450,15 @@ def is_yanked(self) -> bool: @property def has_hash(self) -> bool: - return self.link_hash is not None + return bool(self._hashes) def is_hash_allowed(self, hashes: Optional[Hashes]) -> bool: """ Return True if the link has a hash and it is allowed by `hashes`. """ - if self.link_hash is None: + if hashes is None: return False - return self.link_hash.is_hash_allowed(hashes) + return any(hashes.is_hash_allowed(k, v) for k, v in self._hashes.items()) class _CleanResult(NamedTuple): diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/network/auth.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/network/auth.py index ca42798bd..c16213268 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/network/auth.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/network/auth.py @@ -4,8 +4,12 @@ providing credentials in the context of network requests. """ +import os +import shutil +import subprocess import urllib.parse -from typing import Any, Dict, List, Optional, Tuple +from abc import ABC, abstractmethod +from typing import Any, Dict, List, NamedTuple, Optional, Tuple from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth from pip._vendor.requests.models import Request, Response @@ -23,51 +27,165 @@ logger = getLogger(__name__) -Credentials = Tuple[str, str, str] +KEYRING_DISABLED = False -try: - import keyring -except ImportError: - keyring = None # type: ignore[assignment] -except Exception as exc: - logger.warning( - "Keyring is skipped due to an exception: %s", - str(exc), - ) - keyring = None # type: ignore[assignment] +class Credentials(NamedTuple): + url: str + username: str + password: str -def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]: - """Return the tuple auth for a given url from keyring.""" - global keyring - if not url or not keyring: + +class KeyRingBaseProvider(ABC): + """Keyring base provider interface""" + + @abstractmethod + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + ... + + @abstractmethod + def save_auth_info(self, url: str, username: str, password: str) -> None: + ... + + +class KeyRingNullProvider(KeyRingBaseProvider): + """Keyring null provider""" + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: return None - try: - try: - get_credential = keyring.get_credential - except AttributeError: - pass - else: + def save_auth_info(self, url: str, username: str, password: str) -> None: + return None + + +class KeyRingPythonProvider(KeyRingBaseProvider): + """Keyring interface which uses locally imported `keyring`""" + + def __init__(self) -> None: + import keyring + + self.keyring = keyring + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + # Support keyring's get_credential interface which supports getting + # credentials without a username. This is only available for + # keyring>=15.2.0. + if hasattr(self.keyring, "get_credential"): logger.debug("Getting credentials from keyring for %s", url) - cred = get_credential(url, username) + cred = self.keyring.get_credential(url, username) if cred is not None: return cred.username, cred.password return None - if username: + if username is not None: logger.debug("Getting password from keyring for %s", url) - password = keyring.get_password(url, username) + password = self.keyring.get_password(url, username) if password: return username, password + return None + + def save_auth_info(self, url: str, username: str, password: str) -> None: + self.keyring.set_password(url, username, password) + + +class KeyRingCliProvider(KeyRingBaseProvider): + """Provider which uses `keyring` cli + + Instead of calling the keyring package installed alongside pip + we call keyring on the command line which will enable pip to + use which ever installation of keyring is available first in + PATH. + """ + + def __init__(self, cmd: str) -> None: + self.keyring = cmd + + def get_auth_info(self, url: str, username: Optional[str]) -> Optional[AuthInfo]: + # This is the default implementation of keyring.get_credential + # https://github.com/jaraco/keyring/blob/97689324abcf01bd1793d49063e7ca01e03d7d07/keyring/backend.py#L134-L139 + if username is not None: + password = self._get_password(url, username) + if password is not None: + return username, password + return None + def save_auth_info(self, url: str, username: str, password: str) -> None: + return self._set_password(url, username, password) + + def _get_password(self, service_name: str, username: str) -> Optional[str]: + """Mirror the implementation of keyring.get_password using cli""" + if self.keyring is None: + return None + + cmd = [self.keyring, "get", service_name, username] + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + res = subprocess.run( + cmd, + stdin=subprocess.DEVNULL, + capture_output=True, + env=env, + ) + if res.returncode: + return None + return res.stdout.decode("utf-8").strip(os.linesep) + + def _set_password(self, service_name: str, username: str, password: str) -> None: + """Mirror the implementation of keyring.set_password using cli""" + if self.keyring is None: + return None + + cmd = [self.keyring, "set", service_name, username] + input_ = (password + os.linesep).encode("utf-8") + env = os.environ.copy() + env["PYTHONIOENCODING"] = "utf-8" + res = subprocess.run(cmd, input=input_, env=env) + res.check_returncode() + return None + + +def get_keyring_provider() -> KeyRingBaseProvider: + # keyring has previously failed and been disabled + if not KEYRING_DISABLED: + # Default to trying to use Python provider + try: + return KeyRingPythonProvider() + except ImportError: + pass + except Exception as exc: + # In the event of an unexpected exception + # we should warn the user + logger.warning( + "Installed copy of keyring fails with exception %s, " + "trying to find a keyring executable as a fallback", + str(exc), + ) + + # Fallback to Cli Provider if `keyring` isn't installed + cli = shutil.which("keyring") + if cli: + return KeyRingCliProvider(cli) + + return KeyRingNullProvider() + + +def get_keyring_auth(url: Optional[str], username: Optional[str]) -> Optional[AuthInfo]: + """Return the tuple auth for a given url from keyring.""" + # Do nothing if no url was provided + if not url: + return None + + keyring = get_keyring_provider() + try: + return keyring.get_auth_info(url, username) except Exception as exc: logger.warning( "Keyring is skipped due to an exception: %s", str(exc), ) - keyring = None # type: ignore[assignment] - return None + global KEYRING_DISABLED + KEYRING_DISABLED = True + return None class MultiDomainBasicAuth(AuthBase): @@ -241,7 +359,7 @@ def _prompt_for_password( # Factored out to allow for easy patching in tests def _should_save_password_to_keyring(self) -> bool: - if not keyring: + if get_keyring_provider() is None: return False return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y" @@ -276,7 +394,11 @@ def handle_401(self, resp: Response, **kwargs: Any) -> Response: # Prompt to save the password to keyring if save and self._should_save_password_to_keyring(): - self._credentials_to_save = (parsed.netloc, username, password) + self._credentials_to_save = Credentials( + url=parsed.netloc, + username=username, + password=password, + ) # Consume content and release the original connection to allow our new # request to reuse the same one. @@ -309,15 +431,16 @@ def warn_on_401(self, resp: Response, **kwargs: Any) -> None: def save_credentials(self, resp: Response, **kwargs: Any) -> None: """Response callback to save credentials on success.""" - assert keyring is not None, "should never reach here without keyring" - if not keyring: - return + keyring = get_keyring_provider() + assert not isinstance( + keyring, KeyRingNullProvider + ), "should never reach here without keyring" creds = self._credentials_to_save self._credentials_to_save = None if creds and resp.status_code < 400: try: logger.info("Saving credentials to keyring") - keyring.set_password(*creds) + keyring.save_auth_info(creds.url, creds.username, creds.password) except Exception: logger.exception("Failed to save credentials") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata.py index e2b7b4445..c66ac354d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata.py @@ -3,7 +3,7 @@ import os -from pip._vendor.pep517.wrappers import Pep517HookCaller +from pip._vendor.pyproject_hooks import BuildBackendHookCaller from pip._internal.build_env import BuildEnvironment from pip._internal.exceptions import ( @@ -15,7 +15,7 @@ def generate_metadata( - build_env: BuildEnvironment, backend: Pep517HookCaller, details: str + build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str ) -> str: """Generate metadata using mechanisms described in PEP 517. @@ -26,7 +26,7 @@ def generate_metadata( metadata_dir = metadata_tmpdir.path with build_env: - # Note that Pep517HookCaller implements a fallback for + # Note that BuildBackendHookCaller implements a fallback for # prepare_metadata_for_build_wheel, so we don't have to # consider the possibility that this hook doesn't exist. runner = runner_with_spinner_message("Preparing metadata (pyproject.toml)") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py index 4c3f48b6c..27c69f0d1 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/metadata_editable.py @@ -3,7 +3,7 @@ import os -from pip._vendor.pep517.wrappers import Pep517HookCaller +from pip._vendor.pyproject_hooks import BuildBackendHookCaller from pip._internal.build_env import BuildEnvironment from pip._internal.exceptions import ( @@ -15,7 +15,7 @@ def generate_editable_metadata( - build_env: BuildEnvironment, backend: Pep517HookCaller, details: str + build_env: BuildEnvironment, backend: BuildBackendHookCaller, details: str ) -> str: """Generate metadata using mechanisms described in PEP 660. @@ -26,7 +26,7 @@ def generate_editable_metadata( metadata_dir = metadata_tmpdir.path with build_env: - # Note that Pep517HookCaller implements a fallback for + # Note that BuildBackendHookCaller implements a fallback for # prepare_metadata_for_build_wheel/editable, so we don't have to # consider the possibility that this hook doesn't exist. runner = runner_with_spinner_message( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel.py index b0d2fc9ea..064811ad1 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel.py @@ -2,7 +2,7 @@ import os from typing import Optional -from pip._vendor.pep517.wrappers import Pep517HookCaller +from pip._vendor.pyproject_hooks import BuildBackendHookCaller from pip._internal.utils.subprocess import runner_with_spinner_message @@ -11,7 +11,7 @@ def build_wheel_pep517( name: str, - backend: Pep517HookCaller, + backend: BuildBackendHookCaller, metadata_directory: str, tempd: str, ) -> Optional[str]: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py index cf7b01aed..719d69dd8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/build/wheel_editable.py @@ -2,7 +2,7 @@ import os from typing import Optional -from pip._vendor.pep517.wrappers import HookMissing, Pep517HookCaller +from pip._vendor.pyproject_hooks import BuildBackendHookCaller, HookMissing from pip._internal.utils.subprocess import runner_with_spinner_message @@ -11,7 +11,7 @@ def build_wheel_editable( name: str, - backend: Pep517HookCaller, + backend: BuildBackendHookCaller, metadata_directory: str, tempd: str, ) -> Optional[str]: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/check.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/check.py index fb3ac8b9c..e3bce69b2 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/check.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/operations/check.py @@ -75,7 +75,7 @@ def check_package_set( if name not in package_set: missed = True if req.marker is not None: - missed = req.marker.evaluate() + missed = req.marker.evaluate({"extra": ""}) if missed: missing_deps.add((name, req)) continue diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/pyproject.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/pyproject.py index 1e9119f3e..1de9f0fde 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/pyproject.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/pyproject.py @@ -159,9 +159,8 @@ def load_pyproject_toml( if backend is None: # If the user didn't specify a backend, we assume they want to use # the setuptools backend. But we can't be sure they have included - # a version of setuptools which supplies the backend, or wheel - # (which is needed by the backend) in their requirements. So we - # make a note to check that those requirements are present once + # a version of setuptools which supplies the backend. So we + # make a note to check that this requirement is present once # we have set up the environment. # This is quite a lot of work to check for a very specific case. But # the problem is, that case is potentially quite common - projects that @@ -170,6 +169,6 @@ def load_pyproject_toml( # tools themselves. The original PEP 518 code had a similar check (but # implemented in a different way). backend = "setuptools.build_meta:__legacy__" - check = ["setuptools>=40.8.0", "wheel"] + check = ["setuptools>=40.8.0"] return BuildSystemDetails(requires, backend, check, backend_path) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/req/req_install.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/req/req_install.py index 5f29261c2..bb38ec09d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/req/req_install.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/req/req_install.py @@ -18,7 +18,7 @@ from pip._vendor.packaging.utils import canonicalize_name from pip._vendor.packaging.version import Version from pip._vendor.packaging.version import parse as parse_version -from pip._vendor.pep517.wrappers import Pep517HookCaller +from pip._vendor.pyproject_hooks import BuildBackendHookCaller from pip._internal.build_env import BuildEnvironment, NoOpBuildEnvironment from pip._internal.exceptions import InstallationError, LegacyInstallFailure @@ -51,7 +51,7 @@ ) from pip._internal.utils.hashes import Hashes from pip._internal.utils.misc import ( - ConfiguredPep517HookCaller, + ConfiguredBuildBackendHookCaller, ask_path_exists, backup_dir, display_path, @@ -173,7 +173,7 @@ def __init__( self.requirements_to_check: List[str] = [] # The PEP 517 backend we should use to build the project - self.pep517_backend: Optional[Pep517HookCaller] = None + self.pep517_backend: Optional[BuildBackendHookCaller] = None # Are we using PEP 517 for this requirement? # After pyproject.toml has been loaded, the only valid values are True @@ -195,7 +195,11 @@ def __str__(self) -> str: else: s = "" if self.satisfied_by is not None: - s += " in {}".format(display_path(self.satisfied_by.location)) + if self.satisfied_by.location is not None: + location = display_path(self.satisfied_by.location) + else: + location = "" + s += f" in {location}" if self.comes_from: if isinstance(self.comes_from, str): comes_from: Optional[str] = self.comes_from @@ -482,7 +486,7 @@ def load_pyproject_toml(self) -> None: requires, backend, check, backend_path = pyproject_toml_data self.requirements_to_check = check self.pyproject_requires = requires - self.pep517_backend = ConfiguredPep517HookCaller( + self.pep517_backend = ConfiguredBuildBackendHookCaller( self, self.unpacked_source_directory, backend, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/self_outdated_check.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/self_outdated_check.py index 9e2149c52..41cc42c56 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/self_outdated_check.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/self_outdated_check.py @@ -133,7 +133,7 @@ def __rich__(self) -> Group: return Group( Text(), Text.from_markup( - f"{notice} A new release of pip available: " + f"{notice} A new release of pip is available: " f"[red]{self.old}[reset] -> [green]{self.new}[reset]" ), Text.from_markup( @@ -155,7 +155,7 @@ def was_installed_by_pip(pkg: str) -> bool: def _get_current_remote_pip_version( session: PipSession, options: optparse.Values -) -> str: +) -> Optional[str]: # Lets use PackageFinder to see what the latest pip version is link_collector = LinkCollector.create( session, @@ -176,7 +176,7 @@ def _get_current_remote_pip_version( ) best_candidate = finder.find_best_candidate("pip").best_candidate if best_candidate is None: - return + return None return str(best_candidate.version) @@ -186,11 +186,14 @@ def _self_version_check_logic( state: SelfCheckState, current_time: datetime.datetime, local_version: DistributionVersion, - get_remote_version: Callable[[], str], + get_remote_version: Callable[[], Optional[str]], ) -> Optional[UpgradePrompt]: remote_version_str = state.get(current_time) if remote_version_str is None: remote_version_str = get_remote_version() + if remote_version_str is None: + logger.debug("No remote pip version found") + return None state.set(remote_version_str, current_time) remote_version = parse_version(remote_version_str) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/egg_link.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/egg_link.py index 9e0da8d2d..eb57ed151 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/egg_link.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/egg_link.py @@ -1,10 +1,7 @@ -# The following comment should be removed at some point in the future. -# mypy: strict-optional=False - import os import re import sys -from typing import Optional +from typing import List, Optional from pip._internal.locations import site_packages, user_site from pip._internal.utils.virtualenv import ( @@ -57,7 +54,7 @@ def egg_link_path_from_location(raw_name: str) -> Optional[str]: This method will just return the first one found. """ - sites = [] + sites: List[str] = [] if running_under_virtualenv(): sites.append(site_packages) if not virtualenv_no_global() and user_site: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/misc.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/misc.py index a8f4cb5cf..baa1ba7ea 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/misc.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/misc.py @@ -12,6 +12,7 @@ import shutil import stat import sys +import sysconfig import urllib.parse from io import StringIO from itertools import filterfalse, tee, zip_longest @@ -34,11 +35,11 @@ cast, ) -from pip._vendor.pep517 import Pep517HookCaller +from pip._vendor.pyproject_hooks import BuildBackendHookCaller from pip._vendor.tenacity import retry, stop_after_delay, wait_fixed from pip import __version__ -from pip._internal.exceptions import CommandError +from pip._internal.exceptions import CommandError, ExternallyManagedEnvironment from pip._internal.locations import get_major_minor_version from pip._internal.utils.compat import WINDOWS from pip._internal.utils.virtualenv import running_under_virtualenv @@ -57,10 +58,10 @@ "captured_stdout", "ensure_dir", "remove_auth_from_url", - "ConfiguredPep517HookCaller", + "check_externally_managed", + "ConfiguredBuildBackendHookCaller", ] - logger = logging.getLogger(__name__) T = TypeVar("T") @@ -581,6 +582,21 @@ def protect_pip_from_modification_on_windows(modifying_pip: bool) -> None: ) +def check_externally_managed() -> None: + """Check whether the current environment is externally managed. + + If the ``EXTERNALLY-MANAGED`` config file is found, the current environment + is considered externally managed, and an ExternallyManagedEnvironment is + raised. + """ + if running_under_virtualenv(): + return + marker = os.path.join(sysconfig.get_path("stdlib"), "EXTERNALLY-MANAGED") + if not os.path.isfile(marker): + return + raise ExternallyManagedEnvironment.from_config(marker) + + def is_console_interactive() -> bool: """Is this console interactive?""" return sys.stdin is not None and sys.stdin.isatty() @@ -635,7 +651,7 @@ def partition( return filterfalse(pred, t1), filter(pred, t2) -class ConfiguredPep517HookCaller(Pep517HookCaller): +class ConfiguredBuildBackendHookCaller(BuildBackendHookCaller): def __init__( self, config_holder: Any, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/subprocess.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/subprocess.py index cf5bf6be1..1e8ff50ed 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/subprocess.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/subprocess.py @@ -239,8 +239,8 @@ def call_subprocess( def runner_with_spinner_message(message: str) -> Callable[..., None]: """Provide a subprocess_runner that shows a spinner message. - Intended for use with for pep517's Pep517HookCaller. Thus, the runner has - an API that matches what's expected by Pep517HookCaller.subprocess_runner. + Intended for use with for BuildBackendHookCaller. Thus, the runner has + an API that matches what's expected by BuildBackendHookCaller.subprocess_runner. """ def runner( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/virtualenv.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/virtualenv.py index c926db4c3..882e36f5c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/virtualenv.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/utils/virtualenv.py @@ -19,7 +19,7 @@ def _running_under_venv() -> bool: return sys.prefix != getattr(sys, "base_prefix", sys.prefix) -def _running_under_regular_virtualenv() -> bool: +def _running_under_legacy_virtualenv() -> bool: """Checks if sys.real_prefix is set. This handles virtual environments created with pypa's virtualenv. @@ -29,8 +29,8 @@ def _running_under_regular_virtualenv() -> bool: def running_under_virtualenv() -> bool: - """Return True if we're running inside a virtualenv, False otherwise.""" - return _running_under_venv() or _running_under_regular_virtualenv() + """True if we're running inside a virtual environment, False otherwise.""" + return _running_under_venv() or _running_under_legacy_virtualenv() def _get_pyvenv_cfg_lines() -> Optional[List[str]]: @@ -77,7 +77,7 @@ def _no_global_under_venv() -> bool: return False -def _no_global_under_regular_virtualenv() -> bool: +def _no_global_under_legacy_virtualenv() -> bool: """Check if "no-global-site-packages.txt" exists beside site.py This mirrors logic in pypa/virtualenv for determining whether system @@ -98,7 +98,7 @@ def virtualenv_no_global() -> bool: if _running_under_venv(): return _no_global_under_venv() - if _running_under_regular_virtualenv(): - return _no_global_under_regular_virtualenv() + if _running_under_legacy_virtualenv(): + return _no_global_under_legacy_virtualenv() return False diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/bazaar.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/bazaar.py index 06c80e48a..20a17ed09 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/bazaar.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/bazaar.py @@ -72,7 +72,7 @@ def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: @classmethod def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: - # hotfix the URL scheme after removing bzr+ from bzr+ssh:// readd it + # hotfix the URL scheme after removing bzr+ from bzr+ssh:// re-add it url, rev, user_pass = super().get_url_rev_and_auth(url) if url.startswith("ssh://"): url = "bzr+" + url diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/subversion.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/subversion.py index 2cd6f0ae9..16d93a67b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/subversion.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_internal/vcs/subversion.py @@ -87,7 +87,7 @@ def get_netloc_and_auth( @classmethod def get_url_rev_and_auth(cls, url: str) -> Tuple[str, Optional[str], AuthInfo]: - # hotfix the URL scheme after removing svn+ from svn+ssh:// readd it + # hotfix the URL scheme after removing svn+ from svn+ssh:// re-add it url, rev, user_pass = super().get_url_rev_and_auth(url) if url.startswith("ssh://"): url = "svn+" + url diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/__init__.py index af4bcc151..a3546f125 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/__init__.py @@ -1,4 +1,4 @@ from .core import contents, where __all__ = ["contents", "where"] -__version__ = "2022.09.24" +__version__ = "2022.12.07" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/cacert.pem b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/cacert.pem index 400515511..df9e4e3c7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/cacert.pem +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/certifi/cacert.pem @@ -636,37 +636,6 @@ BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB ZQ== -----END CERTIFICATE----- -# Issuer: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. -# Subject: CN=Network Solutions Certificate Authority O=Network Solutions L.L.C. -# Label: "Network Solutions Certificate Authority" -# Serial: 116697915152937497490437556386812487904 -# MD5 Fingerprint: d3:f3:a6:16:c0:fa:6b:1d:59:b1:2d:96:4d:0e:11:2e -# SHA1 Fingerprint: 74:f8:a3:c3:ef:e7:b3:90:06:4b:83:90:3c:21:64:60:20:e5:df:ce -# SHA256 Fingerprint: 15:f0:ba:00:a3:ac:7a:f3:ac:88:4c:07:2b:10:11:a0:77:bd:77:c0:97:f4:01:64:b2:f8:59:8a:bd:83:86:0c ------BEGIN CERTIFICATE----- -MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi -MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu -MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV -UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO -ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz -c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP -OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl -mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF -BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 -qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw -gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu -bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp -dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 -6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ -h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH -/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv -wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN -pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey ------END CERTIFICATE----- - # Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited # Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited # Label: "COMODO ECC Certification Authority" @@ -2204,46 +2173,6 @@ KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg xwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- -# Issuer: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden -# Subject: CN=Staat der Nederlanden EV Root CA O=Staat der Nederlanden -# Label: "Staat der Nederlanden EV Root CA" -# Serial: 10000013 -# MD5 Fingerprint: fc:06:af:7b:e8:1a:f1:9a:b4:e8:d2:70:1f:c0:f5:ba -# SHA1 Fingerprint: 76:e2:7e:c1:4f:db:82:c1:c0:a6:75:b5:05:be:3d:29:b4:ed:db:bb -# SHA256 Fingerprint: 4d:24:91:41:4c:fe:95:67:46:ec:4c:ef:a6:cf:6f:72:e2:8a:13:29:43:2f:9d:8a:90:7a:c4:cb:5d:ad:c1:5a ------BEGIN CERTIFICATE----- -MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y -MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg -TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS -b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS -M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC -UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d -Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p -rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l -pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb -j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC -KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS -/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X -cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH -1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP -px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 -MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI -eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u -2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS -v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC -wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy -CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e -vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 -Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa -Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL -eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 -FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc -7uzXLg== ------END CERTIFICATE----- - # Issuer: CN=IdenTrust Commercial Root CA 1 O=IdenTrust # Subject: CN=IdenTrust Commercial Root CA 1 O=IdenTrust # Label: "IdenTrust Commercial Root CA 1" @@ -2851,116 +2780,6 @@ T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== -----END CERTIFICATE----- -# Issuer: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Subject: CN=TrustCor RootCert CA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Label: "TrustCor RootCert CA-1" -# Serial: 15752444095811006489 -# MD5 Fingerprint: 6e:85:f1:dc:1a:00:d3:22:d5:b2:b2:ac:6b:37:05:45 -# SHA1 Fingerprint: ff:bd:cd:e7:82:c8:43:5e:3c:6f:26:86:5c:ca:a8:3a:45:5b:c3:0a -# SHA256 Fingerprint: d4:0e:9c:86:cd:8f:e4:68:c1:77:69:59:f4:9e:a7:74:fa:54:86:84:b6:c4:06:f3:90:92:61:f4:dc:e2:57:5c ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD -VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk -MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y -IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB -pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h -IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG -A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU -cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid -RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V -seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme -9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV -EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW -hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ -DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD -ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I -/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf -ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ -yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts -L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN -zl/HHk484IkzlQsPpTLWPFp5LBk= ------END CERTIFICATE----- - -# Issuer: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Subject: CN=TrustCor RootCert CA-2 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Label: "TrustCor RootCert CA-2" -# Serial: 2711694510199101698 -# MD5 Fingerprint: a2:e1:f8:18:0b:ba:45:d5:c7:41:2a:bb:37:52:45:64 -# SHA1 Fingerprint: b8:be:6d:cb:56:f1:55:b9:63:d4:12:ca:4e:06:34:c7:94:b2:1c:c0 -# SHA256 Fingerprint: 07:53:e9:40:37:8c:1b:d5:e3:83:6e:39:5d:ae:a5:cb:83:9e:50:46:f1:bd:0e:ae:19:51:cf:10:fe:c7:c9:65 ------BEGIN CERTIFICATE----- -MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV -BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw -IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy -dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig -Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk -MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg -Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD -VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy -dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ -QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq -1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp -2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK -DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape -az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF -3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 -oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM -g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 -mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh -8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd -BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U -nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw -DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX -dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ -MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL -/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX -CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa -ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW -2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 -N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 -Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB -As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp -5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu -1uwJ ------END CERTIFICATE----- - -# Issuer: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Subject: CN=TrustCor ECA-1 O=TrustCor Systems S. de R.L. OU=TrustCor Certificate Authority -# Label: "TrustCor ECA-1" -# Serial: 9548242946988625984 -# MD5 Fingerprint: 27:92:23:1d:0a:f5:40:7c:e9:e6:6b:9d:d8:f5:e7:6c -# SHA1 Fingerprint: 58:d1:df:95:95:67:6b:63:c0:f0:5b:1c:17:4d:8b:84:0b:c8:78:bd -# SHA256 Fingerprint: 5a:88:5d:b1:9c:01:d9:12:c5:75:93:88:93:8c:af:bb:df:03:1a:b2:d4:8e:91:ee:15:58:9b:42:97:1d:03:9c ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD -VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk -MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y -IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV -BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw -IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy -dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig -RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb -3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA -BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 -3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou -owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ -wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF -ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf -BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ -MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv -civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 -AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F -hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 -soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI -WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi -tJ/X5g== ------END CERTIFICATE----- - # Issuer: CN=SSL.com Root Certification Authority RSA O=SSL Corporation # Subject: CN=SSL.com Root Certification Authority RSA O=SSL Corporation # Label: "SSL.com Root Certification Authority RSA" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/__init__.py index e91ad6182..fe581623d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/__init__.py @@ -15,19 +15,29 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import List, Union + +from .charsetgroupprober import CharSetGroupProber +from .charsetprober import CharSetProber from .enums import InputState +from .resultdict import ResultDict from .universaldetector import UniversalDetector from .version import VERSION, __version__ __all__ = ["UniversalDetector", "detect", "detect_all", "__version__", "VERSION"] -def detect(byte_str): +def detect( + byte_str: Union[bytes, bytearray], should_rename_legacy: bool = False +) -> ResultDict: """ Detect the encoding of the given byte string. :param byte_str: The byte sequence to examine. :type byte_str: ``bytes`` or ``bytearray`` + :param should_rename_legacy: Should we rename legacy encodings + to their more modern equivalents? + :type should_rename_legacy: ``bool`` """ if not isinstance(byte_str, bytearray): if not isinstance(byte_str, bytes): @@ -35,12 +45,16 @@ def detect(byte_str): f"Expected object of type bytes or bytearray, got: {type(byte_str)}" ) byte_str = bytearray(byte_str) - detector = UniversalDetector() + detector = UniversalDetector(should_rename_legacy=should_rename_legacy) detector.feed(byte_str) return detector.close() -def detect_all(byte_str, ignore_threshold=False): +def detect_all( + byte_str: Union[bytes, bytearray], + ignore_threshold: bool = False, + should_rename_legacy: bool = False, +) -> List[ResultDict]: """ Detect all the possible encodings of the given byte string. @@ -50,6 +64,9 @@ def detect_all(byte_str, ignore_threshold=False): ``UniversalDetector.MINIMUM_THRESHOLD`` in results. :type ignore_threshold: ``bool`` + :param should_rename_legacy: Should we rename legacy encodings + to their more modern equivalents? + :type should_rename_legacy: ``bool`` """ if not isinstance(byte_str, bytearray): if not isinstance(byte_str, bytes): @@ -58,15 +75,15 @@ def detect_all(byte_str, ignore_threshold=False): ) byte_str = bytearray(byte_str) - detector = UniversalDetector() + detector = UniversalDetector(should_rename_legacy=should_rename_legacy) detector.feed(byte_str) detector.close() if detector.input_state == InputState.HIGH_BYTE: - results = [] - probers = [] + results: List[ResultDict] = [] + probers: List[CharSetProber] = [] for prober in detector.charset_probers: - if hasattr(prober, "probers"): + if isinstance(prober, CharSetGroupProber): probers.extend(p for p in prober.probers) else: probers.append(prober) @@ -80,6 +97,11 @@ def detect_all(byte_str, ignore_threshold=False): charset_name = detector.ISO_WIN_MAP.get( lower_charset_name, charset_name ) + # Rename legacy encodings with superset encodings if asked + if should_rename_legacy: + charset_name = detector.LEGACY_MAP.get( + charset_name.lower(), charset_name + ) results.append( { "encoding": charset_name, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/big5prober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/big5prober.py index e4dfa7aa0..ef09c60e3 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/big5prober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/big5prober.py @@ -32,16 +32,16 @@ class Big5Prober(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) self.distribution_analyzer = Big5DistributionAnalysis() self.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "Big5" @property - def language(self): + def language(self) -> str: return "Chinese" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/chardistribution.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/chardistribution.py index 27b4a2939..176cb9964 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/chardistribution.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/chardistribution.py @@ -25,6 +25,8 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Tuple, Union + from .big5freq import ( BIG5_CHAR_TO_FREQ_ORDER, BIG5_TABLE_SIZE, @@ -59,22 +61,22 @@ class CharDistributionAnalysis: SURE_NO = 0.01 MINIMUM_DATA_THRESHOLD = 3 - def __init__(self): + def __init__(self) -> None: # Mapping table to get frequency order from char order (get from # GetOrder()) - self._char_to_freq_order = tuple() - self._table_size = None # Size of above table + self._char_to_freq_order: Tuple[int, ...] = tuple() + self._table_size = 0 # Size of above table # This is a constant value which varies from language to language, # used in calculating confidence. See # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html # for further detail. - self.typical_distribution_ratio = None - self._done = None - self._total_chars = None - self._freq_chars = None + self.typical_distribution_ratio = 0.0 + self._done = False + self._total_chars = 0 + self._freq_chars = 0 self.reset() - def reset(self): + def reset(self) -> None: """reset analyser, clear any state""" # If this flag is set to True, detection is done and conclusion has # been made @@ -83,7 +85,7 @@ def reset(self): # The number of characters whose frequency order is less than 512 self._freq_chars = 0 - def feed(self, char, char_len): + def feed(self, char: Union[bytes, bytearray], char_len: int) -> None: """feed a character with known length""" if char_len == 2: # we only care about 2-bytes character in our distribution analysis @@ -97,7 +99,7 @@ def feed(self, char, char_len): if 512 > self._char_to_freq_order[order]: self._freq_chars += 1 - def get_confidence(self): + def get_confidence(self) -> float: """return confidence based on existing data""" # if we didn't receive any character in our consideration range, # return negative answer @@ -114,12 +116,12 @@ def get_confidence(self): # normalize confidence (we don't want to be 100% sure) return self.SURE_YES - def got_enough_data(self): + def got_enough_data(self) -> bool: # It is not necessary to receive all data to draw conclusion. # For charset detection, certain amount of data is enough return self._total_chars > self.ENOUGH_DATA_THRESHOLD - def get_order(self, _): + def get_order(self, _: Union[bytes, bytearray]) -> int: # We do not handle characters based on the original encoding string, # but convert this encoding string to a number, here called order. # This allows multiple encodings of a language to share one frequency @@ -128,13 +130,13 @@ def get_order(self, _): class EUCTWDistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER self._table_size = EUCTW_TABLE_SIZE self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: # for euc-TW encoding, we are interested # first byte range: 0xc4 -- 0xfe # second byte range: 0xa1 -- 0xfe @@ -146,13 +148,13 @@ def get_order(self, byte_str): class EUCKRDistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER self._table_size = EUCKR_TABLE_SIZE self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: # for euc-KR encoding, we are interested # first byte range: 0xb0 -- 0xfe # second byte range: 0xa1 -- 0xfe @@ -164,13 +166,13 @@ def get_order(self, byte_str): class JOHABDistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER self._table_size = EUCKR_TABLE_SIZE self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: first_char = byte_str[0] if 0x88 <= first_char < 0xD4: code = first_char * 256 + byte_str[1] @@ -179,13 +181,13 @@ def get_order(self, byte_str): class GB2312DistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER self._table_size = GB2312_TABLE_SIZE self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: # for GB2312 encoding, we are interested # first byte range: 0xb0 -- 0xfe # second byte range: 0xa1 -- 0xfe @@ -197,13 +199,13 @@ def get_order(self, byte_str): class Big5DistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER self._table_size = BIG5_TABLE_SIZE self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: # for big5 encoding, we are interested # first byte range: 0xa4 -- 0xfe # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe @@ -217,13 +219,13 @@ def get_order(self, byte_str): class SJISDistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER self._table_size = JIS_TABLE_SIZE self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: # for sjis encoding, we are interested # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe @@ -242,13 +244,13 @@ def get_order(self, byte_str): class EUCJPDistributionAnalysis(CharDistributionAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER self._table_size = JIS_TABLE_SIZE self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> int: # for euc-JP encoding, we are interested # first byte range: 0xa0 -- 0xfe # second byte range: 0xa1 -- 0xfe diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py index 778ff332b..6def56b4a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetgroupprober.py @@ -25,29 +25,30 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import List, Optional, Union + from .charsetprober import CharSetProber -from .enums import ProbingState +from .enums import LanguageFilter, ProbingState class CharSetGroupProber(CharSetProber): - def __init__(self, lang_filter=None): + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: super().__init__(lang_filter=lang_filter) self._active_num = 0 - self.probers = [] - self._best_guess_prober = None + self.probers: List[CharSetProber] = [] + self._best_guess_prober: Optional[CharSetProber] = None - def reset(self): + def reset(self) -> None: super().reset() self._active_num = 0 for prober in self.probers: - if prober: - prober.reset() - prober.active = True - self._active_num += 1 + prober.reset() + prober.active = True + self._active_num += 1 self._best_guess_prober = None @property - def charset_name(self): + def charset_name(self) -> Optional[str]: if not self._best_guess_prober: self.get_confidence() if not self._best_guess_prober: @@ -55,17 +56,15 @@ def charset_name(self): return self._best_guess_prober.charset_name @property - def language(self): + def language(self) -> Optional[str]: if not self._best_guess_prober: self.get_confidence() if not self._best_guess_prober: return None return self._best_guess_prober.language - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: for prober in self.probers: - if not prober: - continue if not prober.active: continue state = prober.feed(byte_str) @@ -83,7 +82,7 @@ def feed(self, byte_str): return self.state return self.state - def get_confidence(self): + def get_confidence(self) -> float: state = self.state if state == ProbingState.FOUND_IT: return 0.99 @@ -92,8 +91,6 @@ def get_confidence(self): best_conf = 0.0 self._best_guess_prober = None for prober in self.probers: - if not prober: - continue if not prober.active: self.logger.debug("%s not active", prober.charset_name) continue diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetprober.py index 9f1afd999..a103ca113 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/charsetprober.py @@ -28,8 +28,9 @@ import logging import re +from typing import Optional, Union -from .enums import ProbingState +from .enums import LanguageFilter, ProbingState INTERNATIONAL_WORDS_PATTERN = re.compile( b"[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?" @@ -40,35 +41,40 @@ class CharSetProber: SHORTCUT_THRESHOLD = 0.95 - def __init__(self, lang_filter=None): - self._state = None + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: + self._state = ProbingState.DETECTING + self.active = True self.lang_filter = lang_filter self.logger = logging.getLogger(__name__) - def reset(self): + def reset(self) -> None: self._state = ProbingState.DETECTING @property - def charset_name(self): + def charset_name(self) -> Optional[str]: return None - def feed(self, byte_str): + @property + def language(self) -> Optional[str]: + raise NotImplementedError + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: raise NotImplementedError @property - def state(self): + def state(self) -> ProbingState: return self._state - def get_confidence(self): + def get_confidence(self) -> float: return 0.0 @staticmethod - def filter_high_byte_only(buf): + def filter_high_byte_only(buf: Union[bytes, bytearray]) -> bytes: buf = re.sub(b"([\x00-\x7F])+", b" ", buf) return buf @staticmethod - def filter_international_words(buf): + def filter_international_words(buf: Union[bytes, bytearray]) -> bytearray: """ We define three types of bytes: alphabet: english alphabets [a-zA-Z] @@ -102,7 +108,7 @@ def filter_international_words(buf): return filtered @staticmethod - def remove_xml_tags(buf): + def remove_xml_tags(buf: Union[bytes, bytearray]) -> bytes: """ Returns a copy of ``buf`` that retains only the sequences of English alphabet and high byte characters that are not between <> characters. @@ -117,10 +123,13 @@ def remove_xml_tags(buf): for curr, buf_char in enumerate(buf): # Check if we're coming out of or entering an XML tag - if buf_char == b">": + + # https://github.com/python/typeshed/issues/8182 + if buf_char == b">": # type: ignore[comparison-overlap] prev = curr + 1 in_tag = False - elif buf_char == b"<": + # https://github.com/python/typeshed/issues/8182 + elif buf_char == b"<": # type: ignore[comparison-overlap] if curr > prev and not in_tag: # Keep everything after last non-extended-ASCII, # non-alphabetic character diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py index 7926fa37e..43f6e144f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cli/chardetect.py @@ -15,12 +15,18 @@ import argparse import sys +from typing import Iterable, List, Optional from .. import __version__ from ..universaldetector import UniversalDetector -def description_of(lines, name="stdin"): +def description_of( + lines: Iterable[bytes], + name: str = "stdin", + minimal: bool = False, + should_rename_legacy: bool = False, +) -> Optional[str]: """ Return a string describing the probable encoding of a file or list of strings. @@ -29,8 +35,11 @@ def description_of(lines, name="stdin"): :type lines: Iterable of bytes :param name: Name of file or collection of lines :type name: str + :param should_rename_legacy: Should we rename legacy encodings to + their more modern equivalents? + :type should_rename_legacy: ``bool`` """ - u = UniversalDetector() + u = UniversalDetector(should_rename_legacy=should_rename_legacy) for line in lines: line = bytearray(line) u.feed(line) @@ -39,12 +48,14 @@ def description_of(lines, name="stdin"): break u.close() result = u.result + if minimal: + return result["encoding"] if result["encoding"]: return f'{name}: {result["encoding"]} with confidence {result["confidence"]}' return f"{name}: no result" -def main(argv=None): +def main(argv: Optional[List[str]] = None) -> None: """ Handles command line arguments and gets things started. @@ -54,17 +65,28 @@ def main(argv=None): """ # Get command line arguments parser = argparse.ArgumentParser( - description="Takes one or more file paths and reports their detected \ - encodings" + description=( + "Takes one or more file paths and reports their detected encodings" + ) ) parser.add_argument( "input", - help="File whose encoding we would like to determine. \ - (default: stdin)", + help="File whose encoding we would like to determine. (default: stdin)", type=argparse.FileType("rb"), nargs="*", default=[sys.stdin.buffer], ) + parser.add_argument( + "--minimal", + help="Print only the encoding to standard output", + action="store_true", + ) + parser.add_argument( + "-l", + "--legacy", + help="Rename legacy encodings to more modern ones.", + action="store_true", + ) parser.add_argument( "--version", action="version", version=f"%(prog)s {__version__}" ) @@ -79,7 +101,11 @@ def main(argv=None): "--help\n", file=sys.stderr, ) - print(description_of(f, f.name)) + print( + description_of( + f, f.name, minimal=args.minimal, should_rename_legacy=args.legacy + ) + ) if __name__ == "__main__": diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py index d3e3e825d..8ed4a8773 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachine.py @@ -27,6 +27,7 @@ import logging +from .codingstatemachinedict import CodingStateMachineDict from .enums import MachineState @@ -53,18 +54,19 @@ class CodingStateMachine: encoding from consideration from here on. """ - def __init__(self, sm): + def __init__(self, sm: CodingStateMachineDict) -> None: self._model = sm self._curr_byte_pos = 0 self._curr_char_len = 0 - self._curr_state = None + self._curr_state = MachineState.START + self.active = True self.logger = logging.getLogger(__name__) self.reset() - def reset(self): + def reset(self) -> None: self._curr_state = MachineState.START - def next_state(self, c): + def next_state(self, c: int) -> int: # for each byte we get its class # if it is first byte, we also get byte length byte_class = self._model["class_table"][c] @@ -77,12 +79,12 @@ def next_state(self, c): self._curr_byte_pos += 1 return self._curr_state - def get_current_charlen(self): + def get_current_charlen(self) -> int: return self._curr_char_len - def get_coding_state_machine(self): + def get_coding_state_machine(self) -> str: return self._model["name"] @property - def language(self): + def language(self) -> str: return self._model["language"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py new file mode 100644 index 000000000..7a3c4c7e3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/codingstatemachinedict.py @@ -0,0 +1,19 @@ +from typing import TYPE_CHECKING, Tuple + +if TYPE_CHECKING: + # TypedDict was introduced in Python 3.8. + # + # TODO: Remove the else block and TYPE_CHECKING check when dropping support + # for Python 3.7. + from typing import TypedDict + + class CodingStateMachineDict(TypedDict, total=False): + class_table: Tuple[int, ...] + class_factor: int + state_table: Tuple[int, ...] + char_len_table: Tuple[int, ...] + name: str + language: str # Optional key + +else: + CodingStateMachineDict = dict diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cp949prober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cp949prober.py index 28a1f3dbb..fa7307ed8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cp949prober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/cp949prober.py @@ -32,7 +32,7 @@ class CP949Prober(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(CP949_SM_MODEL) # NOTE: CP949 is a superset of EUC-KR, so the distribution should be @@ -41,9 +41,9 @@ def __init__(self): self.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "CP949" @property - def language(self): + def language(self) -> str: return "Korean" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/enums.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/enums.py index 32a77e76c..5e3e19823 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/enums.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/enums.py @@ -4,6 +4,8 @@ :author: Dan Blanchard (dan.blanchard@gmail.com) """ +from enum import Enum, Flag + class InputState: """ @@ -15,12 +17,13 @@ class InputState: HIGH_BYTE = 2 -class LanguageFilter: +class LanguageFilter(Flag): """ This enum represents the different language filters we can apply to a ``UniversalDetector``. """ + NONE = 0x00 CHINESE_SIMPLIFIED = 0x01 CHINESE_TRADITIONAL = 0x02 JAPANESE = 0x04 @@ -31,7 +34,7 @@ class LanguageFilter: CJK = CHINESE | JAPANESE | KOREAN -class ProbingState: +class ProbingState(Enum): """ This enum represents the different states a prober can be in. """ @@ -62,7 +65,7 @@ class SequenceLikelihood: POSITIVE = 3 @classmethod - def get_num_categories(cls): + def get_num_categories(cls) -> int: """:returns: The number of likelihood categories in the enum.""" return 4 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escprober.py index d9926115d..fd713830d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escprober.py @@ -25,6 +25,8 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Optional, Union + from .charsetprober import CharSetProber from .codingstatemachine import CodingStateMachine from .enums import LanguageFilter, MachineState, ProbingState @@ -43,7 +45,7 @@ class EscCharSetProber(CharSetProber): identify these encodings. """ - def __init__(self, lang_filter=None): + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: super().__init__(lang_filter=lang_filter) self.coding_sm = [] if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: @@ -53,17 +55,15 @@ def __init__(self, lang_filter=None): self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL)) if self.lang_filter & LanguageFilter.KOREAN: self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL)) - self.active_sm_count = None - self._detected_charset = None - self._detected_language = None - self._state = None + self.active_sm_count = 0 + self._detected_charset: Optional[str] = None + self._detected_language: Optional[str] = None + self._state = ProbingState.DETECTING self.reset() - def reset(self): + def reset(self) -> None: super().reset() for coding_sm in self.coding_sm: - if not coding_sm: - continue coding_sm.active = True coding_sm.reset() self.active_sm_count = len(self.coding_sm) @@ -71,20 +71,20 @@ def reset(self): self._detected_language = None @property - def charset_name(self): + def charset_name(self) -> Optional[str]: return self._detected_charset @property - def language(self): + def language(self) -> Optional[str]: return self._detected_language - def get_confidence(self): + def get_confidence(self) -> float: return 0.99 if self._detected_charset else 0.00 - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: for c in byte_str: for coding_sm in self.coding_sm: - if not coding_sm or not coding_sm.active: + if not coding_sm.active: continue coding_state = coding_sm.next_state(c) if coding_state == MachineState.ERROR: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escsm.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escsm.py index 3aa0f4d96..11d4adf77 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escsm.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/escsm.py @@ -25,6 +25,7 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from .codingstatemachinedict import CodingStateMachineDict from .enums import MachineState # fmt: off @@ -75,7 +76,7 @@ HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) -HZ_SM_MODEL = { +HZ_SM_MODEL: CodingStateMachineDict = { "class_table": HZ_CLS, "class_factor": 6, "state_table": HZ_ST, @@ -134,7 +135,7 @@ ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) -ISO2022CN_SM_MODEL = { +ISO2022CN_SM_MODEL: CodingStateMachineDict = { "class_table": ISO2022CN_CLS, "class_factor": 9, "state_table": ISO2022CN_ST, @@ -194,7 +195,7 @@ ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) -ISO2022JP_SM_MODEL = { +ISO2022JP_SM_MODEL: CodingStateMachineDict = { "class_table": ISO2022JP_CLS, "class_factor": 10, "state_table": ISO2022JP_ST, @@ -250,7 +251,7 @@ ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) -ISO2022KR_SM_MODEL = { +ISO2022KR_SM_MODEL: CodingStateMachineDict = { "class_table": ISO2022KR_CLS, "class_factor": 6, "state_table": ISO2022KR_ST, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py index abf2e66e2..39487f409 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/eucjpprober.py @@ -25,6 +25,8 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Union + from .chardistribution import EUCJPDistributionAnalysis from .codingstatemachine import CodingStateMachine from .enums import MachineState, ProbingState @@ -34,26 +36,29 @@ class EUCJPProber(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) self.distribution_analyzer = EUCJPDistributionAnalysis() self.context_analyzer = EUCJPContextAnalysis() self.reset() - def reset(self): + def reset(self) -> None: super().reset() self.context_analyzer.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "EUC-JP" @property - def language(self): + def language(self) -> str: return "Japanese" - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + assert self.coding_sm is not None + assert self.distribution_analyzer is not None + for i, byte in enumerate(byte_str): # PY3K: byte_str is a byte array, so byte is an int, not a byte coding_state = self.coding_sm.next_state(byte) @@ -89,7 +94,9 @@ def feed(self, byte_str): return self.state - def get_confidence(self): + def get_confidence(self) -> float: + assert self.distribution_analyzer is not None + context_conf = self.context_analyzer.get_confidence() distrib_conf = self.distribution_analyzer.get_confidence() return max(context_conf, distrib_conf) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euckrprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euckrprober.py index 154a6d216..1fc5de046 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euckrprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euckrprober.py @@ -32,16 +32,16 @@ class EUCKRProber(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) self.distribution_analyzer = EUCKRDistributionAnalysis() self.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "EUC-KR" @property - def language(self): + def language(self) -> str: return "Korean" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euctwprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euctwprober.py index ca10a23ca..a37ab1899 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euctwprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/euctwprober.py @@ -32,16 +32,16 @@ class EUCTWProber(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) self.distribution_analyzer = EUCTWDistributionAnalysis() self.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "EUC-TW" @property - def language(self): + def language(self) -> str: return "Taiwan" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py index 251c04295..d423e7311 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/gb2312prober.py @@ -32,16 +32,16 @@ class GB2312Prober(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) self.distribution_analyzer = GB2312DistributionAnalysis() self.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "GB2312" @property - def language(self): + def language(self) -> str: return "Chinese" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py index 3ca634bf3..785d0057b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/hebrewprober.py @@ -25,8 +25,11 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Optional, Union + from .charsetprober import CharSetProber from .enums import ProbingState +from .sbcharsetprober import SingleByteCharSetProber # This prober doesn't actually recognize a language or a charset. # It is a helper prober for the use of the Hebrew model probers @@ -127,6 +130,7 @@ class HebrewProber(CharSetProber): + SPACE = 0x20 # windows-1255 / ISO-8859-8 code points of interest FINAL_KAF = 0xEA NORMAL_KAF = 0xEB @@ -152,31 +156,35 @@ class HebrewProber(CharSetProber): VISUAL_HEBREW_NAME = "ISO-8859-8" LOGICAL_HEBREW_NAME = "windows-1255" - def __init__(self): + def __init__(self) -> None: super().__init__() - self._final_char_logical_score = None - self._final_char_visual_score = None - self._prev = None - self._before_prev = None - self._logical_prober = None - self._visual_prober = None + self._final_char_logical_score = 0 + self._final_char_visual_score = 0 + self._prev = self.SPACE + self._before_prev = self.SPACE + self._logical_prober: Optional[SingleByteCharSetProber] = None + self._visual_prober: Optional[SingleByteCharSetProber] = None self.reset() - def reset(self): + def reset(self) -> None: self._final_char_logical_score = 0 self._final_char_visual_score = 0 # The two last characters seen in the previous buffer, # mPrev and mBeforePrev are initialized to space in order to simulate # a word delimiter at the beginning of the data - self._prev = " " - self._before_prev = " " + self._prev = self.SPACE + self._before_prev = self.SPACE # These probers are owned by the group prober. - def set_model_probers(self, logical_prober, visual_prober): + def set_model_probers( + self, + logical_prober: SingleByteCharSetProber, + visual_prober: SingleByteCharSetProber, + ) -> None: self._logical_prober = logical_prober self._visual_prober = visual_prober - def is_final(self, c): + def is_final(self, c: int) -> bool: return c in [ self.FINAL_KAF, self.FINAL_MEM, @@ -185,7 +193,7 @@ def is_final(self, c): self.FINAL_TSADI, ] - def is_non_final(self, c): + def is_non_final(self, c: int) -> bool: # The normal Tsadi is not a good Non-Final letter due to words like # 'lechotet' (to chat) containing an apostrophe after the tsadi. This # apostrophe is converted to a space in FilterWithoutEnglishLetters @@ -198,7 +206,7 @@ def is_non_final(self, c): # since these words are quite rare. return c in [self.NORMAL_KAF, self.NORMAL_MEM, self.NORMAL_NUN, self.NORMAL_PE] - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: # Final letter analysis for logical-visual decision. # Look for evidence that the received buffer is either logical Hebrew # or visual Hebrew. @@ -232,9 +240,9 @@ def feed(self, byte_str): byte_str = self.filter_high_byte_only(byte_str) for cur in byte_str: - if cur == " ": + if cur == self.SPACE: # We stand on a space - a word just ended - if self._before_prev != " ": + if self._before_prev != self.SPACE: # next-to-last char was not a space so self._prev is not a # 1 letter word if self.is_final(self._prev): @@ -247,9 +255,9 @@ def feed(self, byte_str): else: # Not standing on a space if ( - (self._before_prev == " ") + (self._before_prev == self.SPACE) and (self.is_final(self._prev)) - and (cur != " ") + and (cur != self.SPACE) ): # case (3) [-2:space][-1:final letter][cur:not space] self._final_char_visual_score += 1 @@ -261,7 +269,10 @@ def feed(self, byte_str): return ProbingState.DETECTING @property - def charset_name(self): + def charset_name(self) -> str: + assert self._logical_prober is not None + assert self._visual_prober is not None + # Make the decision: is it Logical or Visual? # If the final letter score distance is dominant enough, rely on it. finalsub = self._final_char_logical_score - self._final_char_visual_score @@ -289,11 +300,14 @@ def charset_name(self): return self.LOGICAL_HEBREW_NAME @property - def language(self): + def language(self) -> str: return "Hebrew" @property - def state(self): + def state(self) -> ProbingState: + assert self._logical_prober is not None + assert self._visual_prober is not None + # Remain active as long as any of the model probers are active. if (self._logical_prober.state == ProbingState.NOT_ME) and ( self._visual_prober.state == ProbingState.NOT_ME diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/johabprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/johabprober.py index 6f359d193..d7364ba61 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/johabprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/johabprober.py @@ -32,16 +32,16 @@ class JOHABProber(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(JOHAB_SM_MODEL) self.distribution_analyzer = JOHABDistributionAnalysis() self.reset() @property - def charset_name(self): + def charset_name(self) -> str: return "Johab" @property - def language(self): + def language(self) -> str: return "Korean" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/jpcntx.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/jpcntx.py index 7a8e5be06..2f53bdda0 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/jpcntx.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/jpcntx.py @@ -25,6 +25,7 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import List, Tuple, Union # This is hiragana 2-char sequence table, the number in each cell represents its frequency category # fmt: off @@ -123,15 +124,15 @@ class JapaneseContextAnalysis: MAX_REL_THRESHOLD = 1000 MINIMUM_DATA_THRESHOLD = 4 - def __init__(self): - self._total_rel = None - self._rel_sample = None - self._need_to_skip_char_num = None - self._last_char_order = None - self._done = None + def __init__(self) -> None: + self._total_rel = 0 + self._rel_sample: List[int] = [] + self._need_to_skip_char_num = 0 + self._last_char_order = -1 + self._done = False self.reset() - def reset(self): + def reset(self) -> None: self._total_rel = 0 # total sequence received # category counters, each integer counts sequence in its category self._rel_sample = [0] * self.NUM_OF_CATEGORY @@ -143,7 +144,7 @@ def reset(self): # been made self._done = False - def feed(self, byte_str, num_bytes): + def feed(self, byte_str: Union[bytes, bytearray], num_bytes: int) -> None: if self._done: return @@ -172,29 +173,29 @@ def feed(self, byte_str, num_bytes): ] += 1 self._last_char_order = order - def got_enough_data(self): + def got_enough_data(self) -> bool: return self._total_rel > self.ENOUGH_REL_THRESHOLD - def get_confidence(self): + def get_confidence(self) -> float: # This is just one way to calculate confidence. It works well for me. if self._total_rel > self.MINIMUM_DATA_THRESHOLD: return (self._total_rel - self._rel_sample[0]) / self._total_rel return self.DONT_KNOW - def get_order(self, _): + def get_order(self, _: Union[bytes, bytearray]) -> Tuple[int, int]: return -1, 1 class SJISContextAnalysis(JapaneseContextAnalysis): - def __init__(self): + def __init__(self) -> None: super().__init__() self._charset_name = "SHIFT_JIS" @property - def charset_name(self): + def charset_name(self) -> str: return self._charset_name - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]: if not byte_str: return -1, 1 # find out current char's byte length @@ -216,7 +217,7 @@ def get_order(self, byte_str): class EUCJPContextAnalysis(JapaneseContextAnalysis): - def get_order(self, byte_str): + def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]: if not byte_str: return -1, 1 # find out current char's byte length diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/latin1prober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/latin1prober.py index 241f14ab9..59a01d91b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/latin1prober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/latin1prober.py @@ -26,6 +26,8 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import List, Union + from .charsetprober import CharSetProber from .enums import ProbingState @@ -96,26 +98,26 @@ class Latin1Prober(CharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() - self._last_char_class = None - self._freq_counter = None + self._last_char_class = OTH + self._freq_counter: List[int] = [] self.reset() - def reset(self): + def reset(self) -> None: self._last_char_class = OTH self._freq_counter = [0] * FREQ_CAT_NUM super().reset() @property - def charset_name(self): + def charset_name(self) -> str: return "ISO-8859-1" @property - def language(self): + def language(self) -> str: return "" - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: byte_str = self.remove_xml_tags(byte_str) for c in byte_str: char_class = Latin1_CharToClass[c] @@ -128,7 +130,7 @@ def feed(self, byte_str): return self.state - def get_confidence(self): + def get_confidence(self) -> float: if self.state == ProbingState.NOT_ME: return 0.01 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/macromanprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/macromanprober.py new file mode 100644 index 000000000..1425d10ec --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/macromanprober.py @@ -0,0 +1,162 @@ +######################## BEGIN LICENSE BLOCK ######################## +# This code was modified from latin1prober.py by Rob Speer . +# The Original Code is Mozilla Universal charset detector code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 2001 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Rob Speer - adapt to MacRoman encoding +# Mark Pilgrim - port to Python +# Shy Shalom - original C code +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +# 02110-1301 USA +######################### END LICENSE BLOCK ######################### + +from typing import List, Union + +from .charsetprober import CharSetProber +from .enums import ProbingState + +FREQ_CAT_NUM = 4 + +UDF = 0 # undefined +OTH = 1 # other +ASC = 2 # ascii capital letter +ASS = 3 # ascii small letter +ACV = 4 # accent capital vowel +ACO = 5 # accent capital other +ASV = 6 # accent small vowel +ASO = 7 # accent small other +ODD = 8 # character that is unlikely to appear +CLASS_NUM = 9 # total classes + +# The change from Latin1 is that we explicitly look for extended characters +# that are infrequently-occurring symbols, and consider them to always be +# improbable. This should let MacRoman get out of the way of more likely +# encodings in most situations. + +# fmt: off +MacRoman_CharToClass = ( + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F + OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F + ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 + ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F + OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F + ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 + ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F + ACV, ACV, ACO, ACV, ACO, ACV, ACV, ASV, # 80 - 87 + ASV, ASV, ASV, ASV, ASV, ASO, ASV, ASV, # 88 - 8F + ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASV, # 90 - 97 + ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # 98 - 9F + OTH, OTH, OTH, OTH, OTH, OTH, OTH, ASO, # A0 - A7 + OTH, OTH, ODD, ODD, OTH, OTH, ACV, ACV, # A8 - AF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 + OTH, OTH, OTH, OTH, OTH, OTH, ASV, ASV, # B8 - BF + OTH, OTH, ODD, OTH, ODD, OTH, OTH, OTH, # C0 - C7 + OTH, OTH, OTH, ACV, ACV, ACV, ACV, ASV, # C8 - CF + OTH, OTH, OTH, OTH, OTH, OTH, OTH, ODD, # D0 - D7 + ASV, ACV, ODD, OTH, OTH, OTH, OTH, OTH, # D8 - DF + OTH, OTH, OTH, OTH, OTH, ACV, ACV, ACV, # E0 - E7 + ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # E8 - EF + ODD, ACV, ACV, ACV, ACV, ASV, ODD, ODD, # F0 - F7 + ODD, ODD, ODD, ODD, ODD, ODD, ODD, ODD, # F8 - FF +) + +# 0 : illegal +# 1 : very unlikely +# 2 : normal +# 3 : very likely +MacRomanClassModel = ( +# UDF OTH ASC ASS ACV ACO ASV ASO ODD + 0, 0, 0, 0, 0, 0, 0, 0, 0, # UDF + 0, 3, 3, 3, 3, 3, 3, 3, 1, # OTH + 0, 3, 3, 3, 3, 3, 3, 3, 1, # ASC + 0, 3, 3, 3, 1, 1, 3, 3, 1, # ASS + 0, 3, 3, 3, 1, 2, 1, 2, 1, # ACV + 0, 3, 3, 3, 3, 3, 3, 3, 1, # ACO + 0, 3, 1, 3, 1, 1, 1, 3, 1, # ASV + 0, 3, 1, 3, 1, 1, 3, 3, 1, # ASO + 0, 1, 1, 1, 1, 1, 1, 1, 1, # ODD +) +# fmt: on + + +class MacRomanProber(CharSetProber): + def __init__(self) -> None: + super().__init__() + self._last_char_class = OTH + self._freq_counter: List[int] = [] + self.reset() + + def reset(self) -> None: + self._last_char_class = OTH + self._freq_counter = [0] * FREQ_CAT_NUM + + # express the prior that MacRoman is a somewhat rare encoding; + # this can be done by starting out in a slightly improbable state + # that must be overcome + self._freq_counter[2] = 10 + + super().reset() + + @property + def charset_name(self) -> str: + return "MacRoman" + + @property + def language(self) -> str: + return "" + + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + byte_str = self.remove_xml_tags(byte_str) + for c in byte_str: + char_class = MacRoman_CharToClass[c] + freq = MacRomanClassModel[(self._last_char_class * CLASS_NUM) + char_class] + if freq == 0: + self._state = ProbingState.NOT_ME + break + self._freq_counter[freq] += 1 + self._last_char_class = char_class + + return self.state + + def get_confidence(self) -> float: + if self.state == ProbingState.NOT_ME: + return 0.01 + + total = sum(self._freq_counter) + confidence = ( + 0.0 + if total < 0.01 + else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total + ) + confidence = max(confidence, 0.0) + # lower the confidence of MacRoman so that other more accurate + # detector can take priority. + confidence *= 0.73 + return confidence diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py index bf96ad5d4..666307e8f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcharsetprober.py @@ -27,8 +27,12 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Optional, Union + +from .chardistribution import CharDistributionAnalysis from .charsetprober import CharSetProber -from .enums import MachineState, ProbingState +from .codingstatemachine import CodingStateMachine +from .enums import LanguageFilter, MachineState, ProbingState class MultiByteCharSetProber(CharSetProber): @@ -36,29 +40,24 @@ class MultiByteCharSetProber(CharSetProber): MultiByteCharSetProber """ - def __init__(self, lang_filter=None): + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: super().__init__(lang_filter=lang_filter) - self.distribution_analyzer = None - self.coding_sm = None - self._last_char = [0, 0] + self.distribution_analyzer: Optional[CharDistributionAnalysis] = None + self.coding_sm: Optional[CodingStateMachine] = None + self._last_char = bytearray(b"\0\0") - def reset(self): + def reset(self) -> None: super().reset() if self.coding_sm: self.coding_sm.reset() if self.distribution_analyzer: self.distribution_analyzer.reset() - self._last_char = [0, 0] - - @property - def charset_name(self): - raise NotImplementedError + self._last_char = bytearray(b"\0\0") - @property - def language(self): - raise NotImplementedError + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + assert self.coding_sm is not None + assert self.distribution_analyzer is not None - def feed(self, byte_str): for i, byte in enumerate(byte_str): coding_state = self.coding_sm.next_state(byte) if coding_state == MachineState.ERROR: @@ -91,5 +90,6 @@ def feed(self, byte_str): return self.state - def get_confidence(self): + def get_confidence(self) -> float: + assert self.distribution_analyzer is not None return self.distribution_analyzer.get_confidence() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py index 94488360c..6cb9cc7b3 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcsgroupprober.py @@ -30,6 +30,7 @@ from .big5prober import Big5Prober from .charsetgroupprober import CharSetGroupProber from .cp949prober import CP949Prober +from .enums import LanguageFilter from .eucjpprober import EUCJPProber from .euckrprober import EUCKRProber from .euctwprober import EUCTWProber @@ -40,7 +41,7 @@ class MBCSGroupProber(CharSetGroupProber): - def __init__(self, lang_filter=None): + def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: super().__init__(lang_filter=lang_filter) self.probers = [ UTF8Prober(), diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcssm.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcssm.py index d3b9c4b75..7bbe97e66 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcssm.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/mbcssm.py @@ -25,6 +25,7 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from .codingstatemachinedict import CodingStateMachineDict from .enums import MachineState # BIG5 @@ -74,7 +75,7 @@ BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) -BIG5_SM_MODEL = { +BIG5_SM_MODEL: CodingStateMachineDict = { "class_table": BIG5_CLS, "class_factor": 5, "state_table": BIG5_ST, @@ -117,7 +118,7 @@ CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) -CP949_SM_MODEL = { +CP949_SM_MODEL: CodingStateMachineDict = { "class_table": CP949_CLS, "class_factor": 10, "state_table": CP949_ST, @@ -173,7 +174,7 @@ EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) -EUCJP_SM_MODEL = { +EUCJP_SM_MODEL: CodingStateMachineDict = { "class_table": EUCJP_CLS, "class_factor": 6, "state_table": EUCJP_ST, @@ -226,7 +227,7 @@ EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) -EUCKR_SM_MODEL = { +EUCKR_SM_MODEL: CodingStateMachineDict = { "class_table": EUCKR_CLS, "class_factor": 4, "state_table": EUCKR_ST, @@ -283,7 +284,7 @@ JOHAB_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 0, 0, 2, 2, 2) -JOHAB_SM_MODEL = { +JOHAB_SM_MODEL: CodingStateMachineDict = { "class_table": JOHAB_CLS, "class_factor": 10, "state_table": JOHAB_ST, @@ -340,7 +341,7 @@ EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) -EUCTW_SM_MODEL = { +EUCTW_SM_MODEL: CodingStateMachineDict = { "class_table": EUCTW_CLS, "class_factor": 7, "state_table": EUCTW_ST, @@ -402,7 +403,7 @@ # 2 here. GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) -GB2312_SM_MODEL = { +GB2312_SM_MODEL: CodingStateMachineDict = { "class_table": GB2312_CLS, "class_factor": 7, "state_table": GB2312_ST, @@ -458,7 +459,7 @@ SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) -SJIS_SM_MODEL = { +SJIS_SM_MODEL: CodingStateMachineDict = { "class_table": SJIS_CLS, "class_factor": 6, "state_table": SJIS_ST, @@ -516,7 +517,7 @@ UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) -UCS2BE_SM_MODEL = { +UCS2BE_SM_MODEL: CodingStateMachineDict = { "class_table": UCS2BE_CLS, "class_factor": 6, "state_table": UCS2BE_ST, @@ -574,7 +575,7 @@ UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) -UCS2LE_SM_MODEL = { +UCS2LE_SM_MODEL: CodingStateMachineDict = { "class_table": UCS2LE_CLS, "class_factor": 6, "state_table": UCS2LE_ST, @@ -651,7 +652,7 @@ UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) -UTF8_SM_MODEL = { +UTF8_SM_MODEL: CodingStateMachineDict = { "class_table": UTF8_CLS, "class_factor": 16, "state_table": UTF8_ST, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py index 1d37884c3..eb40c5f0c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/metadata/languages.py @@ -6,6 +6,7 @@ """ from string import ascii_letters +from typing import List, Optional # TODO: Add Ukrainian (KOI8-U) @@ -33,13 +34,13 @@ class Language: def __init__( self, - name=None, - iso_code=None, - use_ascii=True, - charsets=None, - alphabet=None, - wiki_start_pages=None, - ): + name: Optional[str] = None, + iso_code: Optional[str] = None, + use_ascii: bool = True, + charsets: Optional[List[str]] = None, + alphabet: Optional[str] = None, + wiki_start_pages: Optional[List[str]] = None, + ) -> None: super().__init__() self.name = name self.iso_code = iso_code @@ -55,7 +56,7 @@ def __init__( self.alphabet = "".join(sorted(set(alphabet))) if alphabet else None self.wiki_start_pages = wiki_start_pages - def __repr__(self): + def __repr__(self) -> str: param_str = ", ".join( f"{k}={v!r}" for k, v in self.__dict__.items() if not k.startswith("_") ) @@ -103,7 +104,7 @@ def __repr__(self): name="Danish", iso_code="da", use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252"], + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], alphabet="æøåÆØÅ", wiki_start_pages=["Forside"], ), @@ -111,8 +112,8 @@ def __repr__(self): name="German", iso_code="de", use_ascii=True, - charsets=["ISO-8859-1", "WINDOWS-1252"], - alphabet="äöüßÄÖÜ", + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], + alphabet="äöüßẞÄÖÜ", wiki_start_pages=["Wikipedia:Hauptseite"], ), "Greek": Language( @@ -127,7 +128,7 @@ def __repr__(self): name="English", iso_code="en", use_ascii=True, - charsets=["ISO-8859-1", "WINDOWS-1252"], + charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"], wiki_start_pages=["Main_Page"], ), "Esperanto": Language( @@ -143,7 +144,7 @@ def __repr__(self): name="Spanish", iso_code="es", use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252"], + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], alphabet="ñáéíóúüÑÁÉÍÓÚÜ", wiki_start_pages=["Wikipedia:Portada"], ), @@ -161,7 +162,7 @@ def __repr__(self): name="Finnish", iso_code="fi", use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252"], + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], alphabet="ÅÄÖŠŽåäöšž", wiki_start_pages=["Wikipedia:Etusivu"], ), @@ -169,7 +170,7 @@ def __repr__(self): name="French", iso_code="fr", use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252"], + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], alphabet="œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ", wiki_start_pages=["Wikipédia:Accueil_principal", "Bœuf (animal)"], ), @@ -203,7 +204,7 @@ def __repr__(self): name="Italian", iso_code="it", use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252"], + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], alphabet="ÀÈÉÌÒÓÙàèéìòóù", wiki_start_pages=["Pagina_principale"], ), @@ -237,7 +238,7 @@ def __repr__(self): name="Dutch", iso_code="nl", use_ascii=True, - charsets=["ISO-8859-1", "WINDOWS-1252"], + charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"], wiki_start_pages=["Hoofdpagina"], ), "Polish": Language( @@ -253,7 +254,7 @@ def __repr__(self): name="Portuguese", iso_code="pt", use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252"], + charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], alphabet="ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú", wiki_start_pages=["Wikipédia:Página_principal"], ), diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/resultdict.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/resultdict.py new file mode 100644 index 000000000..7d36e64c4 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/resultdict.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + # TypedDict was introduced in Python 3.8. + # + # TODO: Remove the else block and TYPE_CHECKING check when dropping support + # for Python 3.7. + from typing import TypedDict + + class ResultDict(TypedDict): + encoding: Optional[str] + confidence: float + language: Optional[str] + +else: + ResultDict = dict diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py index 31d70e154..0ffbcdd2c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcharsetprober.py @@ -26,23 +26,20 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### -from collections import namedtuple +from typing import Dict, List, NamedTuple, Optional, Union from .charsetprober import CharSetProber from .enums import CharacterCategory, ProbingState, SequenceLikelihood -SingleByteCharSetModel = namedtuple( - "SingleByteCharSetModel", - [ - "charset_name", - "language", - "char_to_order_map", - "language_model", - "typical_positive_ratio", - "keep_ascii_letters", - "alphabet", - ], -) + +class SingleByteCharSetModel(NamedTuple): + charset_name: str + language: str + char_to_order_map: Dict[int, int] + language_model: Dict[int, Dict[int, int]] + typical_positive_ratio: float + keep_ascii_letters: bool + alphabet: str class SingleByteCharSetProber(CharSetProber): @@ -51,22 +48,27 @@ class SingleByteCharSetProber(CharSetProber): POSITIVE_SHORTCUT_THRESHOLD = 0.95 NEGATIVE_SHORTCUT_THRESHOLD = 0.05 - def __init__(self, model, is_reversed=False, name_prober=None): + def __init__( + self, + model: SingleByteCharSetModel, + is_reversed: bool = False, + name_prober: Optional[CharSetProber] = None, + ) -> None: super().__init__() self._model = model # TRUE if we need to reverse every pair in the model lookup self._reversed = is_reversed # Optional auxiliary prober for name decision self._name_prober = name_prober - self._last_order = None - self._seq_counters = None - self._total_seqs = None - self._total_char = None - self._control_char = None - self._freq_char = None + self._last_order = 255 + self._seq_counters: List[int] = [] + self._total_seqs = 0 + self._total_char = 0 + self._control_char = 0 + self._freq_char = 0 self.reset() - def reset(self): + def reset(self) -> None: super().reset() # char order of last character self._last_order = 255 @@ -78,18 +80,18 @@ def reset(self): self._freq_char = 0 @property - def charset_name(self): + def charset_name(self) -> Optional[str]: if self._name_prober: return self._name_prober.charset_name return self._model.charset_name @property - def language(self): + def language(self) -> Optional[str]: if self._name_prober: return self._name_prober.language return self._model.language - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: # TODO: Make filter_international_words keep things in self.alphabet if not self._model.keep_ascii_letters: byte_str = self.filter_international_words(byte_str) @@ -139,7 +141,7 @@ def feed(self, byte_str): return self.state - def get_confidence(self): + def get_confidence(self) -> float: r = 0.01 if self._total_seqs > 0: r = ( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py index cad001cb1..890ae8465 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sbcsgroupprober.py @@ -48,7 +48,7 @@ class SBCSGroupProber(CharSetGroupProber): - def __init__(self): + def __init__(self) -> None: super().__init__() hebrew_prober = HebrewProber() logical_hebrew_prober = SingleByteCharSetProber( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sjisprober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sjisprober.py index 3bcbdb71d..91df07796 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sjisprober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/sjisprober.py @@ -25,6 +25,8 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Union + from .chardistribution import SJISDistributionAnalysis from .codingstatemachine import CodingStateMachine from .enums import MachineState, ProbingState @@ -34,26 +36,29 @@ class SJISProber(MultiByteCharSetProber): - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) self.distribution_analyzer = SJISDistributionAnalysis() self.context_analyzer = SJISContextAnalysis() self.reset() - def reset(self): + def reset(self) -> None: super().reset() self.context_analyzer.reset() @property - def charset_name(self): + def charset_name(self) -> str: return self.context_analyzer.charset_name @property - def language(self): + def language(self) -> str: return "Japanese" - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: + assert self.coding_sm is not None + assert self.distribution_analyzer is not None + for i, byte in enumerate(byte_str): coding_state = self.coding_sm.next_state(byte) if coding_state == MachineState.ERROR: @@ -92,7 +97,9 @@ def feed(self, byte_str): return self.state - def get_confidence(self): + def get_confidence(self) -> float: + assert self.distribution_analyzer is not None + context_conf = self.context_analyzer.get_confidence() distrib_conf = self.distribution_analyzer.get_confidence() return max(context_conf, distrib_conf) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/universaldetector.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/universaldetector.py index 22fcf8290..30c441dc2 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/universaldetector.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/universaldetector.py @@ -39,12 +39,16 @@ class a user of ``chardet`` should use. import codecs import logging import re +from typing import List, Optional, Union from .charsetgroupprober import CharSetGroupProber +from .charsetprober import CharSetProber from .enums import InputState, LanguageFilter, ProbingState from .escprober import EscCharSetProber from .latin1prober import Latin1Prober +from .macromanprober import MacRomanProber from .mbcsgroupprober import MBCSGroupProber +from .resultdict import ResultDict from .sbcsgroupprober import SBCSGroupProber from .utf1632prober import UTF1632Prober @@ -80,34 +84,55 @@ class UniversalDetector: "iso-8859-9": "Windows-1254", "iso-8859-13": "Windows-1257", } + # Based on https://encoding.spec.whatwg.org/#names-and-labels + # but altered to match Python names for encodings and remove mappings + # that break tests. + LEGACY_MAP = { + "ascii": "Windows-1252", + "iso-8859-1": "Windows-1252", + "tis-620": "ISO-8859-11", + "iso-8859-9": "Windows-1254", + "gb2312": "GB18030", + "euc-kr": "CP949", + "utf-16le": "UTF-16", + } - def __init__(self, lang_filter=LanguageFilter.ALL): - self._esc_charset_prober = None - self._utf1632_prober = None - self._charset_probers = [] - self.result = None - self.done = None - self._got_data = None - self._input_state = None - self._last_char = None + def __init__( + self, + lang_filter: LanguageFilter = LanguageFilter.ALL, + should_rename_legacy: bool = False, + ) -> None: + self._esc_charset_prober: Optional[EscCharSetProber] = None + self._utf1632_prober: Optional[UTF1632Prober] = None + self._charset_probers: List[CharSetProber] = [] + self.result: ResultDict = { + "encoding": None, + "confidence": 0.0, + "language": None, + } + self.done = False + self._got_data = False + self._input_state = InputState.PURE_ASCII + self._last_char = b"" self.lang_filter = lang_filter self.logger = logging.getLogger(__name__) - self._has_win_bytes = None + self._has_win_bytes = False + self.should_rename_legacy = should_rename_legacy self.reset() @property - def input_state(self): + def input_state(self) -> int: return self._input_state @property - def has_win_bytes(self): + def has_win_bytes(self) -> bool: return self._has_win_bytes @property - def charset_probers(self): + def charset_probers(self) -> List[CharSetProber]: return self._charset_probers - def reset(self): + def reset(self) -> None: """ Reset the UniversalDetector and all of its probers back to their initial states. This is called by ``__init__``, so you only need to @@ -126,7 +151,7 @@ def reset(self): for prober in self._charset_probers: prober.reset() - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> None: """ Takes a chunk of a document and feeds it through all of the relevant charset probers. @@ -166,6 +191,7 @@ def feed(self, byte_str): elif byte_str.startswith(b"\xFE\xFF\x00\x00"): # FE FF 00 00 UCS-4, unusual octet order BOM (3412) self.result = { + # TODO: This encoding is not supported by Python. Should remove? "encoding": "X-ISO-10646-UCS-4-3412", "confidence": 1.0, "language": "", @@ -173,6 +199,7 @@ def feed(self, byte_str): elif byte_str.startswith(b"\x00\x00\xFF\xFE"): # 00 00 FF FE UCS-4, unusual octet order BOM (2143) self.result = { + # TODO: This encoding is not supported by Python. Should remove? "encoding": "X-ISO-10646-UCS-4-2143", "confidence": 1.0, "language": "", @@ -242,6 +269,7 @@ def feed(self, byte_str): if self.lang_filter & LanguageFilter.NON_CJK: self._charset_probers.append(SBCSGroupProber()) self._charset_probers.append(Latin1Prober()) + self._charset_probers.append(MacRomanProber()) for prober in self._charset_probers: if prober.feed(byte_str) == ProbingState.FOUND_IT: self.result = { @@ -254,7 +282,7 @@ def feed(self, byte_str): if self.WIN_BYTE_DETECTOR.search(byte_str): self._has_win_bytes = True - def close(self): + def close(self) -> ResultDict: """ Stop analyzing the current document and come up with a final prediction. @@ -288,7 +316,8 @@ def close(self): max_prober = prober if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): charset_name = max_prober.charset_name - lower_charset_name = max_prober.charset_name.lower() + assert charset_name is not None + lower_charset_name = charset_name.lower() confidence = max_prober.get_confidence() # Use Windows encoding name instead of ISO-8859 if we saw any # extra Windows-specific bytes @@ -297,6 +326,11 @@ def close(self): charset_name = self.ISO_WIN_MAP.get( lower_charset_name, charset_name ) + # Rename legacy encodings with superset encodings if asked + if self.should_rename_legacy: + charset_name = self.LEGACY_MAP.get( + (charset_name or "").lower(), charset_name + ) self.result = { "encoding": charset_name, "confidence": confidence, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py index 9fd1580b8..6bdec63d6 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf1632prober.py @@ -18,6 +18,8 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import List, Union + from .charsetprober import CharSetProber from .enums import ProbingState @@ -36,7 +38,7 @@ class UTF1632Prober(CharSetProber): # a fixed constant ratio of expected zeros or non-zeros in modulo-position. EXPECTED_RATIO = 0.94 - def __init__(self): + def __init__(self) -> None: super().__init__() self.position = 0 self.zeros_at_mod = [0] * 4 @@ -51,7 +53,7 @@ def __init__(self): self.first_half_surrogate_pair_detected_16le = False self.reset() - def reset(self): + def reset(self) -> None: super().reset() self.position = 0 self.zeros_at_mod = [0] * 4 @@ -66,7 +68,7 @@ def reset(self): self.quad = [0, 0, 0, 0] @property - def charset_name(self): + def charset_name(self) -> str: if self.is_likely_utf32be(): return "utf-32be" if self.is_likely_utf32le(): @@ -79,16 +81,16 @@ def charset_name(self): return "utf-16" @property - def language(self): + def language(self) -> str: return "" - def approx_32bit_chars(self): + def approx_32bit_chars(self) -> float: return max(1.0, self.position / 4.0) - def approx_16bit_chars(self): + def approx_16bit_chars(self) -> float: return max(1.0, self.position / 2.0) - def is_likely_utf32be(self): + def is_likely_utf32be(self) -> bool: approx_chars = self.approx_32bit_chars() return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( self.zeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO @@ -98,7 +100,7 @@ def is_likely_utf32be(self): and not self.invalid_utf32be ) - def is_likely_utf32le(self): + def is_likely_utf32le(self) -> bool: approx_chars = self.approx_32bit_chars() return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( self.nonzeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO @@ -108,7 +110,7 @@ def is_likely_utf32le(self): and not self.invalid_utf32le ) - def is_likely_utf16be(self): + def is_likely_utf16be(self) -> bool: approx_chars = self.approx_16bit_chars() return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( (self.nonzeros_at_mod[1] + self.nonzeros_at_mod[3]) / approx_chars @@ -118,7 +120,7 @@ def is_likely_utf16be(self): and not self.invalid_utf16be ) - def is_likely_utf16le(self): + def is_likely_utf16le(self) -> bool: approx_chars = self.approx_16bit_chars() return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( (self.nonzeros_at_mod[0] + self.nonzeros_at_mod[2]) / approx_chars @@ -128,7 +130,7 @@ def is_likely_utf16le(self): and not self.invalid_utf16le ) - def validate_utf32_characters(self, quad): + def validate_utf32_characters(self, quad: List[int]) -> None: """ Validate if the quad of bytes is valid UTF-32. @@ -150,7 +152,7 @@ def validate_utf32_characters(self, quad): ): self.invalid_utf32le = True - def validate_utf16_characters(self, pair): + def validate_utf16_characters(self, pair: List[int]) -> None: """ Validate if the pair of bytes is valid UTF-16. @@ -182,7 +184,7 @@ def validate_utf16_characters(self, pair): else: self.invalid_utf16le = True - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: for c in byte_str: mod4 = self.position % 4 self.quad[mod4] = c @@ -198,7 +200,7 @@ def feed(self, byte_str): return self.state @property - def state(self): + def state(self) -> ProbingState: if self._state in {ProbingState.NOT_ME, ProbingState.FOUND_IT}: # terminal, decided states return self._state @@ -210,7 +212,7 @@ def state(self): self._state = ProbingState.NOT_ME return self._state - def get_confidence(self): + def get_confidence(self) -> float: return ( 0.85 if ( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf8prober.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf8prober.py index 3aae09e86..d96354d97 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf8prober.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/utf8prober.py @@ -25,6 +25,8 @@ # 02110-1301 USA ######################### END LICENSE BLOCK ######################### +from typing import Union + from .charsetprober import CharSetProber from .codingstatemachine import CodingStateMachine from .enums import MachineState, ProbingState @@ -34,26 +36,26 @@ class UTF8Prober(CharSetProber): ONE_CHAR_PROB = 0.5 - def __init__(self): + def __init__(self) -> None: super().__init__() self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) - self._num_mb_chars = None + self._num_mb_chars = 0 self.reset() - def reset(self): + def reset(self) -> None: super().reset() self.coding_sm.reset() self._num_mb_chars = 0 @property - def charset_name(self): + def charset_name(self) -> str: return "utf-8" @property - def language(self): + def language(self) -> str: return "" - def feed(self, byte_str): + def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: for c in byte_str: coding_state = self.coding_sm.next_state(c) if coding_state == MachineState.ERROR: @@ -72,7 +74,7 @@ def feed(self, byte_str): return self.state - def get_confidence(self): + def get_confidence(self) -> float: unlike = 0.99 if self._num_mb_chars < 6: unlike *= self.ONE_CHAR_PROB**self._num_mb_chars diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/version.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/version.py index a08a06b9a..c5e9d85cd 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/chardet/version.py @@ -1,9 +1,9 @@ """ This module exists only to simplify retrieving the version number of chardet -from within setup.py and from chardet subpackages. +from within setuptools and from chardet subpackages. :author: Dan Blanchard (dan.blanchard@gmail.com) """ -__version__ = "5.0.0" +__version__ = "5.1.0" VERSION = __version__.split(".") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/__init__.py index 9138a8cc8..383101cdb 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/__init__.py @@ -1,6 +1,7 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from .initialise import init, deinit, reinit, colorama_text +from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console from .ansi import Fore, Back, Style, Cursor from .ansitowin32 import AnsiToWin32 -__version__ = '0.4.5' +__version__ = '0.4.6' + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py index 3db248baa..abf209e60 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/ansitowin32.py @@ -4,7 +4,7 @@ import os from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL -from .winterm import WinTerm, WinColor, WinStyle +from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle from .win32 import windll, winapi_test @@ -94,15 +94,22 @@ def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # (e.g. Cygwin Terminal). In this case it's up to the terminal # to support the ANSI codes. conversion_supported = on_windows and winapi_test() + try: + fd = wrapped.fileno() + except Exception: + fd = -1 + system_has_native_ansi = not on_windows or enable_vt_processing(fd) + have_tty = not self.stream.closed and self.stream.isatty() + need_conversion = conversion_supported and not system_has_native_ansi # should we strip ANSI sequences from our output? if strip is None: - strip = conversion_supported or (not self.stream.closed and not self.stream.isatty()) + strip = need_conversion or not have_tty self.strip = strip # should we should convert ANSI sequences into win32 calls? if convert is None: - convert = conversion_supported and not self.stream.closed and self.stream.isatty() + convert = need_conversion and have_tty self.convert = convert # dict of ansi codes to win32 functions and parameters @@ -264,3 +271,7 @@ def convert_osc(self, text): if params[0] in '02': winterm.set_title(params[1]) return text + + + def flush(self): + self.wrapped.flush() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/initialise.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/initialise.py index 430d06687..d5fd4b71f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/initialise.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/initialise.py @@ -6,13 +6,27 @@ from .ansitowin32 import AnsiToWin32 -orig_stdout = None -orig_stderr = None +def _wipe_internal_state_for_tests(): + global orig_stdout, orig_stderr + orig_stdout = None + orig_stderr = None + + global wrapped_stdout, wrapped_stderr + wrapped_stdout = None + wrapped_stderr = None -wrapped_stdout = None -wrapped_stderr = None + global atexit_done + atexit_done = False + + global fixed_windows_console + fixed_windows_console = False -atexit_done = False + try: + # no-op if it wasn't registered + atexit.unregister(reset_all) + except AttributeError: + # python 2: no atexit.unregister. Oh well, we did our best. + pass def reset_all(): @@ -55,6 +69,29 @@ def deinit(): sys.stderr = orig_stderr +def just_fix_windows_console(): + global fixed_windows_console + + if sys.platform != "win32": + return + if fixed_windows_console: + return + if wrapped_stdout is not None or wrapped_stderr is not None: + # Someone already ran init() and it did stuff, so we won't second-guess them + return + + # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the + # native ANSI support in the console as a side-effect. We only need to actually + # replace sys.stdout/stderr if we're in the old-style conversion mode. + new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) + if new_stdout.convert: + sys.stdout = new_stdout + new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) + if new_stderr.convert: + sys.stderr = new_stderr + + fixed_windows_console = True + @contextlib.contextmanager def colorama_text(*args, **kwargs): init(*args, **kwargs) @@ -78,3 +115,7 @@ def wrap_stream(stream, convert, strip, autoreset, wrap): if wrapper.should_wrap(): stream = wrapper.stream return stream + + +# Use this for initial setup as well, to reduce code duplication +_wipe_internal_state_for_tests() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py new file mode 100644 index 000000000..8c5661e93 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/__init__.py @@ -0,0 +1 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py new file mode 100644 index 000000000..0a20c80f8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansi_test.py @@ -0,0 +1,76 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansi import Back, Fore, Style +from ..ansitowin32 import AnsiToWin32 + +stdout_orig = sys.stdout +stderr_orig = sys.stderr + + +class AnsiTest(TestCase): + + def setUp(self): + # sanity check: stdout should be a file or StringIO object. + # It will only be AnsiToWin32 if init() has previously wrapped it + self.assertNotEqual(type(sys.stdout), AnsiToWin32) + self.assertNotEqual(type(sys.stderr), AnsiToWin32) + + def tearDown(self): + sys.stdout = stdout_orig + sys.stderr = stderr_orig + + + def testForeAttributes(self): + self.assertEqual(Fore.BLACK, '\033[30m') + self.assertEqual(Fore.RED, '\033[31m') + self.assertEqual(Fore.GREEN, '\033[32m') + self.assertEqual(Fore.YELLOW, '\033[33m') + self.assertEqual(Fore.BLUE, '\033[34m') + self.assertEqual(Fore.MAGENTA, '\033[35m') + self.assertEqual(Fore.CYAN, '\033[36m') + self.assertEqual(Fore.WHITE, '\033[37m') + self.assertEqual(Fore.RESET, '\033[39m') + + # Check the light, extended versions. + self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') + self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') + self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') + self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') + self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') + self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') + self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') + self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') + + + def testBackAttributes(self): + self.assertEqual(Back.BLACK, '\033[40m') + self.assertEqual(Back.RED, '\033[41m') + self.assertEqual(Back.GREEN, '\033[42m') + self.assertEqual(Back.YELLOW, '\033[43m') + self.assertEqual(Back.BLUE, '\033[44m') + self.assertEqual(Back.MAGENTA, '\033[45m') + self.assertEqual(Back.CYAN, '\033[46m') + self.assertEqual(Back.WHITE, '\033[47m') + self.assertEqual(Back.RESET, '\033[49m') + + # Check the light, extended versions. + self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') + self.assertEqual(Back.LIGHTRED_EX, '\033[101m') + self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') + self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') + self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') + self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') + self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') + self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') + + + def testStyleAttributes(self): + self.assertEqual(Style.DIM, '\033[2m') + self.assertEqual(Style.NORMAL, '\033[22m') + self.assertEqual(Style.BRIGHT, '\033[1m') + + +if __name__ == '__main__': + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py new file mode 100644 index 000000000..91ca551f9 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/ansitowin32_test.py @@ -0,0 +1,294 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from io import StringIO, TextIOWrapper +from unittest import TestCase, main +try: + from contextlib import ExitStack +except ImportError: + # python 2 + from contextlib2 import ExitStack + +try: + from unittest.mock import MagicMock, Mock, patch +except ImportError: + from mock import MagicMock, Mock, patch + +from ..ansitowin32 import AnsiToWin32, StreamWrapper +from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING +from .utils import osname + + +class StreamWrapperTest(TestCase): + + def testIsAProxy(self): + mockStream = Mock() + wrapper = StreamWrapper(mockStream, None) + self.assertTrue( wrapper.random_attr is mockStream.random_attr ) + + def testDelegatesWrite(self): + mockStream = Mock() + mockConverter = Mock() + wrapper = StreamWrapper(mockStream, mockConverter) + wrapper.write('hello') + self.assertTrue(mockConverter.write.call_args, (('hello',), {})) + + def testDelegatesContext(self): + mockConverter = Mock() + s = StringIO() + with StreamWrapper(s, mockConverter) as fp: + fp.write(u'hello') + self.assertTrue(s.closed) + + def testProxyNoContextManager(self): + mockStream = MagicMock() + mockStream.__enter__.side_effect = AttributeError() + mockConverter = Mock() + with self.assertRaises(AttributeError) as excinfo: + with StreamWrapper(mockStream, mockConverter) as wrapper: + wrapper.write('hello') + + def test_closed_shouldnt_raise_on_closed_stream(self): + stream = StringIO() + stream.close() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + + def test_closed_shouldnt_raise_on_detached_stream(self): + stream = TextIOWrapper(StringIO()) + stream.detach() + wrapper = StreamWrapper(stream, None) + self.assertEqual(wrapper.closed, True) + +class AnsiToWin32Test(TestCase): + + def testInit(self): + mockStdout = Mock() + auto = Mock() + stream = AnsiToWin32(mockStdout, autoreset=auto) + self.assertEqual(stream.wrapped, mockStdout) + self.assertEqual(stream.autoreset, auto) + + @patch('colorama.ansitowin32.winterm', None) + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + def testStripIsTrueOnWindows(self): + with osname('nt'): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + self.assertTrue(stream.strip) + + def testStripIsFalseOffWindows(self): + with osname('posix'): + mockStdout = Mock(closed=False) + stream = AnsiToWin32(mockStdout) + self.assertFalse(stream.strip) + + def testWriteStripsAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = True + + stream.write('abc') + + self.assertFalse(stream.wrapped.write.called) + self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) + + def testWriteDoesNotStripAnsi(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout) + stream.wrapped = Mock() + stream.write_and_convert = Mock() + stream.strip = False + stream.convert = False + + stream.write('abc') + + self.assertFalse(stream.write_and_convert.called) + self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) + + def assert_autoresets(self, convert, autoreset=True): + stream = AnsiToWin32(Mock()) + stream.convert = convert + stream.reset_all = Mock() + stream.autoreset = autoreset + stream.winterm = Mock() + + stream.write('abc') + + self.assertEqual(stream.reset_all.called, autoreset) + + def testWriteAutoresets(self): + self.assert_autoresets(convert=True) + self.assert_autoresets(convert=False) + self.assert_autoresets(convert=True, autoreset=False) + self.assert_autoresets(convert=False, autoreset=False) + + def testWriteAndConvertWritesPlainText(self): + stream = AnsiToWin32(Mock()) + stream.write_and_convert( 'abc' ) + self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) + + def testWriteAndConvertStripsAllValidAnsi(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + data = [ + 'abc\033[mdef', + 'abc\033[0mdef', + 'abc\033[2mdef', + 'abc\033[02mdef', + 'abc\033[002mdef', + 'abc\033[40mdef', + 'abc\033[040mdef', + 'abc\033[0;1mdef', + 'abc\033[40;50mdef', + 'abc\033[50;30;40mdef', + 'abc\033[Adef', + 'abc\033[0Gdef', + 'abc\033[1;20;128Hdef', + ] + for datum in data: + stream.wrapped.write.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( + [args[0] for args in stream.wrapped.write.call_args_list], + [ ('abc',), ('def',) ] + ) + + def testWriteAndConvertSkipsEmptySnippets(self): + stream = AnsiToWin32(Mock()) + stream.call_win32 = Mock() + stream.write_and_convert( '\033[40m\033[41m' ) + self.assertFalse( stream.wrapped.write.called ) + + def testWriteAndConvertCallsWin32WithParamsAndCommand(self): + stream = AnsiToWin32(Mock()) + stream.convert = True + stream.call_win32 = Mock() + stream.extract_params = Mock(return_value='params') + data = { + 'abc\033[adef': ('a', 'params'), + 'abc\033[;;bdef': ('b', 'params'), + 'abc\033[0cdef': ('c', 'params'), + 'abc\033[;;0;;Gdef': ('G', 'params'), + 'abc\033[1;20;128Hdef': ('H', 'params'), + } + for datum, expected in data.items(): + stream.call_win32.reset_mock() + stream.write_and_convert( datum ) + self.assertEqual( stream.call_win32.call_args[0], expected ) + + def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + converter = AnsiToWin32(stream) + stream.close() + + converter.reset_all() + + def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): + stream = StringIO() + stream.close() + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(stream) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def test_wrap_shouldnt_raise_on_missing_closed_attr(self): + with \ + patch("colorama.ansitowin32.os.name", "nt"), \ + patch("colorama.ansitowin32.winapi_test", lambda: True): + converter = AnsiToWin32(object()) + self.assertTrue(converter.strip) + self.assertFalse(converter.convert) + + def testExtractParams(self): + stream = AnsiToWin32(Mock()) + data = { + '': (0,), + ';;': (0,), + '2': (2,), + ';;002;;': (2,), + '0;1': (0, 1), + ';;003;;456;;': (3, 456), + '11;22;33;44;55': (11, 22, 33, 44, 55), + } + for datum, expected in data.items(): + self.assertEqual(stream.extract_params('m', datum), expected) + + def testCallWin32UsesLookup(self): + listener = Mock() + stream = AnsiToWin32(listener) + stream.win32_calls = { + 1: (lambda *_, **__: listener(11),), + 2: (lambda *_, **__: listener(22),), + 3: (lambda *_, **__: listener(33),), + } + stream.call_win32('m', (3, 1, 99, 2)) + self.assertEqual( + [a[0][0] for a in listener.call_args_list], + [33, 11, 22] ) + + def test_osc_codes(self): + mockStdout = Mock() + stream = AnsiToWin32(mockStdout, convert=True) + with patch('colorama.ansitowin32.winterm') as winterm: + data = [ + '\033]0\x07', # missing arguments + '\033]0;foo\x08', # wrong OSC command + '\033]0;colorama_test_title\x07', # should work + '\033]1;colorama_test_title\x07', # wrong set command + '\033]2;colorama_test_title\x07', # should work + '\033]' + ';' * 64 + '\x08', # see issue #247 + ] + for code in data: + stream.write(code) + self.assertEqual(winterm.set_title.call_count, 2) + + def test_native_windows_ansi(self): + with ExitStack() as stack: + def p(a, b): + stack.enter_context(patch(a, b, create=True)) + # Pretend to be on Windows + p("colorama.ansitowin32.os.name", "nt") + p("colorama.ansitowin32.winapi_test", lambda: True) + p("colorama.win32.winapi_test", lambda: True) + p("colorama.winterm.win32.windll", "non-None") + p("colorama.winterm.get_osfhandle", lambda _: 1234) + + # Pretend that our mock stream has native ANSI support + p( + "colorama.winterm.win32.GetConsoleMode", + lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = True + stdout.fileno.return_value = 1 + + # Our fake console says it has native vt support, so AnsiToWin32 should + # enable that support and do nothing else. + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertFalse(stream.strip) + self.assertFalse(stream.convert) + self.assertFalse(stream.should_wrap()) + + # Now let's pretend we're on an old Windows console, that doesn't have + # native ANSI support. + p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) + SetConsoleMode = Mock() + p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) + + stream = AnsiToWin32(stdout) + SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) + self.assertTrue(stream.strip) + self.assertTrue(stream.convert) + self.assertTrue(stream.should_wrap()) + + +if __name__ == '__main__': + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py new file mode 100644 index 000000000..89f9b0751 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/initialise_test.py @@ -0,0 +1,189 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import patch, Mock +except ImportError: + from mock import patch, Mock + +from ..ansitowin32 import StreamWrapper +from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests +from .utils import osname, replace_by + +orig_stdout = sys.stdout +orig_stderr = sys.stderr + + +class InitTest(TestCase): + + @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") + def setUp(self): + # sanity check + self.assertNotWrapped() + + def tearDown(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def assertWrapped(self): + self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') + self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') + self.assertTrue(isinstance(sys.stdout, StreamWrapper), + 'bad stdout wrapper') + self.assertTrue(isinstance(sys.stderr, StreamWrapper), + 'bad stderr wrapper') + + def assertNotWrapped(self): + self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') + self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: True) + @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) + def testInitWrapsOnWindows(self, _): + with osname("nt"): + init() + self.assertWrapped() + + @patch('colorama.initialise.reset_all') + @patch('colorama.ansitowin32.winapi_test', lambda *_: False) + def testInitDoesntWrapOnEmulatedWindows(self, _): + with osname("nt"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapOnNonWindows(self): + with osname("posix"): + init() + self.assertNotWrapped() + + def testInitDoesntWrapIfNone(self): + with replace_by(None): + init() + # We can't use assertNotWrapped here because replace_by(None) + # changes stdout/stderr already. + self.assertIsNone(sys.stdout) + self.assertIsNone(sys.stderr) + + def testInitAutoresetOnWrapsOnAllPlatforms(self): + with osname("posix"): + init(autoreset=True) + self.assertWrapped() + + def testInitWrapOffDoesntWrapOnWindows(self): + with osname("nt"): + init(wrap=False) + self.assertNotWrapped() + + def testInitWrapOffIncompatibleWithAutoresetOn(self): + self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) + + @patch('colorama.win32.SetConsoleTextAttribute') + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetPassedOn(self, mockATW32, _): + with osname("nt"): + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 2) + self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) + + @patch('colorama.initialise.AnsiToWin32') + def testAutoResetChangeable(self, mockATW32): + with osname("nt"): + init() + + init(autoreset=True) + self.assertEqual(len(mockATW32.call_args_list), 4) + self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) + self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) + + init() + self.assertEqual(len(mockATW32.call_args_list), 6) + self.assertEqual( + mockATW32.call_args_list[4][1]['autoreset'], False) + self.assertEqual( + mockATW32.call_args_list[5][1]['autoreset'], False) + + + @patch('colorama.initialise.atexit.register') + def testAtexitRegisteredOnlyOnce(self, mockRegister): + init() + self.assertTrue(mockRegister.called) + mockRegister.reset_mock() + init() + self.assertFalse(mockRegister.called) + + +class JustFixWindowsConsoleTest(TestCase): + def _reset(self): + _wipe_internal_state_for_tests() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + + def tearDown(self): + self._reset() + + @patch("colorama.ansitowin32.winapi_test", lambda: True) + def testJustFixWindowsConsole(self): + if sys.platform != "win32": + # just_fix_windows_console should be a no-op + just_fix_windows_console() + self.assertIs(sys.stdout, orig_stdout) + self.assertIs(sys.stderr, orig_stderr) + else: + def fake_std(): + # Emulate stdout=not a tty, stderr=tty + # to check that we handle both cases correctly + stdout = Mock() + stdout.closed = False + stdout.isatty.return_value = False + stdout.fileno.return_value = 1 + sys.stdout = stdout + + stderr = Mock() + stderr.closed = False + stderr.isatty.return_value = True + stderr.fileno.return_value = 2 + sys.stderr = stderr + + for native_ansi in [False, True]: + with patch( + 'colorama.ansitowin32.enable_vt_processing', + lambda *_: native_ansi + ): + self._reset() + fake_std() + + # Regular single-call test + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + if native_ansi: + self.assertIs(sys.stderr, prev_stderr) + else: + self.assertIsNot(sys.stderr, prev_stderr) + + # second call without resetting is always a no-op + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(sys.stdout, prev_stdout) + self.assertIs(sys.stderr, prev_stderr) + + self._reset() + fake_std() + + # If init() runs first, just_fix_windows_console should be a no-op + init() + prev_stdout = sys.stdout + prev_stderr = sys.stderr + just_fix_windows_console() + self.assertIs(prev_stdout, sys.stdout) + self.assertIs(prev_stderr, sys.stderr) + + +if __name__ == '__main__': + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py new file mode 100644 index 000000000..0f84e4bef --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/isatty_test.py @@ -0,0 +1,57 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main + +from ..ansitowin32 import StreamWrapper, AnsiToWin32 +from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY + + +def is_a_tty(stream): + return StreamWrapper(stream, None).isatty() + +class IsattyTest(TestCase): + + def test_TTY(self): + tty = StreamTTY() + self.assertTrue(is_a_tty(tty)) + with pycharm(): + self.assertTrue(is_a_tty(tty)) + + def test_nonTTY(self): + non_tty = StreamNonTTY() + self.assertFalse(is_a_tty(non_tty)) + with pycharm(): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharm(self): + with pycharm(): + self.assertTrue(is_a_tty(sys.stderr)) + self.assertTrue(is_a_tty(sys.stdout)) + + def test_withPycharmTTYOverride(self): + tty = StreamTTY() + with pycharm(), replace_by(tty): + self.assertTrue(is_a_tty(tty)) + + def test_withPycharmNonTTYOverride(self): + non_tty = StreamNonTTY() + with pycharm(), replace_by(non_tty): + self.assertFalse(is_a_tty(non_tty)) + + def test_withPycharmNoneOverride(self): + with pycharm(): + with replace_by(None), replace_original_by(None): + self.assertFalse(is_a_tty(None)) + self.assertFalse(is_a_tty(StreamNonTTY())) + self.assertTrue(is_a_tty(StreamTTY())) + + def test_withPycharmStreamWrapped(self): + with pycharm(): + self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) + self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) + self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) + + +if __name__ == '__main__': + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/utils.py new file mode 100644 index 000000000..472fafb44 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/utils.py @@ -0,0 +1,49 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +from contextlib import contextmanager +from io import StringIO +import sys +import os + + +class StreamTTY(StringIO): + def isatty(self): + return True + +class StreamNonTTY(StringIO): + def isatty(self): + return False + +@contextmanager +def osname(name): + orig = os.name + os.name = name + yield + os.name = orig + +@contextmanager +def replace_by(stream): + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = stream + sys.stderr = stream + yield + sys.stdout = orig_stdout + sys.stderr = orig_stderr + +@contextmanager +def replace_original_by(stream): + orig_stdout = sys.__stdout__ + orig_stderr = sys.__stderr__ + sys.__stdout__ = stream + sys.__stderr__ = stream + yield + sys.__stdout__ = orig_stdout + sys.__stderr__ = orig_stderr + +@contextmanager +def pycharm(): + os.environ["PYCHARM_HOSTED"] = "1" + non_tty = StreamNonTTY() + with replace_by(non_tty), replace_original_by(non_tty): + yield + del os.environ["PYCHARM_HOSTED"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py new file mode 100644 index 000000000..d0955f9e6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/tests/winterm_test.py @@ -0,0 +1,131 @@ +# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. +import sys +from unittest import TestCase, main, skipUnless + +try: + from unittest.mock import Mock, patch +except ImportError: + from mock import Mock, patch + +from ..winterm import WinColor, WinStyle, WinTerm + + +class WinTermTest(TestCase): + + @patch('colorama.winterm.win32') + def testInit(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 7 + 6 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + self.assertEqual(term._fore, 7) + self.assertEqual(term._back, 6) + self.assertEqual(term._style, 8) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testGetAttrs(self): + term = WinTerm() + + term._fore = 0 + term._back = 0 + term._style = 0 + self.assertEqual(term.get_attrs(), 0) + + term._fore = WinColor.YELLOW + self.assertEqual(term.get_attrs(), WinColor.YELLOW) + + term._back = WinColor.MAGENTA + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16) + + term._style = WinStyle.BRIGHT + self.assertEqual( + term.get_attrs(), + WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) + + @patch('colorama.winterm.win32') + def testResetAll(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 1 + 2 * 16 + 8 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + + term.set_console = Mock() + term._fore = -1 + term._back = -1 + term._style = -1 + + term.reset_all() + + self.assertEqual(term._fore, 1) + self.assertEqual(term._back, 2) + self.assertEqual(term._style, 8) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testFore(self): + term = WinTerm() + term.set_console = Mock() + term._fore = 0 + + term.fore(5) + + self.assertEqual(term._fore, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testBack(self): + term = WinTerm() + term.set_console = Mock() + term._back = 0 + + term.back(5) + + self.assertEqual(term._back, 5) + self.assertEqual(term.set_console.called, True) + + @skipUnless(sys.platform.startswith("win"), "requires Windows") + def testStyle(self): + term = WinTerm() + term.set_console = Mock() + term._style = 0 + + term.style(22) + + self.assertEqual(term._style, 22) + self.assertEqual(term.set_console.called, True) + + @patch('colorama.winterm.win32') + def testSetConsole(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console() + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDOUT, term.get_attrs()), {}) + ) + + @patch('colorama.winterm.win32') + def testSetConsoleOnStderr(self, mockWin32): + mockAttr = Mock() + mockAttr.wAttributes = 0 + mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr + term = WinTerm() + term.windll = Mock() + + term.set_console(on_stderr=True) + + self.assertEqual( + mockWin32.SetConsoleTextAttribute.call_args, + ((mockWin32.STDERR, term.get_attrs()), {}) + ) + + +if __name__ == '__main__': + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/win32.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/win32.py index c2d836033..841b0e270 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/win32.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/win32.py @@ -4,6 +4,8 @@ STDOUT = -11 STDERR = -12 +ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + try: import ctypes from ctypes import LibraryLoader @@ -89,6 +91,20 @@ def __str__(self): ] _SetConsoleTitleW.restype = wintypes.BOOL + _GetConsoleMode = windll.kernel32.GetConsoleMode + _GetConsoleMode.argtypes = [ + wintypes.HANDLE, + POINTER(wintypes.DWORD) + ] + _GetConsoleMode.restype = wintypes.BOOL + + _SetConsoleMode = windll.kernel32.SetConsoleMode + _SetConsoleMode.argtypes = [ + wintypes.HANDLE, + wintypes.DWORD + ] + _SetConsoleMode.restype = wintypes.BOOL + def _winapi_test(handle): csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( @@ -150,3 +166,15 @@ def FillConsoleOutputAttribute(stream_id, attr, length, start): def SetConsoleTitle(title): return _SetConsoleTitleW(title) + + def GetConsoleMode(handle): + mode = wintypes.DWORD() + success = _GetConsoleMode(handle, byref(mode)) + if not success: + raise ctypes.WinError() + return mode.value + + def SetConsoleMode(handle, mode): + success = _SetConsoleMode(handle, mode) + if not success: + raise ctypes.WinError() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/winterm.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/winterm.py index 0fdb4ec4e..aad867e8c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/winterm.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/colorama/winterm.py @@ -1,6 +1,12 @@ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. -from . import win32 +try: + from msvcrt import get_osfhandle +except ImportError: + def get_osfhandle(_): + raise OSError("This isn't windows!") + +from . import win32 # from wincon.h class WinColor(object): @@ -167,3 +173,23 @@ def erase_line(self, mode=0, on_stderr=False): def set_title(self, title): win32.SetConsoleTitle(title) + + +def enable_vt_processing(fd): + if win32.windll is None or not win32.winapi_test(): + return False + + try: + handle = get_osfhandle(fd) + mode = win32.GetConsoleMode(handle) + win32.SetConsoleMode( + handle, + mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, + ) + + mode = win32.GetConsoleMode(handle) + if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: + return True + # Can get TypeError in testsuite where 'fd' is a Mock() + except (OSError, TypeError): + return False diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/distro/distro.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/distro/distro.py index 49066ae83..89e186804 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/distro/distro.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/distro/distro.py @@ -55,7 +55,7 @@ # Python 3.7 TypedDict = dict -__version__ = "1.7.0" +__version__ = "1.8.0" class VersionDict(TypedDict): @@ -122,6 +122,26 @@ class InfoDict(TypedDict): # Pattern for base file name of distro release file _DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$") +# Base file names to be looked up for if _UNIXCONFDIR is not readable. +_DISTRO_RELEASE_BASENAMES = [ + "SuSE-release", + "arch-release", + "base-release", + "centos-release", + "fedora-release", + "gentoo-release", + "mageia-release", + "mandrake-release", + "mandriva-release", + "mandrivalinux-release", + "manjaro-release", + "oracle-release", + "redhat-release", + "rocky-release", + "sl-release", + "slackware-version", +] + # Base file names to be ignored when searching for distro release file _DISTRO_RELEASE_IGNORE_BASENAMES = ( "debian_version", @@ -200,6 +220,7 @@ def id() -> str: "opensuse" openSUSE "amzn" Amazon Linux "arch" Arch Linux + "buildroot" Buildroot "cloudlinux" CloudLinux OS "exherbo" Exherbo Linux "gentoo" GenToo Linux @@ -221,6 +242,7 @@ def id() -> str: "midnightbsd" MidnightBSD "rocky" Rocky Linux "aix" AIX + "guix" Guix System ============== ========================================= If you have a need to get distros for reliable IDs added into this set, @@ -876,6 +898,9 @@ def version(self, pretty: bool = False, best: bool = False) -> str: if self.uname_attr("id").startswith("aix"): # On AIX platforms, prefer oslevel command output. versions.insert(0, self.oslevel_info()) + elif self.id() == "debian" or "debian" in self.like().split(): + # On Debian-like, add debian_version file content to candidates list. + versions.append(self._debian_version) version = "" if best: # This algorithm uses the last version in priority order that has @@ -1186,6 +1211,16 @@ def _oslevel_info(self) -> str: return "" return self._to_str(stdout).strip() + @cached_property + def _debian_version(self) -> str: + try: + with open( + os.path.join(self.etc_dir, "debian_version"), encoding="ascii" + ) as fp: + return fp.readline().rstrip() + except FileNotFoundError: + return "" + @staticmethod def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]: if not lines: @@ -1228,14 +1263,14 @@ def _distro_release_info(self) -> Dict[str, str]: # file), because we want to use what was specified as best as # possible. match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if "name" in distro_info and "cloudlinux" in distro_info["name"].lower(): - distro_info["id"] = "cloudlinux" - elif match: - distro_info["id"] = match.group(1) - return distro_info else: try: - basenames = os.listdir(self.etc_dir) + basenames = [ + basename + for basename in os.listdir(self.etc_dir) + if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES + and os.path.isfile(os.path.join(self.etc_dir, basename)) + ] # We sort for repeatability in cases where there are multiple # distro specific files; e.g. CentOS, Oracle, Enterprise all # containing `redhat-release` on top of their own. @@ -1245,39 +1280,29 @@ def _distro_release_info(self) -> Dict[str, str]: # sure about the *-release files. Check common entries of # /etc for information. If they turn out to not be there the # error is handled in `_parse_distro_release_file()`. - basenames = [ - "SuSE-release", - "arch-release", - "base-release", - "centos-release", - "fedora-release", - "gentoo-release", - "mageia-release", - "mandrake-release", - "mandriva-release", - "mandrivalinux-release", - "manjaro-release", - "oracle-release", - "redhat-release", - "rocky-release", - "sl-release", - "slackware-version", - ] + basenames = _DISTRO_RELEASE_BASENAMES for basename in basenames: - if basename in _DISTRO_RELEASE_IGNORE_BASENAMES: - continue match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename) - if match: - filepath = os.path.join(self.etc_dir, basename) - distro_info = self._parse_distro_release_file(filepath) - if "name" in distro_info: - # The name is always present if the pattern matches - self.distro_release_file = filepath - distro_info["id"] = match.group(1) - if "cloudlinux" in distro_info["name"].lower(): - distro_info["id"] = "cloudlinux" - return distro_info - return {} + if match is None: + continue + filepath = os.path.join(self.etc_dir, basename) + distro_info = self._parse_distro_release_file(filepath) + # The name is always present if the pattern matches. + if "name" not in distro_info: + continue + self.distro_release_file = filepath + break + else: # the loop didn't "break": no candidate. + return {} + + if match is not None: + distro_info["id"] = match.group(1) + + # CloudLinux < 7: manually enrich info with proper id. + if "cloudlinux" in distro_info.get("name", "").lower(): + distro_info["id"] = "cloudlinux" + + return distro_info def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]: """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/__init__.py deleted file mode 100644 index 38ea0f5f1..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Wrappers to build Python packages using PEP 517 hooks -""" - -__version__ = '0.13.0' - -from .wrappers import * # noqa: F401, F403 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/build.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/build.py deleted file mode 100644 index b30909c87..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/build.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Build a project using PEP 517 hooks. -""" -import argparse -import logging -import os -import shutil -import tempfile - -from ._compat import tomllib -from .envbuild import BuildEnvironment -from .wrappers import Pep517HookCaller - -log = logging.getLogger(__name__) - - -def validate_system(system): - """ - Ensure build system has the requisite fields. - """ - required = {'requires', 'build-backend'} - if not (required <= set(system)): - message = "Missing required fields: {missing}".format( - missing=required-set(system), - ) - raise ValueError(message) - - -def load_system(source_dir): - """ - Load the build system from a source dir (pyproject.toml). - """ - pyproject = os.path.join(source_dir, 'pyproject.toml') - with open(pyproject, 'rb') as f: - pyproject_data = tomllib.load(f) - return pyproject_data['build-system'] - - -def compat_system(source_dir): - """ - Given a source dir, attempt to get a build system backend - and requirements from pyproject.toml. Fallback to - setuptools but only if the file was not found or a build - system was not indicated. - """ - try: - system = load_system(source_dir) - except (FileNotFoundError, KeyError): - system = {} - system.setdefault( - 'build-backend', - 'setuptools.build_meta:__legacy__', - ) - system.setdefault('requires', ['setuptools', 'wheel']) - return system - - -def _do_build(hooks, env, dist, dest): - get_requires_name = 'get_requires_for_build_{dist}'.format(**locals()) - get_requires = getattr(hooks, get_requires_name) - reqs = get_requires({}) - log.info('Got build requires: %s', reqs) - - env.pip_install(reqs) - log.info('Installed dynamic build dependencies') - - with tempfile.TemporaryDirectory() as td: - log.info('Trying to build %s in %s', dist, td) - build_name = 'build_{dist}'.format(**locals()) - build = getattr(hooks, build_name) - filename = build(td, {}) - source = os.path.join(td, filename) - shutil.move(source, os.path.join(dest, os.path.basename(filename))) - - -def build(source_dir, dist, dest=None, system=None): - system = system or load_system(source_dir) - dest = os.path.join(source_dir, dest or 'dist') - os.makedirs(dest, exist_ok=True) - - validate_system(system) - hooks = Pep517HookCaller( - source_dir, system['build-backend'], system.get('backend-path') - ) - - with BuildEnvironment() as env: - env.pip_install(system['requires']) - _do_build(hooks, env, dist, dest) - - -parser = argparse.ArgumentParser() -parser.add_argument( - 'source_dir', - help="A directory containing pyproject.toml", -) -parser.add_argument( - '--binary', '-b', - action='store_true', - default=False, -) -parser.add_argument( - '--source', '-s', - action='store_true', - default=False, -) -parser.add_argument( - '--out-dir', '-o', - help="Destination in which to save the builds relative to source dir", -) - - -def main(args): - log.warning('pep517.build is deprecated. ' - 'Consider switching to https://pypi.org/project/build/') - - # determine which dists to build - dists = list(filter(None, ( - 'sdist' if args.source or not args.binary else None, - 'wheel' if args.binary or not args.source else None, - ))) - - for dist in dists: - build(args.source_dir, dist, args.out_dir) - - -if __name__ == '__main__': - main(parser.parse_args()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/check.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/check.py deleted file mode 100644 index b79f6270b..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/check.py +++ /dev/null @@ -1,207 +0,0 @@ -"""Check a project and backend by attempting to build using PEP 517 hooks. -""" -import argparse -import logging -import os -import shutil -import sys -import tarfile -import zipfile -from os.path import isfile -from os.path import join as pjoin -from subprocess import CalledProcessError -from tempfile import mkdtemp - -from ._compat import tomllib -from .colorlog import enable_colourful_output -from .envbuild import BuildEnvironment -from .wrappers import Pep517HookCaller - -log = logging.getLogger(__name__) - - -def check_build_sdist(hooks, build_sys_requires): - with BuildEnvironment() as env: - try: - env.pip_install(build_sys_requires) - log.info('Installed static build dependencies') - except CalledProcessError: - log.error('Failed to install static build dependencies') - return False - - try: - reqs = hooks.get_requires_for_build_sdist({}) - log.info('Got build requires: %s', reqs) - except Exception: - log.error('Failure in get_requires_for_build_sdist', exc_info=True) - return False - - try: - env.pip_install(reqs) - log.info('Installed dynamic build dependencies') - except CalledProcessError: - log.error('Failed to install dynamic build dependencies') - return False - - td = mkdtemp() - log.info('Trying to build sdist in %s', td) - try: - try: - filename = hooks.build_sdist(td, {}) - log.info('build_sdist returned %r', filename) - except Exception: - log.info('Failure in build_sdist', exc_info=True) - return False - - if not filename.endswith('.tar.gz'): - log.error( - "Filename %s doesn't have .tar.gz extension", filename) - return False - - path = pjoin(td, filename) - if isfile(path): - log.info("Output file %s exists", path) - else: - log.error("Output file %s does not exist", path) - return False - - if tarfile.is_tarfile(path): - log.info("Output file is a tar file") - else: - log.error("Output file is not a tar file") - return False - - finally: - shutil.rmtree(td) - - return True - - -def check_build_wheel(hooks, build_sys_requires): - with BuildEnvironment() as env: - try: - env.pip_install(build_sys_requires) - log.info('Installed static build dependencies') - except CalledProcessError: - log.error('Failed to install static build dependencies') - return False - - try: - reqs = hooks.get_requires_for_build_wheel({}) - log.info('Got build requires: %s', reqs) - except Exception: - log.error('Failure in get_requires_for_build_sdist', exc_info=True) - return False - - try: - env.pip_install(reqs) - log.info('Installed dynamic build dependencies') - except CalledProcessError: - log.error('Failed to install dynamic build dependencies') - return False - - td = mkdtemp() - log.info('Trying to build wheel in %s', td) - try: - try: - filename = hooks.build_wheel(td, {}) - log.info('build_wheel returned %r', filename) - except Exception: - log.info('Failure in build_wheel', exc_info=True) - return False - - if not filename.endswith('.whl'): - log.error("Filename %s doesn't have .whl extension", filename) - return False - - path = pjoin(td, filename) - if isfile(path): - log.info("Output file %s exists", path) - else: - log.error("Output file %s does not exist", path) - return False - - if zipfile.is_zipfile(path): - log.info("Output file is a zip file") - else: - log.error("Output file is not a zip file") - return False - - finally: - shutil.rmtree(td) - - return True - - -def check(source_dir): - pyproject = pjoin(source_dir, 'pyproject.toml') - if isfile(pyproject): - log.info('Found pyproject.toml') - else: - log.error('Missing pyproject.toml') - return False - - try: - with open(pyproject, 'rb') as f: - pyproject_data = tomllib.load(f) - # Ensure the mandatory data can be loaded - buildsys = pyproject_data['build-system'] - requires = buildsys['requires'] - backend = buildsys['build-backend'] - backend_path = buildsys.get('backend-path') - log.info('Loaded pyproject.toml') - except (tomllib.TOMLDecodeError, KeyError): - log.error("Invalid pyproject.toml", exc_info=True) - return False - - hooks = Pep517HookCaller(source_dir, backend, backend_path) - - sdist_ok = check_build_sdist(hooks, requires) - wheel_ok = check_build_wheel(hooks, requires) - - if not sdist_ok: - log.warning('Sdist checks failed; scroll up to see') - if not wheel_ok: - log.warning('Wheel checks failed') - - return sdist_ok - - -def main(argv=None): - log.warning('pep517.check is deprecated. ' - 'Consider switching to https://pypi.org/project/build/') - - ap = argparse.ArgumentParser() - ap.add_argument( - 'source_dir', - help="A directory containing pyproject.toml") - args = ap.parse_args(argv) - - enable_colourful_output() - - ok = check(args.source_dir) - - if ok: - print(ansi('Checks passed', 'green')) - else: - print(ansi('Checks failed', 'red')) - sys.exit(1) - - -ansi_codes = { - 'reset': '\x1b[0m', - 'bold': '\x1b[1m', - 'red': '\x1b[31m', - 'green': '\x1b[32m', -} - - -def ansi(s, attr): - if os.name != 'nt' and sys.stdout.isatty(): - return ansi_codes[attr] + str(s) + ansi_codes['reset'] - else: - return str(s) - - -if __name__ == '__main__': - main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/colorlog.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/colorlog.py deleted file mode 100644 index 66310a79a..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/colorlog.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Nicer log formatting with colours. - -Code copied from Tornado, Apache licensed. -""" -# Copyright 2012 Facebook -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import logging -import sys - -try: - import curses -except ImportError: - curses = None - - -def _stderr_supports_color(): - color = False - if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty(): - try: - curses.setupterm() - if curses.tigetnum("colors") > 0: - color = True - except Exception: - pass - return color - - -class LogFormatter(logging.Formatter): - """Log formatter with colour support - """ - DEFAULT_COLORS = { - logging.INFO: 2, # Green - logging.WARNING: 3, # Yellow - logging.ERROR: 1, # Red - logging.CRITICAL: 1, - } - - def __init__(self, color=True, datefmt=None): - r""" - :arg bool color: Enables color support. - :arg string fmt: Log message format. - It will be applied to the attributes dict of log records. The - text between ``%(color)s`` and ``%(end_color)s`` will be colored - depending on the level if color support is on. - :arg dict colors: color mappings from logging level to terminal color - code - :arg string datefmt: Datetime format. - Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``. - .. versionchanged:: 3.2 - Added ``fmt`` and ``datefmt`` arguments. - """ - logging.Formatter.__init__(self, datefmt=datefmt) - self._colors = {} - if color and _stderr_supports_color(): - # The curses module has some str/bytes confusion in - # python3. Until version 3.2.3, most methods return - # bytes, but only accept strings. In addition, we want to - # output these strings with the logging module, which - # works with unicode strings. The explicit calls to - # unicode() below are harmless in python2 but will do the - # right conversion in python 3. - fg_color = (curses.tigetstr("setaf") or - curses.tigetstr("setf") or "") - - for levelno, code in self.DEFAULT_COLORS.items(): - self._colors[levelno] = str( - curses.tparm(fg_color, code), "ascii") - self._normal = str(curses.tigetstr("sgr0"), "ascii") - - scr = curses.initscr() - self.termwidth = scr.getmaxyx()[1] - curses.endwin() - else: - self._normal = '' - # Default width is usually 80, but too wide is - # worse than too narrow - self.termwidth = 70 - - def formatMessage(self, record): - mlen = len(record.message) - right_text = '{initial}-{name}'.format(initial=record.levelname[0], - name=record.name) - if mlen + len(right_text) < self.termwidth: - space = ' ' * (self.termwidth - (mlen + len(right_text))) - else: - space = ' ' - - if record.levelno in self._colors: - start_color = self._colors[record.levelno] - end_color = self._normal - else: - start_color = end_color = '' - - return record.message + space + start_color + right_text + end_color - - -def enable_colourful_output(level=logging.INFO): - handler = logging.StreamHandler() - handler.setFormatter(LogFormatter()) - logging.root.addHandler(handler) - logging.root.setLevel(level) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/dirtools.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/dirtools.py deleted file mode 100644 index 3eff4d801..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/dirtools.py +++ /dev/null @@ -1,19 +0,0 @@ -import io -import os -import zipfile - - -def dir_to_zipfile(root): - """Construct an in-memory zip file for a directory.""" - buffer = io.BytesIO() - zip_file = zipfile.ZipFile(buffer, 'w') - for root, dirs, files in os.walk(root): - for path in dirs: - fs_path = os.path.join(root, path) - rel_path = os.path.relpath(fs_path, root) - zip_file.writestr(rel_path + '/', '') - for path in files: - fs_path = os.path.join(root, path) - rel_path = os.path.relpath(fs_path, root) - zip_file.write(fs_path, rel_path) - return zip_file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/envbuild.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/envbuild.py deleted file mode 100644 index c0415c4d7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/envbuild.py +++ /dev/null @@ -1,170 +0,0 @@ -"""Build wheels/sdists by installing build deps to a temporary environment. -""" - -import logging -import os -import shutil -import sys -from subprocess import check_call -from sysconfig import get_paths -from tempfile import mkdtemp - -from ._compat import tomllib -from .wrappers import LoggerWrapper, Pep517HookCaller - -log = logging.getLogger(__name__) - - -def _load_pyproject(source_dir): - with open( - os.path.join(source_dir, 'pyproject.toml'), - 'rb', - ) as f: - pyproject_data = tomllib.load(f) - buildsys = pyproject_data['build-system'] - return ( - buildsys['requires'], - buildsys['build-backend'], - buildsys.get('backend-path'), - ) - - -class BuildEnvironment: - """Context manager to install build deps in a simple temporary environment - - Based on code I wrote for pip, which is MIT licensed. - """ - # Copyright (c) 2008-2016 The pip developers (see AUTHORS.txt file) - # - # Permission is hereby granted, free of charge, to any person obtaining - # a copy of this software and associated documentation files (the - # "Software"), to deal in the Software without restriction, including - # without limitation the rights to use, copy, modify, merge, publish, - # distribute, sublicense, and/or sell copies of the Software, and to - # permit persons to whom the Software is furnished to do so, subject to - # the following conditions: - # - # The above copyright notice and this permission notice shall be - # included in all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - path = None - - def __init__(self, cleanup=True): - self._cleanup = cleanup - - def __enter__(self): - self.path = mkdtemp(prefix='pep517-build-env-') - log.info('Temporary build environment: %s', self.path) - - self.save_path = os.environ.get('PATH', None) - self.save_pythonpath = os.environ.get('PYTHONPATH', None) - - install_scheme = 'nt' if (os.name == 'nt') else 'posix_prefix' - install_dirs = get_paths(install_scheme, vars={ - 'base': self.path, - 'platbase': self.path, - }) - - scripts = install_dirs['scripts'] - if self.save_path: - os.environ['PATH'] = scripts + os.pathsep + self.save_path - else: - os.environ['PATH'] = scripts + os.pathsep + os.defpath - - if install_dirs['purelib'] == install_dirs['platlib']: - lib_dirs = install_dirs['purelib'] - else: - lib_dirs = install_dirs['purelib'] + os.pathsep + \ - install_dirs['platlib'] - if self.save_pythonpath: - os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \ - self.save_pythonpath - else: - os.environ['PYTHONPATH'] = lib_dirs - - return self - - def pip_install(self, reqs): - """Install dependencies into this env by calling pip in a subprocess""" - if not reqs: - return - log.info('Calling pip to install %s', reqs) - cmd = [ - sys.executable, '-m', 'pip', 'install', '--ignore-installed', - '--prefix', self.path] + list(reqs) - check_call( - cmd, - stdout=LoggerWrapper(log, logging.INFO), - stderr=LoggerWrapper(log, logging.ERROR), - ) - - def __exit__(self, exc_type, exc_val, exc_tb): - needs_cleanup = ( - self._cleanup and - self.path is not None and - os.path.isdir(self.path) - ) - if needs_cleanup: - shutil.rmtree(self.path) - - if self.save_path is None: - os.environ.pop('PATH', None) - else: - os.environ['PATH'] = self.save_path - - if self.save_pythonpath is None: - os.environ.pop('PYTHONPATH', None) - else: - os.environ['PYTHONPATH'] = self.save_pythonpath - - -def build_wheel(source_dir, wheel_dir, config_settings=None): - """Build a wheel from a source directory using PEP 517 hooks. - - :param str source_dir: Source directory containing pyproject.toml - :param str wheel_dir: Target directory to create wheel in - :param dict config_settings: Options to pass to build backend - - This is a blocking function which will run pip in a subprocess to install - build requirements. - """ - if config_settings is None: - config_settings = {} - requires, backend, backend_path = _load_pyproject(source_dir) - hooks = Pep517HookCaller(source_dir, backend, backend_path) - - with BuildEnvironment() as env: - env.pip_install(requires) - reqs = hooks.get_requires_for_build_wheel(config_settings) - env.pip_install(reqs) - return hooks.build_wheel(wheel_dir, config_settings) - - -def build_sdist(source_dir, sdist_dir, config_settings=None): - """Build an sdist from a source directory using PEP 517 hooks. - - :param str source_dir: Source directory containing pyproject.toml - :param str sdist_dir: Target directory to place sdist in - :param dict config_settings: Options to pass to build backend - - This is a blocking function which will run pip in a subprocess to install - build requirements. - """ - if config_settings is None: - config_settings = {} - requires, backend, backend_path = _load_pyproject(source_dir) - hooks = Pep517HookCaller(source_dir, backend, backend_path) - - with BuildEnvironment() as env: - env.pip_install(requires) - reqs = hooks.get_requires_for_build_sdist(config_settings) - env.pip_install(reqs) - return hooks.build_sdist(sdist_dir, config_settings) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py deleted file mode 100644 index 281a356cf..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -"""This is a subpackage because the directory is on sys.path for _in_process.py - -The subpackage should stay as empty as possible to avoid shadowing modules that -the backend might import. -""" -from contextlib import contextmanager -from os.path import abspath, dirname -from os.path import join as pjoin - -try: - import importlib.resources as resources - try: - resources.files - except AttributeError: - # Python 3.8 compatibility - def _in_proc_script_path(): - return resources.path(__package__, '_in_process.py') - else: - def _in_proc_script_path(): - return resources.as_file( - resources.files(__package__).joinpath('_in_process.py')) -except ImportError: - # Python 3.6 compatibility - @contextmanager - def _in_proc_script_path(): - yield pjoin(dirname(abspath(__file__)), '_in_process.py') diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/meta.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/meta.py deleted file mode 100644 index 4afc3c047..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/meta.py +++ /dev/null @@ -1,93 +0,0 @@ -"""Build metadata for a project using PEP 517 hooks. -""" -import argparse -import functools -import logging -import os -import shutil -import tempfile - -try: - import importlib.metadata as imp_meta -except ImportError: - import importlib_metadata as imp_meta - -try: - from zipfile import Path -except ImportError: - from zipp import Path - -from .build import compat_system, load_system, validate_system -from .dirtools import dir_to_zipfile -from .envbuild import BuildEnvironment -from .wrappers import Pep517HookCaller, quiet_subprocess_runner - -log = logging.getLogger(__name__) - - -def _prep_meta(hooks, env, dest): - reqs = hooks.get_requires_for_build_wheel({}) - log.info('Got build requires: %s', reqs) - - env.pip_install(reqs) - log.info('Installed dynamic build dependencies') - - with tempfile.TemporaryDirectory() as td: - log.info('Trying to build metadata in %s', td) - filename = hooks.prepare_metadata_for_build_wheel(td, {}) - source = os.path.join(td, filename) - shutil.move(source, os.path.join(dest, os.path.basename(filename))) - - -def build(source_dir='.', dest=None, system=None): - system = system or load_system(source_dir) - dest = os.path.join(source_dir, dest or 'dist') - os.makedirs(dest, exist_ok=True) - validate_system(system) - hooks = Pep517HookCaller( - source_dir, system['build-backend'], system.get('backend-path') - ) - - with hooks.subprocess_runner(quiet_subprocess_runner): - with BuildEnvironment() as env: - env.pip_install(system['requires']) - _prep_meta(hooks, env, dest) - - -def build_as_zip(builder=build): - with tempfile.TemporaryDirectory() as out_dir: - builder(dest=out_dir) - return dir_to_zipfile(out_dir) - - -def load(root): - """ - Given a source directory (root) of a package, - return an importlib.metadata.Distribution object - with metadata build from that package. - """ - root = os.path.expanduser(root) - system = compat_system(root) - builder = functools.partial(build, source_dir=root, system=system) - path = Path(build_as_zip(builder)) - return imp_meta.PathDistribution(path) - - -parser = argparse.ArgumentParser() -parser.add_argument( - 'source_dir', - help="A directory containing pyproject.toml", -) -parser.add_argument( - '--out-dir', '-o', - help="Destination in which to save the builds relative to source dir", -) - - -def main(): - args = parser.parse_args() - build(args.source_dir, args.out_dir) - - -if __name__ == '__main__': - main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/__init__.py index 9d513dcf1..82d907163 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/__init__.py @@ -7,13 +7,15 @@ import os import sys from pathlib import Path -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from pip._vendor.typing_extensions import Literal # pragma: no cover +if sys.version_info >= (3, 8): # pragma: no cover (py38+) + from typing import Literal +else: # pragma: no cover (py38+) + from pip._vendor.typing_extensions import Literal from .api import PlatformDirsABC -from .version import __version__, __version_info__ +from .version import __version__ +from .version import __version_tuple__ as __version_info__ def _set_platform_dir_class() -> type[PlatformDirsABC]: @@ -26,7 +28,7 @@ def _set_platform_dir_class() -> type[PlatformDirsABC]: if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": - if os.getenv("SHELL") is not None: + if os.getenv("SHELL") or os.getenv("PREFIX"): return Result from pip._vendor.platformdirs.android import _android_folder diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/unix.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/unix.py index 2fbd4d4f3..9aca5a030 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/unix.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/unix.py @@ -107,9 +107,9 @@ def user_state_dir(self) -> str: @property def user_log_dir(self) -> str: """ - :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``log`` in it + :return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it """ - path = self.user_cache_dir + path = self.user_state_dir if self.opinion: path = os.path.join(path, "log") return path diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/version.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/version.py index 4552c02af..9f6eb98e8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/version.py @@ -1,4 +1,4 @@ -"""Version information""" - -__version__ = "2.5.2" -__version_info__ = (2, 5, 2) +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '2.6.2' +__version_tuple__ = version_tuple = (2, 6, 2) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/windows.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/windows.py index ef972bdf2..d5c27b341 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/windows.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/platformdirs/windows.py @@ -2,6 +2,7 @@ import ctypes import os +import sys from functools import lru_cache from typing import Callable @@ -132,7 +133,8 @@ def get_win_folder_from_registry(csidl_name: str) -> str: }.get(csidl_name) if shell_folder_name is None: raise ValueError(f"Unknown CSIDL name: {csidl_name}") - + if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows + raise NotImplementedError import winreg key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py new file mode 100644 index 000000000..ddfcf7f72 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/__init__.py @@ -0,0 +1,23 @@ +"""Wrappers to call pyproject.toml-based build backend hooks. +""" + +from ._impl import ( + BackendInvalid, + BackendUnavailable, + BuildBackendHookCaller, + HookMissing, + UnsupportedOperation, + default_subprocess_runner, + quiet_subprocess_runner, +) + +__version__ = '1.0.0' +__all__ = [ + 'BackendUnavailable', + 'BackendInvalid', + 'HookMissing', + 'UnsupportedOperation', + 'default_subprocess_runner', + 'quiet_subprocess_runner', + 'BuildBackendHookCaller', +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/_compat.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_compat.py similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/_compat.py rename to dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_compat.py diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/wrappers.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_impl.py similarity index 63% rename from dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/wrappers.py rename to dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_impl.py index 987a62aaa..37b0e6531 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/wrappers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_impl.py @@ -2,23 +2,12 @@ import os import sys import tempfile -import threading from contextlib import contextmanager from os.path import abspath from os.path import join as pjoin from subprocess import STDOUT, check_call, check_output -from .in_process import _in_proc_script_path - -__all__ = [ - 'BackendUnavailable', - 'BackendInvalid', - 'HookMissing', - 'UnsupportedOperation', - 'default_subprocess_runner', - 'quiet_subprocess_runner', - 'Pep517HookCaller', -] +from ._in_process import _in_proc_script_path def write_json(obj, path, **kwargs): @@ -40,13 +29,13 @@ def __init__(self, traceback): class BackendInvalid(Exception): """Will be raised if the backend is invalid.""" def __init__(self, backend_name, backend_path, message): + super().__init__(message) self.backend_name = backend_name self.backend_path = backend_path - self.message = message class HookMissing(Exception): - """Will be raised on missing hooks.""" + """Will be raised on missing hooks (if a fallback can't be used).""" def __init__(self, hook_name): super().__init__(hook_name) self.hook_name = hook_name @@ -59,7 +48,10 @@ def __init__(self, traceback): def default_subprocess_runner(cmd, cwd=None, extra_environ=None): - """The default method of calling the wrapper subprocess.""" + """The default method of calling the wrapper subprocess. + + This uses :func:`subprocess.check_call` under the hood. + """ env = os.environ.copy() if extra_environ: env.update(extra_environ) @@ -68,7 +60,10 @@ def default_subprocess_runner(cmd, cwd=None, extra_environ=None): def quiet_subprocess_runner(cmd, cwd=None, extra_environ=None): - """A method of calling the wrapper subprocess while suppressing output.""" + """Call the subprocess while suppressing output. + + This uses :func:`subprocess.check_output` under the hood. + """ env = os.environ.copy() if extra_environ: env.update(extra_environ) @@ -100,26 +95,10 @@ def norm_and_check(source_tree, requested): return abs_requested -class Pep517HookCaller: - """A wrapper around a source directory to be built with a PEP 517 backend. - - :param source_dir: The path to the source directory, containing - pyproject.toml. - :param build_backend: The build backend spec, as per PEP 517, from - pyproject.toml. - :param backend_path: The backend path, as per PEP 517, from pyproject.toml. - :param runner: A callable that invokes the wrapper subprocess. - :param python_executable: The Python executable used to invoke the backend - - The 'runner', if provided, must expect the following: - - - cmd: a list of strings representing the command and arguments to - execute, as would be passed to e.g. 'subprocess.check_call'. - - cwd: a string representing the working directory that must be - used for the subprocess. Corresponds to the provided source_dir. - - extra_environ: a dict mapping environment variable names to values - which must be set for the subprocess execution. +class BuildBackendHookCaller: + """A wrapper to call the build backend hooks for a source directory. """ + def __init__( self, source_dir, @@ -128,6 +107,14 @@ def __init__( runner=None, python_executable=None, ): + """ + :param source_dir: The source directory to invoke the build backend for + :param build_backend: The build backend spec + :param backend_path: Additional path entries for the build backend spec + :param runner: The :ref:`subprocess runner ` to use + :param python_executable: + The Python executable used to invoke the build backend + """ if runner is None: runner = default_subprocess_runner @@ -145,8 +132,14 @@ def __init__( @contextmanager def subprocess_runner(self, runner): - """A context manager for temporarily overriding the default subprocess - runner. + """A context manager for temporarily overriding the default + :ref:`subprocess runner `. + + .. code-block:: python + + hook_caller = BuildBackendHookCaller(...) + with hook_caller.subprocess_runner(quiet_subprocess_runner): + ... """ prev = self._subprocess_runner self._subprocess_runner = runner @@ -160,15 +153,15 @@ def _supported_features(self): return self._call_hook('_supported_features', {}) def get_requires_for_build_wheel(self, config_settings=None): - """Identify packages required for building a wheel + """Get additional dependencies required for building a wheel. - Returns a list of dependency specifications, e.g.:: + :returns: A list of :pep:`dependency specifiers <508>`. + :rtype: list[str] - ["wheel >= 0.25", "setuptools"] + .. admonition:: Fallback - This does not include requirements specified in pyproject.toml. - It returns the result of calling the equivalently named hook in a - subprocess. + If the build backend does not defined a hook with this name, an + empty list will be returned. """ return self._call_hook('get_requires_for_build_wheel', { 'config_settings': config_settings @@ -179,12 +172,16 @@ def prepare_metadata_for_build_wheel( _allow_fallback=True): """Prepare a ``*.dist-info`` folder with metadata for this project. - Returns the name of the newly created folder. + :returns: Name of the newly created subfolder within + ``metadata_directory``, containing the metadata. + :rtype: str + + .. admonition:: Fallback - If the build backend defines a hook with this name, it will be called - in a subprocess. If not, the backend will be asked to build a wheel, - and the dist-info extracted from that (unless _allow_fallback is - False). + If the build backend does not define a hook with this name and + ``_allow_fallback`` is truthy, the backend will be asked to build a + wheel via the ``build_wheel`` hook and the dist-info extracted from + that will be returned. """ return self._call_hook('prepare_metadata_for_build_wheel', { 'metadata_directory': abspath(metadata_directory), @@ -197,12 +194,15 @@ def build_wheel( metadata_directory=None): """Build a wheel from this project. - Returns the name of the newly created file. + :returns: + The name of the newly created wheel within ``wheel_directory``. - In general, this will call the 'build_wheel' hook in the backend. - However, if that was previously called by - 'prepare_metadata_for_build_wheel', and the same metadata_directory is - used, the previously built wheel will be copied to wheel_directory. + .. admonition:: Interaction with fallback + + If the ``build_wheel`` hook was called in the fallback for + :meth:`prepare_metadata_for_build_wheel`, the build backend would + not be invoked. Instead, the previously built wheel will be copied + to ``wheel_directory`` and the name of that file will be returned. """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) @@ -213,15 +213,15 @@ def build_wheel( }) def get_requires_for_build_editable(self, config_settings=None): - """Identify packages required for building an editable wheel + """Get additional dependencies required for building an editable wheel. - Returns a list of dependency specifications, e.g.:: + :returns: A list of :pep:`dependency specifiers <508>`. + :rtype: list[str] - ["wheel >= 0.25", "setuptools"] + .. admonition:: Fallback - This does not include requirements specified in pyproject.toml. - It returns the result of calling the equivalently named hook in a - subprocess. + If the build backend does not defined a hook with this name, an + empty list will be returned. """ return self._call_hook('get_requires_for_build_editable', { 'config_settings': config_settings @@ -232,12 +232,16 @@ def prepare_metadata_for_build_editable( _allow_fallback=True): """Prepare a ``*.dist-info`` folder with metadata for this project. - Returns the name of the newly created folder. + :returns: Name of the newly created subfolder within + ``metadata_directory``, containing the metadata. + :rtype: str + + .. admonition:: Fallback - If the build backend defines a hook with this name, it will be called - in a subprocess. If not, the backend will be asked to build an editable - wheel, and the dist-info extracted from that (unless _allow_fallback is - False). + If the build backend does not define a hook with this name and + ``_allow_fallback`` is truthy, the backend will be asked to build a + wheel via the ``build_editable`` hook and the dist-info + extracted from that will be returned. """ return self._call_hook('prepare_metadata_for_build_editable', { 'metadata_directory': abspath(metadata_directory), @@ -250,12 +254,16 @@ def build_editable( metadata_directory=None): """Build an editable wheel from this project. - Returns the name of the newly created file. + :returns: + The name of the newly created wheel within ``wheel_directory``. - In general, this will call the 'build_editable' hook in the backend. - However, if that was previously called by - 'prepare_metadata_for_build_editable', and the same metadata_directory - is used, the previously built wheel will be copied to wheel_directory. + .. admonition:: Interaction with fallback + + If the ``build_editable`` hook was called in the fallback for + :meth:`prepare_metadata_for_build_editable`, the build backend + would not be invoked. Instead, the previously built wheel will be + copied to ``wheel_directory`` and the name of that file will be + returned. """ if metadata_directory is not None: metadata_directory = abspath(metadata_directory) @@ -266,15 +274,10 @@ def build_editable( }) def get_requires_for_build_sdist(self, config_settings=None): - """Identify packages required for building a wheel - - Returns a list of dependency specifications, e.g.:: + """Get additional dependencies required for building an sdist. - ["setuptools >= 26"] - - This does not include requirements specified in pyproject.toml. - It returns the result of calling the equivalently named hook in a - subprocess. + :returns: A list of :pep:`dependency specifiers <508>`. + :rtype: list[str] """ return self._call_hook('get_requires_for_build_sdist', { 'config_settings': config_settings @@ -283,9 +286,8 @@ def get_requires_for_build_sdist(self, config_settings=None): def build_sdist(self, sdist_directory, config_settings=None): """Build an sdist from this project. - Returns the name of the newly created file. - - This calls the 'build_sdist' backend hook in a subprocess. + :returns: + The name of the newly created sdist within ``wheel_directory``. """ return self._call_hook('build_sdist', { 'sdist_directory': abspath(sdist_directory), @@ -326,37 +328,3 @@ def _call_hook(self, hook_name, kwargs): if data.get('hook_missing'): raise HookMissing(data.get('missing_hook_name') or hook_name) return data['return_val'] - - -class LoggerWrapper(threading.Thread): - """ - Read messages from a pipe and redirect them - to a logger (see python's logging module). - """ - - def __init__(self, logger, level): - threading.Thread.__init__(self) - self.daemon = True - - self.logger = logger - self.level = level - - # create the pipe and reader - self.fd_read, self.fd_write = os.pipe() - self.reader = os.fdopen(self.fd_read) - - self.start() - - def fileno(self): - return self.fd_write - - @staticmethod - def remove_newline(msg): - return msg[:-1] if msg.endswith(os.linesep) else msg - - def run(self): - for line in self.reader: - self._write(self.remove_newline(line)) - - def _write(self, message): - self.logger.log(self.level, message) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py new file mode 100644 index 000000000..917fa065b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/__init__.py @@ -0,0 +1,18 @@ +"""This is a subpackage because the directory is on sys.path for _in_process.py + +The subpackage should stay as empty as possible to avoid shadowing modules that +the backend might import. +""" + +import importlib.resources as resources + +try: + resources.files +except AttributeError: + # Python 3.8 compatibility + def _in_proc_script_path(): + return resources.path(__package__, '_in_process.py') +else: + def _in_proc_script_path(): + return resources.as_file( + resources.files(__package__).joinpath('_in_process.py')) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py similarity index 96% rename from dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py rename to dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py index ae4cf9e9c..ee511ff20 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pep517/in_process/_in_process.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py @@ -145,11 +145,13 @@ def prepare_metadata_for_build_wheel( except AttributeError: if not _allow_fallback: raise HookMissing() - whl_basename = backend.build_wheel(metadata_directory, config_settings) - return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, - config_settings) else: return hook(metadata_directory, config_settings) + # fallback to build_wheel outside the try block to avoid exception chaining + # which can be confusing to users and is not relevant + whl_basename = backend.build_wheel(metadata_directory, config_settings) + return _get_wheel_metadata_from_wheel(whl_basename, metadata_directory, + config_settings) def prepare_metadata_for_build_editable( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__init__.py index 9e97059d1..a47762480 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__init__.py @@ -77,8 +77,8 @@ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_ver elif charset_normalizer_version: major, minor, patch = charset_normalizer_version.split(".")[:3] major, minor, patch = int(major), int(minor), int(patch) - # charset_normalizer >= 2.0.0 < 3.0.0 - assert (2, 0, 0) <= (major, minor, patch) < (3, 0, 0) + # charset_normalizer >= 2.0.0 < 4.0.0 + assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0) else: raise Exception("You need either charset_normalizer or chardet installed") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__version__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__version__.py index e725ada65..69be3dec7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__version__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/__version__.py @@ -5,10 +5,10 @@ __title__ = "requests" __description__ = "Python HTTP for Humans." __url__ = "https://requests.readthedocs.io" -__version__ = "2.28.1" -__build__ = 0x022801 +__version__ = "2.28.2" +__build__ = 0x022802 __author__ = "Kenneth Reitz" __author_email__ = "me@kennethreitz.org" __license__ = "Apache 2.0" -__copyright__ = "Copyright 2022 Kenneth Reitz" +__copyright__ = "Copyright Kenneth Reitz" __cake__ = "\u2728 \U0001f370 \u2728" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/models.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/models.py index b45e81032..76e6f199c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/models.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/requests/models.py @@ -438,7 +438,7 @@ def prepare_url(self, url, params): if not scheme: raise MissingSchema( f"Invalid URL {url!r}: No scheme supplied. " - f"Perhaps you meant http://{url}?" + f"Perhaps you meant https://{url}?" ) if not host: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__init__.py index d35875dbb..73f58d774 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__init__.py @@ -5,7 +5,7 @@ from ._extension import load_ipython_extension # noqa: F401 -__all__ = ["get_console", "reconfigure", "print", "inspect"] +__all__ = ["get_console", "reconfigure", "print", "inspect", "print_json"] if TYPE_CHECKING: from .console import Console @@ -40,7 +40,8 @@ def reconfigure(*args: Any, **kwargs: Any) -> None: """Reconfigures the global console by replacing it with another. Args: - console (Console): Replacement console instance. + *args (Any): Positional arguments for the replacement :class:`~rich.console.Console`. + **kwargs (Any): Keyword arguments for the replacement :class:`~rich.console.Console`. """ from pip._vendor.rich.console import Console @@ -80,7 +81,7 @@ def print_json( indent: Union[None, int, str] = 2, highlight: bool = True, skip_keys: bool = False, - ensure_ascii: bool = True, + ensure_ascii: bool = False, check_circular: bool = True, allow_nan: bool = True, default: Optional[Callable[[Any], Any]] = None, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__main__.py index 54e6d5e8a..270629fd8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__main__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/__main__.py @@ -227,10 +227,6 @@ def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: c = Console(record=True) c.print(test_card) - # c.save_svg( - # path="/Users/darrenburns/Library/Application Support/JetBrains/PyCharm2021.3/scratches/svg_export.svg", - # title="Rich can export to SVG", - # ) print(f"rendered in {pre_cache_taken}ms (cold cache)") print(f"rendered in {taken}ms (warm cache)") @@ -247,10 +243,6 @@ def iter_last(values: Iterable[T]) -> Iterable[Tuple[bool, T]]: "Textualize", "[u blue link=https://github.com/textualize]https://github.com/textualize", ) - sponsor_message.add_row( - "Buy devs a :coffee:", - "[u blue link=https://ko-fi.com/textualize]https://ko-fi.com/textualize", - ) sponsor_message.add_row( "Twitter", "[u blue link=https://twitter.com/willmcgugan]https://twitter.com/willmcgugan", diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/_null_file.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/_null_file.py new file mode 100644 index 000000000..49038bfcb --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/_null_file.py @@ -0,0 +1,83 @@ +from types import TracebackType +from typing import IO, Iterable, Iterator, List, Optional, Type + + +class NullFile(IO[str]): + + # TODO: "mode", "name" and "closed" are only required for Python 3.6. + + @property + def mode(self) -> str: + return "" + + @property + def name(self) -> str: + return "NullFile" + + def closed(self) -> bool: + return False + + def close(self) -> None: + pass + + def isatty(self) -> bool: + return False + + def read(self, __n: int = 1) -> str: + return "" + + def readable(self) -> bool: + return False + + def readline(self, __limit: int = 1) -> str: + return "" + + def readlines(self, __hint: int = 1) -> List[str]: + return [] + + def seek(self, __offset: int, __whence: int = 1) -> int: + return 0 + + def seekable(self) -> bool: + return False + + def tell(self) -> int: + return 0 + + def truncate(self, __size: Optional[int] = 1) -> int: + return 0 + + def writable(self) -> bool: + return False + + def writelines(self, __lines: Iterable[str]) -> None: + pass + + def __next__(self) -> str: + return "" + + def __iter__(self) -> Iterator[str]: + return iter([""]) + + def __enter__(self) -> IO[str]: + pass + + def __exit__( + self, + __t: Optional[Type[BaseException]], + __value: Optional[BaseException], + __traceback: Optional[TracebackType], + ) -> None: + pass + + def write(self, text: str) -> int: + return 0 + + def flush(self) -> None: + pass + + def fileno(self) -> int: + return -1 + + +NULL_FILE = NullFile() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/ansi.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/ansi.py index d4c32cef1..92ef51941 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/ansi.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/ansi.py @@ -120,7 +120,7 @@ def __init__(self) -> None: self.style = Style.null() def decode(self, terminal_text: str) -> Iterable[Text]: - """Decode ANSI codes in an interable of lines. + """Decode ANSI codes in an iterable of lines. Args: lines (Iterable[str]): An iterable of lines of terminal output. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/box.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/box.py index d0b07cf57..97d2a9444 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/box.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/box.py @@ -514,4 +514,4 @@ def get_bottom(self, widths: Iterable[int]) -> str: columns.add_renderable(table) console.print(columns) - # console.save_html("box.html", inline_styles=True) + # console.save_svg("box.svg") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/color.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/color.py index 6bca2da92..ef2e895d7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/color.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/color.py @@ -29,6 +29,9 @@ class ColorSystem(IntEnum): def __repr__(self) -> str: return f"ColorSystem.{self.name}" + def __str__(self) -> str: + return repr(self) + class ColorType(IntEnum): """Type of color stored in Color class.""" @@ -310,7 +313,7 @@ class Color(NamedTuple): """A triplet of color components, if an RGB color.""" def __rich__(self) -> "Text": - """Dispays the actual color if Rich printed.""" + """Displays the actual color if Rich printed.""" from .style import Style from .text import Text diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/console.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/console.py index 93a10b0b5..f805f2dea 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/console.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/console.py @@ -34,6 +34,8 @@ cast, ) +from pip._vendor.rich._null_file import NULL_FILE + if sys.version_info >= (3, 8): from typing import Literal, Protocol, runtime_checkable else: @@ -104,7 +106,11 @@ class NoChange: _STD_STREAMS_OUTPUT = (_STDOUT_FILENO, _STDERR_FILENO) -_TERM_COLORS = {"256color": ColorSystem.EIGHT_BIT, "16color": ColorSystem.STANDARD} +_TERM_COLORS = { + "kitty": ColorSystem.EIGHT_BIT, + "256color": ColorSystem.EIGHT_BIT, + "16color": ColorSystem.STANDARD, +} class ConsoleDimensions(NamedTuple): @@ -516,7 +522,11 @@ def _is_jupyter() -> bool: # pragma: no cover return False ipython = get_ipython() # type: ignore[name-defined] shell = ipython.__class__.__name__ - if "google.colab" in str(ipython.__class__) or shell == "ZMQInteractiveShell": + if ( + "google.colab" in str(ipython.__class__) + or os.getenv("DATABRICKS_RUNTIME_VERSION") + or shell == "ZMQInteractiveShell" + ): return True # Jupyter notebook or qtconsole elif shell == "TerminalInteractiveShell": return False # Terminal running IPython @@ -697,7 +707,16 @@ def __init__( self._height = height self._color_system: Optional[ColorSystem] - self._force_terminal = force_terminal + + self._force_terminal = None + if force_terminal is not None: + self._force_terminal = force_terminal + else: + # If FORCE_COLOR env var has any value at all, we force terminal. + force_color = self._environ.get("FORCE_COLOR") + if force_color is not None: + self._force_terminal = True + self._file = file self.quiet = quiet self.stderr = stderr @@ -746,6 +765,8 @@ def file(self) -> IO[str]: """Get the file object to write to.""" file = self._file or (sys.stderr if self.stderr else sys.stdout) file = getattr(file, "rich_proxied_file", file) + if file is None: + file = NULL_FILE return file @file.setter @@ -1701,7 +1722,7 @@ def print_json( indent: Union[None, int, str] = 2, highlight: bool = True, skip_keys: bool = False, - ensure_ascii: bool = True, + ensure_ascii: bool = False, check_circular: bool = True, allow_nan: bool = True, default: Optional[Callable[[Any], Any]] = None, @@ -1996,9 +2017,11 @@ def _check_buffer(self) -> None: from pip._vendor.rich._win32_console import LegacyWindowsTerm from pip._vendor.rich._windows_renderer import legacy_windows_render - legacy_windows_render( - self._buffer[:], LegacyWindowsTerm(self.file) - ) + buffer = self._buffer[:] + if self.no_color and self._color_system: + buffer = list(Segment.remove_color(buffer)) + + legacy_windows_render(buffer, LegacyWindowsTerm(self.file)) else: # Either a non-std stream on legacy Windows, or modern Windows. text = self._render_buffer(self._buffer[:]) @@ -2238,18 +2261,24 @@ def export_svg( theme: Optional[TerminalTheme] = None, clear: bool = True, code_format: str = CONSOLE_SVG_FORMAT, + font_aspect_ratio: float = 0.61, + unique_id: Optional[str] = None, ) -> str: """ Generate an SVG from the console contents (requires record=True in Console constructor). Args: - path (str): The path to write the SVG to. - title (str): The title of the tab in the output image + title (str, optional): The title of the tab in the output image theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal clear (bool, optional): Clear record buffer after exporting. Defaults to ``True`` - code_format (str): Format string used to generate the SVG. Rich will inject a number of variables + code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables into the string in order to form the final SVG output. The default template used and the variables injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable. + font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format`` + string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font). + If you aren't specifying a different font inside ``code_format``, you probably don't need this. + unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node + ids). If not set, this defaults to a computed value based on the recorded content. """ from pip._vendor.rich.cells import cell_len @@ -2293,7 +2322,7 @@ def get_svg_style(style: Style) -> str: width = self.width char_height = 20 - char_width = char_height * 0.61 + char_width = char_height * font_aspect_ratio line_height = char_height * 1.22 margin_top = 1 @@ -2345,14 +2374,16 @@ def stringify(value: object) -> str: if clear: self._record_buffer.clear() - unique_id = "terminal-" + str( - zlib.adler32( - ("".join(segment.text for segment in segments)).encode( - "utf-8", "ignore" + if unique_id is None: + unique_id = "terminal-" + str( + zlib.adler32( + ("".join(repr(segment) for segment in segments)).encode( + "utf-8", + "ignore", + ) + + title.encode("utf-8", "ignore") ) - + title.encode("utf-8", "ignore") ) - ) y = 0 for y, line in enumerate(Segment.split_and_crop_lines(segments, length=width)): x = 0 @@ -2482,23 +2513,32 @@ def save_svg( theme: Optional[TerminalTheme] = None, clear: bool = True, code_format: str = CONSOLE_SVG_FORMAT, + font_aspect_ratio: float = 0.61, + unique_id: Optional[str] = None, ) -> None: """Generate an SVG file from the console contents (requires record=True in Console constructor). Args: path (str): The path to write the SVG to. - title (str): The title of the tab in the output image + title (str, optional): The title of the tab in the output image theme (TerminalTheme, optional): The ``TerminalTheme`` object to use to style the terminal clear (bool, optional): Clear record buffer after exporting. Defaults to ``True`` - code_format (str): Format string used to generate the SVG. Rich will inject a number of variables + code_format (str, optional): Format string used to generate the SVG. Rich will inject a number of variables into the string in order to form the final SVG output. The default template used and the variables injected by Rich can be found by inspecting the ``console.CONSOLE_SVG_FORMAT`` variable. + font_aspect_ratio (float, optional): The width to height ratio of the font used in the ``code_format`` + string. Defaults to 0.61, which is the width to height ratio of Fira Code (the default font). + If you aren't specifying a different font inside ``code_format``, you probably don't need this. + unique_id (str, optional): unique id that is used as the prefix for various elements (CSS styles, node + ids). If not set, this defaults to a computed value based on the recorded content. """ svg = self.export_svg( title=title, theme=theme, clear=clear, code_format=code_format, + font_aspect_ratio=font_aspect_ratio, + unique_id=unique_id, ) with open(path, "wt", encoding="utf-8") as write_file: write_file.write(svg) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/filesize.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/filesize.py index 61be47510..99f118e20 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/filesize.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/filesize.py @@ -2,7 +2,7 @@ """Functions for reporting filesizes. Borrowed from https://github.com/PyFilesystem/pyfilesystem2 The functions declared in this module should cover the different -usecases needed to generate a string representation of a file size +use cases needed to generate a string representation of a file size using several different units. Since there are many standards regarding file size units, three different functions have been implemented. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/json.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/json.py index 23583871e..21b642ab8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/json.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/json.py @@ -27,7 +27,7 @@ def __init__( indent: Union[None, int, str] = 2, highlight: bool = True, skip_keys: bool = False, - ensure_ascii: bool = True, + ensure_ascii: bool = False, check_circular: bool = True, allow_nan: bool = True, default: Optional[Callable[[Any], Any]] = None, @@ -56,7 +56,7 @@ def from_data( indent: Union[None, int, str] = 2, highlight: bool = True, skip_keys: bool = False, - ensure_ascii: bool = True, + ensure_ascii: bool = False, check_circular: bool = True, allow_nan: bool = True, default: Optional[Callable[[Any], Any]] = None, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/layout.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/layout.py index 1d704652e..849356ea9 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/layout.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/layout.py @@ -20,8 +20,8 @@ from .highlighter import ReprHighlighter from .panel import Panel from .pretty import Pretty -from .repr import rich_repr, Result from .region import Region +from .repr import Result, rich_repr from .segment import Segment from .style import StyleType @@ -162,7 +162,6 @@ def __init__( minimum_size: int = 1, ratio: int = 1, visible: bool = True, - height: Optional[int] = None, ) -> None: self._renderable = renderable or _Placeholder(self) self.size = size @@ -170,7 +169,6 @@ def __init__( self.ratio = ratio self.name = name self.visible = visible - self.height = height self.splitter: Splitter = self.splitters["column"]() self._children: List[Layout] = [] self._render_map: RenderMap = {} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/logging.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/logging.py index 58188fd8a..91368dda7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/logging.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/logging.py @@ -3,10 +3,12 @@ from logging import Handler, LogRecord from pathlib import Path from types import ModuleType -from typing import ClassVar, List, Optional, Iterable, Type, Union +from typing import ClassVar, Iterable, List, Optional, Type, Union + +from pip._vendor.rich._null_file import NullFile from . import get_console -from ._log_render import LogRender, FormatTimeCallable +from ._log_render import FormatTimeCallable, LogRender from .console import Console, ConsoleRenderable from .highlighter import Highlighter, ReprHighlighter from .text import Text @@ -158,16 +160,23 @@ def emit(self, record: LogRecord) -> None: log_renderable = self.render( record=record, traceback=traceback, message_renderable=message_renderable ) - try: - self.console.print(log_renderable) - except Exception: + if isinstance(self.console.file, NullFile): + # Handles pythonw, where stdout/stderr are null, and we return NullFile + # instance from Console.file. In this case, we still want to make a log record + # even though we won't be writing anything to a file. self.handleError(record) + else: + try: + self.console.print(log_renderable) + except Exception: + self.handleError(record) def render_message(self, record: LogRecord, message: str) -> "ConsoleRenderable": """Render message text in to Text. - record (LogRecord): logging Record. - message (str): String containing log message. + Args: + record (LogRecord): logging Record. + message (str): String containing log message. Returns: ConsoleRenderable: Renderable to display log message. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/panel.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/panel.py index fc2807c31..d522d80b5 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/panel.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/panel.py @@ -2,11 +2,12 @@ from .align import AlignMethod from .box import ROUNDED, Box +from .cells import cell_len from .jupyter import JupyterMixin from .measure import Measurement, measure_renderables from .padding import Padding, PaddingDimensions from .segment import Segment -from .style import StyleType +from .style import Style, StyleType from .text import Text, TextType if TYPE_CHECKING: @@ -149,9 +150,53 @@ def __rich_console__( safe_box: bool = console.safe_box if self.safe_box is None else self.safe_box box = self.box.substitute(options, safe=safe_box) + def align_text( + text: Text, width: int, align: str, character: str, style: Style + ) -> Text: + """Gets new aligned text. + + Args: + text (Text): Title or subtitle text. + width (int): Desired width. + align (str): Alignment. + character (str): Character for alignment. + style (Style): Border style + + Returns: + Text: New text instance + """ + text = text.copy() + text.truncate(width) + excess_space = width - cell_len(text.plain) + if excess_space: + if align == "left": + return Text.assemble( + text, + (character * excess_space, style), + no_wrap=True, + end="", + ) + elif align == "center": + left = excess_space // 2 + return Text.assemble( + (character * left, style), + text, + (character * (excess_space - left), style), + no_wrap=True, + end="", + ) + else: + return Text.assemble( + (character * excess_space, style), + text, + no_wrap=True, + end="", + ) + return text + title_text = self._title if title_text is not None: - title_text.style = border_style + title_text.stylize_before(border_style) child_width = ( width - 2 @@ -180,7 +225,13 @@ def __rich_console__( if title_text is None or width <= 4: yield Segment(box.get_top([width - 2]), border_style) else: - title_text.align(self.title_align, width - 4, character=box.top) + title_text = align_text( + title_text, + width - 4, + self.title_align, + box.top, + border_style, + ) yield Segment(box.top_left + box.top, border_style) yield from console.render(title_text, child_options.update_width(width - 4)) yield Segment(box.top + box.top_right, border_style) @@ -194,12 +245,18 @@ def __rich_console__( subtitle_text = self._subtitle if subtitle_text is not None: - subtitle_text.style = border_style + subtitle_text.stylize_before(border_style) if subtitle_text is None or width <= 4: yield Segment(box.get_bottom([width - 2]), border_style) else: - subtitle_text.align(self.subtitle_align, width - 4, character=box.bottom) + subtitle_text = align_text( + subtitle_text, + width - 4, + self.subtitle_align, + box.bottom, + border_style, + ) yield Segment(box.bottom_left + box.bottom, border_style) yield from console.render( subtitle_text, child_options.update_width(width - 4) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/pretty.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/pretty.py index 4a5ddaaf7..847b558c9 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/pretty.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/pretty.py @@ -120,6 +120,7 @@ def _ipy_display_hook( indent_guides: bool = False, max_length: Optional[int] = None, max_string: Optional[int] = None, + max_depth: Optional[int] = None, expand_all: bool = False, ) -> None: # needed here to prevent circular import: @@ -177,6 +178,7 @@ def _ipy_display_hook( indent_guides=indent_guides, max_length=max_length, max_string=max_string, + max_depth=max_depth, expand_all=expand_all, margin=12, ), @@ -202,6 +204,7 @@ def install( indent_guides: bool = False, max_length: Optional[int] = None, max_string: Optional[int] = None, + max_depth: Optional[int] = None, expand_all: bool = False, ) -> None: """Install automatic pretty printing in the Python REPL. @@ -214,6 +217,7 @@ def install( max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. Defaults to None. max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None. + max_depth (int, optional): Maximum depth of nested data structures, or None for no maximum. Defaults to None. expand_all (bool, optional): Expand all containers. Defaults to False. max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100. """ @@ -236,6 +240,7 @@ def display_hook(value: Any) -> None: indent_guides=indent_guides, max_length=max_length, max_string=max_string, + max_depth=max_depth, expand_all=expand_all, ), crop=crop, @@ -258,6 +263,7 @@ def __call__(self, value: Any) -> Any: indent_guides=indent_guides, max_length=max_length, max_string=max_string, + max_depth=max_depth, expand_all=expand_all, ) else: @@ -333,7 +339,7 @@ def __rich_console__( max_depth=self.max_depth, expand_all=self.expand_all, ) - pretty_text = Text( + pretty_text = Text.from_ansi( pretty_str, justify=self.justify or options.justify, overflow=self.overflow or options.overflow, @@ -630,6 +636,11 @@ def to_repr(obj: Any) -> str: def _traverse(obj: Any, root: bool = False, depth: int = 0) -> Node: """Walk the object depth first.""" + obj_id = id(obj) + if obj_id in visited_ids: + # Recursion detected + return Node(value_repr="...") + obj_type = type(obj) py_version = (sys.version_info.major, sys.version_info.minor) children: List[Node] @@ -667,6 +678,7 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]: pass if rich_repr_result is not None: + push_visited(obj_id) angular = getattr(obj.__rich_repr__, "angular", False) args = list(iter_rich_args(rich_repr_result)) class_name = obj.__class__.__name__ @@ -676,7 +688,10 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]: append = children.append if reached_max_depth: - node = Node(value_repr=f"...") + if angular: + node = Node(value_repr=f"<{class_name}...>") + else: + node = Node(value_repr=f"{class_name}(...)") else: if angular: node = Node( @@ -711,14 +726,16 @@ def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]: children=[], last=root, ) + pop_visited(obj_id) elif _is_attr_object(obj) and not fake_attributes: + push_visited(obj_id) children = [] append = children.append attr_fields = _get_attr_fields(obj) if attr_fields: if reached_max_depth: - node = Node(value_repr=f"...") + node = Node(value_repr=f"{obj.__class__.__name__}(...)") else: node = Node( open_brace=f"{obj.__class__.__name__}(", @@ -758,23 +775,18 @@ def iter_attrs() -> Iterable[ node = Node( value_repr=f"{obj.__class__.__name__}()", children=[], last=root ) - + pop_visited(obj_id) elif ( is_dataclass(obj) and not _safe_isinstance(obj, type) and not fake_attributes and (_is_dataclass_repr(obj) or py_version == (3, 6)) ): - obj_id = id(obj) - if obj_id in visited_ids: - # Recursion detected - return Node(value_repr="...") push_visited(obj_id) - children = [] append = children.append if reached_max_depth: - node = Node(value_repr=f"...") + node = Node(value_repr=f"{obj.__class__.__name__}(...)") else: node = Node( open_brace=f"{obj.__class__.__name__}(", @@ -792,42 +804,43 @@ def iter_attrs() -> Iterable[ child_node.key_separator = "=" append(child_node) - pop_visited(obj_id) + pop_visited(obj_id) elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj): + push_visited(obj_id) + class_name = obj.__class__.__name__ if reached_max_depth: - node = Node(value_repr="...") + # If we've reached the max depth, we still show the class name, but not its contents + node = Node( + value_repr=f"{class_name}(...)", + ) else: children = [] - class_name = obj.__class__.__name__ + append = children.append node = Node( open_brace=f"{class_name}(", close_brace=")", children=children, empty=f"{class_name}()", ) - append = children.append for last, (key, value) in loop_last(obj._asdict().items()): child_node = _traverse(value, depth=depth + 1) child_node.key_repr = key child_node.last = last child_node.key_separator = "=" append(child_node) + pop_visited(obj_id) elif _safe_isinstance(obj, _CONTAINERS): for container_type in _CONTAINERS: if _safe_isinstance(obj, container_type): obj_type = container_type break - obj_id = id(obj) - if obj_id in visited_ids: - # Recursion detected - return Node(value_repr="...") push_visited(obj_id) open_brace, close_brace, empty = _BRACES[obj_type](obj) if reached_max_depth: - node = Node(value_repr=f"...", last=root) + node = Node(value_repr=f"{open_brace}...{close_brace}") elif obj_type.__repr__ != type(obj).__repr__: node = Node(value_repr=to_repr(obj), last=root) elif obj: @@ -1007,4 +1020,10 @@ class StockKeepingUnit(NamedTuple): from pip._vendor.rich import print - print(Pretty(data, indent_guides=True, max_string=20)) + # print(Pretty(data, indent_guides=True, max_string=20)) + + class Thing: + def __repr__(self) -> str: + return "Hello\x1b[38;5;239m World!" + + print(Pretty(Thing())) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress.py index 92cfa8023..e7d163c13 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress.py @@ -129,7 +129,7 @@ def track( refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10. style (StyleType, optional): Style for the bar background. Defaults to "bar.back". complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete". - finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done". + finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished". pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse". update_period (float, optional): Minimum time (in seconds) between calls to update(). Defaults to 0.1. disable (bool, optional): Disable display of progress. @@ -216,6 +216,10 @@ def fileno(self) -> int: def isatty(self) -> bool: return self.handle.isatty() + @property + def mode(self) -> str: + return self.handle.mode + @property def name(self) -> str: return self.handle.name @@ -315,7 +319,7 @@ def wrap_file( refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10. style (StyleType, optional): Style for the bar background. Defaults to "bar.back". complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete". - finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done". + finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished". pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse". disable (bool, optional): Disable display of progress. Returns: @@ -440,7 +444,7 @@ def open( refresh_per_second (float): Number of times per second to refresh the progress information. Defaults to 10. style (StyleType, optional): Style for the bar background. Defaults to "bar.back". complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete". - finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done". + finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished". pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse". disable (bool, optional): Disable display of progress. encoding (str, optional): The encoding to use when reading in text mode. @@ -634,7 +638,7 @@ class BarColumn(ProgressColumn): bar_width (Optional[int], optional): Width of bar or None for full width. Defaults to 40. style (StyleType, optional): Style for the bar background. Defaults to "bar.back". complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete". - finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done". + finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished". pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse". """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress_bar.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress_bar.py index 9c3a4f25a..67361df2e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress_bar.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/progress_bar.py @@ -25,7 +25,7 @@ class ProgressBar(JupyterMixin): pulse (bool, optional): Enable pulse effect. Defaults to False. Will pulse if a None total was passed. style (StyleType, optional): Style for the bar background. Defaults to "bar.back". complete_style (StyleType, optional): Style for the completed bar. Defaults to "bar.complete". - finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.done". + finished_style (StyleType, optional): Style for a finished bar. Defaults to "bar.finished". pulse_style (StyleType, optional): Style for pulsing bars. Defaults to "bar.pulse". animation_time (Optional[float], optional): Time in seconds to use for animation, or None to use system time. """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/repr.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/repr.py index 36966e70f..72d1a7e30 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/repr.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/repr.py @@ -1,21 +1,18 @@ -from functools import partial import inspect -import sys - +from functools import partial from typing import ( Any, Callable, Iterable, List, Optional, - overload, - Union, Tuple, Type, TypeVar, + Union, + overload, ) - T = TypeVar("T") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/scope.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/scope.py index 6822b8ca5..c9d134cc3 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/scope.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/scope.py @@ -26,7 +26,7 @@ def render_scope( scope (Mapping): A mapping containing variable names and values. title (str, optional): Optional title. Defaults to None. sort_keys (bool, optional): Enable sorting of items. Defaults to True. - indent_guides (bool, optional): Enable indentaton guides. Defaults to False. + indent_guides (bool, optional): Enable indentation guides. Defaults to False. max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation. Defaults to None. max_string (int, optional): Maximum length of string before truncating, or None to disable. Defaults to None. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/style.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/style.py index b2e8aff71..ad388aadb 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/style.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/style.py @@ -188,8 +188,10 @@ def _make_color(color: Union[Color, str]) -> Color: ) self._link = link - self._link_id = f"{randint(0, 999999)}" if link else "" self._meta = None if meta is None else dumps(meta) + self._link_id = ( + f"{randint(0, 999999)}{hash(self._meta)}" if (link or meta) else "" + ) self._hash: Optional[int] = None self._null = not (self._set_attributes or color or bgcolor or link or meta) @@ -237,8 +239,8 @@ def from_meta(cls, meta: Optional[Dict[str, Any]]) -> "Style": style._set_attributes = 0 style._attributes = 0 style._link = None - style._link_id = "" style._meta = dumps(meta) + style._link_id = f"{randint(0, 999999)}{hash(style._meta)}" style._hash = None style._null = not (meta) return style diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/syntax.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/syntax.py index dace718c1..01bdd0439 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/syntax.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/syntax.py @@ -40,6 +40,7 @@ from pip._vendor.rich.padding import Padding, PaddingDimensions from ._loop import loop_first +from .cells import cell_len from .color import Color, blend_rgb from .console import Console, ConsoleOptions, JustifyMethod, RenderResult from .jupyter import JupyterMixin @@ -586,11 +587,21 @@ def _get_number_styles(self, console: Console) -> Tuple[Style, Style, Style]: def __rich_measure__( self, console: "Console", options: "ConsoleOptions" ) -> "Measurement": + _, right, _, left = Padding.unpack(self.padding) + padding = left + right if self.code_width is not None: - width = self.code_width + self._numbers_column_width + right + left + width = self.code_width + self._numbers_column_width + padding + 1 return Measurement(self._numbers_column_width, width) - return Measurement(self._numbers_column_width, options.max_width) + lines = self.code.splitlines() + width = ( + self._numbers_column_width + + padding + + (max(cell_len(line) for line in lines) if lines else 0) + ) + if self.line_numbers: + width += 1 + return Measurement(self._numbers_column_width, width) def __rich_console__( self, console: Console, options: ConsoleOptions diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/table.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/table.py index 8fc28ef2f..17409f2ee 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/table.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/table.py @@ -462,6 +462,12 @@ def add_cell(column: Column, renderable: "RenderableType") -> None: ) self.rows.append(Row(style=style, end_section=end_section)) + def add_section(self) -> None: + """Add a new section (draw a line after current row).""" + + if self.rows: + self.rows[-1].end_section = True + def __rich_console__( self, console: "Console", options: "ConsoleOptions" ) -> "RenderResult": diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/text.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/text.py index 12037d0cf..b14055aa7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/text.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/text.py @@ -450,7 +450,6 @@ def stylize( style (Union[str, Style]): Style instance or style definition to apply. start (int): Start offset (negative indexing is supported). Defaults to 0. end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None. - """ if style: length = len(self) @@ -465,6 +464,32 @@ def stylize( return self._spans.append(Span(start, min(length, end), style)) + def stylize_before( + self, + style: Union[str, Style], + start: int = 0, + end: Optional[int] = None, + ) -> None: + """Apply a style to the text, or a portion of the text. Styles will be applied before other styles already present. + + Args: + style (Union[str, Style]): Style instance or style definition to apply. + start (int): Start offset (negative indexing is supported). Defaults to 0. + end (Optional[int], optional): End offset (negative indexing is supported), or None for end of text. Defaults to None. + """ + if style: + length = len(self) + if start < 0: + start = length + start + if end is None: + end = length + if end < 0: + end = length + end + if start >= length or end <= start: + # Span not in text or not valid + return + self._spans.insert(0, Span(start, min(length, end), style)) + def apply_meta( self, meta: Dict[str, Any], start: int = 0, end: Optional[int] = None ) -> None: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/traceback.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/traceback.py index e5023c77a..1f481298f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/traceback.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/rich/traceback.py @@ -337,7 +337,7 @@ def extract( from pip._vendor.rich import _IMPORT_CWD def safe_str(_object: Any) -> str: - """Don't allow exceptions from __str__ to propegate.""" + """Don't allow exceptions from __str__ to propagate.""" try: return str(_object) except Exception: @@ -389,19 +389,17 @@ def safe_str(_object: Any) -> str: del stack.frames[:] cause = getattr(exc_value, "__cause__", None) - if cause and cause.__traceback__: + if cause: exc_type = cause.__class__ exc_value = cause + # __traceback__ can be None, e.g. for exceptions raised by the + # 'multiprocessing' module traceback = cause.__traceback__ is_cause = True continue cause = exc_value.__context__ - if ( - cause - and cause.__traceback__ - and not getattr(exc_value, "__suppress_context__", False) - ): + if cause and not getattr(exc_value, "__suppress_context__", False): exc_type = cause.__class__ exc_value = cause traceback = cause.__traceback__ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/_version.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/_version.py index 6fbc84b30..7c031661b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/_version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/_version.py @@ -1,2 +1,2 @@ # This file is protected via CODEOWNERS -__version__ = "1.26.12" +__version__ = "1.26.14" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py index 96339e90a..708739279 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/connectionpool.py @@ -862,7 +862,7 @@ def _is_ssl_error_message_from_http_proxy(ssl_error): ) # Check if we should retry the HTTP response. - has_retry_after = bool(response.getheader("Retry-After")) + has_retry_after = bool(response.headers.get("Retry-After")) if retries.is_retry(method, response.status, has_retry_after): try: retries = retries.increment(method, url, response=response, _pool=self) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py index 668538695..1717ee22c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/appengine.py @@ -224,7 +224,7 @@ def urlopen( ) # Check if we should retry the HTTP response. - has_retry_after = bool(http_response.getheader("Retry-After")) + has_retry_after = bool(http_response.headers.get("Retry-After")) if retries.is_retry(method, http_response.status, has_retry_after): retries = retries.increment(method, url, response=http_response, _pool=self) log.debug("Retry: %s", url) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py index 41a8fd174..471665754 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py @@ -69,7 +69,7 @@ def _new_conn(self): log.debug("Request headers: %s", headers) conn.request("GET", self.authurl, None, headers) res = conn.getresponse() - reshdr = dict(res.getheaders()) + reshdr = dict(res.headers) log.debug("Response status: %s %s", res.status, res.reason) log.debug("Response headers: %s", reshdr) log.debug("Response data: %s [...]", res.read(100)) @@ -101,7 +101,7 @@ def _new_conn(self): conn.request("GET", self.authurl, None, headers) res = conn.getresponse() log.debug("Response status: %s %s", res.status, res.reason) - log.debug("Response headers: %s", dict(res.getheaders())) + log.debug("Response headers: %s", dict(res.headers)) log.debug("Response data: %s [...]", res.read()[:100]) if res.status != 200: if res.status == 401: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py index 528764a03..19e4aa97c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py @@ -47,10 +47,10 @@ """ from __future__ import absolute_import +import OpenSSL.crypto import OpenSSL.SSL from cryptography import x509 from cryptography.hazmat.backends.openssl import backend as openssl_backend -from cryptography.hazmat.backends.openssl.x509 import _Certificate try: from cryptography.x509 import UnsupportedExtension @@ -228,9 +228,8 @@ def get_subj_alt_name(peer_cert): if hasattr(peer_cert, "to_cryptography"): cert = peer_cert.to_cryptography() else: - # This is technically using private APIs, but should work across all - # relevant versions before PyOpenSSL got a proper API for this. - cert = _Certificate(openssl_backend, peer_cert._x509) + der = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, peer_cert) + cert = x509.load_der_x509_certificate(der, openssl_backend) # We want to find the SAN extension. Ask Cryptography to locate it (it's # faster than looping in Python) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/response.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/response.py index 4969b70e3..8909f8454 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/response.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/response.py @@ -3,6 +3,7 @@ import io import logging import sys +import warnings import zlib from contextlib import contextmanager from socket import error as SocketError @@ -657,9 +658,21 @@ def from_httplib(ResponseCls, r, **response_kw): # Backwards-compatibility methods for http.client.HTTPResponse def getheaders(self): + warnings.warn( + "HTTPResponse.getheaders() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead access HTTPResponse.headers directly.", + category=DeprecationWarning, + stacklevel=2, + ) return self.headers def getheader(self, name, default=None): + warnings.warn( + "HTTPResponse.getheader() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead use HTTPResponse.headers.get(name, default).", + category=DeprecationWarning, + stacklevel=2, + ) return self.headers.get(name, default) # Backwards compatibility for http.cookiejar diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/retry.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/retry.py index 3398323fd..2490d5e5b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/retry.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/retry.py @@ -394,7 +394,7 @@ def parse_retry_after(self, retry_after): def get_retry_after(self, response): """Get the value of Retry-After in seconds.""" - retry_after = response.getheader("Retry-After") + retry_after = response.headers.get("Retry-After") if retry_after is None: return None diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/url.py b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/url.py index 86bd8b48a..d6d0bbcea 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/url.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/urllib3/util/url.py @@ -63,7 +63,7 @@ BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + IPV6_ADDRZ_PAT[2:-2] + "$") ZONE_ID_RE = re.compile("(" + ZONE_ID_PAT + r")\]$") -_HOST_PORT_PAT = ("^(%s|%s|%s)(?::([0-9]{0,5}))?$") % ( +_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % ( REG_NAME_PAT, IPV4_PAT, IPV6_ADDRZ_PAT, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/vendor.txt b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/vendor.txt index 9e9d4c11f..67452d89f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/vendor.txt +++ b/dependencies/windows_amd64/python/Lib/site-packages/pip/_vendor/vendor.txt @@ -1,18 +1,18 @@ CacheControl==0.12.11 # Make sure to update the license in pyproject.toml for this. -colorama==0.4.5 +colorama==0.4.6 distlib==0.3.6 -distro==1.7.0 +distro==1.8.0 msgpack==1.0.4 packaging==21.3 -pep517==0.13.0 -platformdirs==2.5.2 +platformdirs==2.6.2 pyparsing==3.0.9 -requests==2.28.1 - certifi==2022.09.24 - chardet==5.0.0 +pyproject-hooks==1.0.0 +requests==2.28.2 + certifi==2022.12.7 + chardet==5.1.0 idna==3.4 - urllib3==1.26.12 -rich==12.5.1 + urllib3==1.26.14 +rich==12.6.0 pygments==2.13.0 typing_extensions==4.4.0 resolvelib==0.8.1 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/__init__.py index d59226af9..a73a1df3b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/__init__.py @@ -12,6 +12,12 @@ .egg files, and unpacked .egg files. It can also work in a limited way with .zip files and with custom PEP 302 loaders that support the ``get_data()`` method. + +This module is deprecated. Users are directed to +`importlib.resources `_ +and +`importlib.metadata `_ +instead. """ import sys @@ -34,7 +40,6 @@ import errno import tempfile import textwrap -import itertools import inspect import ntpath import posixpath @@ -54,8 +59,10 @@ # capture these to bypass sandboxing from os import utime + try: from os import mkdir, rename, unlink + WRITE_SUPPORT = True except ImportError: # no write support, probably under GAE @@ -66,6 +73,7 @@ try: import importlib.machinery as importlib_machinery + # access attribute to force import under delayed import mechanisms. importlib_machinery.__name__ except ImportError: @@ -77,8 +85,9 @@ join_continuation, ) -from pkg_resources.extern import appdirs +from pkg_resources.extern import platformdirs from pkg_resources.extern import packaging + __import__('pkg_resources.extern.packaging.version') __import__('pkg_resources.extern.packaging.specifiers') __import__('pkg_resources.extern.packaging.requirements') @@ -109,6 +118,12 @@ _namespace_packages = None +warnings.warn("pkg_resources is deprecated as an API", DeprecationWarning) + + +_PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) + + class PEP440Warning(RuntimeWarning): """ Used when there is an issue with a version or specifier not complying with @@ -116,16 +131,7 @@ class PEP440Warning(RuntimeWarning): """ -def parse_version(v): - try: - return packaging.version.Version(v) - except packaging.version.InvalidVersion: - warnings.warn( - f"{v} is an invalid version and will not be supported in " - "a future release", - PkgResourcesDeprecationWarning, - ) - return packaging.version.LegacyVersion(v) +parse_version = packaging.version.Version _state_vars = {} @@ -197,51 +203,87 @@ def get_supported_platform(): __all__ = [ # Basic resource access and distribution/entry point discovery - 'require', 'run_script', 'get_provider', 'get_distribution', - 'load_entry_point', 'get_entry_map', 'get_entry_info', + 'require', + 'run_script', + 'get_provider', + 'get_distribution', + 'load_entry_point', + 'get_entry_map', + 'get_entry_info', 'iter_entry_points', - 'resource_string', 'resource_stream', 'resource_filename', - 'resource_listdir', 'resource_exists', 'resource_isdir', - + 'resource_string', + 'resource_stream', + 'resource_filename', + 'resource_listdir', + 'resource_exists', + 'resource_isdir', # Environmental control - 'declare_namespace', 'working_set', 'add_activation_listener', - 'find_distributions', 'set_extraction_path', 'cleanup_resources', + 'declare_namespace', + 'working_set', + 'add_activation_listener', + 'find_distributions', + 'set_extraction_path', + 'cleanup_resources', 'get_default_cache', - # Primary implementation classes - 'Environment', 'WorkingSet', 'ResourceManager', - 'Distribution', 'Requirement', 'EntryPoint', - + 'Environment', + 'WorkingSet', + 'ResourceManager', + 'Distribution', + 'Requirement', + 'EntryPoint', # Exceptions - 'ResolutionError', 'VersionConflict', 'DistributionNotFound', - 'UnknownExtra', 'ExtractionError', - + 'ResolutionError', + 'VersionConflict', + 'DistributionNotFound', + 'UnknownExtra', + 'ExtractionError', # Warnings 'PEP440Warning', - # Parsing functions and string utilities - 'parse_requirements', 'parse_version', 'safe_name', 'safe_version', - 'get_platform', 'compatible_platforms', 'yield_lines', 'split_sections', - 'safe_extra', 'to_filename', 'invalid_marker', 'evaluate_marker', - + 'parse_requirements', + 'parse_version', + 'safe_name', + 'safe_version', + 'get_platform', + 'compatible_platforms', + 'yield_lines', + 'split_sections', + 'safe_extra', + 'to_filename', + 'invalid_marker', + 'evaluate_marker', # filesystem utilities - 'ensure_directory', 'normalize_path', - + 'ensure_directory', + 'normalize_path', # Distribution "precedence" constants - 'EGG_DIST', 'BINARY_DIST', 'SOURCE_DIST', 'CHECKOUT_DIST', 'DEVELOP_DIST', - + 'EGG_DIST', + 'BINARY_DIST', + 'SOURCE_DIST', + 'CHECKOUT_DIST', + 'DEVELOP_DIST', # "Provider" interfaces, implementations, and registration/lookup APIs - 'IMetadataProvider', 'IResourceProvider', 'FileMetadata', - 'PathMetadata', 'EggMetadata', 'EmptyProvider', 'empty_provider', - 'NullProvider', 'EggProvider', 'DefaultProvider', 'ZipProvider', - 'register_finder', 'register_namespace_handler', 'register_loader_type', - 'fixup_namespace_packages', 'get_importer', - + 'IMetadataProvider', + 'IResourceProvider', + 'FileMetadata', + 'PathMetadata', + 'EggMetadata', + 'EmptyProvider', + 'empty_provider', + 'NullProvider', + 'EggProvider', + 'DefaultProvider', + 'ZipProvider', + 'register_finder', + 'register_namespace_handler', + 'register_loader_type', + 'fixup_namespace_packages', + 'get_importer', # Warnings 'PkgResourcesDeprecationWarning', - # Deprecated/backward compatibility only - 'run_main', 'AvailableDistributions', + 'run_main', + 'AvailableDistributions', ] @@ -300,8 +342,10 @@ def required_by(self): class DistributionNotFound(ResolutionError): """A requested distribution was not found""" - _template = ("The '{self.req}' distribution was not found " - "and is required by {self.requirers_str}") + _template = ( + "The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}" + ) @property def req(self): @@ -395,7 +439,8 @@ def get_build_platform(): version = _macos_vers() machine = os.uname()[4].replace(" ", "_") return "macosx-%d.%d-%s" % ( - int(version[0]), int(version[1]), + int(version[0]), + int(version[1]), _macos_arch(machine), ) except ValueError: @@ -436,15 +481,18 @@ def compatible_platforms(provided, required): if provDarwin: dversion = int(provDarwin.group(1)) macosversion = "%s.%s" % (reqMac.group(1), reqMac.group(2)) - if dversion == 7 and macosversion >= "10.3" or \ - dversion == 8 and macosversion >= "10.4": + if ( + dversion == 7 + and macosversion >= "10.3" + or dversion == 8 + and macosversion >= "10.4" + ): return True # egg isn't macOS or legacy darwin return False # are they the same major version and machine type? - if provMac.group(1) != reqMac.group(1) or \ - provMac.group(3) != reqMac.group(3): + if provMac.group(1) != reqMac.group(1) or provMac.group(3) != reqMac.group(3): return False # is the required OS major update >= the provided one? @@ -506,8 +554,8 @@ def get_metadata(name): def get_metadata_lines(name): """Yield named metadata resource as list of non-blank non-comment lines - Leading and trailing whitespace is stripped from each line, and lines - with ``#`` as the first non-blank character are omitted.""" + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" def metadata_isdir(name): """Is the named metadata a directory? (like ``os.path.isdir()``)""" @@ -720,9 +768,14 @@ def add(self, dist, entry=None, insert=True, replace=False): keys2.append(dist.key) self._added_new(dist) - # FIXME: 'WorkingSet.resolve' is too complex (11) - def resolve(self, requirements, env=None, installer=None, # noqa: C901 - replace_conflicting=False, extras=None): + def resolve( + self, + requirements, + env=None, + installer=None, + replace_conflicting=False, + extras=None, + ): """List all distributions needed to (recursively) meet `requirements` `requirements` must be a sequence of ``Requirement`` objects. `env`, @@ -771,33 +824,9 @@ def resolve(self, requirements, env=None, installer=None, # noqa: C901 if not req_extras.markers_pass(req, extras): continue - dist = best.get(req.key) - if dist is None: - # Find the best distribution and add it to the map - dist = self.by_key.get(req.key) - if dist is None or (dist not in req and replace_conflicting): - ws = self - if env is None: - if dist is None: - env = Environment(self.entries) - else: - # Use an empty environment and workingset to avoid - # any further conflicts with the conflicting - # distribution - env = Environment([]) - ws = WorkingSet([]) - dist = best[req.key] = env.best_match( - req, ws, installer, - replace_conflicting=replace_conflicting - ) - if dist is None: - requirers = required_by.get(req, None) - raise DistributionNotFound(req, requirers) - to_activate.append(dist) - if dist not in req: - # Oops, the "best" so far conflicts with a dependency - dependent_req = required_by[req] - raise VersionConflict(dist, req).with_context(dependent_req) + dist = self._resolve_dist( + req, best, replace_conflicting, env, installer, required_by, to_activate + ) # push the new requirements onto the stack new_requirements = dist.requires(req.extras)[::-1] @@ -813,8 +842,38 @@ def resolve(self, requirements, env=None, installer=None, # noqa: C901 # return list of distros to activate return to_activate - def find_plugins( - self, plugin_env, full_env=None, installer=None, fallback=True): + def _resolve_dist( + self, req, best, replace_conflicting, env, installer, required_by, to_activate + ): + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + return dist + + def find_plugins(self, plugin_env, full_env=None, installer=None, fallback=True): """Find all activatable distributions in `plugin_env` Example usage:: @@ -867,9 +926,7 @@ def find_plugins( list(map(shadow_set.add, self)) for project_name in plugin_projects: - for dist in plugin_env[project_name]: - req = [dist.as_requirement()] try: @@ -933,8 +990,11 @@ def _added_new(self, dist): def __getstate__(self): return ( - self.entries[:], self.entry_keys.copy(), self.by_key.copy(), - self.normalized_to_canonical_keys.copy(), self.callbacks[:] + self.entries[:], + self.entry_keys.copy(), + self.by_key.copy(), + self.normalized_to_canonical_keys.copy(), + self.callbacks[:], ) def __setstate__(self, e_k_b_n_c): @@ -970,8 +1030,8 @@ class Environment: """Searchable snapshot of distributions on a search path""" def __init__( - self, search_path=None, platform=get_supported_platform(), - python=PY_MAJOR): + self, search_path=None, platform=get_supported_platform(), python=PY_MAJOR + ): """Snapshot distributions available on a search path Any distributions found on `search_path` are added to the environment. @@ -1038,16 +1098,14 @@ def __getitem__(self, project_name): return self._distmap.get(distribution_key, []) def add(self, dist): - """Add `dist` if we ``can_add()`` it and it has not already been added - """ + """Add `dist` if we ``can_add()`` it and it has not already been added""" if self.can_add(dist) and dist.has_version(): dists = self._distmap.setdefault(dist.key, []) if dist not in dists: dists.append(dist) dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) - def best_match( - self, req, working_set, installer=None, replace_conflicting=False): + def best_match(self, req, working_set, installer=None, replace_conflicting=False): """Find distribution best matching `req` and usable on `working_set` This calls the ``find(req)`` method of the `working_set` to see if a @@ -1134,6 +1192,7 @@ class ExtractionError(RuntimeError): class ResourceManager: """Manage resource extraction and packages""" + extraction_path = None def __init__(self): @@ -1145,9 +1204,7 @@ def resource_exists(self, package_or_requirement, resource_name): def resource_isdir(self, package_or_requirement, resource_name): """Is the named resource an existing directory?""" - return get_provider(package_or_requirement).resource_isdir( - resource_name - ) + return get_provider(package_or_requirement).resource_isdir(resource_name) def resource_filename(self, package_or_requirement, resource_name): """Return a true filesystem path for specified resource""" @@ -1169,9 +1226,7 @@ def resource_string(self, package_or_requirement, resource_name): def resource_listdir(self, package_or_requirement, resource_name): """List the contents of the named resource directory""" - return get_provider(package_or_requirement).resource_listdir( - resource_name - ) + return get_provider(package_or_requirement).resource_listdir(resource_name) def extraction_error(self): """Give an error message for problems extracting file(s)""" @@ -1179,7 +1234,8 @@ def extraction_error(self): old_exc = sys.exc_info()[1] cache_path = self.extraction_path or get_default_cache() - tmpl = textwrap.dedent(""" + tmpl = textwrap.dedent( + """ Can't extract file(s) to egg cache The following error occurred while trying to extract file(s) @@ -1194,7 +1250,8 @@ def extraction_error(self): Perhaps your account does not have write access to this directory? You can change the cache directory by setting the PYTHON_EGG_CACHE environment variable to point to an accessible directory. - """).lstrip() + """ + ).lstrip() err = ExtractionError(tmpl.format(**locals())) err.manager = self err.cache_path = cache_path @@ -1293,9 +1350,7 @@ def set_extraction_path(self, path): ``cleanup_resources()``.) """ if self.cached_files: - raise ValueError( - "Can't change extraction path, files already extracted" - ) + raise ValueError("Can't change extraction path, files already extracted") self.extraction_path = path @@ -1319,9 +1374,8 @@ def get_default_cache(): or a platform-relevant user cache dir for an app named "Python-Eggs". """ - return ( - os.environ.get('PYTHON_EGG_CACHE') - or appdirs.user_cache_dir(appname='Python-Eggs') + return os.environ.get('PYTHON_EGG_CACHE') or platformdirs.user_cache_dir( + appname='Python-Eggs' ) @@ -1345,6 +1399,38 @@ def safe_version(version): return re.sub('[^A-Za-z0-9.]+', '-', version) +def _forgiving_version(version): + """Fallback when ``safe_version`` is not safe enough + >>> parse_version(_forgiving_version('0.23ubuntu1')) + + >>> parse_version(_forgiving_version('0.23-')) + + >>> parse_version(_forgiving_version('0.-_')) + + >>> parse_version(_forgiving_version('42.+?1')) + + >>> parse_version(_forgiving_version('hello world')) + + """ + version = version.replace(' ', '.') + match = _PEP440_FALLBACK.search(version) + if match: + safe = match["safe"] + rest = version[len(safe):] + else: + safe = "0" + rest = version + local = f"sanitized.{_safe_segment(rest)}".strip(".") + return f"{safe}.dev0+{local}" + + +def _safe_segment(segment): + """Convert an arbitrary string into a safe segment""" + segment = re.sub('[^A-Za-z0-9.]+', '-', segment) + segment = re.sub('-[^A-Za-z0-9]+', '-', segment) + return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-") + + def safe_extra(extra): """Convert an arbitrary string to a standard 'extra' name @@ -1458,8 +1544,9 @@ def run_script(self, script_name, namespace): script = 'scripts/' + script_name if not self.has_metadata(script): raise ResolutionError( - "Script {script!r} not found in metadata at {self.egg_info!r}" - .format(**locals()), + "Script {script!r} not found in metadata at {self.egg_info!r}".format( + **locals() + ), ) script_text = self.get_metadata(script).replace('\r\n', '\n') script_text = script_text.replace('\r', '\n') @@ -1472,8 +1559,12 @@ def run_script(self, script_name, namespace): exec(code, namespace, namespace) else: from linecache import cache + cache[script_filename] = ( - len(script_text), 0, script_text.split('\n'), script_filename + len(script_text), + 0, + script_text.split('\n'), + script_filename, ) script_code = compile(script_text, script_filename, 'exec') exec(script_code, namespace, namespace) @@ -1553,9 +1644,9 @@ def _validate_resource_path(path): AttributeError: ... """ invalid = ( - os.path.pardir in path.split(posixpath.sep) or - posixpath.isabs(path) or - ntpath.isabs(path) + os.path.pardir in path.split(posixpath.sep) + or posixpath.isabs(path) + or ntpath.isabs(path) ) if not invalid: return @@ -1637,7 +1728,10 @@ def _get(self, path): @classmethod def _register(cls): - loader_names = 'SourceFileLoader', 'SourcelessFileLoader', + loader_names = ( + 'SourceFileLoader', + 'SourcelessFileLoader', + ) for name in loader_names: loader_cls = getattr(importlib_machinery, name, type(None)) register_loader_type(loader_cls, cls) @@ -1697,6 +1791,7 @@ class MemoizedZipManifests(ZipManifests): """ Memoized zipfile manifests. """ + manifest_mod = collections.namedtuple('manifest_mod', 'manifest mtime') def load(self, path): @@ -1730,20 +1825,16 @@ def _zipinfo_name(self, fspath): if fspath == self.loader.archive: return '' if fspath.startswith(self.zip_pre): - return fspath[len(self.zip_pre):] - raise AssertionError( - "%s is not a subpath of %s" % (fspath, self.zip_pre) - ) + return fspath[len(self.zip_pre) :] + raise AssertionError("%s is not a subpath of %s" % (fspath, self.zip_pre)) def _parts(self, zip_path): # Convert a zipfile subpath into an egg-relative path part list. # pseudo-fs path fspath = self.zip_pre + zip_path if fspath.startswith(self.egg_root + os.sep): - return fspath[len(self.egg_root) + 1:].split(os.sep) - raise AssertionError( - "%s is not a subpath of %s" % (fspath, self.egg_root) - ) + return fspath[len(self.egg_root) + 1 :].split(os.sep) + raise AssertionError("%s is not a subpath of %s" % (fspath, self.egg_root)) @property def zipinfo(self): @@ -1773,25 +1864,20 @@ def _get_date_and_size(zip_stat): # FIXME: 'ZipProvider._extract_resource' is too complex (12) def _extract_resource(self, manager, zip_path): # noqa: C901 - if zip_path in self._index(): for name in self._index()[zip_path]: - last = self._extract_resource( - manager, os.path.join(zip_path, name) - ) + last = self._extract_resource(manager, os.path.join(zip_path, name)) # return the extracted directory name return os.path.dirname(last) timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) if not WRITE_SUPPORT: - raise IOError('"os.rename" and "os.unlink" are not supported ' - 'on this platform') - try: - - real_path = manager.get_cache_path( - self.egg_name, self._parts(zip_path) + raise IOError( + '"os.rename" and "os.unlink" are not supported ' 'on this platform' ) + try: + real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path)) if self._is_current(real_path, zip_path): return real_path @@ -2027,70 +2113,21 @@ def find_nothing(importer, path_item, only=False): register_finder(object, find_nothing) -def _by_version_descending(names): - """ - Given a list of filenames, return them in descending order - by version number. - - >>> names = 'bar', 'foo', 'Python-2.7.10.egg', 'Python-2.7.2.egg' - >>> _by_version_descending(names) - ['Python-2.7.10.egg', 'Python-2.7.2.egg', 'bar', 'foo'] - >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.egg' - >>> _by_version_descending(names) - ['Setuptools-1.2.3.egg', 'Setuptools-1.2.3b1.egg'] - >>> names = 'Setuptools-1.2.3b1.egg', 'Setuptools-1.2.3.post1.egg' - >>> _by_version_descending(names) - ['Setuptools-1.2.3.post1.egg', 'Setuptools-1.2.3b1.egg'] - """ - def try_parse(name): - """ - Attempt to parse as a version or return a null version. - """ - try: - return packaging.version.Version(name) - except Exception: - return packaging.version.Version('0') - - def _by_version(name): - """ - Parse each component of the filename - """ - name, ext = os.path.splitext(name) - parts = itertools.chain(name.split('-'), [ext]) - return [try_parse(part) for part in parts] - - return sorted(names, key=_by_version, reverse=True) - - def find_on_path(importer, path_item, only=False): """Yield distributions accessible on a sys.path directory""" path_item = _normalize_cached(path_item) if _is_unpacked_egg(path_item): yield Distribution.from_filename( - path_item, metadata=PathMetadata( - path_item, os.path.join(path_item, 'EGG-INFO') - ) + path_item, + metadata=PathMetadata(path_item, os.path.join(path_item, 'EGG-INFO')), ) return - entries = ( - os.path.join(path_item, child) - for child in safe_listdir(path_item) - ) - - # for performance, before sorting by version, - # screen entries for only those that will yield - # distributions - filtered = ( - entry - for entry in entries - if dist_factory(path_item, entry, only) - ) + entries = (os.path.join(path_item, child) for child in safe_listdir(path_item)) # scan for .egg and .egg-info in directory - path_item_entries = _by_version_descending(filtered) - for entry in path_item_entries: + for entry in sorted(entries): fullpath = os.path.join(path_item, entry) factory = dist_factory(path_item, entry, only) for dist in factory(fullpath): @@ -2101,19 +2138,18 @@ def dist_factory(path_item, entry, only): """Return a dist_factory for the given entry.""" lower = entry.lower() is_egg_info = lower.endswith('.egg-info') - is_dist_info = ( - lower.endswith('.dist-info') and - os.path.isdir(os.path.join(path_item, entry)) + is_dist_info = lower.endswith('.dist-info') and os.path.isdir( + os.path.join(path_item, entry) ) is_meta = is_egg_info or is_dist_info return ( distributions_from_metadata - if is_meta else - find_distributions - if not only and _is_egg_path(entry) else - resolve_egg_link - if not only and lower.endswith('.egg-link') else - NoDists() + if is_meta + else find_distributions + if not only and _is_egg_path(entry) + else resolve_egg_link + if not only and lower.endswith('.egg-link') + else NoDists() ) @@ -2125,6 +2161,7 @@ class NoDists: >>> list(NoDists()('anything')) [] """ + def __bool__(self): return False @@ -2159,7 +2196,10 @@ def distributions_from_metadata(path): metadata = FileMetadata(path) entry = os.path.basename(path) yield Distribution.from_location( - root, entry, metadata, precedence=DEVELOP_DIST, + root, + entry, + metadata, + precedence=DEVELOP_DIST, ) @@ -2181,17 +2221,16 @@ def resolve_egg_link(path): """ referenced_paths = non_empty_lines(path) resolved_paths = ( - os.path.join(os.path.dirname(path), ref) - for ref in referenced_paths + os.path.join(os.path.dirname(path), ref) for ref in referenced_paths ) dist_groups = map(find_distributions, resolved_paths) return next(dist_groups, ()) -register_finder(pkgutil.ImpImporter, find_on_path) +if hasattr(pkgutil, 'ImpImporter'): + register_finder(pkgutil.ImpImporter, find_on_path) -if hasattr(importlib_machinery, 'FileFinder'): - register_finder(importlib_machinery.FileFinder, find_on_path) +register_finder(importlib_machinery.FileFinder, find_on_path) _declare_state('dict', _namespace_handlers={}) _declare_state('dict', _namespace_packages={}) @@ -2289,6 +2328,15 @@ def position_in_sys_path(path): def declare_namespace(packageName): """Declare that package 'packageName' is a namespace package""" + msg = ( + f"Deprecated call to `pkg_resources.declare_namespace({packageName!r})`.\n" + "Implementing implicit namespace packages (as specified in PEP 420) " + "is preferred to `pkg_resources.declare_namespace`. " + "See https://setuptools.pypa.io/en/latest/references/" + "keywords.html#keyword-namespace-packages" + ) + warnings.warn(msg, DeprecationWarning, stacklevel=2) + _imp.acquire_lock() try: if packageName in _namespace_packages: @@ -2345,11 +2393,11 @@ def file_ns_handler(importer, path_item, packageName, module): return subpath -register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) -register_namespace_handler(zipimport.zipimporter, file_ns_handler) +if hasattr(pkgutil, 'ImpImporter'): + register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) -if hasattr(importlib_machinery, 'FileFinder'): - register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) +register_namespace_handler(zipimport.zipimporter, file_ns_handler) +register_namespace_handler(importlib_machinery.FileFinder, file_ns_handler) def null_ns_handler(importer, path_item, packageName, module): @@ -2361,8 +2409,7 @@ def null_ns_handler(importer, path_item, packageName, module): def normalize_path(filename): """Normalize a file/dir name for comparison purposes""" - return os.path.normcase(os.path.realpath(os.path.normpath( - _cygwin_patch(filename)))) + return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) def _cygwin_patch(filename): # pragma: nocover @@ -2393,9 +2440,9 @@ def _is_egg_path(path): def _is_zip_egg(path): return ( - path.lower().endswith('.egg') and - os.path.isfile(path) and - zipfile.is_zipfile(path) + path.lower().endswith('.egg') + and os.path.isfile(path) + and zipfile.is_zipfile(path) ) @@ -2403,9 +2450,8 @@ def _is_unpacked_egg(path): """ Determine if given path appears to be an unpacked egg. """ - return ( - path.lower().endswith('.egg') and - os.path.isfile(os.path.join(path, 'EGG-INFO', 'PKG-INFO')) + return path.lower().endswith('.egg') and os.path.isfile( + os.path.join(path, 'EGG-INFO', 'PKG-INFO') ) @@ -2569,8 +2615,10 @@ def _version_from_file(lines): Given an iterable of lines from a Metadata file, return the value of the Version field, if present, or None otherwise. """ + def is_version_line(line): return line.lower().startswith('version:') + version_lines = filter(is_version_line, lines) line = next(iter(version_lines), '') _, _, value = line.partition(':') @@ -2579,12 +2627,19 @@ def is_version_line(line): class Distribution: """Wrap an actual or potential sys.path entry w/metadata""" + PKG_INFO = 'PKG-INFO' def __init__( - self, location=None, metadata=None, project_name=None, - version=None, py_version=PY_MAJOR, platform=None, - precedence=EGG_DIST): + self, + location=None, + metadata=None, + project_name=None, + version=None, + py_version=PY_MAJOR, + platform=None, + precedence=EGG_DIST, + ): self.project_name = safe_name(project_name or 'Unknown') if version is not None: self._version = safe_version(version) @@ -2607,8 +2662,13 @@ def from_location(cls, location, basename, metadata=None, **kw): 'name', 'ver', 'pyver', 'plat' ) return cls( - location, metadata, project_name=project_name, version=version, - py_version=py_version, platform=platform, **kw + location, + metadata, + project_name=project_name, + version=version, + py_version=py_version, + platform=platform, + **kw, )._reload_version() def _reload_version(self): @@ -2617,7 +2677,7 @@ def _reload_version(self): @property def hashcmp(self): return ( - self.parsed_version, + self._forgiving_parsed_version, self.precedence, self.key, self.location, @@ -2664,35 +2724,42 @@ def key(self): @property def parsed_version(self): if not hasattr(self, "_parsed_version"): - self._parsed_version = parse_version(self.version) + try: + self._parsed_version = parse_version(self.version) + except packaging.version.InvalidVersion as ex: + info = f"(package: {self.project_name})" + if hasattr(ex, "add_note"): + ex.add_note(info) # PEP 678 + raise + raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None return self._parsed_version - def _warn_legacy_version(self): - LV = packaging.version.LegacyVersion - is_legacy = isinstance(self._parsed_version, LV) - if not is_legacy: - return + @property + def _forgiving_parsed_version(self): + try: + return self.parsed_version + except packaging.version.InvalidVersion as ex: + self._parsed_version = parse_version(_forgiving_version(self.version)) - # While an empty version is technically a legacy version and - # is not a valid PEP 440 version, it's also unlikely to - # actually come from someone and instead it is more likely that - # it comes from setuptools attempting to parse a filename and - # including it in the list. So for that we'll gate this warning - # on if the version is anything at all or not. - if not self.version: - return + notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 + msg = f"""!!\n\n + ************************************************************************* + {str(ex)}\n{notes} + + This is a long overdue deprecation. + For the time being, `pkg_resources` will use `{self._parsed_version}` + as a replacement to avoid breaking existing environments, + but no future compatibility is guaranteed. - tmpl = textwrap.dedent(""" - '{project_name} ({version})' is being parsed as a legacy, - non PEP 440, - version. You may find odd behavior and sort order. - In particular it will be sorted as less than 0.0. It - is recommended to migrate to PEP 440 compatible - versions. - """).strip().replace('\n', ' ') + If you maintain package {self.project_name} you should implement + the relevant changes to adequate the project to PEP 440 immediately. + ************************************************************************* + \n\n!! + """ + warnings.warn(msg, DeprecationWarning) - warnings.warn(tmpl.format(**vars(self)), PEP440Warning) + return self._parsed_version @property def version(self): @@ -2702,9 +2769,9 @@ def version(self): version = self._get_version() if version is None: path = self._get_metadata_path_for_display(self.PKG_INFO) - msg = ( - "Missing 'Version:' header and/or {} file at path: {}" - ).format(self.PKG_INFO, path) + msg = ("Missing 'Version:' header and/or {} file at path: {}").format( + self.PKG_INFO, path + ) raise ValueError(msg, self) from e return version @@ -2733,8 +2800,7 @@ def _filter_extras(dm): reqs = dm.pop(extra) new_extra, _, marker = extra.partition(':') fails_marker = marker and ( - invalid_marker(marker) - or not evaluate_marker(marker) + invalid_marker(marker) or not evaluate_marker(marker) ) if fails_marker: reqs = [] @@ -2806,8 +2872,9 @@ def activate(self, path=None, replace=False): def egg_name(self): """Return what this distribution's standard .egg filename should be""" filename = "%s-%s-py%s" % ( - to_filename(self.project_name), to_filename(self.version), - self.py_version or PY_MAJOR + to_filename(self.project_name), + to_filename(self.version), + self.py_version or PY_MAJOR, ) if self.platform: @@ -2837,17 +2904,13 @@ def __getattr__(self, attr): def __dir__(self): return list( set(super(Distribution, self).__dir__()) - | set( - attr for attr in self._provider.__dir__() - if not attr.startswith('_') - ) + | set(attr for attr in self._provider.__dir__() if not attr.startswith('_')) ) @classmethod def from_filename(cls, filename, metadata=None, **kw): return cls.from_location( - _normalize_cached(filename), os.path.basename(filename), metadata, - **kw + _normalize_cached(filename), os.path.basename(filename), metadata, **kw ) def as_requirement(self): @@ -2959,14 +3022,18 @@ def check_version_conflict(self): nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) loc = normalize_path(self.location) for modname in self._get_metadata('top_level.txt'): - if (modname not in sys.modules or modname in nsp - or modname in _namespace_packages): + if ( + modname not in sys.modules + or modname in nsp + or modname in _namespace_packages + ): continue if modname in ('pkg_resources', 'setuptools', 'site'): continue fn = getattr(sys.modules[modname], '__file__', None) - if fn and (normalize_path(fn).startswith(loc) or - fn.startswith(self.location)): + if fn and ( + normalize_path(fn).startswith(loc) or fn.startswith(self.location) + ): continue issue_warning( "Module %s was already imported from %s, but %s is being added" @@ -3018,6 +3085,7 @@ class DistInfoDistribution(Distribution): Wrap an actual or potential sys.path entry w/metadata, .dist-info style. """ + PKG_INFO = 'METADATA' EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") @@ -3103,8 +3171,7 @@ def __init__(self, requirement_string): self.unsafe_name = self.name project_name = safe_name(self.name) self.project_name, self.key = project_name, project_name.lower() - self.specs = [ - (spec.operator, spec.version) for spec in self.specifier] + self.specs = [(spec.operator, spec.version) for spec in self.specifier] self.extras = tuple(map(safe_extra, self.extras)) self.hashCmp = ( self.key, @@ -3116,10 +3183,7 @@ def __init__(self, requirement_string): self.__hash = hash(self.hashCmp) def __eq__(self, other): - return ( - isinstance(other, Requirement) and - self.hashCmp == other.hashCmp - ) + return isinstance(other, Requirement) and self.hashCmp == other.hashCmp def __ne__(self, other): return not self == other @@ -3144,7 +3208,7 @@ def __repr__(self): @staticmethod def parse(s): - req, = parse_requirements(s) + (req,) = parse_requirements(s) return req @@ -3282,10 +3346,7 @@ def _initialize_master_working_set(): # ensure that all distributions added to the working set in the future # (e.g. by calling ``require()``) will get activated as well, # with higher priority (replace=True). - tuple( - dist.activate(replace=False) - for dist in working_set - ) + tuple(dist.activate(replace=False) for dist in working_set) add_activation_listener( lambda dist: dist.activate(replace=True), existing=False, diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/appdirs.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/appdirs.py deleted file mode 100644 index ae67001af..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/appdirs.py +++ /dev/null @@ -1,608 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (c) 2005-2010 ActiveState Software Inc. -# Copyright (c) 2013 Eddy Petrișor - -"""Utilities for determining application-specific dirs. - -See for details and usage. -""" -# Dev Notes: -# - MSDN on where to store app data files: -# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120 -# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html -# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - -__version_info__ = (1, 4, 3) -__version__ = '.'.join(map(str, __version_info__)) - - -import sys -import os - -PY3 = sys.version_info[0] == 3 - -if PY3: - unicode = str - -if sys.platform.startswith('java'): - import platform - os_name = platform.java_ver()[3][0] - if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc. - system = 'win32' - elif os_name.startswith('Mac'): # "Mac OS X", etc. - system = 'darwin' - else: # "Linux", "SunOS", "FreeBSD", etc. - # Setting this to "linux2" is not ideal, but only Windows or Mac - # are actually checked for and the rest of the module expects - # *sys.platform* style strings. - system = 'linux2' -else: - system = sys.platform - - - -def user_data_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user data directories are: - Mac OS X: ~/Library/Application Support/ - Unix: ~/.local/share/ # or in $XDG_DATA_HOME, if defined - Win XP (not roaming): C:\Documents and Settings\\Application Data\\ - Win XP (roaming): C:\Documents and Settings\\Local Settings\Application Data\\ - Win 7 (not roaming): C:\Users\\AppData\Local\\ - Win 7 (roaming): C:\Users\\AppData\Roaming\\ - - For Unix, we follow the XDG spec and support $XDG_DATA_HOME. - That means, by default "~/.local/share/". - """ - if system == "win32": - if appauthor is None: - appauthor = appname - const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" - path = os.path.normpath(_get_win_folder(const)) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('~/Library/Application Support/') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_data_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of data dirs should be - returned. By default, the first item from XDG_DATA_DIRS is - returned, or '/usr/local/share/', - if XDG_DATA_DIRS is not set - - Typical site data directories are: - Mac OS X: /Library/Application Support/ - Unix: /usr/local/share/ or /usr/share/ - Win XP: C:\Documents and Settings\All Users\Application Data\\ - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - Win 7: C:\ProgramData\\ # Hidden, but writeable on Win 7. - - For Unix, this is using the $XDG_DATA_DIRS[0] default. - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - elif system == 'darwin': - path = os.path.expanduser('/Library/Application Support') - if appname: - path = os.path.join(path, appname) - else: - # XDG default for $XDG_DATA_DIRS - # only first, if multipath is False - path = os.getenv('XDG_DATA_DIRS', - os.pathsep.join(['/usr/local/share', '/usr/share'])) - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.sep.join([x, appname]) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - if appname and version: - path = os.path.join(path, version) - return path - - -def user_config_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific config dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user config directories are: - Mac OS X: same as user_data_dir - Unix: ~/.config/ # or in $XDG_CONFIG_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME. - That means, by default "~/.config/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def site_config_dir(appname=None, appauthor=None, version=None, multipath=False): - r"""Return full path to the user-shared data dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "multipath" is an optional parameter only applicable to *nix - which indicates that the entire list of config dirs should be - returned. By default, the first item from XDG_CONFIG_DIRS is - returned, or '/etc/xdg/', if XDG_CONFIG_DIRS is not set - - Typical site config directories are: - Mac OS X: same as site_data_dir - Unix: /etc/xdg/ or $XDG_CONFIG_DIRS[i]/ for each value in - $XDG_CONFIG_DIRS - Win *: same as site_data_dir - Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.) - - For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False - - WARNING: Do not use this on Windows. See the Vista-Fail note above for why. - """ - if system in ["win32", "darwin"]: - path = site_data_dir(appname, appauthor) - if appname and version: - path = os.path.join(path, version) - else: - # XDG default for $XDG_CONFIG_DIRS - # only first, if multipath is False - path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg') - pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)] - if appname: - if version: - appname = os.path.join(appname, version) - pathlist = [os.sep.join([x, appname]) for x in pathlist] - - if multipath: - path = os.pathsep.join(pathlist) - else: - path = pathlist[0] - return path - - -def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific cache dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Cache" to the base app data dir for Windows. See - discussion below. - - Typical user cache directories are: - Mac OS X: ~/Library/Caches/ - Unix: ~/.cache/ (XDG default) - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Cache - Vista: C:\Users\\AppData\Local\\\Cache - - On Windows the only suggestion in the MSDN docs is that local settings go in - the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming - app data dir (the default returned by `user_data_dir` above). Apps typically - put cache data somewhere *under* the given dir here. Some examples: - ...\Mozilla\Firefox\Profiles\\Cache - ...\Acme\SuperApp\Cache\1.0 - OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value. - This can be disabled with the `opinion=False` option. - """ - if system == "win32": - if appauthor is None: - appauthor = appname - path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA")) - if appname: - if appauthor is not False: - path = os.path.join(path, appauthor, appname) - else: - path = os.path.join(path, appname) - if opinion: - path = os.path.join(path, "Cache") - elif system == 'darwin': - path = os.path.expanduser('~/Library/Caches') - if appname: - path = os.path.join(path, appname) - else: - path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache')) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_state_dir(appname=None, appauthor=None, version=None, roaming=False): - r"""Return full path to the user-specific state dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "roaming" (boolean, default False) can be set True to use the Windows - roaming appdata directory. That means that for users on a Windows - network setup for roaming profiles, this user data will be - sync'd on login. See - - for a discussion of issues. - - Typical user state directories are: - Mac OS X: same as user_data_dir - Unix: ~/.local/state/ # or in $XDG_STATE_HOME, if defined - Win *: same as user_data_dir - - For Unix, we follow this Debian proposal - to extend the XDG spec and support $XDG_STATE_HOME. - - That means, by default "~/.local/state/". - """ - if system in ["win32", "darwin"]: - path = user_data_dir(appname, appauthor, None, roaming) - else: - path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state")) - if appname: - path = os.path.join(path, appname) - if appname and version: - path = os.path.join(path, version) - return path - - -def user_log_dir(appname=None, appauthor=None, version=None, opinion=True): - r"""Return full path to the user-specific log dir for this application. - - "appname" is the name of application. - If None, just the system directory is returned. - "appauthor" (only used on Windows) is the name of the - appauthor or distributing body for this application. Typically - it is the owning company name. This falls back to appname. You may - pass False to disable it. - "version" is an optional version path element to append to the - path. You might want to use this if you want multiple versions - of your app to be able to run independently. If used, this - would typically be ".". - Only applied when appname is present. - "opinion" (boolean) can be False to disable the appending of - "Logs" to the base app data dir for Windows, and "log" to the - base cache dir for Unix. See discussion below. - - Typical user log directories are: - Mac OS X: ~/Library/Logs/ - Unix: ~/.cache//log # or under $XDG_CACHE_HOME if defined - Win XP: C:\Documents and Settings\\Local Settings\Application Data\\\Logs - Vista: C:\Users\\AppData\Local\\\Logs - - On Windows the only suggestion in the MSDN docs is that local settings - go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in - examples of what some windows apps use for a logs dir.) - - OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA` - value for Windows and appends "log" to the user cache dir for Unix. - This can be disabled with the `opinion=False` option. - """ - if system == "darwin": - path = os.path.join( - os.path.expanduser('~/Library/Logs'), - appname) - elif system == "win32": - path = user_data_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "Logs") - else: - path = user_cache_dir(appname, appauthor, version) - version = False - if opinion: - path = os.path.join(path, "log") - if appname and version: - path = os.path.join(path, version) - return path - - -class AppDirs(object): - """Convenience wrapper for getting application dirs.""" - def __init__(self, appname=None, appauthor=None, version=None, - roaming=False, multipath=False): - self.appname = appname - self.appauthor = appauthor - self.version = version - self.roaming = roaming - self.multipath = multipath - - @property - def user_data_dir(self): - return user_data_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_data_dir(self): - return site_data_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_config_dir(self): - return user_config_dir(self.appname, self.appauthor, - version=self.version, roaming=self.roaming) - - @property - def site_config_dir(self): - return site_config_dir(self.appname, self.appauthor, - version=self.version, multipath=self.multipath) - - @property - def user_cache_dir(self): - return user_cache_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_state_dir(self): - return user_state_dir(self.appname, self.appauthor, - version=self.version) - - @property - def user_log_dir(self): - return user_log_dir(self.appname, self.appauthor, - version=self.version) - - -#---- internal support stuff - -def _get_win_folder_from_registry(csidl_name): - """This is a fallback technique at best. I'm not sure if using the - registry for this guarantees us the correct answer for all CSIDL_* - names. - """ - if PY3: - import winreg as _winreg - else: - import _winreg - - shell_folder_name = { - "CSIDL_APPDATA": "AppData", - "CSIDL_COMMON_APPDATA": "Common AppData", - "CSIDL_LOCAL_APPDATA": "Local AppData", - }[csidl_name] - - key = _winreg.OpenKey( - _winreg.HKEY_CURRENT_USER, - r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" - ) - dir, type = _winreg.QueryValueEx(key, shell_folder_name) - return dir - - -def _get_win_folder_with_pywin32(csidl_name): - from win32com.shell import shellcon, shell - dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0) - # Try to make this a unicode path because SHGetFolderPath does - # not return unicode strings when there is unicode data in the - # path. - try: - dir = unicode(dir) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - try: - import win32api - dir = win32api.GetShortPathName(dir) - except ImportError: - pass - except UnicodeError: - pass - return dir - - -def _get_win_folder_with_ctypes(csidl_name): - import ctypes - - csidl_const = { - "CSIDL_APPDATA": 26, - "CSIDL_COMMON_APPDATA": 35, - "CSIDL_LOCAL_APPDATA": 28, - }[csidl_name] - - buf = ctypes.create_unicode_buffer(1024) - ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in buf: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf2 = ctypes.create_unicode_buffer(1024) - if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): - buf = buf2 - - return buf.value - -def _get_win_folder_with_jna(csidl_name): - import array - from com.sun import jna - from com.sun.jna.platform import win32 - - buf_size = win32.WinDef.MAX_PATH * 2 - buf = array.zeros('c', buf_size) - shell = win32.Shell32.INSTANCE - shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf) - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - # Downgrade to short path name if have highbit chars. See - # . - has_high_char = False - for c in dir: - if ord(c) > 255: - has_high_char = True - break - if has_high_char: - buf = array.zeros('c', buf_size) - kernel = win32.Kernel32.INSTANCE - if kernel.GetShortPathName(dir, buf, buf_size): - dir = jna.Native.toString(buf.tostring()).rstrip("\0") - - return dir - -if system == "win32": - try: - import win32com.shell - _get_win_folder = _get_win_folder_with_pywin32 - except ImportError: - try: - from ctypes import windll - _get_win_folder = _get_win_folder_with_ctypes - except ImportError: - try: - import com.sun.jna - _get_win_folder = _get_win_folder_with_jna - except ImportError: - _get_win_folder = _get_win_folder_from_registry - - -#---- self test code - -if __name__ == "__main__": - appname = "MyApp" - appauthor = "MyCompany" - - props = ("user_data_dir", - "user_config_dir", - "user_cache_dir", - "user_state_dir", - "user_log_dir", - "site_data_dir", - "site_config_dir") - - print("-- app dirs %s --" % __version__) - - print("-- app dirs (with optional 'version')") - dirs = AppDirs(appname, appauthor, version="1.0") - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'version')") - dirs = AppDirs(appname, appauthor) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (without optional 'appauthor')") - dirs = AppDirs(appname) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) - - print("\n-- app dirs (with disabled 'appauthor')") - dirs = AppDirs(appname, appauthor=False) - for prop in props: - print("%s: %s" % (prop, getattr(dirs, prop))) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py index a12e2c75d..3c6de1cfb 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_common.py @@ -5,25 +5,58 @@ import contextlib import types import importlib +import inspect +import warnings +import itertools -from typing import Union, Optional +from typing import Union, Optional, cast from .abc import ResourceReader, Traversable from ._compat import wrap_spec Package = Union[types.ModuleType, str] +Anchor = Package -def files(package): - # type: (Package) -> Traversable +def package_to_anchor(func): """ - Get a Traversable resource from a package + Replace 'package' parameter as 'anchor' and warn about the change. + + Other errors should fall through. + + >>> files('a', 'b') + Traceback (most recent call last): + TypeError: files() takes from 0 to 1 positional arguments but 2 were given + """ + undefined = object() + + @functools.wraps(func) + def wrapper(anchor=undefined, package=undefined): + if package is not undefined: + if anchor is not undefined: + return func(anchor, package) + warnings.warn( + "First parameter to files is renamed to 'anchor'", + DeprecationWarning, + stacklevel=2, + ) + return func(package) + elif anchor is undefined: + return func() + return func(anchor) + + return wrapper + + +@package_to_anchor +def files(anchor: Optional[Anchor] = None) -> Traversable: + """ + Get a Traversable resource for an anchor. """ - return from_package(get_package(package)) + return from_package(resolve(anchor)) -def get_resource_reader(package): - # type: (types.ModuleType) -> Optional[ResourceReader] +def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: """ Return the package's loader if it's a ResourceReader. """ @@ -39,24 +72,39 @@ def get_resource_reader(package): return reader(spec.name) # type: ignore -def resolve(cand): - # type: (Package) -> types.ModuleType - return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand) +@functools.singledispatch +def resolve(cand: Optional[Anchor]) -> types.ModuleType: + return cast(types.ModuleType, cand) + + +@resolve.register +def _(cand: str) -> types.ModuleType: + return importlib.import_module(cand) + +@resolve.register +def _(cand: None) -> types.ModuleType: + return resolve(_infer_caller().f_globals['__name__']) -def get_package(package): - # type: (Package) -> types.ModuleType - """Take a package name or module object and return the module. - Raise an exception if the resolved module is not a package. +def _infer_caller(): """ - resolved = resolve(package) - if wrap_spec(resolved).submodule_search_locations is None: - raise TypeError(f'{package!r} is not a package') - return resolved + Walk the stack and find the frame of the first caller not in this module. + """ + + def is_this_file(frame_info): + return frame_info.filename == __file__ + + def is_wrapper(frame_info): + return frame_info.function == 'wrapper' + + not_this_file = itertools.filterfalse(is_this_file, inspect.stack()) + # also exclude 'wrapper' due to singledispatch in the call stack + callers = itertools.filterfalse(is_wrapper, not_this_file) + return next(callers).frame -def from_package(package): +def from_package(package: types.ModuleType): """ Return a Traversable object for the given package. @@ -67,7 +115,14 @@ def from_package(package): @contextlib.contextmanager -def _tempfile(reader, suffix=''): +def _tempfile( + reader, + suffix='', + # gh-93353: Keep a reference to call os.remove() in late Python + # finalization. + *, + _os_remove=os.remove, +): # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try' # blocks due to the need to close the temporary file to work on Windows # properly. @@ -81,18 +136,35 @@ def _tempfile(reader, suffix=''): yield pathlib.Path(raw_path) finally: try: - os.remove(raw_path) + _os_remove(raw_path) except FileNotFoundError: pass +def _temp_file(path): + return _tempfile(path.read_bytes, suffix=path.name) + + +def _is_present_dir(path: Traversable) -> bool: + """ + Some Traversables implement ``is_dir()`` to raise an + exception (i.e. ``FileNotFoundError``) when the + directory doesn't exist. This function wraps that call + to always return a boolean and only return True + if there's a dir and it exists. + """ + with contextlib.suppress(FileNotFoundError): + return path.is_dir() + return False + + @functools.singledispatch def as_file(path): """ Given a Traversable object, return that object as a path on the local file system in a context manager. """ - return _tempfile(path.read_bytes, suffix=path.name) + return _temp_dir(path) if _is_present_dir(path) else _temp_file(path) @as_file.register(pathlib.Path) @@ -102,3 +174,34 @@ def _(path): Degenerate behavior for pathlib.Path objects. """ yield path + + +@contextlib.contextmanager +def _temp_path(dir: tempfile.TemporaryDirectory): + """ + Wrap tempfile.TemporyDirectory to return a pathlib object. + """ + with dir as result: + yield pathlib.Path(result) + + +@contextlib.contextmanager +def _temp_dir(path): + """ + Given a traversable dir, recursively replicate the whole tree + to the file system in a context manager. + """ + assert path.is_dir() + with _temp_path(tempfile.TemporaryDirectory()) as temp_dir: + yield _write_contents(temp_dir, path) + + +def _write_contents(target, source): + child = target.joinpath(source.name) + if source.is_dir(): + child.mkdir() + for item in source.iterdir(): + _write_contents(child, item) + else: + child.write_bytes(source.read_bytes()) + return child diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py index cb9fc820c..8b5b1d280 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_compat.py @@ -1,9 +1,12 @@ # flake8: noqa import abc +import os import sys import pathlib from contextlib import suppress +from typing import Union + if sys.version_info >= (3, 10): from zipfile import Path as ZipPath # type: ignore @@ -96,3 +99,10 @@ def wrap_spec(package): from . import _adapters return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py index 1d5d3f1fb..b1ea8105d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/_legacy.py @@ -27,8 +27,7 @@ def wrapper(*args, **kwargs): return wrapper -def normalize_path(path): - # type: (Any) -> str +def normalize_path(path: Any) -> str: """Normalize a path by ensuring it is a string. If the resulting string contains path separators, an exception is raised. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py index d39dc1adb..23b6aeafe 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/abc.py @@ -1,7 +1,13 @@ import abc -from typing import BinaryIO, Iterable, Text +import io +import itertools +import pathlib +from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional -from ._compat import runtime_checkable, Protocol +from ._compat import runtime_checkable, Protocol, StrPath + + +__all__ = ["ResourceReader", "Traversable", "TraversableResources"] class ResourceReader(metaclass=abc.ABCMeta): @@ -46,27 +52,34 @@ def contents(self) -> Iterable[str]: raise FileNotFoundError +class TraversalError(Exception): + pass + + @runtime_checkable class Traversable(Protocol): """ An object with a subset of pathlib.Path methods suitable for traversing directories and opening files. + + Any exceptions that occur when accessing the backing resource + may propagate unaltered. """ @abc.abstractmethod - def iterdir(self): + def iterdir(self) -> Iterator["Traversable"]: """ Yield Traversable objects in self """ - def read_bytes(self): + def read_bytes(self) -> bytes: """ Read contents of self as bytes """ with self.open('rb') as strm: return strm.read() - def read_text(self, encoding=None): + def read_text(self, encoding: Optional[str] = None) -> str: """ Read contents of self as text """ @@ -85,13 +98,32 @@ def is_file(self) -> bool: Return True if self is a file """ - @abc.abstractmethod - def joinpath(self, child): + def joinpath(self, *descendants: StrPath) -> "Traversable": """ - Return Traversable child in self + Return Traversable resolved with any descendants applied. + + Each descendant should be a path segment relative to self + and each may contain multiple levels separated by + ``posixpath.sep`` (``/``). """ + if not descendants: + return self + names = itertools.chain.from_iterable( + path.parts for path in map(pathlib.PurePosixPath, descendants) + ) + target = next(names) + matches = ( + traversable for traversable in self.iterdir() if traversable.name == target + ) + try: + match = next(matches) + except StopIteration: + raise TraversalError( + "Target not found during traversal.", target, list(names) + ) + return match.joinpath(*names) - def __truediv__(self, child): + def __truediv__(self, child: StrPath) -> "Traversable": """ Return Traversable child in self """ @@ -107,7 +139,8 @@ def open(self, mode='r', *args, **kwargs): accepted by io.TextIOWrapper. """ - @abc.abstractproperty + @property + @abc.abstractmethod def name(self) -> str: """ The base name of this object without any parent references. @@ -121,17 +154,17 @@ class TraversableResources(ResourceReader): """ @abc.abstractmethod - def files(self): + def files(self) -> "Traversable": """Return a Traversable object for the loaded package.""" - def open_resource(self, resource): + def open_resource(self, resource: StrPath) -> io.BufferedReader: return self.files().joinpath(resource).open('rb') - def resource_path(self, resource): + def resource_path(self, resource: Any) -> NoReturn: raise FileNotFoundError(resource) - def is_resource(self, path): + def is_resource(self, path: StrPath) -> bool: return self.files().joinpath(path).is_file() - def contents(self): + def contents(self) -> Iterator[str]: return (item.name for item in self.files().iterdir()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py index f1190ca45..ab34db740 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/readers.py @@ -82,15 +82,13 @@ def is_dir(self): def is_file(self): return False - def joinpath(self, child): - # first try to find child in current paths - for file in self.iterdir(): - if file.name == child: - return file - # if it does not exist, construct it with the first path - return self._paths[0] / child - - __truediv__ = joinpath + def joinpath(self, *descendants): + try: + return super().joinpath(*descendants) + except abc.TraversalError: + # One of the paths did not resolve (a directory does not exist). + # Just return something that will not exist. + return self._paths[0].joinpath(*descendants) def open(self, *args, **kwargs): raise FileNotFoundError(f'{self} is not a file') diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py index da073cbdb..7770c922c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/importlib_resources/simple.py @@ -16,31 +16,28 @@ class SimpleReader(abc.ABC): provider. """ - @abc.abstractproperty - def package(self): - # type: () -> str + @property + @abc.abstractmethod + def package(self) -> str: """ The name of the package for which this reader loads resources. """ @abc.abstractmethod - def children(self): - # type: () -> List['SimpleReader'] + def children(self) -> List['SimpleReader']: """ Obtain an iterable of SimpleReader for available child containers (e.g. directories). """ @abc.abstractmethod - def resources(self): - # type: () -> List[str] + def resources(self) -> List[str]: """ Obtain available named resources for this virtual package. """ @abc.abstractmethod - def open_binary(self, resource): - # type: (str) -> BinaryIO + def open_binary(self, resource: str) -> BinaryIO: """ Obtain a File-like for a named resource. """ @@ -50,39 +47,12 @@ def name(self): return self.package.split('.')[-1] -class ResourceHandle(Traversable): - """ - Handle to a named resource in a ResourceReader. - """ - - def __init__(self, parent, name): - # type: (ResourceContainer, str) -> None - self.parent = parent - self.name = name # type: ignore - - def is_file(self): - return True - - def is_dir(self): - return False - - def open(self, mode='r', *args, **kwargs): - stream = self.parent.reader.open_binary(self.name) - if 'b' not in mode: - stream = io.TextIOWrapper(*args, **kwargs) - return stream - - def joinpath(self, name): - raise RuntimeError("Cannot traverse into a resource") - - class ResourceContainer(Traversable): """ Traversable container for a package's resources via its reader. """ - def __init__(self, reader): - # type: (SimpleReader) -> None + def __init__(self, reader: SimpleReader): self.reader = reader def is_dir(self): @@ -99,10 +69,30 @@ def iterdir(self): def open(self, *args, **kwargs): raise IsADirectoryError() + +class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + + def __init__(self, parent: ResourceContainer, name: str): + self.parent = parent + self.name = name # type: ignore + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.parent.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + def joinpath(self, name): - return next( - traversable for traversable in self.iterdir() if traversable.name == name - ) + raise RuntimeError("Cannot traverse into a resource") class TraversableReader(TraversableResources, SimpleReader): diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py index 87a4e3dca..b0d1ef37c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/jaraco/context.py @@ -5,10 +5,18 @@ import tempfile import shutil import operator +import warnings @contextlib.contextmanager def pushd(dir): + """ + >>> tmp_path = getfixture('tmp_path') + >>> with pushd(tmp_path): + ... assert os.getcwd() == os.fspath(tmp_path) + >>> assert os.getcwd() != os.fspath(tmp_path) + """ + orig = os.getcwd() os.chdir(dir) try: @@ -29,6 +37,8 @@ def tarball_context(url, target_dir=None, runner=None, pushd=pushd): target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '') if runner is None: runner = functools.partial(subprocess.check_call, shell=True) + else: + warnings.warn("runner parameter is deprecated", DeprecationWarning) # In the tar command, use --strip-components=1 to strip the first path and # then # use -C to cause the files to be extracted to {target_dir}. This ensures @@ -48,6 +58,15 @@ def tarball_context(url, target_dir=None, runner=None, pushd=pushd): def infer_compression(url): """ Given a URL or filename, infer the compression code for tar. + + >>> infer_compression('http://foo/bar.tar.gz') + 'z' + >>> infer_compression('http://foo/bar.tgz') + 'z' + >>> infer_compression('file.bz') + 'j' + >>> infer_compression('file.xz') + 'J' """ # cheat and just assume it's the last two characters compression_indicator = url[-2:] @@ -61,6 +80,12 @@ def temp_dir(remover=shutil.rmtree): """ Create a temporary directory context. Pass a custom remover to override the removal behavior. + + >>> import pathlib + >>> with temp_dir() as the_dir: + ... assert os.path.isdir(the_dir) + ... _ = pathlib.Path(the_dir).joinpath('somefile').write_text('contents') + >>> assert not os.path.exists(the_dir) """ temp_dir = tempfile.mkdtemp() try: @@ -90,6 +115,12 @@ def repo_context(url, branch=None, quiet=True, dest_ctx=temp_dir): @contextlib.contextmanager def null(): + """ + A null context suitable to stand in for a meaningful context. + + >>> with null() as value: + ... assert value is None + """ yield @@ -112,6 +143,10 @@ class ExceptionTrap: ... raise ValueError("1 + 1 is not 3") >>> bool(trap) True + >>> trap.value + ValueError('1 + 1 is not 3') + >>> trap.tb + >>> with ExceptionTrap(ValueError) as trap: ... raise Exception() @@ -211,3 +246,43 @@ class suppress(contextlib.suppress, contextlib.ContextDecorator): ... {}[''] >>> key_error() """ + + +class on_interrupt(contextlib.ContextDecorator): + """ + Replace a KeyboardInterrupt with SystemExit(1) + + >>> def do_interrupt(): + ... raise KeyboardInterrupt() + >>> on_interrupt('error')(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 1 + >>> on_interrupt('error', code=255)(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 255 + >>> on_interrupt('suppress')(do_interrupt)() + >>> with __import__('pytest').raises(KeyboardInterrupt): + ... on_interrupt('ignore')(do_interrupt)() + """ + + def __init__( + self, + action='error', + # py3.7 compat + # /, + code=1, + ): + self.action = action + self.code = code + + def __enter__(self): + return self + + def __exit__(self, exctype, excinst, exctb): + if exctype is not KeyboardInterrupt or self.action == 'ignore': + return + elif self.action == 'error': + raise SystemExit(self.code) from excinst + return self.action == 'suppress' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py index ea38bef1f..557bfc206 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/__init__.py @@ -1,4 +1,6 @@ +"""More routines for operating on iterables, beyond itertools""" + from .more import * # noqa from .recipes import * # noqa -__version__ = '8.12.0' +__version__ = '9.0.0' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py index 6b6a5cab2..0b29fca02 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/more.py @@ -3,7 +3,7 @@ from collections import Counter, defaultdict, deque, abc from collections.abc import Sequence from functools import partial, reduce, wraps -from heapq import merge, heapify, heapreplace, heappop +from heapq import heapify, heapreplace, heappop from itertools import ( chain, compress, @@ -26,12 +26,16 @@ from time import monotonic from .recipes import ( + _marker, + _zip_equal, + UnequalIterablesError, consume, flatten, pairwise, powerset, take, unique_everseen, + all_equal, ) __all__ = [ @@ -48,9 +52,9 @@ 'chunked_even', 'circular_shifts', 'collapse', - 'collate', 'combination_index', 'consecutive_groups', + 'constrained_batches', 'consumer', 'count_cycle', 'countable', @@ -66,6 +70,7 @@ 'first', 'groupby_transform', 'ichunked', + 'iequals', 'ilen', 'interleave', 'interleave_evenly', @@ -76,6 +81,7 @@ 'iterate', 'last', 'locate', + 'longest_common_prefix', 'lstrip', 'make_decorator', 'map_except', @@ -132,9 +138,6 @@ ] -_marker = object() - - def chunked(iterable, n, strict=False): """Break *iterable* into lists of length *n*: @@ -409,44 +412,6 @@ def __getitem__(self, index): return self._cache[index] -def collate(*iterables, **kwargs): - """Return a sorted merge of the items from each of several already-sorted - *iterables*. - - >>> list(collate('ACDZ', 'AZ', 'JKL')) - ['A', 'A', 'C', 'D', 'J', 'K', 'L', 'Z', 'Z'] - - Works lazily, keeping only the next value from each iterable in memory. Use - :func:`collate` to, for example, perform a n-way mergesort of items that - don't fit in memory. - - If a *key* function is specified, the iterables will be sorted according - to its result: - - >>> key = lambda s: int(s) # Sort by numeric value, not by string - >>> list(collate(['1', '10'], ['2', '11'], key=key)) - ['1', '2', '10', '11'] - - - If the *iterables* are sorted in descending order, set *reverse* to - ``True``: - - >>> list(collate([5, 3, 1], [4, 2, 0], reverse=True)) - [5, 4, 3, 2, 1, 0] - - If the elements of the passed-in iterables are out of order, you might get - unexpected results. - - On Python 3.5+, this function is an alias for :func:`heapq.merge`. - - """ - warnings.warn( - "collate is no longer part of more_itertools, use heapq.merge", - DeprecationWarning, - ) - return merge(*iterables, **kwargs) - - def consumer(func): """Decorator that automatically advances a PEP-342-style "reverse iterator" to its first yield point so you don't have to call ``next()`` on it @@ -872,7 +837,9 @@ def windowed(seq, n, fillvalue=None, step=1): yield tuple(window) size = len(window) - if size < n: + if size == 0: + return + elif size < n: yield tuple(chain(window, repeat(fillvalue, n - size))) elif 0 < i < min(step, n): window += (fillvalue,) * i @@ -1645,45 +1612,6 @@ def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None): ) -class UnequalIterablesError(ValueError): - def __init__(self, details=None): - msg = 'Iterables have different lengths' - if details is not None: - msg += (': index 0 has length {}; index {} has length {}').format( - *details - ) - - super().__init__(msg) - - -def _zip_equal_generator(iterables): - for combo in zip_longest(*iterables, fillvalue=_marker): - for val in combo: - if val is _marker: - raise UnequalIterablesError() - yield combo - - -def _zip_equal(*iterables): - # Check whether the iterables are all the same size. - try: - first_size = len(iterables[0]) - for i, it in enumerate(iterables[1:], 1): - size = len(it) - if size != first_size: - break - else: - # If we didn't break out, we can use the built-in zip. - return zip(*iterables) - - # If we did break out, there was a mismatch. - raise UnequalIterablesError(details=(first_size, i, size)) - # If any one of the iterables didn't have a length, start reading - # them until one runs out. - except TypeError: - return _zip_equal_generator(iterables) - - def zip_equal(*iterables): """``zip`` the input *iterables* together, but raise ``UnequalIterablesError`` if they aren't all the same length. @@ -1825,7 +1753,7 @@ def unzip(iterable): of the zipped *iterable*. The ``i``-th iterable contains the ``i``-th element from each element - of the zipped iterable. The first element is used to to determine the + of the zipped iterable. The first element is used to determine the length of the remaining elements. >>> iterable = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] @@ -2375,6 +2303,16 @@ def locate(iterable, pred=bool, window_size=None): return compress(count(), starmap(pred, it)) +def longest_common_prefix(iterables): + """Yield elements of the longest common prefix amongst given *iterables*. + + >>> ''.join(longest_common_prefix(['abcd', 'abc', 'abf'])) + 'ab' + + """ + return (c[0] for c in takewhile(all_equal, zip(*iterables))) + + def lstrip(iterable, pred): """Yield the items from *iterable*, but strip any from the beginning for which *pred* returns ``True``. @@ -2683,7 +2621,7 @@ def difference(iterable, func=sub, *, initial=None): if initial is not None: first = [] - return chain(first, starmap(func, zip(b, a))) + return chain(first, map(func, b, a)) class SequenceView(Sequence): @@ -3326,6 +3264,27 @@ def only(iterable, default=None, too_long=None): return first_value +class _IChunk: + def __init__(self, iterable, n): + self._it = islice(iterable, n) + self._cache = deque() + + def fill_cache(self): + self._cache.extend(self._it) + + def __iter__(self): + return self + + def __next__(self): + try: + return next(self._it) + except StopIteration: + if self._cache: + return self._cache.popleft() + else: + raise + + def ichunked(iterable, n): """Break *iterable* into sub-iterables with *n* elements each. :func:`ichunked` is like :func:`chunked`, but it yields iterables @@ -3347,20 +3306,39 @@ def ichunked(iterable, n): [8, 9, 10, 11] """ - source = iter(iterable) - + source = peekable(iter(iterable)) + ichunk_marker = object() while True: # Check to see whether we're at the end of the source iterable - item = next(source, _marker) - if item is _marker: + item = source.peek(ichunk_marker) + if item is ichunk_marker: return - # Clone the source and yield an n-length slice - source, it = tee(chain([item], source)) - yield islice(it, n) + chunk = _IChunk(source, n) + yield chunk + + # Advance the source iterable and fill previous chunk's cache + chunk.fill_cache() + + +def iequals(*iterables): + """Return ``True`` if all given *iterables* are equal to each other, + which means that they contain the same elements in the same order. - # Advance the source iterable - consume(source, n) + The function is useful for comparing iterables of different data types + or iterables that do not support equality checks. + + >>> iequals("abc", ['a', 'b', 'c'], ('a', 'b', 'c'), iter("abc")) + True + + >>> iequals("abc", "acb") + False + + Not to be confused with :func:`all_equals`, which checks whether all + elements of iterable are equal to each other. + + """ + return all(map(all_equal, zip_longest(*iterables, fillvalue=object()))) def distinct_combinations(iterable, r): @@ -3655,7 +3633,9 @@ def __init__(self, func, callback_kwd='callback', wait_seconds=0.1): self._aborted = False self._future = None self._wait_seconds = wait_seconds - self._executor = __import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1) + # Lazily import concurrent.future + self._executor = __import__( + ).futures.__import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1) self._iterator = self._reader() def __enter__(self): @@ -3960,7 +3940,7 @@ def combination_index(element, iterable): n, _ = last(pool, default=(n, None)) - # Python versiosn below 3.8 don't have math.comb + # Python versions below 3.8 don't have math.comb index = 1 for i, j in enumerate(reversed(indexes), start=1): j = n - j @@ -4113,7 +4093,7 @@ def zip_broadcast(*objects, scalar_types=(str, bytes), strict=False): If the *strict* keyword argument is ``True``, then ``UnequalIterablesError`` will be raised if any of the iterables have - different lengthss. + different lengths. """ def is_scalar(obj): @@ -4314,3 +4294,53 @@ def minmax(iterable_or_value, *others, key=None, default=_marker): hi, hi_key = y, y_key return lo, hi + + +def constrained_batches( + iterable, max_size, max_count=None, get_len=len, strict=True +): + """Yield batches of items from *iterable* with a combined size limited by + *max_size*. + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10)) + [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')] + + If a *max_count* is supplied, the number of items per batch is also + limited: + + >>> iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1'] + >>> list(constrained_batches(iterable, 10, max_count = 2)) + [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)] + + If a *get_len* function is supplied, use that instead of :func:`len` to + determine item size. + + If *strict* is ``True``, raise ``ValueError`` if any single item is bigger + than *max_size*. Otherwise, allow single items to exceed *max_size*. + """ + if max_size <= 0: + raise ValueError('maximum size must be greater than zero') + + batch = [] + batch_size = 0 + batch_count = 0 + for item in iterable: + item_len = get_len(item) + if strict and item_len > max_size: + raise ValueError('item size exceeds maximum size') + + reached_count = batch_count == max_count + reached_size = item_len + batch_size > max_size + if batch_count and (reached_size or reached_count): + yield tuple(batch) + batch.clear() + batch_size = 0 + batch_count = 0 + + batch.append(item) + batch_size += item_len + batch_count += 1 + + if batch: + yield tuple(batch) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py index a2596423a..857962078 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/more_itertools/recipes.py @@ -7,11 +7,16 @@ .. [1] http://docs.python.org/library/itertools.html#recipes """ -import warnings +import math +import operator + from collections import deque +from collections.abc import Sized +from functools import reduce from itertools import ( chain, combinations, + compress, count, cycle, groupby, @@ -21,11 +26,11 @@ tee, zip_longest, ) -import operator from random import randrange, sample, choice __all__ = [ 'all_equal', + 'batched', 'before_and_after', 'consume', 'convolve', @@ -41,6 +46,7 @@ 'pad_none', 'pairwise', 'partition', + 'polynomial_from_roots', 'powerset', 'prepend', 'quantify', @@ -50,7 +56,9 @@ 'random_product', 'repeatfunc', 'roundrobin', + 'sieve', 'sliding_window', + 'subslices', 'tabulate', 'tail', 'take', @@ -59,6 +67,8 @@ 'unique_justseen', ] +_marker = object() + def take(n, iterable): """Return first *n* items of the iterable as a list. @@ -102,7 +112,14 @@ def tail(n, iterable): ['E', 'F', 'G'] """ - return iter(deque(iterable, maxlen=n)) + # If the given iterable has a length, then we can use islice to get its + # final elements. Note that if the iterable is not actually Iterable, + # either islice or deque will throw a TypeError. This is why we don't + # check if it is Iterable. + if isinstance(iterable, Sized): + yield from islice(iterable, max(0, len(iterable) - n), None) + else: + yield from iter(deque(iterable, maxlen=n)) def consume(iterator, n=None): @@ -284,20 +301,83 @@ def pairwise(iterable): pairwise.__doc__ = _pairwise.__doc__ -def grouper(iterable, n, fillvalue=None): - """Collect data into fixed-length chunks or blocks. +class UnequalIterablesError(ValueError): + def __init__(self, details=None): + msg = 'Iterables have different lengths' + if details is not None: + msg += (': index 0 has length {}; index {} has length {}').format( + *details + ) + + super().__init__(msg) + + +def _zip_equal_generator(iterables): + for combo in zip_longest(*iterables, fillvalue=_marker): + for val in combo: + if val is _marker: + raise UnequalIterablesError() + yield combo + + +def _zip_equal(*iterables): + # Check whether the iterables are all the same size. + try: + first_size = len(iterables[0]) + for i, it in enumerate(iterables[1:], 1): + size = len(it) + if size != first_size: + break + else: + # If we didn't break out, we can use the built-in zip. + return zip(*iterables) - >>> list(grouper('ABCDEFG', 3, 'x')) + # If we did break out, there was a mismatch. + raise UnequalIterablesError(details=(first_size, i, size)) + # If any one of the iterables didn't have a length, start reading + # them until one runs out. + except TypeError: + return _zip_equal_generator(iterables) + + +def grouper(iterable, n, incomplete='fill', fillvalue=None): + """Group elements from *iterable* into fixed-length groups of length *n*. + + >>> list(grouper('ABCDEF', 3)) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + The keyword arguments *incomplete* and *fillvalue* control what happens for + iterables whose length is not a multiple of *n*. + + When *incomplete* is `'fill'`, the last group will contain instances of + *fillvalue*. + + >>> list(grouper('ABCDEFG', 3, incomplete='fill', fillvalue='x')) [('A', 'B', 'C'), ('D', 'E', 'F'), ('G', 'x', 'x')] + When *incomplete* is `'ignore'`, the last group will not be emitted. + + >>> list(grouper('ABCDEFG', 3, incomplete='ignore', fillvalue='x')) + [('A', 'B', 'C'), ('D', 'E', 'F')] + + When *incomplete* is `'strict'`, a subclass of `ValueError` will be raised. + + >>> it = grouper('ABCDEFG', 3, incomplete='strict') + >>> list(it) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + UnequalIterablesError + """ - if isinstance(iterable, int): - warnings.warn( - "grouper expects iterable as first parameter", DeprecationWarning - ) - n, iterable = iterable, n args = [iter(iterable)] * n - return zip_longest(fillvalue=fillvalue, *args) + if incomplete == 'fill': + return zip_longest(*args, fillvalue=fillvalue) + if incomplete == 'strict': + return _zip_equal(*args) + if incomplete == 'ignore': + return zip(*args) + else: + raise ValueError('Expected fill, strict, or ignore') def roundrobin(*iterables): @@ -658,11 +738,12 @@ def true_iterator(): transition.append(elem) return - def remainder_iterator(): - yield from transition - yield from it + # Note: this is different from itertools recipes to allow nesting + # before_and_after remainders into before_and_after again. See tests + # for an example. + remainder_iterator = chain(transition, it) - return true_iterator(), remainder_iterator() + return true_iterator(), remainder_iterator def triplewise(iterable): @@ -696,3 +777,65 @@ def sliding_window(iterable, n): for x in it: window.append(x) yield tuple(window) + + +def subslices(iterable): + """Return all contiguous non-empty subslices of *iterable*. + + >>> list(subslices('ABC')) + [['A'], ['A', 'B'], ['A', 'B', 'C'], ['B'], ['B', 'C'], ['C']] + + This is similar to :func:`substrings`, but emits items in a different + order. + """ + seq = list(iterable) + slices = starmap(slice, combinations(range(len(seq) + 1), 2)) + return map(operator.getitem, repeat(seq), slices) + + +def polynomial_from_roots(roots): + """Compute a polynomial's coefficients from its roots. + + >>> roots = [5, -4, 3] # (x - 5) * (x + 4) * (x - 3) + >>> polynomial_from_roots(roots) # x^3 - 4 * x^2 - 17 * x + 60 + [1, -4, -17, 60] + """ + # Use math.prod for Python 3.8+, + prod = getattr(math, 'prod', lambda x: reduce(operator.mul, x, 1)) + roots = list(map(operator.neg, roots)) + return [ + sum(map(prod, combinations(roots, k))) for k in range(len(roots) + 1) + ] + + +def sieve(n): + """Yield the primes less than n. + + >>> list(sieve(30)) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + """ + isqrt = getattr(math, 'isqrt', lambda x: int(math.sqrt(x))) + limit = isqrt(n) + 1 + data = bytearray([1]) * n + data[:2] = 0, 0 + for p in compress(range(limit), data): + data[p + p : n : p] = bytearray(len(range(p + p, n, p))) + + return compress(count(), data) + + +def batched(iterable, n): + """Batch data into lists of length *n*. The last batch may be shorter. + + >>> list(batched('ABCDEFG', 3)) + [['A', 'B', 'C'], ['D', 'E', 'F'], ['G']] + + This recipe is from the ``itertools`` docs. This library also provides + :func:`chunked`, which has a different implementation. + """ + it = iter(iterable) + while True: + batch = list(islice(it, n)) + if not batch: + break + yield batch diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py deleted file mode 100644 index 3551bc2d2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__about__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "21.3" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014-2019 %s" % __author__ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py index 3c50c5dcf..4112fec0a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/__init__.py @@ -2,24 +2,14 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] +__version__ = "23.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py new file mode 100644 index 000000000..6fb19b30b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +import enum +import os +import struct +from typing import IO, Optional, Tuple + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error: + raise ELFInvalid("unable to parse identification") + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or " + f"encoding ({self.encoding})" + ) + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> Optional[str]: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py index 4c379aa6f..2f0cc7439 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_manylinux.py @@ -1,121 +1,58 @@ import collections +import contextlib import functools import os import re -import struct import sys import warnings -from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader: - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file: IO[bytes]) -> None: - def unpack(fmt: str) -> int: - try: - data = file.read(struct.calcsize(fmt)) - result: Tuple[int, ...] = struct.unpack(fmt, data) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result[0] - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header() -> Optional[_ELFFileHeader]: +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None -def _is_linux_armhf() -> bool: +def _is_linux_armhf(executable: str) -> bool: # hard-float ABI can be detected from the ELF header of the running # process # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686() -> bool: - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) -def _have_compatible_abi(arch: str) -> bool: +def _have_compatible_abi(executable: str, arch: str) -> bool: if arch == "armv7l": - return _is_linux_armhf() + return _is_linux_armhf(executable) if arch == "i686": - return _is_linux_i686() + return _is_linux_i686(executable) return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} @@ -141,10 +78,10 @@ def _glibc_version_string_confstr() -> Optional[str]: # platform module. # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr("CS_GNU_LIBC_VERSION") + # Should be a string like "glibc 2.17". + version_string: str = getattr(os, "confstr")("CS_GNU_LIBC_VERSION") assert version_string is not None - _, version = version_string.split() + _, version = version_string.rsplit() except (AssertionError, AttributeError, OSError, ValueError): # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... return None @@ -211,8 +148,8 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]: m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) if not m: warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", RuntimeWarning, ) return -1, -1 @@ -265,7 +202,7 @@ def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: def platform_tags(linux: str, arch: str) -> Iterator[str]: - if not _have_compatible_abi(arch): + if not _have_compatible_abi(sys.executable, arch): return # Oldest glibc to be supported regardless of architecture is (2, 17). too_old_glibc2 = _GLibCVersion(2, 16) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py index 8ac3059ba..706ba600a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_musllinux.py @@ -4,68 +4,13 @@ linked against musl, and what musl version is used. """ -import contextlib import functools -import operator -import os import re -import struct import subprocess import sys -from typing import IO, Iterator, NamedTuple, Optional, Tuple +from typing import Iterator, NamedTuple, Optional - -def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]: - return struct.unpack(fmt, f.read(struct.calcsize(fmt))) - - -def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]: - """Detect musl libc location by parsing the Python executable. - - Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca - ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html - """ - f.seek(0) - try: - ident = _read_unpacked(f, "16B") - except struct.error: - return None - if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. - return None - f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, p_fmt, p_idx = { - 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. - 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. - }[ident[4]] - except KeyError: - return None - else: - p_get = operator.itemgetter(*p_idx) - - # Find the interpreter section and return its content. - try: - _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) - except struct.error: - return None - for i in range(e_phnum + 1): - f.seek(e_phoff + e_phentsize * i) - try: - p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) - except struct.error: - return None - if p_type != 3: # Not PT_INTERP. - continue - f.seek(p_offset) - interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") - if "musl" not in interpreter: - return None - return interpreter - return None +from ._elffile import ELFFile class _MuslVersion(NamedTuple): @@ -95,13 +40,12 @@ def _get_musl_version(executable: str) -> Optional[_MuslVersion]: Version 1.2.2 Dynamic Program Loader """ - with contextlib.ExitStack() as stack: - try: - f = stack.enter_context(open(executable, "rb")) - except OSError: - return None - ld = _parse_ld_musl_from_elf(f) - if not ld: + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: return None proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) return _parse_musl_version(proc.stderr) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py new file mode 100644 index 000000000..2bc6a8f98 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_parser.py @@ -0,0 +1,328 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains ENBF-inspired grammar representing +the implementation. +""" + +import ast +from typing import Any, List, NamedTuple, Optional, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] +# MarkerList = List[Union["MarkerList", MarkerAtom, str]] +# mypy does not support recursive type definition +# https://github.com/python/mypy/issues/731 +MarkerAtom = Any +MarkerList = List[Any] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: List[str] + specifier: str + marker: Optional[MarkerList] + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> Tuple[str, str, Optional[MarkerList]]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, span_start=url_start, after="URL and whitespace" + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + after=( + "version specifier" + if specifier + else "name and no valid version specifier" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, after: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected end or semicolon (after {after})", + span_start=span_start, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> List[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens("LEFT_BRACKET", "RIGHT_BRACKET"): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: List[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if ( + env_var == "platform_python_implementation" + or env_var == "python_implementation" + ): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of " + "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py new file mode 100644 index 000000000..b1fb207c7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/_tokenizer.py @@ -0,0 +1,188 @@ +import contextlib +import re +from dataclasses import dataclass +from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: Tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return "\n ".join([self.message, self.source, marker]) + + +DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { + "LEFT_PARENTHESIS": r"\(", + "RIGHT_PARENTHESIS": r"\)", + "LEFT_BRACKET": r"\[", + "RIGHT_BRACKET": r"\]", + "SEMICOLON": r";", + "COMMA": r",", + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": r"(===|==|~=|!=|<=|>=|<|>)", + "BOOLOP": r"\b(or|and)\b", + "IN": r"\bin\b", + "NOT": r"\bnot\b", + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extra + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": r"\@", + "URL": r"[^ \t]+", + "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", + "WS": r"[ \t]+", + "END": r"$", +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: "Dict[str, Union[str, re.Pattern[str]]]", + ) -> None: + self.source = source + self.rules: Dict[str, re.Pattern[str]] = { + name: re.compile(pattern) for name, pattern in rules.items() + } + self.next_token: Optional[Token] = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert ( + self.next_token is None + ), f"Cannot check for {name!r}, already have {self.next_token!r}" + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: Optional[int] = None, + span_end: Optional[int] = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens(self, open_token: str, close_token: str) -> Iterator[bool]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield open_position is not None + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected closing {close_token}", + span_start=open_position, + ) + + self.read() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py index 18769b09a..68369c981 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/markers.py @@ -8,19 +8,10 @@ import sys from typing import Any, Callable, Dict, List, Optional, Tuple, Union -from pkg_resources.extern.pyparsing import ( # noqa: N817 - Forward, - Group, - Literal as L, - ParseException, - ParseResults, - QuotedString, - ZeroOrMore, - stringEnd, - stringStart, -) - +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable, parse_marker +from ._tokenizer import ParserSyntaxError from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name __all__ = [ "InvalidMarker", @@ -52,101 +43,24 @@ class UndefinedEnvironmentName(ValueError): """ -class Node: - def __init__(self, value: Any) -> None: - self.value = value - - def __str__(self) -> str: - return str(self.value) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def serialize(self) -> str: - raise NotImplementedError - - -class Variable(Node): - def serialize(self) -> str: - return str(self) - - -class Value(Node): - def serialize(self) -> str: - return f'"{self}"' - - -class Op(Node): - def serialize(self) -> str: - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results +def _normalize_extra_values(results: Any) -> Any: + """ + Normalize extra values. + """ + if isinstance(results[0], tuple): + lhs, op, rhs = results[0] + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + results[0] = lhs, op, rhs + return results def _format_marker( - marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True + marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True ) -> str: assert isinstance(marker, (list, tuple, str)) @@ -192,7 +106,7 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool: except InvalidSpecifier: pass else: - return spec.contains(lhs) + return spec.contains(lhs, prereleases=True) oper: Optional[Operator] = _operators.get(op.serialize()) if oper is None: @@ -201,25 +115,19 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool: return oper(lhs, rhs) -class Undefined: - pass - +def _normalize(*values: str, key: str) -> Tuple[str, ...]: + # PEP 685 – Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + return tuple(canonicalize_name(v) for v in values) -_undefined = Undefined() + # other environment markers don't have such standards + return values -def _get_env(environment: Dict[str, str], name: str) -> str: - value: Union[str, Undefined] = environment.get(name, _undefined) - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - f"{name!r} does not exist in evaluation environment." - ) - - return value - - -def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: +def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: groups: List[List[bool]] = [[]] for marker in markers: @@ -231,12 +139,15 @@ def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: lhs, op, rhs = marker if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) + environment_key = lhs.value + lhs_value = environment[environment_key] rhs_value = rhs.value else: lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) + environment_key = rhs.value + rhs_value = environment[environment_key] + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) groups[-1].append(_eval_op(lhs_value, op, rhs_value)) else: assert marker in ["and", "or"] @@ -274,13 +185,29 @@ def default_environment() -> Dict[str, str]: class Marker: def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - raise InvalidMarker( - f"Invalid marker: {marker!r}, parse error at " - f"{marker[e.loc : e.loc + 8]!r}" - ) + self._markers = _normalize_extra_values(parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e def __str__(self) -> str: return _format_marker(self._markers) @@ -288,6 +215,15 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"" + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: """Evaluate a marker. @@ -298,7 +234,12 @@ def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: The environment is determined from the current Python process. """ current_environment = default_environment() + current_environment["extra"] = "" if environment is not None: current_environment.update(environment) + # The API used to allow setting extra to None. We need to handle this + # case for backwards compatibility. + if current_environment["extra"] is None: + current_environment["extra"] = "" return _evaluate_markers(self._markers, current_environment) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py index 6af14ec4c..a9f9b9c7c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/requirements.py @@ -2,26 +2,13 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -import re -import string import urllib.parse -from typing import List, Optional as TOptional, Set +from typing import Any, List, Optional, Set -from pkg_resources.extern.pyparsing import ( # noqa - Combine, - Literal as L, - Optional, - ParseException, - Regex, - Word, - ZeroOrMore, - originalTextFor, - stringEnd, - stringStart, -) - -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet +from ._parser import parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet class InvalidRequirement(ValueError): @@ -30,60 +17,6 @@ class InvalidRequirement(ValueError): """ -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# pkg_resources.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - class Requirement: """Parse a requirement. @@ -99,28 +32,29 @@ class Requirement: def __init__(self, requirement_string: str) -> None: try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' - ) - - self.name: str = req.name - if req.url: - parsed_url = urllib.parse.urlparse(req.url) + parsed = parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + if parsed.url: + parsed_url = urllib.parse.urlparse(parsed.url) if parsed_url.scheme == "file": - if urllib.parse.urlunparse(parsed_url) != req.url: + if urllib.parse.urlunparse(parsed_url) != parsed.url: raise InvalidRequirement("Invalid URL given") elif not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): - raise InvalidRequirement(f"Invalid URL: {req.url}") - self.url: TOptional[str] = req.url + raise InvalidRequirement(f"Invalid URL: {parsed.url}") + self.url: Optional[str] = parsed.url else: self.url = None - self.extras: Set[str] = set(req.extras.asList() if req.extras else []) - self.specifier: SpecifierSet = SpecifierSet(req.specifier) - self.marker: TOptional[Marker] = req.marker if req.marker else None + self.extras: Set[str] = set(parsed.extras if parsed.extras else []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Optional[Marker] = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) def __str__(self) -> str: parts: List[str] = [self.name] @@ -144,3 +78,18 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + self.name == other.name + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py index 0e218a6f9..e715ecc8c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/specifiers.py @@ -1,20 +1,22 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" import abc -import functools import itertools import re -import warnings from typing import ( Callable, - Dict, Iterable, Iterator, List, Optional, - Pattern, Set, Tuple, TypeVar, @@ -22,17 +24,28 @@ ) from .utils import canonicalize_version -from .version import LegacyVersion, Version, parse +from .version import Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] -ParsedVersion = Union[Version, LegacyVersion] -UnparsedVersion = Union[Version, LegacyVersion, str] -VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) -CallableOperator = Callable[[ParsedVersion, str], bool] + +def _coerce_version(version: UnparsedVersion) -> Version: + if not isinstance(version, Version): + version = Version(version) + return version class InvalidSpecifier(ValueError): """ - An invalid specifier was found, users should refer to PEP 440. + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' """ @@ -40,35 +53,39 @@ class BaseSpecifier(metaclass=abc.ABCMeta): @abc.abstractmethod def __str__(self) -> str: """ - Returns the str representation of this Specifier like object. This + Returns the str representation of this Specifier-like object. This should be representative of the Specifier itself. """ @abc.abstractmethod def __hash__(self) -> int: """ - Returns a hash value for this Specifier like object. + Returns a hash value for this Specifier-like object. """ @abc.abstractmethod def __eq__(self, other: object) -> bool: """ - Returns a boolean representing whether or not the two Specifier like + Returns a boolean representing whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. """ - @abc.abstractproperty + @property + @abc.abstractmethod def prereleases(self) -> Optional[bool]: - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. """ @prereleases.setter def prereleases(self, value: bool) -> None: - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. + """Setter for :attr:`prereleases`. + + :param value: The value to set. """ @abc.abstractmethod @@ -79,227 +96,28 @@ def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: @abc.abstractmethod def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. """ -class _IndividualSpecifier(BaseSpecifier): - - _operators: Dict[str, str] = {} - _regex: Pattern[str] - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - - self._spec: Tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self) -> str: - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"<{self.__class__.__name__}({str(self)!r}{pre})>" - - def __str__(self) -> str: - return "{}{}".format(*self._spec) - - @property - def _canonical_spec(self) -> Tuple[str, str]: - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self) -> int: - return hash(self._canonical_spec) - - def __eq__(self, other: object) -> bool: - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def _get_operator(self, op: str) -> CallableOperator: - operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" - ) - return operator_callable - - def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. - @property - def operator(self) -> str: - return self._spec[0] + .. tip:: - @property - def version(self) -> str: - return self._spec[1] - - @property - def prereleases(self) -> Optional[bool]: - return self._prereleases - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __contains__(self, item: str) -> bool: - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable: CallableOperator = self._get_operator(self.operator) - return operator_callable(normalized_item, self.version) - - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later in case nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - super().__init__(spec, prereleases) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal( - self, prospective: LegacyVersion, spec: str - ) -> bool: - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn: Callable[["Specifier", ParsedVersion, str], bool] -) -> Callable[["Specifier", ParsedVersion, str], bool]: - @functools.wraps(fn) - def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ - _regex_str = r""" + _operator_regex_str = r""" (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" (?P (?: # The identity operators allow for an escape hatch that will @@ -309,8 +127,10 @@ class Specifier(_IndividualSpecifier): # but included entirely as an escape hatch. (?<====) # Only match for the identity operator \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. + [^\s;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. ) | (?: @@ -323,23 +143,23 @@ class Specifier(_IndividualSpecifier): v? (?:[0-9]+!)? # epoch [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* )? ) | @@ -354,7 +174,7 @@ class Specifier(_IndividualSpecifier): [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) (?: # pre release [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) + (alpha|beta|preview|pre|a|b|c|rc) [-_\.]? [0-9]* )? @@ -379,7 +199,7 @@ class Specifier(_IndividualSpecifier): [0-9]+(?:\.[0-9]+)* # release (?: # pre release [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) + (alpha|beta|preview|pre|a|b|c|rc) [-_\.]? [0-9]* )? @@ -391,7 +211,10 @@ class Specifier(_IndividualSpecifier): ) """ - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile( + r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$", + re.VERBOSE | re.IGNORECASE, + ) _operators = { "~=": "compatible", @@ -404,8 +227,152 @@ class Specifier(_IndividualSpecifier): "===": "arbitrary", } - @_require_version_compare - def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + @property + def prereleases(self) -> bool: + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if Version(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + canonical_version = canonicalize_version( + self._spec[1], + strip_trailing_zero=(self._spec[0] != "~="), + ) + return self._spec[0], canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: str) -> bool: # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to @@ -426,34 +393,33 @@ def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: prospective, prefix ) - @_require_version_compare - def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: + def _compare_equal(self, prospective: Version, spec: str) -> bool: # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) + normalized_prospective = canonicalize_version(prospective.public) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* + split_spec = _version_split(normalized_spec) # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. - split_prospective = _version_split(str(prospective)) + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] + shortened_prospective = padded_prospective[: len(split_spec)] - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec + return shortened_prospective == split_spec else: # Convert our spec string into a Version spec_version = Version(spec) @@ -466,30 +432,24 @@ def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: return prospective == spec_version - @_require_version_compare - def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: return not self._compare_equal(prospective, spec) - @_require_version_compare - def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: # NB: Local version identifiers are NOT permitted in the version # specifier, so local version labels can be universally removed from # the prospective version. return Version(prospective.public) <= Version(spec) - @_require_version_compare - def _compare_greater_than_equal( - self, prospective: ParsedVersion, spec: str - ) -> bool: + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: # NB: Local version identifiers are NOT permitted in the version # specifier, so local version labels can be universally removed from # the prospective version. return Version(prospective.public) >= Version(spec) - @_require_version_compare - def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: # Convert our spec to a Version instance, since we'll want to work with # it as a version. @@ -514,8 +474,7 @@ def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: # version in the spec. return True - @_require_version_compare - def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: # Convert our spec to a Version instance, since we'll want to work with # it as a version. @@ -549,34 +508,133 @@ def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bo def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: return str(prospective).lower() == str(spec).lower() - @property - def prereleases(self) -> bool: + def __contains__(self, item: Union[str, Version]) -> bool: + """Return whether or not the item is contained in this specifier. - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases + :param item: The item to check for. - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) - return False + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + False + >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") + True + >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) + True + """ - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version, this allows us to have a shortcut for + # "2.0" in Specifier(">=2") + normalized_item = _coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(Specifier().contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = _coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later in case nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") @@ -618,22 +676,39 @@ def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + def __init__( self, specifiers: str = "", prereleases: Optional[bool] = None ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ - # Split on , to break each individual specifier into it's own item, and + # Split on `,` to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed: Set[_IndividualSpecifier] = set() + # Specifier. + parsed: Set[Specifier] = set() for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) + parsed.add(Specifier(specifier)) # Turn our parsed specifiers into a frozen set and save them for later. self._specs = frozenset(parsed) @@ -642,7 +717,40 @@ def __init__( # we accept prereleases or not. self._prereleases = prereleases + @property + def prereleases(self) -> Optional[bool]: + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ pre = ( f", prereleases={self.prereleases!r}" if self._prereleases is not None @@ -652,12 +760,31 @@ def __repr__(self) -> str: return f"" def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ return ",".join(sorted(str(s) for s in self._specs)) def __hash__(self) -> int: return hash(self._specs) def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ if isinstance(other, str): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): @@ -681,7 +808,25 @@ def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": return specifier def __eq__(self, other: object) -> bool: - if isinstance(other, (str, _IndividualSpecifier)): + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -689,43 +834,72 @@ def __eq__(self, other: object) -> bool: return self._specs == other._specs def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" return len(self._specs) - def __iter__(self) -> Iterator[_IndividualSpecifier]: - return iter(self._specs) - - @property - def prereleases(self) -> Optional[bool]: - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ return self.contains(item) def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None + self, + item: UnparsedVersion, + prereleases: Optional[bool] = None, + installed: Optional[bool] = None, ) -> bool: - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + # Ensure that our item is a Version instance. + if not isinstance(item, Version): + item = Version(item) # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the @@ -742,6 +916,9 @@ def contains( if not prereleases and item.is_prerelease: return False + if installed and item.is_prerelease: + item = Version(item.base_version) + # We simply dispatch to the underlying specs here to make sure that the # given version is contained within all of them. # Note: This use of all() here means that an empty set of specifiers @@ -749,9 +926,46 @@ def contains( return all(s.contains(item, prereleases=prereleases) for s in self._specs) def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + [] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. @@ -764,27 +978,16 @@ def filter( if self._specs: for spec in self._specs: iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable + return iter(iterable) # If we do not have any specifiers, then we need to have a rough filter # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. + # releases. else: - filtered: List[VersionTypeVar] = [] - found_prereleases: List[VersionTypeVar] = [] - - item: UnparsedVersion - parsed_version: Union[Version, LegacyVersion] + filtered: List[UnparsedVersionVar] = [] + found_prereleases: List[UnparsedVersionVar] = [] for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue + parsed_version = _coerce_version(item) # Store any item which is a pre-release for later unless we've # already found a final version or we are accepting prereleases @@ -797,6 +1000,6 @@ def filter( # If we've found no items except for pre-releases, then we'll go # ahead and use the pre-releases if not filtered and found_prereleases and prereleases is None: - return found_prereleases + return iter(found_prereleases) - return filtered + return iter(filtered) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py index 9a3d25a71..19ccbde3e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/tags.py @@ -4,6 +4,7 @@ import logging import platform +import subprocess import sys import sysconfig from importlib.machinery import EXTENSION_SUFFIXES @@ -36,7 +37,7 @@ } -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 +_32_BIT_INTERPRETER = sys.maxsize <= 2**32 class Tag: @@ -224,10 +225,45 @@ def cpython_tags( yield Tag(interpreter, "abi3", platform_) -def _generic_abi() -> Iterator[str]: - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) +def _generic_abi() -> List[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] def generic_tags( @@ -251,8 +287,9 @@ def generic_tags( interpreter = "".join([interp_name, interp_version]) if abis is None: abis = _generic_abi() + else: + abis = list(abis) platforms = list(platforms or platform_tags()) - abis = list(abis) if "none" not in abis: abis.append("none") for abi in abis: @@ -356,6 +393,22 @@ def mac_platforms( version_str, _, cpu_arch = platform.mac_ver() if version is None: version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) else: version = version if arch is None: @@ -446,6 +499,9 @@ def platform_tags() -> Iterator[str]: def interpreter_name() -> str: """ Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. """ name = sys.implementation.name return INTERPRETER_SHORT_NAMES.get(name) or name @@ -482,6 +538,9 @@ def sys_tags(*, warn: bool = False) -> Iterator[Tag]: yield from generic_tags() if interp_name == "pp": - yield from compatible_tags(interpreter="pp3") + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) else: - yield from compatible_tags() + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py index bab11b80c..33c613b74 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/utils.py @@ -35,7 +35,9 @@ def canonicalize_name(name: str) -> NormalizedName: return cast(NormalizedName, value) -def canonicalize_version(version: Union[Version, str]) -> str: +def canonicalize_version( + version: Union[Version, str], *, strip_trailing_zero: bool = True +) -> str: """ This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. @@ -56,8 +58,11 @@ def canonicalize_version(version: Union[Version, str]) -> str: parts.append(f"{parsed.epoch}!") # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) + release_segment = ".".join(str(x) for x in parsed.release) + if strip_trailing_zero: + # NB: This strips trailing '.0's to normalize + release_segment = re.sub(r"(\.0)+$", "", release_segment) + parts.append(release_segment) # Pre-release if parsed.pre is not None: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/version.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/version.py index de9a09a4e..e5c738cfd 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/packaging/version.py @@ -1,16 +1,20 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +""" +.. testsetup:: + + from packaging.version import parse, Version +""" import collections import itertools import re -import warnings -from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union +from typing import Callable, Optional, SupportsInt, Tuple, Union from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] +__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] InfiniteTypes = Union[InfinityType, NegativeInfinityType] PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] @@ -29,36 +33,37 @@ CmpKey = Tuple[ int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType ] -LegacyCmpKey = Tuple[int, Tuple[str, ...]] -VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool -] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] _Version = collections.namedtuple( "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) -def parse(version: str) -> Union["LegacyVersion", "Version"]: - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. +def parse(version: str) -> "Version": + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) + return Version(version) class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' """ class _BaseVersion: - _key: Union[CmpKey, LegacyCmpKey] + _key: CmpKey def __hash__(self) -> int: return hash(self._key) @@ -103,126 +108,9 @@ def __ne__(self, other: object) -> bool: return self._key != other._key -class LegacyVersion(_BaseVersion): - def __init__(self, version: str) -> None: - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def __str__(self) -> str: - return self._version - - def __repr__(self) -> str: - return f"" - - @property - def public(self) -> str: - return self._version - - @property - def base_version(self) -> str: - return self._version - - @property - def epoch(self) -> int: - return -1 - - @property - def release(self) -> None: - return None - - @property - def pre(self) -> None: - return None - - @property - def post(self) -> None: - return None - - @property - def dev(self) -> None: - return None - - @property - def local(self) -> None: - return None - - @property - def is_prerelease(self) -> bool: - return False - - @property - def is_postrelease(self) -> bool: - return False - - @property - def is_devrelease(self) -> bool: - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s: str) -> Iterator[str]: - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version: str) -> LegacyCmpKey: - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts: List[str] = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - # Deliberately not anchored to the start and end of the string, to make it # easier for 3rd party code to reuse -VERSION_PATTERN = r""" +_VERSION_PATTERN = r""" v? (?: (?:(?P[0-9]+)!)? # epoch @@ -253,12 +141,55 @@ def _legacy_cmpkey(version: str) -> LegacyCmpKey: (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version """ +VERSION_PATTERN = _VERSION_PATTERN +""" +A string containing the regular expression used to match a valid version. + +The pattern is not anchored at either end, and is intended for embedding in larger +expressions (for example, matching a version number as part of a file name). The +regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE`` +flags set. + +:meta hide-value: +""" + class Version(_BaseVersion): + """This class abstracts handling of a project's versions. + + A :class:`Version` instance is comparison aware and can be compared and + sorted using the standard Python interfaces. + + >>> v1 = Version("1.0a5") + >>> v2 = Version("1.0") + >>> v1 + + >>> v2 + + >>> v1 < v2 + True + >>> v1 == v2 + False + >>> v1 > v2 + False + >>> v1 >= v2 + False + >>> v1 <= v2 + True + """ _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) def __init__(self, version: str) -> None: + """Initialize a Version object. + + :param version: + The string representation of a version which will be parsed and normalized + before use. + :raises InvalidVersion: + If the ``version`` does not conform to PEP 440 in any way then this + exception will be raised. + """ # Validate the version and parse it into pieces match = self._regex.search(version) @@ -288,9 +219,19 @@ def __init__(self, version: str) -> None: ) def __repr__(self) -> str: + """A representation of the Version that shows all internal state. + + >>> Version('1.0.0') + + """ return f"" def __str__(self) -> str: + """A string representation of the version that can be rounded-tripped. + + >>> str(Version("1.0a5")) + '1.0a5' + """ parts = [] # Epoch @@ -320,29 +261,80 @@ def __str__(self) -> str: @property def epoch(self) -> int: + """The epoch of the version. + + >>> Version("2.0.0").epoch + 0 + >>> Version("1!2.0.0").epoch + 1 + """ _epoch: int = self._version.epoch return _epoch @property def release(self) -> Tuple[int, ...]: + """The components of the "release" segment of the version. + + >>> Version("1.2.3").release + (1, 2, 3) + >>> Version("2.0.0").release + (2, 0, 0) + >>> Version("1!2.0.0.post0").release + (2, 0, 0) + + Includes trailing zeroes but not the epoch or any pre-release / development / + post-release suffixes. + """ _release: Tuple[int, ...] = self._version.release return _release @property def pre(self) -> Optional[Tuple[str, int]]: + """The pre-release segment of the version. + + >>> print(Version("1.2.3").pre) + None + >>> Version("1.2.3a1").pre + ('a', 1) + >>> Version("1.2.3b1").pre + ('b', 1) + >>> Version("1.2.3rc1").pre + ('rc', 1) + """ _pre: Optional[Tuple[str, int]] = self._version.pre return _pre @property def post(self) -> Optional[int]: + """The post-release number of the version. + + >>> print(Version("1.2.3").post) + None + >>> Version("1.2.3.post1").post + 1 + """ return self._version.post[1] if self._version.post else None @property def dev(self) -> Optional[int]: + """The development number of the version. + + >>> print(Version("1.2.3").dev) + None + >>> Version("1.2.3.dev1").dev + 1 + """ return self._version.dev[1] if self._version.dev else None @property def local(self) -> Optional[str]: + """The local version segment of the version. + + >>> print(Version("1.2.3").local) + None + >>> Version("1.2.3+abc").local + 'abc' + """ if self._version.local: return ".".join(str(x) for x in self._version.local) else: @@ -350,10 +342,31 @@ def local(self) -> Optional[str]: @property def public(self) -> str: + """The public portion of the version. + + >>> Version("1.2.3").public + '1.2.3' + >>> Version("1.2.3+abc").public + '1.2.3' + >>> Version("1.2.3+abc.dev1").public + '1.2.3' + """ return str(self).split("+", 1)[0] @property def base_version(self) -> str: + """The "base version" of the version. + + >>> Version("1.2.3").base_version + '1.2.3' + >>> Version("1.2.3+abc").base_version + '1.2.3' + >>> Version("1!1.2.3+abc.dev1").base_version + '1!1.2.3' + + The "base version" is the public version of the project without any pre or post + release markers. + """ parts = [] # Epoch @@ -367,26 +380,72 @@ def base_version(self) -> str: @property def is_prerelease(self) -> bool: + """Whether this version is a pre-release. + + >>> Version("1.2.3").is_prerelease + False + >>> Version("1.2.3a1").is_prerelease + True + >>> Version("1.2.3b1").is_prerelease + True + >>> Version("1.2.3rc1").is_prerelease + True + >>> Version("1.2.3dev1").is_prerelease + True + """ return self.dev is not None or self.pre is not None @property def is_postrelease(self) -> bool: + """Whether this version is a post-release. + + >>> Version("1.2.3").is_postrelease + False + >>> Version("1.2.3.post1").is_postrelease + True + """ return self.post is not None @property def is_devrelease(self) -> bool: + """Whether this version is a development release. + + >>> Version("1.2.3").is_devrelease + False + >>> Version("1.2.3.dev1").is_devrelease + True + """ return self.dev is not None @property def major(self) -> int: + """The first item of :attr:`release` or ``0`` if unavailable. + + >>> Version("1.2.3").major + 1 + """ return self.release[0] if len(self.release) >= 1 else 0 @property def minor(self) -> int: + """The second item of :attr:`release` or ``0`` if unavailable. + + >>> Version("1.2.3").minor + 2 + >>> Version("1").minor + 0 + """ return self.release[1] if len(self.release) >= 2 else 0 @property def micro(self) -> int: + """The third item of :attr:`release` or ``0`` if unavailable. + + >>> Version("1.2.3").micro + 3 + >>> Version("1").micro + 0 + """ return self.release[2] if len(self.release) >= 3 else 0 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py new file mode 100644 index 000000000..aef2821b8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__init__.py @@ -0,0 +1,342 @@ +""" +Utilities for determining application-specific dirs. See for details and +usage. +""" +from __future__ import annotations + +import os +import sys +from pathlib import Path + +if sys.version_info >= (3, 8): # pragma: no cover (py38+) + from typing import Literal +else: # pragma: no cover (py38+) + from ..typing_extensions import Literal + +from .api import PlatformDirsABC +from .version import __version__ +from .version import __version_tuple__ as __version_info__ + + +def _set_platform_dir_class() -> type[PlatformDirsABC]: + if sys.platform == "win32": + from .windows import Windows as Result + elif sys.platform == "darwin": + from .macos import MacOS as Result + else: + from .unix import Unix as Result + + if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": + + if os.getenv("SHELL") or os.getenv("PREFIX"): + return Result + + from .android import _android_folder + + if _android_folder() is not None: + from .android import Android + + return Android # return to avoid redefinition of result + + return Result + + +PlatformDirs = _set_platform_dir_class() #: Currently active platform +AppDirs = PlatformDirs #: Backwards compatibility with appdirs + + +def user_data_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: data directory tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_dir + + +def site_data_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + multipath: bool = False, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :returns: data directory shared by users + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_dir + + +def user_config_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: config directory tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_dir + + +def site_config_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + multipath: bool = False, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :returns: config directory shared by the users + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_dir + + +def user_cache_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + opinion: bool = True, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :returns: cache directory tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_dir + + +def user_state_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: state directory tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_dir + + +def user_log_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + opinion: bool = True, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :returns: log directory tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_dir + + +def user_documents_dir() -> str: + """ + :returns: documents directory tied to the user + """ + return PlatformDirs().user_documents_dir + + +def user_runtime_dir( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + opinion: bool = True, +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :returns: runtime directory tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_dir + + +def user_data_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: data path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_data_path + + +def site_data_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + multipath: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `multipath `. + :returns: data path shared by users + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_data_path + + +def user_config_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: config path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_config_path + + +def site_config_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + multipath: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :returns: config path shared by the users + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, multipath=multipath).site_config_path + + +def user_cache_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + opinion: bool = True, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :returns: cache path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_cache_path + + +def user_state_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :returns: state path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, roaming=roaming).user_state_path + + +def user_log_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + opinion: bool = True, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :returns: log path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_log_path + + +def user_documents_path() -> Path: + """ + :returns: documents path tied to the user + """ + return PlatformDirs().user_documents_path + + +def user_runtime_path( + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + opinion: bool = True, +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :returns: runtime path tied to the user + """ + return PlatformDirs(appname=appname, appauthor=appauthor, version=version, opinion=opinion).user_runtime_path + + +__all__ = [ + "__version__", + "__version_info__", + "PlatformDirs", + "AppDirs", + "PlatformDirsABC", + "user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "user_documents_dir", + "user_runtime_dir", + "site_data_dir", + "site_config_dir", + "user_data_path", + "user_config_path", + "user_cache_path", + "user_state_path", + "user_log_path", + "user_documents_path", + "user_runtime_path", + "site_data_path", + "site_config_path", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py new file mode 100644 index 000000000..0fc1edd59 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/__main__.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from platformdirs import PlatformDirs, __version__ + +PROPS = ( + "user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "user_documents_dir", + "user_runtime_dir", + "site_data_dir", + "site_config_dir", +) + + +def main() -> None: + app_name = "MyApp" + app_author = "MyCompany" + + print(f"-- platformdirs {__version__} --") + + print("-- app dirs (with optional 'version')") + dirs = PlatformDirs(app_name, app_author, version="1.0") + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") + + print("\n-- app dirs (without optional 'version')") + dirs = PlatformDirs(app_name, app_author) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") + + print("\n-- app dirs (without optional 'appauthor')") + dirs = PlatformDirs(app_name) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") + + print("\n-- app dirs (with disabled 'appauthor')") + dirs = PlatformDirs(app_name, appauthor=False) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") + + +if __name__ == "__main__": + main() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/android.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/android.py new file mode 100644 index 000000000..eda809351 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/android.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +import os +import re +import sys +from functools import lru_cache +from typing import cast + +from .api import PlatformDirsABC + + +class Android(PlatformDirsABC): + """ + Follows the guidance `from here `_. Makes use of the + `appname ` and + `version `. + """ + + @property + def user_data_dir(self) -> str: + """:return: data directory tied to the user, e.g. ``/data/user///files/``""" + return self._append_app_name_and_version(cast(str, _android_folder()), "files") + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_config_dir(self) -> str: + """ + :return: config directory tied to the user, e.g. ``/data/user///shared_prefs/`` + """ + return self._append_app_name_and_version(cast(str, _android_folder()), "shared_prefs") + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, same as `user_config_dir`""" + return self.user_config_dir + + @property + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user, e.g. e.g. ``/data/user///cache/``""" + return self._append_app_name_and_version(cast(str, _android_folder()), "cache") + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_log_dir(self) -> str: + """ + :return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it, + e.g. ``/data/user///cache//log`` + """ + path = self.user_cache_dir + if self.opinion: + path = os.path.join(path, "log") + return path + + @property + def user_documents_dir(self) -> str: + """ + :return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents`` + """ + return _android_documents_folder() + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, + e.g. ``/data/user///cache//tmp`` + """ + path = self.user_cache_dir + if self.opinion: + path = os.path.join(path, "tmp") + return path + + +@lru_cache(maxsize=1) +def _android_folder() -> str | None: + """:return: base folder for the Android OS or None if cannot be found""" + try: + # First try to get path to android app via pyjnius + from jnius import autoclass + + Context = autoclass("android.content.Context") # noqa: N806 + result: str | None = Context.getFilesDir().getParentFile().getAbsolutePath() + except Exception: + # if fails find an android folder looking path on the sys.path + pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files") + for path in sys.path: + if pattern.match(path): + result = path.split("/files")[0] + break + else: + result = None + return result + + +@lru_cache(maxsize=1) +def _android_documents_folder() -> str: + """:return: documents folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass + + Context = autoclass("android.content.Context") # noqa: N806 + Environment = autoclass("android.os.Environment") # noqa: N806 + documents_dir: str = Context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + except Exception: + documents_dir = "/storage/emulated/0/Documents" + + return documents_dir + + +__all__ = [ + "Android", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py new file mode 100644 index 000000000..6f6e2c2c6 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/api.py @@ -0,0 +1,156 @@ +from __future__ import annotations + +import os +import sys +from abc import ABC, abstractmethod +from pathlib import Path + +if sys.version_info >= (3, 8): # pragma: no branch + from typing import Literal # pragma: no cover + + +class PlatformDirsABC(ABC): + """ + Abstract base class for platform directories. + """ + + def __init__( + self, + appname: str | None = None, + appauthor: str | None | Literal[False] = None, + version: str | None = None, + roaming: bool = False, + multipath: bool = False, + opinion: bool = True, + ): + """ + Create a new platform directory. + + :param appname: See `appname`. + :param appauthor: See `appauthor`. + :param version: See `version`. + :param roaming: See `roaming`. + :param multipath: See `multipath`. + :param opinion: See `opinion`. + """ + self.appname = appname #: The name of application. + self.appauthor = appauthor + """ + The name of the app author or distributing body for this application. Typically, it is the owning company name. + Defaults to `appname`. You may pass ``False`` to disable it. + """ + self.version = version + """ + An optional version path element to append to the path. You might want to use this if you want multiple versions + of your app to be able to run independently. If used, this would typically be ``.``. + """ + self.roaming = roaming + """ + Whether to use the roaming appdata directory on Windows. That means that for users on a Windows network setup + for roaming profiles, this user data will be synced on login (see + `here `_). + """ + self.multipath = multipath + """ + An optional parameter only applicable to Unix/Linux which indicates that the entire list of data dirs should be + returned. By default, the first item would only be returned. + """ + self.opinion = opinion #: A flag to indicating to use opinionated values. + + def _append_app_name_and_version(self, *base: str) -> str: + params = list(base[1:]) + if self.appname: + params.append(self.appname) + if self.version: + params.append(self.version) + return os.path.join(base[0], *params) + + @property + @abstractmethod + def user_data_dir(self) -> str: + """:return: data directory tied to the user""" + + @property + @abstractmethod + def site_data_dir(self) -> str: + """:return: data directory shared by users""" + + @property + @abstractmethod + def user_config_dir(self) -> str: + """:return: config directory tied to the user""" + + @property + @abstractmethod + def site_config_dir(self) -> str: + """:return: config directory shared by the users""" + + @property + @abstractmethod + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user""" + + @property + @abstractmethod + def user_state_dir(self) -> str: + """:return: state directory tied to the user""" + + @property + @abstractmethod + def user_log_dir(self) -> str: + """:return: log directory tied to the user""" + + @property + @abstractmethod + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user""" + + @property + @abstractmethod + def user_runtime_dir(self) -> str: + """:return: runtime directory tied to the user""" + + @property + def user_data_path(self) -> Path: + """:return: data path tied to the user""" + return Path(self.user_data_dir) + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users""" + return Path(self.site_data_dir) + + @property + def user_config_path(self) -> Path: + """:return: config path tied to the user""" + return Path(self.user_config_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users""" + return Path(self.site_config_dir) + + @property + def user_cache_path(self) -> Path: + """:return: cache path tied to the user""" + return Path(self.user_cache_dir) + + @property + def user_state_path(self) -> Path: + """:return: state path tied to the user""" + return Path(self.user_state_dir) + + @property + def user_log_path(self) -> Path: + """:return: log path tied to the user""" + return Path(self.user_log_dir) + + @property + def user_documents_path(self) -> Path: + """:return: documents path tied to the user""" + return Path(self.user_documents_dir) + + @property + def user_runtime_path(self) -> Path: + """:return: runtime path tied to the user""" + return Path(self.user_runtime_dir) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py new file mode 100644 index 000000000..a01337c77 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/macos.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import os + +from .api import PlatformDirsABC + + +class MacOS(PlatformDirsABC): + """ + Platform directories for the macOS operating system. Follows the guidance from `Apple documentation + `_. + Makes use of the `appname ` and + `version `. + """ + + @property + def user_data_dir(self) -> str: + """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support/")) + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``""" + return self._append_app_name_and_version("/Library/Application Support") + + @property + def user_config_dir(self) -> str: + """:return: config directory tied to the user, e.g. ``~/Library/Preferences/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Preferences/")) + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, e.g. ``/Library/Preferences/$appname``""" + return self._append_app_name_and_version("/Library/Preferences") + + @property + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches")) + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_log_dir(self) -> str: + """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user, e.g. ``~/Documents``""" + return os.path.expanduser("~/Documents") + + @property + def user_runtime_dir(self) -> str: + """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) + + +__all__ = [ + "MacOS", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py new file mode 100644 index 000000000..9aca5a030 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/unix.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +import os +import sys +from configparser import ConfigParser +from pathlib import Path + +from .api import PlatformDirsABC + +if sys.platform.startswith("linux"): # pragma: no branch # no op check, only to please the type checker + from os import getuid +else: + + def getuid() -> int: + raise RuntimeError("should only be used on Linux") + + +class Unix(PlatformDirsABC): + """ + On Unix/Linux, we follow the + `XDG Basedir Spec `_. The spec allows + overriding directories with environment variables. The examples show are the default values, alongside the name of + the environment variable that overrides them. Makes use of the + `appname `, + `version `, + `multipath `, + `opinion `. + """ + + @property + def user_data_dir(self) -> str: + """ + :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or + ``$XDG_DATA_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_DATA_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.local/share") + return self._append_app_name_and_version(path) + + @property + def site_data_dir(self) -> str: + """ + :return: data directories shared by users (if `multipath ` is + enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS + path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version`` + """ + # XDG default for $XDG_DATA_DIRS; only first, if multipath is False + path = os.environ.get("XDG_DATA_DIRS", "") + if not path.strip(): + path = f"/usr/local/share{os.pathsep}/usr/share" + return self._with_multi_path(path) + + def _with_multi_path(self, path: str) -> str: + path_list = path.split(os.pathsep) + if not self.multipath: + path_list = path_list[0:1] + path_list = [self._append_app_name_and_version(os.path.expanduser(p)) for p in path_list] + return os.pathsep.join(path_list) + + @property + def user_config_dir(self) -> str: + """ + :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or + ``$XDG_CONFIG_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_CONFIG_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.config") + return self._append_app_name_and_version(path) + + @property + def site_config_dir(self) -> str: + """ + :return: config directories shared by users (if `multipath ` + is enabled and ``XDG_DATA_DIR`` is set and a multi path the response is also a multi path separated by the OS + path separator), e.g. ``/etc/xdg/$appname/$version`` + """ + # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False + path = os.environ.get("XDG_CONFIG_DIRS", "") + if not path.strip(): + path = "/etc/xdg" + return self._with_multi_path(path) + + @property + def user_cache_dir(self) -> str: + """ + :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or + ``~/$XDG_CACHE_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_CACHE_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.cache") + return self._append_app_name_and_version(path) + + @property + def user_state_dir(self) -> str: + """ + :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or + ``$XDG_STATE_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_STATE_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.local/state") + return self._append_app_name_and_version(path) + + @property + def user_log_dir(self) -> str: + """ + :return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it + """ + path = self.user_state_dir + if self.opinion: + path = os.path.join(path, "log") + return path + + @property + def user_documents_dir(self) -> str: + """ + :return: documents directory tied to the user, e.g. ``~/Documents`` + """ + documents_dir = _get_user_dirs_folder("XDG_DOCUMENTS_DIR") + if documents_dir is None: + documents_dir = os.environ.get("XDG_DOCUMENTS_DIR", "").strip() + if not documents_dir: + documents_dir = os.path.expanduser("~/Documents") + + return documents_dir + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or + ``$XDG_RUNTIME_DIR/$appname/$version`` + """ + path = os.environ.get("XDG_RUNTIME_DIR", "") + if not path.strip(): + path = f"/run/user/{getuid()}" + return self._append_app_name_and_version(path) + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users. Only return first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_data_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users. Only return first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_config_dir) + + def _first_item_as_path_if_multipath(self, directory: str) -> Path: + if self.multipath: + # If multipath is True, the first path is returned. + directory = directory.split(os.pathsep)[0] + return Path(directory) + + +def _get_user_dirs_folder(key: str) -> str | None: + """Return directory from user-dirs.dirs config file. See https://freedesktop.org/wiki/Software/xdg-user-dirs/""" + user_dirs_config_path = os.path.join(Unix().user_config_dir, "user-dirs.dirs") + if os.path.exists(user_dirs_config_path): + parser = ConfigParser() + + with open(user_dirs_config_path) as stream: + # Add fake section header, so ConfigParser doesn't complain + parser.read_string(f"[top]\n{stream.read()}") + + if key not in parser["top"]: + return None + + path = parser["top"][key].strip('"') + # Handle relative home paths + path = path.replace("$HOME", os.path.expanduser("~")) + return path + + return None + + +__all__ = [ + "Unix", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py new file mode 100644 index 000000000..9f6eb98e8 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/version.py @@ -0,0 +1,4 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +__version__ = version = '2.6.2' +__version_tuple__ = version_tuple = (2, 6, 2) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py new file mode 100644 index 000000000..d5c27b341 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/platformdirs/windows.py @@ -0,0 +1,184 @@ +from __future__ import annotations + +import ctypes +import os +import sys +from functools import lru_cache +from typing import Callable + +from .api import PlatformDirsABC + + +class Windows(PlatformDirsABC): + """`MSDN on where to store app data files + `_. + Makes use of the + `appname `, + `appauthor `, + `version `, + `roaming `, + `opinion `.""" + + @property + def user_data_dir(self) -> str: + """ + :return: data directory tied to the user, e.g. + ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or + ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming) + """ + const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(get_win_folder(const)) + return self._append_parts(path) + + def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str: + params = [] + if self.appname: + if self.appauthor is not False: + author = self.appauthor or self.appname + params.append(author) + params.append(self.appname) + if opinion_value is not None and self.opinion: + params.append(opinion_value) + if self.version: + params.append(self.version) + return os.path.join(path, *params) + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``""" + path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) + return self._append_parts(path) + + @property + def user_config_dir(self) -> str: + """:return: config directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, same as `site_data_dir`""" + return self.site_data_dir + + @property + def user_cache_dir(self) -> str: + """ + :return: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g. + ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version`` + """ + path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA")) + return self._append_parts(path, opinion_value="Cache") + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_log_dir(self) -> str: + """ + :return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it + """ + path = self.user_data_dir + if self.opinion: + path = os.path.join(path, "Logs") + return path + + @property + def user_documents_dir(self) -> str: + """ + :return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents`` + """ + return os.path.normpath(get_win_folder("CSIDL_PERSONAL")) + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, e.g. + ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname`` + """ + path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) + return self._append_parts(path) + + +def get_win_folder_from_env_vars(csidl_name: str) -> str: + """Get folder from environment variables.""" + if csidl_name == "CSIDL_PERSONAL": # does not have an environment name + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") + + env_var_name = { + "CSIDL_APPDATA": "APPDATA", + "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE", + "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA", + }.get(csidl_name) + if env_var_name is None: + raise ValueError(f"Unknown CSIDL name: {csidl_name}") + result = os.environ.get(env_var_name) + if result is None: + raise ValueError(f"Unset environment variable: {env_var_name}") + return result + + +def get_win_folder_from_registry(csidl_name: str) -> str: + """Get folder from the registry. + + This is a fallback technique at best. I'm not sure if using the + registry for this guarantees us the correct answer for all CSIDL_* + names. + """ + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + "CSIDL_PERSONAL": "Personal", + }.get(csidl_name) + if shell_folder_name is None: + raise ValueError(f"Unknown CSIDL name: {csidl_name}") + if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows + raise NotImplementedError + import winreg + + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") + directory, _ = winreg.QueryValueEx(key, shell_folder_name) + return str(directory) + + +def get_win_folder_via_ctypes(csidl_name: str) -> str: + """Get folder with ctypes.""" + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + "CSIDL_PERSONAL": 5, + }.get(csidl_name) + if csidl_const is None: + raise ValueError(f"Unknown CSIDL name: {csidl_name}") + + buf = ctypes.create_unicode_buffer(1024) + windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker + windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if it has highbit chars. + if any(ord(c) > 255 for c in buf): + buf2 = ctypes.create_unicode_buffer(1024) + if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + return buf.value + + +def _pick_get_win_folder() -> Callable[[str], str]: + if hasattr(ctypes, "windll"): + return get_win_folder_via_ctypes + try: + import winreg # noqa: F401 + except ImportError: + return get_win_folder_from_env_vars + else: + return get_win_folder_from_registry + + +get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder()) + +__all__ = [ + "Windows", +] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py deleted file mode 100644 index 7802ff158..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/__init__.py +++ /dev/null @@ -1,331 +0,0 @@ -# module pyparsing.py -# -# Copyright (c) 2003-2022 Paul T. McGuire -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__doc__ = """ -pyparsing module - Classes and methods to define and execute parsing grammars -============================================================================= - -The pyparsing module is an alternative approach to creating and -executing simple grammars, vs. the traditional lex/yacc approach, or the -use of regular expressions. With pyparsing, you don't need to learn -a new syntax for defining grammars or matching expressions - the parsing -module provides a library of classes that you use to construct the -grammar directly in Python. - -Here is a program to parse "Hello, World!" (or any greeting of the form -``", !"``), built up using :class:`Word`, -:class:`Literal`, and :class:`And` elements -(the :meth:`'+'` operators create :class:`And` expressions, -and the strings are auto-converted to :class:`Literal` expressions):: - - from pyparsing import Word, alphas - - # define grammar of a greeting - greet = Word(alphas) + "," + Word(alphas) + "!" - - hello = "Hello, World!" - print(hello, "->", greet.parse_string(hello)) - -The program outputs the following:: - - Hello, World! -> ['Hello', ',', 'World', '!'] - -The Python representation of the grammar is quite readable, owing to the -self-explanatory class names, and the use of :class:`'+'`, -:class:`'|'`, :class:`'^'` and :class:`'&'` operators. - -The :class:`ParseResults` object returned from -:class:`ParserElement.parseString` can be -accessed as a nested list, a dictionary, or an object with named -attributes. - -The pyparsing module handles some of the problems that are typically -vexing when writing text parsers: - - - extra or missing whitespace (the above program will also handle - "Hello,World!", "Hello , World !", etc.) - - quoted strings - - embedded comments - - -Getting Started - ------------------ -Visit the classes :class:`ParserElement` and :class:`ParseResults` to -see the base classes that most other pyparsing -classes inherit from. Use the docstrings for examples of how to: - - - construct literal match expressions from :class:`Literal` and - :class:`CaselessLiteral` classes - - construct character word-group expressions using the :class:`Word` - class - - see how to create repetitive expressions using :class:`ZeroOrMore` - and :class:`OneOrMore` classes - - use :class:`'+'`, :class:`'|'`, :class:`'^'`, - and :class:`'&'` operators to combine simple expressions into - more complex ones - - associate names with your parsed results using - :class:`ParserElement.setResultsName` - - access the parsed data, which is returned as a :class:`ParseResults` - object - - find some helpful expression short-cuts like :class:`delimitedList` - and :class:`oneOf` - - find more useful common expressions in the :class:`pyparsing_common` - namespace class -""" -from typing import NamedTuple - - -class version_info(NamedTuple): - major: int - minor: int - micro: int - releaselevel: str - serial: int - - @property - def __version__(self): - return ( - "{}.{}.{}".format(self.major, self.minor, self.micro) - + ( - "{}{}{}".format( - "r" if self.releaselevel[0] == "c" else "", - self.releaselevel[0], - self.serial, - ), - "", - )[self.releaselevel == "final"] - ) - - def __str__(self): - return "{} {} / {}".format(__name__, self.__version__, __version_time__) - - def __repr__(self): - return "{}.{}({})".format( - __name__, - type(self).__name__, - ", ".join("{}={!r}".format(*nv) for nv in zip(self._fields, self)), - ) - - -__version_info__ = version_info(3, 0, 9, "final", 0) -__version_time__ = "05 May 2022 07:02 UTC" -__version__ = __version_info__.__version__ -__versionTime__ = __version_time__ -__author__ = "Paul McGuire " - -from .util import * -from .exceptions import * -from .actions import * -from .core import __diag__, __compat__ -from .results import * -from .core import * -from .core import _builtin_exprs as core_builtin_exprs -from .helpers import * -from .helpers import _builtin_exprs as helper_builtin_exprs - -from .unicode import unicode_set, UnicodeRangeList, pyparsing_unicode as unicode -from .testing import pyparsing_test as testing -from .common import ( - pyparsing_common as common, - _builtin_exprs as common_builtin_exprs, -) - -# define backward compat synonyms -if "pyparsing_unicode" not in globals(): - pyparsing_unicode = unicode -if "pyparsing_common" not in globals(): - pyparsing_common = common -if "pyparsing_test" not in globals(): - pyparsing_test = testing - -core_builtin_exprs += common_builtin_exprs + helper_builtin_exprs - - -__all__ = [ - "__version__", - "__version_time__", - "__author__", - "__compat__", - "__diag__", - "And", - "AtLineStart", - "AtStringStart", - "CaselessKeyword", - "CaselessLiteral", - "CharsNotIn", - "Combine", - "Dict", - "Each", - "Empty", - "FollowedBy", - "Forward", - "GoToColumn", - "Group", - "IndentedBlock", - "Keyword", - "LineEnd", - "LineStart", - "Literal", - "Located", - "PrecededBy", - "MatchFirst", - "NoMatch", - "NotAny", - "OneOrMore", - "OnlyOnce", - "OpAssoc", - "Opt", - "Optional", - "Or", - "ParseBaseException", - "ParseElementEnhance", - "ParseException", - "ParseExpression", - "ParseFatalException", - "ParseResults", - "ParseSyntaxException", - "ParserElement", - "PositionToken", - "QuotedString", - "RecursiveGrammarException", - "Regex", - "SkipTo", - "StringEnd", - "StringStart", - "Suppress", - "Token", - "TokenConverter", - "White", - "Word", - "WordEnd", - "WordStart", - "ZeroOrMore", - "Char", - "alphanums", - "alphas", - "alphas8bit", - "any_close_tag", - "any_open_tag", - "c_style_comment", - "col", - "common_html_entity", - "counted_array", - "cpp_style_comment", - "dbl_quoted_string", - "dbl_slash_comment", - "delimited_list", - "dict_of", - "empty", - "hexnums", - "html_comment", - "identchars", - "identbodychars", - "java_style_comment", - "line", - "line_end", - "line_start", - "lineno", - "make_html_tags", - "make_xml_tags", - "match_only_at_col", - "match_previous_expr", - "match_previous_literal", - "nested_expr", - "null_debug_action", - "nums", - "one_of", - "printables", - "punc8bit", - "python_style_comment", - "quoted_string", - "remove_quotes", - "replace_with", - "replace_html_entity", - "rest_of_line", - "sgl_quoted_string", - "srange", - "string_end", - "string_start", - "trace_parse_action", - "unicode_string", - "with_attribute", - "indentedBlock", - "original_text_for", - "ungroup", - "infix_notation", - "locatedExpr", - "with_class", - "CloseMatch", - "token_map", - "pyparsing_common", - "pyparsing_unicode", - "unicode_set", - "condition_as_parse_action", - "pyparsing_test", - # pre-PEP8 compatibility names - "__versionTime__", - "anyCloseTag", - "anyOpenTag", - "cStyleComment", - "commonHTMLEntity", - "countedArray", - "cppStyleComment", - "dblQuotedString", - "dblSlashComment", - "delimitedList", - "dictOf", - "htmlComment", - "javaStyleComment", - "lineEnd", - "lineStart", - "makeHTMLTags", - "makeXMLTags", - "matchOnlyAtCol", - "matchPreviousExpr", - "matchPreviousLiteral", - "nestedExpr", - "nullDebugAction", - "oneOf", - "opAssoc", - "pythonStyleComment", - "quotedString", - "removeQuotes", - "replaceHTMLEntity", - "replaceWith", - "restOfLine", - "sglQuotedString", - "stringEnd", - "stringStart", - "traceParseAction", - "unicodeString", - "withAttribute", - "indentedBlock", - "originalTextFor", - "infixNotation", - "locatedExpr", - "withClass", - "tokenMap", - "conditionAsParseAction", - "autoname_elements", -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py deleted file mode 100644 index f72c66e74..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/actions.py +++ /dev/null @@ -1,207 +0,0 @@ -# actions.py - -from .exceptions import ParseException -from .util import col - - -class OnlyOnce: - """ - Wrapper for parse actions, to ensure they are only called once. - """ - - def __init__(self, method_call): - from .core import _trim_arity - - self.callable = _trim_arity(method_call) - self.called = False - - def __call__(self, s, l, t): - if not self.called: - results = self.callable(s, l, t) - self.called = True - return results - raise ParseException(s, l, "OnlyOnce obj called multiple times w/out reset") - - def reset(self): - """ - Allow the associated parse action to be called once more. - """ - - self.called = False - - -def match_only_at_col(n): - """ - Helper method for defining parse actions that require matching at - a specific column in the input text. - """ - - def verify_col(strg, locn, toks): - if col(locn, strg) != n: - raise ParseException(strg, locn, "matched token not at column {}".format(n)) - - return verify_col - - -def replace_with(repl_str): - """ - Helper method for common parse actions that simply return - a literal value. Especially useful when used with - :class:`transform_string` (). - - Example:: - - num = Word(nums).set_parse_action(lambda toks: int(toks[0])) - na = one_of("N/A NA").set_parse_action(replace_with(math.nan)) - term = na | num - - term[1, ...].parse_string("324 234 N/A 234") # -> [324, 234, nan, 234] - """ - return lambda s, l, t: [repl_str] - - -def remove_quotes(s, l, t): - """ - Helper parse action for removing quotation marks from parsed - quoted strings. - - Example:: - - # by default, quotation marks are included in parsed results - quoted_string.parse_string("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] - - # use remove_quotes to strip quotation marks from parsed results - quoted_string.set_parse_action(remove_quotes) - quoted_string.parse_string("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] - """ - return t[0][1:-1] - - -def with_attribute(*args, **attr_dict): - """ - Helper to create a validating parse action to be used with start - tags created with :class:`make_xml_tags` or - :class:`make_html_tags`. Use ``with_attribute`` to qualify - a starting tag with a required attribute value, to avoid false - matches on common tags such as ```` or ``
      ``. - - Call ``with_attribute`` with a series of attribute names and - values. Specify the list of filter attributes names and values as: - - - keyword arguments, as in ``(align="right")``, or - - as an explicit dict with ``**`` operator, when an attribute - name is also a Python reserved word, as in ``**{"class":"Customer", "align":"right"}`` - - a list of name-value tuples, as in ``(("ns1:class", "Customer"), ("ns2:align", "right"))`` - - For attribute names with a namespace prefix, you must use the second - form. Attribute names are matched insensitive to upper/lower case. - - If just testing for ``class`` (with or without a namespace), use - :class:`with_class`. - - To verify that the attribute exists, but without specifying a value, - pass ``with_attribute.ANY_VALUE`` as the value. - - Example:: - - html = ''' -
      - Some text -
      1 4 0 1 0
      -
      1,3 2,3 1,1
      -
      this has no type
      -
      - - ''' - div,div_end = make_html_tags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().set_parse_action(with_attribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.search_string(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().set_parse_action(with_attribute(type=with_attribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.search_string(html): - print(div_header.body) - - prints:: - - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attr_dict.items() - attrs = [(k, v) for k, v in attrs] - - def pa(s, l, tokens): - for attrName, attrValue in attrs: - if attrName not in tokens: - raise ParseException(s, l, "no matching attribute " + attrName) - if attrValue != with_attribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException( - s, - l, - "attribute {!r} has value {!r}, must be {!r}".format( - attrName, tokens[attrName], attrValue - ), - ) - - return pa - - -with_attribute.ANY_VALUE = object() - - -def with_class(classname, namespace=""): - """ - Simplified version of :class:`with_attribute` when - matching on a div class - made difficult because ``class`` is - a reserved word in Python. - - Example:: - - html = ''' -
      - Some text -
      1 4 0 1 0
      -
      1,3 2,3 1,1
      -
      this <div> has no class
      -
      - - ''' - div,div_end = make_html_tags("div") - div_grid = div().set_parse_action(with_class("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.search_string(html): - print(grid_header.body) - - div_any_type = div().set_parse_action(with_class(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.search_string(html): - print(div_header.body) - - prints:: - - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "{}:class".format(namespace) if namespace else "class" - return with_attribute(**{classattr: classname}) - - -# pre-PEP8 compatibility symbols -replaceWith = replace_with -removeQuotes = remove_quotes -withAttribute = with_attribute -withClass = with_class -matchOnlyAtCol = match_only_at_col diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py deleted file mode 100644 index 1859fb79c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/common.py +++ /dev/null @@ -1,424 +0,0 @@ -# common.py -from .core import * -from .helpers import delimited_list, any_open_tag, any_close_tag -from datetime import datetime - - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """Here are some common low-level expressions that may be useful in - jump-starting parser development: - - - numeric forms (:class:`integers`, :class:`reals`, - :class:`scientific notation`) - - common :class:`programming identifiers` - - network addresses (:class:`MAC`, - :class:`IPv4`, :class:`IPv6`) - - ISO8601 :class:`dates` and - :class:`datetime` - - :class:`UUID` - - :class:`comma-separated list` - - :class:`url` - - Parse actions: - - - :class:`convertToInteger` - - :class:`convertToFloat` - - :class:`convertToDate` - - :class:`convertToDatetime` - - :class:`stripHTMLTags` - - :class:`upcaseTokens` - - :class:`downcaseTokens` - - Example:: - - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - - prints:: - - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convert_to_integer = token_map(int) - """ - Parse action for converting parsed integers to Python int - """ - - convert_to_float = token_map(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).set_name("integer").set_parse_action(convert_to_integer) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = ( - Word(hexnums).set_name("hex integer").set_parse_action(token_map(int, 16)) - ) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = ( - Regex(r"[+-]?\d+") - .set_name("signed integer") - .set_parse_action(convert_to_integer) - ) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = ( - signed_integer().set_parse_action(convert_to_float) - + "/" - + signed_integer().set_parse_action(convert_to_float) - ).set_name("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.add_parse_action(lambda tt: tt[0] / tt[-1]) - - mixed_integer = ( - fraction | signed_integer + Opt(Opt("-").suppress() + fraction) - ).set_name("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.add_parse_action(sum) - - real = ( - Regex(r"[+-]?(?:\d+\.\d*|\.\d+)") - .set_name("real number") - .set_parse_action(convert_to_float) - ) - """expression that parses a floating point number and returns a float""" - - sci_real = ( - Regex(r"[+-]?(?:\d+(?:[eE][+-]?\d+)|(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?)") - .set_name("real number with scientific notation") - .set_parse_action(convert_to_float) - ) - """expression that parses a floating point number with optional - scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).setName("number").streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = ( - Regex(r"[+-]?\d+\.?\d*([eE][+-]?\d+)?") - .set_name("fnumber") - .set_parse_action(convert_to_float) - ) - """any int or real number, returned as float""" - - identifier = Word(identchars, identbodychars).set_name("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex( - r"(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}" - ).set_name("IPv4 address") - "IPv4 address (``0.0.0.0 - 255.255.255.255``)" - - _ipv6_part = Regex(r"[0-9a-fA-F]{1,4}").set_name("hex_integer") - _full_ipv6_address = (_ipv6_part + (":" + _ipv6_part) * 7).set_name( - "full IPv6 address" - ) - _short_ipv6_address = ( - Opt(_ipv6_part + (":" + _ipv6_part) * (0, 6)) - + "::" - + Opt(_ipv6_part + (":" + _ipv6_part) * (0, 6)) - ).set_name("short IPv6 address") - _short_ipv6_address.add_condition( - lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8 - ) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).set_name("mixed IPv6 address") - ipv6_address = Combine( - (_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).set_name( - "IPv6 address" - ) - ).set_name("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex( - r"[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}" - ).set_name("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convert_to_date(fmt: str = "%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%d"``) - - Example:: - - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - - prints:: - - [datetime.date(1999, 12, 31)] - """ - - def cvt_fn(ss, ll, tt): - try: - return datetime.strptime(tt[0], fmt).date() - except ValueError as ve: - raise ParseException(ss, ll, str(ve)) - - return cvt_fn - - @staticmethod - def convert_to_datetime(fmt: str = "%Y-%m-%dT%H:%M:%S.%f"): - """Helper to create a parse action for converting parsed - datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%dT%H:%M:%S.%f"``) - - Example:: - - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - - prints:: - - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - - def cvt_fn(s, l, t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - - return cvt_fn - - iso8601_date = Regex( - r"(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?" - ).set_name("ISO8601 date") - "ISO8601 date (``yyyy-mm-dd``)" - - iso8601_datetime = Regex( - r"(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?" - ).set_name("ISO8601 datetime") - "ISO8601 datetime (``yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)``) - trailing seconds, milliseconds, and timezone optional; accepts separating ``'T'`` or ``' '``" - - uuid = Regex(r"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}").set_name("UUID") - "UUID (``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``)" - - _html_stripper = any_open_tag.suppress() | any_close_tag.suppress() - - @staticmethod - def strip_html_tags(s: str, l: int, tokens: ParseResults): - """Parse action to remove HTML tags from web page HTML source - - Example:: - - # strip HTML links from normal text - text = 'More info at the
      pyparsing wiki page' - td, td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - print(table_text.parseString(text).body) - - Prints:: - - More info at the pyparsing wiki page - """ - return pyparsing_common._html_stripper.transform_string(tokens[0]) - - _commasepitem = ( - Combine( - OneOrMore( - ~Literal(",") - + ~LineEnd() - + Word(printables, exclude_chars=",") - + Opt(White(" \t") + ~FollowedBy(LineEnd() | ",")) - ) - ) - .streamline() - .set_name("commaItem") - ) - comma_separated_list = delimited_list( - Opt(quoted_string.copy() | _commasepitem, default="") - ).set_name("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcase_tokens = staticmethod(token_map(lambda t: t.upper())) - """Parse action to convert tokens to upper case.""" - - downcase_tokens = staticmethod(token_map(lambda t: t.lower())) - """Parse action to convert tokens to lower case.""" - - # fmt: off - url = Regex( - # https://mathiasbynens.be/demo/url-regex - # https://gist.github.com/dperini/729294 - r"^" + - # protocol identifier (optional) - # short syntax // still required - r"(?:(?:(?Phttps?|ftp):)?\/\/)" + - # user:pass BasicAuth (optional) - r"(?:(?P\S+(?::\S*)?)@)?" + - r"(?P" + - # IP address exclusion - # private & local networks - r"(?!(?:10|127)(?:\.\d{1,3}){3})" + - r"(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})" + - r"(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})" + - # IP address dotted notation octets - # excludes loopback network 0.0.0.0 - # excludes reserved space >= 224.0.0.0 - # excludes network & broadcast addresses - # (first & last IP address of each class) - r"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])" + - r"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}" + - r"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))" + - r"|" + - # host & domain names, may end with dot - # can be replaced by a shortest alternative - # (?![-_])(?:[-\w\u00a1-\uffff]{0,63}[^-_]\.)+ - r"(?:" + - r"(?:" + - r"[a-z0-9\u00a1-\uffff]" + - r"[a-z0-9\u00a1-\uffff_-]{0,62}" + - r")?" + - r"[a-z0-9\u00a1-\uffff]\." + - r")+" + - # TLD identifier name, may end with dot - r"(?:[a-z\u00a1-\uffff]{2,}\.?)" + - r")" + - # port number (optional) - r"(:(?P\d{2,5}))?" + - # resource path (optional) - r"(?P\/[^?# ]*)?" + - # query string (optional) - r"(\?(?P[^#]*))?" + - # fragment (optional) - r"(#(?P\S*))?" + - r"$" - ).set_name("url") - # fmt: on - - # pre-PEP8 compatibility names - convertToInteger = convert_to_integer - convertToFloat = convert_to_float - convertToDate = convert_to_date - convertToDatetime = convert_to_datetime - stripHTMLTags = strip_html_tags - upcaseTokens = upcase_tokens - downcaseTokens = downcase_tokens - - -_builtin_exprs = [ - v for v in vars(pyparsing_common).values() if isinstance(v, ParserElement) -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py deleted file mode 100644 index 9acba3f3e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/core.py +++ /dev/null @@ -1,5814 +0,0 @@ -# -# core.py -# -import os -import typing -from typing import ( - NamedTuple, - Union, - Callable, - Any, - Generator, - Tuple, - List, - TextIO, - Set, - Sequence, -) -from abc import ABC, abstractmethod -from enum import Enum -import string -import copy -import warnings -import re -import sys -from collections.abc import Iterable -import traceback -import types -from operator import itemgetter -from functools import wraps -from threading import RLock -from pathlib import Path - -from .util import ( - _FifoCache, - _UnboundedCache, - __config_flags, - _collapse_string_to_ranges, - _escape_regex_range_chars, - _bslash, - _flatten, - LRUMemo as _LRUMemo, - UnboundedMemo as _UnboundedMemo, -) -from .exceptions import * -from .actions import * -from .results import ParseResults, _ParseResultsWithOffset -from .unicode import pyparsing_unicode - -_MAX_INT = sys.maxsize -str_type: Tuple[type, ...] = (str, bytes) - -# -# Copyright (c) 2003-2022 Paul T. McGuire -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - - -if sys.version_info >= (3, 8): - from functools import cached_property -else: - - class cached_property: - def __init__(self, func): - self._func = func - - def __get__(self, instance, owner=None): - ret = instance.__dict__[self._func.__name__] = self._func(instance) - return ret - - -class __compat__(__config_flags): - """ - A cross-version compatibility configuration for pyparsing features that will be - released in a future version. By setting values in this configuration to True, - those features can be enabled in prior versions for compatibility development - and testing. - - - ``collect_all_And_tokens`` - flag to enable fix for Issue #63 that fixes erroneous grouping - of results names when an :class:`And` expression is nested within an :class:`Or` or :class:`MatchFirst`; - maintained for compatibility, but setting to ``False`` no longer restores pre-2.3.1 - behavior - """ - - _type_desc = "compatibility" - - collect_all_And_tokens = True - - _all_names = [__ for __ in locals() if not __.startswith("_")] - _fixed_names = """ - collect_all_And_tokens - """.split() - - -class __diag__(__config_flags): - _type_desc = "diagnostic" - - warn_multiple_tokens_in_named_alternation = False - warn_ungrouped_named_tokens_in_collection = False - warn_name_set_on_empty_Forward = False - warn_on_parse_using_empty_Forward = False - warn_on_assignment_to_Forward = False - warn_on_multiple_string_args_to_oneof = False - warn_on_match_first_with_lshift_operator = False - enable_debug_on_named_expressions = False - - _all_names = [__ for __ in locals() if not __.startswith("_")] - _warning_names = [name for name in _all_names if name.startswith("warn")] - _debug_names = [name for name in _all_names if name.startswith("enable_debug")] - - @classmethod - def enable_all_warnings(cls) -> None: - for name in cls._warning_names: - cls.enable(name) - - -class Diagnostics(Enum): - """ - Diagnostic configuration (all default to disabled) - - ``warn_multiple_tokens_in_named_alternation`` - flag to enable warnings when a results - name is defined on a :class:`MatchFirst` or :class:`Or` expression with one or more :class:`And` subexpressions - - ``warn_ungrouped_named_tokens_in_collection`` - flag to enable warnings when a results - name is defined on a containing expression with ungrouped subexpressions that also - have results names - - ``warn_name_set_on_empty_Forward`` - flag to enable warnings when a :class:`Forward` is defined - with a results name, but has no contents defined - - ``warn_on_parse_using_empty_Forward`` - flag to enable warnings when a :class:`Forward` is - defined in a grammar but has never had an expression attached to it - - ``warn_on_assignment_to_Forward`` - flag to enable warnings when a :class:`Forward` is defined - but is overwritten by assigning using ``'='`` instead of ``'<<='`` or ``'<<'`` - - ``warn_on_multiple_string_args_to_oneof`` - flag to enable warnings when :class:`one_of` is - incorrectly called with multiple str arguments - - ``enable_debug_on_named_expressions`` - flag to auto-enable debug on all subsequent - calls to :class:`ParserElement.set_name` - - Diagnostics are enabled/disabled by calling :class:`enable_diag` and :class:`disable_diag`. - All warnings can be enabled by calling :class:`enable_all_warnings`. - """ - - warn_multiple_tokens_in_named_alternation = 0 - warn_ungrouped_named_tokens_in_collection = 1 - warn_name_set_on_empty_Forward = 2 - warn_on_parse_using_empty_Forward = 3 - warn_on_assignment_to_Forward = 4 - warn_on_multiple_string_args_to_oneof = 5 - warn_on_match_first_with_lshift_operator = 6 - enable_debug_on_named_expressions = 7 - - -def enable_diag(diag_enum: Diagnostics) -> None: - """ - Enable a global pyparsing diagnostic flag (see :class:`Diagnostics`). - """ - __diag__.enable(diag_enum.name) - - -def disable_diag(diag_enum: Diagnostics) -> None: - """ - Disable a global pyparsing diagnostic flag (see :class:`Diagnostics`). - """ - __diag__.disable(diag_enum.name) - - -def enable_all_warnings() -> None: - """ - Enable all global pyparsing diagnostic warnings (see :class:`Diagnostics`). - """ - __diag__.enable_all_warnings() - - -# hide abstract class -del __config_flags - - -def _should_enable_warnings( - cmd_line_warn_options: typing.Iterable[str], warn_env_var: typing.Optional[str] -) -> bool: - enable = bool(warn_env_var) - for warn_opt in cmd_line_warn_options: - w_action, w_message, w_category, w_module, w_line = (warn_opt + "::::").split( - ":" - )[:5] - if not w_action.lower().startswith("i") and ( - not (w_message or w_category or w_module) or w_module == "pyparsing" - ): - enable = True - elif w_action.lower().startswith("i") and w_module in ("pyparsing", ""): - enable = False - return enable - - -if _should_enable_warnings( - sys.warnoptions, os.environ.get("PYPARSINGENABLEALLWARNINGS") -): - enable_all_warnings() - - -# build list of single arg builtins, that can be used as parse actions -_single_arg_builtins = { - sum, - len, - sorted, - reversed, - list, - tuple, - set, - any, - all, - min, - max, -} - -_generatorType = types.GeneratorType -ParseAction = Union[ - Callable[[], Any], - Callable[[ParseResults], Any], - Callable[[int, ParseResults], Any], - Callable[[str, int, ParseResults], Any], -] -ParseCondition = Union[ - Callable[[], bool], - Callable[[ParseResults], bool], - Callable[[int, ParseResults], bool], - Callable[[str, int, ParseResults], bool], -] -ParseFailAction = Callable[[str, int, "ParserElement", Exception], None] -DebugStartAction = Callable[[str, int, "ParserElement", bool], None] -DebugSuccessAction = Callable[ - [str, int, int, "ParserElement", ParseResults, bool], None -] -DebugExceptionAction = Callable[[str, int, "ParserElement", Exception, bool], None] - - -alphas = string.ascii_uppercase + string.ascii_lowercase -identchars = pyparsing_unicode.Latin1.identchars -identbodychars = pyparsing_unicode.Latin1.identbodychars -nums = "0123456789" -hexnums = nums + "ABCDEFabcdef" -alphanums = alphas + nums -printables = "".join([c for c in string.printable if c not in string.whitespace]) - -_trim_arity_call_line: traceback.StackSummary = None - - -def _trim_arity(func, max_limit=3): - """decorator to trim function calls to match the arity of the target""" - global _trim_arity_call_line - - if func in _single_arg_builtins: - return lambda s, l, t: func(t) - - limit = 0 - found_arity = False - - def extract_tb(tb, limit=0): - frames = traceback.extract_tb(tb, limit=limit) - frame_summary = frames[-1] - return [frame_summary[:2]] - - # synthesize what would be returned by traceback.extract_stack at the call to - # user's parse action 'func', so that we don't incur call penalty at parse time - - # fmt: off - LINE_DIFF = 7 - # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND - # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! - _trim_arity_call_line = (_trim_arity_call_line or traceback.extract_stack(limit=2)[-1]) - pa_call_line_synth = (_trim_arity_call_line[0], _trim_arity_call_line[1] + LINE_DIFF) - - def wrapper(*args): - nonlocal found_arity, limit - while 1: - try: - ret = func(*args[limit:]) - found_arity = True - return ret - except TypeError as te: - # re-raise TypeErrors if they did not come from our arity testing - if found_arity: - raise - else: - tb = te.__traceback__ - trim_arity_type_error = ( - extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth - ) - del tb - - if trim_arity_type_error: - if limit < max_limit: - limit += 1 - continue - - raise - # fmt: on - - # copy func name to wrapper for sensible debug output - # (can't use functools.wraps, since that messes with function signature) - func_name = getattr(func, "__name__", getattr(func, "__class__").__name__) - wrapper.__name__ = func_name - wrapper.__doc__ = func.__doc__ - - return wrapper - - -def condition_as_parse_action( - fn: ParseCondition, message: str = None, fatal: bool = False -) -> ParseAction: - """ - Function to convert a simple predicate function that returns ``True`` or ``False`` - into a parse action. Can be used in places when a parse action is required - and :class:`ParserElement.add_condition` cannot be used (such as when adding a condition - to an operator level in :class:`infix_notation`). - - Optional keyword arguments: - - - ``message`` - define a custom message to be used in the raised exception - - ``fatal`` - if True, will raise :class:`ParseFatalException` to stop parsing immediately; - otherwise will raise :class:`ParseException` - - """ - msg = message if message is not None else "failed user-defined condition" - exc_type = ParseFatalException if fatal else ParseException - fn = _trim_arity(fn) - - @wraps(fn) - def pa(s, l, t): - if not bool(fn(s, l, t)): - raise exc_type(s, l, msg) - - return pa - - -def _default_start_debug_action( - instring: str, loc: int, expr: "ParserElement", cache_hit: bool = False -): - cache_hit_str = "*" if cache_hit else "" - print( - ( - "{}Match {} at loc {}({},{})\n {}\n {}^".format( - cache_hit_str, - expr, - loc, - lineno(loc, instring), - col(loc, instring), - line(loc, instring), - " " * (col(loc, instring) - 1), - ) - ) - ) - - -def _default_success_debug_action( - instring: str, - startloc: int, - endloc: int, - expr: "ParserElement", - toks: ParseResults, - cache_hit: bool = False, -): - cache_hit_str = "*" if cache_hit else "" - print("{}Matched {} -> {}".format(cache_hit_str, expr, toks.as_list())) - - -def _default_exception_debug_action( - instring: str, - loc: int, - expr: "ParserElement", - exc: Exception, - cache_hit: bool = False, -): - cache_hit_str = "*" if cache_hit else "" - print( - "{}Match {} failed, {} raised: {}".format( - cache_hit_str, expr, type(exc).__name__, exc - ) - ) - - -def null_debug_action(*args): - """'Do-nothing' debug action, to suppress debugging output during parsing.""" - - -class ParserElement(ABC): - """Abstract base level parser element class.""" - - DEFAULT_WHITE_CHARS: str = " \n\t\r" - verbose_stacktrace: bool = False - _literalStringClass: typing.Optional[type] = None - - @staticmethod - def set_default_whitespace_chars(chars: str) -> None: - r""" - Overrides the default whitespace chars - - Example:: - - # default whitespace chars are space, and newline - Word(alphas)[1, ...].parse_string("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] - - # change to just treat newline as significant - ParserElement.set_default_whitespace_chars(" \t") - Word(alphas)[1, ...].parse_string("abc def\nghi jkl") # -> ['abc', 'def'] - """ - ParserElement.DEFAULT_WHITE_CHARS = chars - - # update whitespace all parse expressions defined in this module - for expr in _builtin_exprs: - if expr.copyDefaultWhiteChars: - expr.whiteChars = set(chars) - - @staticmethod - def inline_literals_using(cls: type) -> None: - """ - Set class to be used for inclusion of string literals into a parser. - - Example:: - - # default literal class used is Literal - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - date_str.parse_string("1999/12/31") # -> ['1999', '/', '12', '/', '31'] - - - # change to Suppress - ParserElement.inline_literals_using(Suppress) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - date_str.parse_string("1999/12/31") # -> ['1999', '12', '31'] - """ - ParserElement._literalStringClass = cls - - class DebugActions(NamedTuple): - debug_try: typing.Optional[DebugStartAction] - debug_match: typing.Optional[DebugSuccessAction] - debug_fail: typing.Optional[DebugExceptionAction] - - def __init__(self, savelist: bool = False): - self.parseAction: List[ParseAction] = list() - self.failAction: typing.Optional[ParseFailAction] = None - self.customName = None - self._defaultName = None - self.resultsName = None - self.saveAsList = savelist - self.skipWhitespace = True - self.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS) - self.copyDefaultWhiteChars = True - # used when checking for left-recursion - self.mayReturnEmpty = False - self.keepTabs = False - self.ignoreExprs: List["ParserElement"] = list() - self.debug = False - self.streamlined = False - # optimize exception handling for subclasses that don't advance parse index - self.mayIndexError = True - self.errmsg = "" - # mark results names as modal (report only last) or cumulative (list all) - self.modalResults = True - # custom debug actions - self.debugActions = self.DebugActions(None, None, None) - # avoid redundant calls to preParse - self.callPreparse = True - self.callDuringTry = False - self.suppress_warnings_: List[Diagnostics] = [] - - def suppress_warning(self, warning_type: Diagnostics) -> "ParserElement": - """ - Suppress warnings emitted for a particular diagnostic on this expression. - - Example:: - - base = pp.Forward() - base.suppress_warning(Diagnostics.warn_on_parse_using_empty_Forward) - - # statement would normally raise a warning, but is now suppressed - print(base.parseString("x")) - - """ - self.suppress_warnings_.append(warning_type) - return self - - def copy(self) -> "ParserElement": - """ - Make a copy of this :class:`ParserElement`. Useful for defining - different parse actions for the same parsing pattern, using copies of - the original parse element. - - Example:: - - integer = Word(nums).set_parse_action(lambda toks: int(toks[0])) - integerK = integer.copy().add_parse_action(lambda toks: toks[0] * 1024) + Suppress("K") - integerM = integer.copy().add_parse_action(lambda toks: toks[0] * 1024 * 1024) + Suppress("M") - - print((integerK | integerM | integer)[1, ...].parse_string("5K 100 640K 256M")) - - prints:: - - [5120, 100, 655360, 268435456] - - Equivalent form of ``expr.copy()`` is just ``expr()``:: - - integerM = integer().add_parse_action(lambda toks: toks[0] * 1024 * 1024) + Suppress("M") - """ - cpy = copy.copy(self) - cpy.parseAction = self.parseAction[:] - cpy.ignoreExprs = self.ignoreExprs[:] - if self.copyDefaultWhiteChars: - cpy.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS) - return cpy - - def set_results_name( - self, name: str, list_all_matches: bool = False, *, listAllMatches: bool = False - ) -> "ParserElement": - """ - Define name for referencing matching tokens as a nested attribute - of the returned parse results. - - Normally, results names are assigned as you would assign keys in a dict: - any existing value is overwritten by later values. If it is necessary to - keep all values captured for a particular results name, call ``set_results_name`` - with ``list_all_matches`` = True. - - NOTE: ``set_results_name`` returns a *copy* of the original :class:`ParserElement` object; - this is so that the client can define a basic element, such as an - integer, and reference it in multiple places with different names. - - You can also set results names using the abbreviated syntax, - ``expr("name")`` in place of ``expr.set_results_name("name")`` - - see :class:`__call__`. If ``list_all_matches`` is required, use - ``expr("name*")``. - - Example:: - - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - - # equivalent form: - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - """ - listAllMatches = listAllMatches or list_all_matches - return self._setResultsName(name, listAllMatches) - - def _setResultsName(self, name, listAllMatches=False): - if name is None: - return self - newself = self.copy() - if name.endswith("*"): - name = name[:-1] - listAllMatches = True - newself.resultsName = name - newself.modalResults = not listAllMatches - return newself - - def set_break(self, break_flag: bool = True) -> "ParserElement": - """ - Method to invoke the Python pdb debugger when this element is - about to be parsed. Set ``break_flag`` to ``True`` to enable, ``False`` to - disable. - """ - if break_flag: - _parseMethod = self._parse - - def breaker(instring, loc, doActions=True, callPreParse=True): - import pdb - - # this call to pdb.set_trace() is intentional, not a checkin error - pdb.set_trace() - return _parseMethod(instring, loc, doActions, callPreParse) - - breaker._originalParseMethod = _parseMethod - self._parse = breaker - else: - if hasattr(self._parse, "_originalParseMethod"): - self._parse = self._parse._originalParseMethod - return self - - def set_parse_action(self, *fns: ParseAction, **kwargs) -> "ParserElement": - """ - Define one or more actions to perform when successfully matching parse element definition. - - Parse actions can be called to perform data conversions, do extra validation, - update external data structures, or enhance or replace the parsed tokens. - Each parse action ``fn`` is a callable method with 0-3 arguments, called as - ``fn(s, loc, toks)`` , ``fn(loc, toks)`` , ``fn(toks)`` , or just ``fn()`` , where: - - - s = the original string being parsed (see note below) - - loc = the location of the matching substring - - toks = a list of the matched tokens, packaged as a :class:`ParseResults` object - - The parsed tokens are passed to the parse action as ParseResults. They can be - modified in place using list-style append, extend, and pop operations to update - the parsed list elements; and with dictionary-style item set and del operations - to add, update, or remove any named results. If the tokens are modified in place, - it is not necessary to return them with a return statement. - - Parse actions can also completely replace the given tokens, with another ``ParseResults`` - object, or with some entirely different object (common for parse actions that perform data - conversions). A convenient way to build a new parse result is to define the values - using a dict, and then create the return value using :class:`ParseResults.from_dict`. - - If None is passed as the ``fn`` parse action, all previously added parse actions for this - expression are cleared. - - Optional keyword arguments: - - - call_during_try = (default= ``False``) indicate if parse action should be run during - lookaheads and alternate testing. For parse actions that have side effects, it is - important to only call the parse action once it is determined that it is being - called as part of a successful parse. For parse actions that perform additional - validation, then call_during_try should be passed as True, so that the validation - code is included in the preliminary "try" parses. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`parse_string` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - - Example:: - - # parse dates in the form YYYY/MM/DD - - # use parse action to convert toks from str to int at parse time - def convert_to_int(toks): - return int(toks[0]) - - # use a parse action to verify that the date is a valid date - def is_valid_date(instring, loc, toks): - from datetime import date - year, month, day = toks[::2] - try: - date(year, month, day) - except ValueError: - raise ParseException(instring, loc, "invalid date given") - - integer = Word(nums) - date_str = integer + '/' + integer + '/' + integer - - # add parse actions - integer.set_parse_action(convert_to_int) - date_str.set_parse_action(is_valid_date) - - # note that integer fields are now ints, not strings - date_str.run_tests(''' - # successful parse - note that integer fields were converted to ints - 1999/12/31 - - # fail - invalid date - 1999/13/31 - ''') - """ - if list(fns) == [None]: - self.parseAction = [] - else: - if not all(callable(fn) for fn in fns): - raise TypeError("parse actions must be callable") - self.parseAction = [_trim_arity(fn) for fn in fns] - self.callDuringTry = kwargs.get( - "call_during_try", kwargs.get("callDuringTry", False) - ) - return self - - def add_parse_action(self, *fns: ParseAction, **kwargs) -> "ParserElement": - """ - Add one or more parse actions to expression's list of parse actions. See :class:`set_parse_action`. - - See examples in :class:`copy`. - """ - self.parseAction += [_trim_arity(fn) for fn in fns] - self.callDuringTry = self.callDuringTry or kwargs.get( - "call_during_try", kwargs.get("callDuringTry", False) - ) - return self - - def add_condition(self, *fns: ParseCondition, **kwargs) -> "ParserElement": - """Add a boolean predicate function to expression's list of parse actions. See - :class:`set_parse_action` for function call signatures. Unlike ``set_parse_action``, - functions passed to ``add_condition`` need to return boolean success/fail of the condition. - - Optional keyword arguments: - - - message = define a custom message to be used in the raised exception - - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise - ParseException - - call_during_try = boolean to indicate if this method should be called during internal tryParse calls, - default=False - - Example:: - - integer = Word(nums).set_parse_action(lambda toks: int(toks[0])) - year_int = integer.copy() - year_int.add_condition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") - date_str = year_int + '/' + integer + '/' + integer - - result = date_str.parse_string("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), - (line:1, col:1) - """ - for fn in fns: - self.parseAction.append( - condition_as_parse_action( - fn, message=kwargs.get("message"), fatal=kwargs.get("fatal", False) - ) - ) - - self.callDuringTry = self.callDuringTry or kwargs.get( - "call_during_try", kwargs.get("callDuringTry", False) - ) - return self - - def set_fail_action(self, fn: ParseFailAction) -> "ParserElement": - """ - Define action to perform if parsing fails at this expression. - Fail acton fn is a callable function that takes the arguments - ``fn(s, loc, expr, err)`` where: - - - s = string being parsed - - loc = location where expression match was attempted and failed - - expr = the parse expression that failed - - err = the exception thrown - - The function returns no value. It may throw :class:`ParseFatalException` - if it is desired to stop parsing immediately.""" - self.failAction = fn - return self - - def _skipIgnorables(self, instring, loc): - exprsFound = True - while exprsFound: - exprsFound = False - for e in self.ignoreExprs: - try: - while 1: - loc, dummy = e._parse(instring, loc) - exprsFound = True - except ParseException: - pass - return loc - - def preParse(self, instring, loc): - if self.ignoreExprs: - loc = self._skipIgnorables(instring, loc) - - if self.skipWhitespace: - instrlen = len(instring) - white_chars = self.whiteChars - while loc < instrlen and instring[loc] in white_chars: - loc += 1 - - return loc - - def parseImpl(self, instring, loc, doActions=True): - return loc, [] - - def postParse(self, instring, loc, tokenlist): - return tokenlist - - # @profile - def _parseNoCache( - self, instring, loc, doActions=True, callPreParse=True - ) -> Tuple[int, ParseResults]: - TRY, MATCH, FAIL = 0, 1, 2 - debugging = self.debug # and doActions) - len_instring = len(instring) - - if debugging or self.failAction: - # print("Match {} at loc {}({}, {})".format(self, loc, lineno(loc, instring), col(loc, instring))) - try: - if callPreParse and self.callPreparse: - pre_loc = self.preParse(instring, loc) - else: - pre_loc = loc - tokens_start = pre_loc - if self.debugActions.debug_try: - self.debugActions.debug_try(instring, tokens_start, self, False) - if self.mayIndexError or pre_loc >= len_instring: - try: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - except IndexError: - raise ParseException(instring, len_instring, self.errmsg, self) - else: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - except Exception as err: - # print("Exception raised:", err) - if self.debugActions.debug_fail: - self.debugActions.debug_fail( - instring, tokens_start, self, err, False - ) - if self.failAction: - self.failAction(instring, tokens_start, self, err) - raise - else: - if callPreParse and self.callPreparse: - pre_loc = self.preParse(instring, loc) - else: - pre_loc = loc - tokens_start = pre_loc - if self.mayIndexError or pre_loc >= len_instring: - try: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - except IndexError: - raise ParseException(instring, len_instring, self.errmsg, self) - else: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - - tokens = self.postParse(instring, loc, tokens) - - ret_tokens = ParseResults( - tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults - ) - if self.parseAction and (doActions or self.callDuringTry): - if debugging: - try: - for fn in self.parseAction: - try: - tokens = fn(instring, tokens_start, ret_tokens) - except IndexError as parse_action_exc: - exc = ParseException("exception raised in parse action") - raise exc from parse_action_exc - - if tokens is not None and tokens is not ret_tokens: - ret_tokens = ParseResults( - tokens, - self.resultsName, - asList=self.saveAsList - and isinstance(tokens, (ParseResults, list)), - modal=self.modalResults, - ) - except Exception as err: - # print "Exception raised in user parse action:", err - if self.debugActions.debug_fail: - self.debugActions.debug_fail( - instring, tokens_start, self, err, False - ) - raise - else: - for fn in self.parseAction: - try: - tokens = fn(instring, tokens_start, ret_tokens) - except IndexError as parse_action_exc: - exc = ParseException("exception raised in parse action") - raise exc from parse_action_exc - - if tokens is not None and tokens is not ret_tokens: - ret_tokens = ParseResults( - tokens, - self.resultsName, - asList=self.saveAsList - and isinstance(tokens, (ParseResults, list)), - modal=self.modalResults, - ) - if debugging: - # print("Matched", self, "->", ret_tokens.as_list()) - if self.debugActions.debug_match: - self.debugActions.debug_match( - instring, tokens_start, loc, self, ret_tokens, False - ) - - return loc, ret_tokens - - def try_parse(self, instring: str, loc: int, raise_fatal: bool = False) -> int: - try: - return self._parse(instring, loc, doActions=False)[0] - except ParseFatalException: - if raise_fatal: - raise - raise ParseException(instring, loc, self.errmsg, self) - - def can_parse_next(self, instring: str, loc: int) -> bool: - try: - self.try_parse(instring, loc) - except (ParseException, IndexError): - return False - else: - return True - - # cache for left-recursion in Forward references - recursion_lock = RLock() - recursion_memos: typing.Dict[ - Tuple[int, "Forward", bool], Tuple[int, Union[ParseResults, Exception]] - ] = {} - - # argument cache for optimizing repeated calls when backtracking through recursive expressions - packrat_cache = ( - {} - ) # this is set later by enabled_packrat(); this is here so that reset_cache() doesn't fail - packrat_cache_lock = RLock() - packrat_cache_stats = [0, 0] - - # this method gets repeatedly called during backtracking with the same arguments - - # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression - def _parseCache( - self, instring, loc, doActions=True, callPreParse=True - ) -> Tuple[int, ParseResults]: - HIT, MISS = 0, 1 - TRY, MATCH, FAIL = 0, 1, 2 - lookup = (self, instring, loc, callPreParse, doActions) - with ParserElement.packrat_cache_lock: - cache = ParserElement.packrat_cache - value = cache.get(lookup) - if value is cache.not_in_cache: - ParserElement.packrat_cache_stats[MISS] += 1 - try: - value = self._parseNoCache(instring, loc, doActions, callPreParse) - except ParseBaseException as pe: - # cache a copy of the exception, without the traceback - cache.set(lookup, pe.__class__(*pe.args)) - raise - else: - cache.set(lookup, (value[0], value[1].copy(), loc)) - return value - else: - ParserElement.packrat_cache_stats[HIT] += 1 - if self.debug and self.debugActions.debug_try: - try: - self.debugActions.debug_try(instring, loc, self, cache_hit=True) - except TypeError: - pass - if isinstance(value, Exception): - if self.debug and self.debugActions.debug_fail: - try: - self.debugActions.debug_fail( - instring, loc, self, value, cache_hit=True - ) - except TypeError: - pass - raise value - - loc_, result, endloc = value[0], value[1].copy(), value[2] - if self.debug and self.debugActions.debug_match: - try: - self.debugActions.debug_match( - instring, loc_, endloc, self, result, cache_hit=True - ) - except TypeError: - pass - - return loc_, result - - _parse = _parseNoCache - - @staticmethod - def reset_cache() -> None: - ParserElement.packrat_cache.clear() - ParserElement.packrat_cache_stats[:] = [0] * len( - ParserElement.packrat_cache_stats - ) - ParserElement.recursion_memos.clear() - - _packratEnabled = False - _left_recursion_enabled = False - - @staticmethod - def disable_memoization() -> None: - """ - Disables active Packrat or Left Recursion parsing and their memoization - - This method also works if neither Packrat nor Left Recursion are enabled. - This makes it safe to call before activating Packrat nor Left Recursion - to clear any previous settings. - """ - ParserElement.reset_cache() - ParserElement._left_recursion_enabled = False - ParserElement._packratEnabled = False - ParserElement._parse = ParserElement._parseNoCache - - @staticmethod - def enable_left_recursion( - cache_size_limit: typing.Optional[int] = None, *, force=False - ) -> None: - """ - Enables "bounded recursion" parsing, which allows for both direct and indirect - left-recursion. During parsing, left-recursive :class:`Forward` elements are - repeatedly matched with a fixed recursion depth that is gradually increased - until finding the longest match. - - Example:: - - import pyparsing as pp - pp.ParserElement.enable_left_recursion() - - E = pp.Forward("E") - num = pp.Word(pp.nums) - # match `num`, or `num '+' num`, or `num '+' num '+' num`, ... - E <<= E + '+' - num | num - - print(E.parse_string("1+2+3")) - - Recursion search naturally memoizes matches of ``Forward`` elements and may - thus skip reevaluation of parse actions during backtracking. This may break - programs with parse actions which rely on strict ordering of side-effects. - - Parameters: - - - cache_size_limit - (default=``None``) - memoize at most this many - ``Forward`` elements during matching; if ``None`` (the default), - memoize all ``Forward`` elements. - - Bounded Recursion parsing works similar but not identical to Packrat parsing, - thus the two cannot be used together. Use ``force=True`` to disable any - previous, conflicting settings. - """ - if force: - ParserElement.disable_memoization() - elif ParserElement._packratEnabled: - raise RuntimeError("Packrat and Bounded Recursion are not compatible") - if cache_size_limit is None: - ParserElement.recursion_memos = _UnboundedMemo() - elif cache_size_limit > 0: - ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) - else: - raise NotImplementedError("Memo size of %s" % cache_size_limit) - ParserElement._left_recursion_enabled = True - - @staticmethod - def enable_packrat(cache_size_limit: int = 128, *, force: bool = False) -> None: - """ - Enables "packrat" parsing, which adds memoizing to the parsing logic. - Repeated parse attempts at the same string location (which happens - often in many complex grammars) can immediately return a cached value, - instead of re-executing parsing/validating code. Memoizing is done of - both valid results and parsing exceptions. - - Parameters: - - - cache_size_limit - (default= ``128``) - if an integer value is provided - will limit the size of the packrat cache; if None is passed, then - the cache size will be unbounded; if 0 is passed, the cache will - be effectively disabled. - - This speedup may break existing programs that use parse actions that - have side-effects. For this reason, packrat parsing is disabled when - you first import pyparsing. To activate the packrat feature, your - program must call the class method :class:`ParserElement.enable_packrat`. - For best results, call ``enable_packrat()`` immediately after - importing pyparsing. - - Example:: - - import pyparsing - pyparsing.ParserElement.enable_packrat() - - Packrat parsing works similar but not identical to Bounded Recursion parsing, - thus the two cannot be used together. Use ``force=True`` to disable any - previous, conflicting settings. - """ - if force: - ParserElement.disable_memoization() - elif ParserElement._left_recursion_enabled: - raise RuntimeError("Packrat and Bounded Recursion are not compatible") - if not ParserElement._packratEnabled: - ParserElement._packratEnabled = True - if cache_size_limit is None: - ParserElement.packrat_cache = _UnboundedCache() - else: - ParserElement.packrat_cache = _FifoCache(cache_size_limit) - ParserElement._parse = ParserElement._parseCache - - def parse_string( - self, instring: str, parse_all: bool = False, *, parseAll: bool = False - ) -> ParseResults: - """ - Parse a string with respect to the parser definition. This function is intended as the primary interface to the - client code. - - :param instring: The input string to be parsed. - :param parse_all: If set, the entire input string must match the grammar. - :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. - :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. - :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or - an object with attributes if the given parser includes results names. - - If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This - is also equivalent to ending the grammar with :class:`StringEnd`(). - - To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are - converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string - contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string - being parsed, one can ensure a consistent view of the input string by doing one of the following: - - - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the - parse action's ``s`` argument, or - - explicitly expand the tabs in your input string before calling ``parse_string``. - - Examples: - - By default, partial matches are OK. - - >>> res = Word('a').parse_string('aaaaabaaa') - >>> print(res) - ['aaaaa'] - - The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children - directly to see more examples. - - It raises an exception if parse_all flag is set and instring does not match the whole grammar. - - >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) - Traceback (most recent call last): - ... - pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) - """ - parseAll = parse_all or parseAll - - ParserElement.reset_cache() - if not self.streamlined: - self.streamline() - for e in self.ignoreExprs: - e.streamline() - if not self.keepTabs: - instring = instring.expandtabs() - try: - loc, tokens = self._parse(instring, 0) - if parseAll: - loc = self.preParse(instring, loc) - se = Empty() + StringEnd() - se._parse(instring, loc) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clearing out pyparsing internal stack trace - raise exc.with_traceback(None) - else: - return tokens - - def scan_string( - self, - instring: str, - max_matches: int = _MAX_INT, - overlap: bool = False, - *, - debug: bool = False, - maxMatches: int = _MAX_INT, - ) -> Generator[Tuple[ParseResults, int, int], None, None]: - """ - Scan the input string for expression matches. Each match will return the - matching tokens, start location, and end location. May be called with optional - ``max_matches`` argument, to clip scanning after 'n' matches are found. If - ``overlap`` is specified, then overlapping matches will be reported. - - Note that the start and end locations are reported relative to the string - being parsed. See :class:`parse_string` for more information on parsing - strings with embedded tabs. - - Example:: - - source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" - print(source) - for tokens, start, end in Word(alphas).scan_string(source): - print(' '*start + '^'*(end-start)) - print(' '*start + tokens[0]) - - prints:: - - sldjf123lsdjjkf345sldkjf879lkjsfd987 - ^^^^^ - sldjf - ^^^^^^^ - lsdjjkf - ^^^^^^ - sldkjf - ^^^^^^ - lkjsfd - """ - maxMatches = min(maxMatches, max_matches) - if not self.streamlined: - self.streamline() - for e in self.ignoreExprs: - e.streamline() - - if not self.keepTabs: - instring = str(instring).expandtabs() - instrlen = len(instring) - loc = 0 - preparseFn = self.preParse - parseFn = self._parse - ParserElement.resetCache() - matches = 0 - try: - while loc <= instrlen and matches < maxMatches: - try: - preloc = preparseFn(instring, loc) - nextLoc, tokens = parseFn(instring, preloc, callPreParse=False) - except ParseException: - loc = preloc + 1 - else: - if nextLoc > loc: - matches += 1 - if debug: - print( - { - "tokens": tokens.asList(), - "start": preloc, - "end": nextLoc, - } - ) - yield tokens, preloc, nextLoc - if overlap: - nextloc = preparseFn(instring, loc) - if nextloc > loc: - loc = nextLoc - else: - loc += 1 - else: - loc = nextLoc - else: - loc = preloc + 1 - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def transform_string(self, instring: str, *, debug: bool = False) -> str: - """ - Extension to :class:`scan_string`, to modify matching text with modified tokens that may - be returned from a parse action. To use ``transform_string``, define a grammar and - attach a parse action to it that modifies the returned token list. - Invoking ``transform_string()`` on a target string will then scan for matches, - and replace the matched text patterns according to the logic in the parse - action. ``transform_string()`` returns the resulting transformed string. - - Example:: - - wd = Word(alphas) - wd.set_parse_action(lambda toks: toks[0].title()) - - print(wd.transform_string("now is the winter of our discontent made glorious summer by this sun of york.")) - - prints:: - - Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. - """ - out: List[str] = [] - lastE = 0 - # force preservation of s, to minimize unwanted transformation of string, and to - # keep string locs straight between transform_string and scan_string - self.keepTabs = True - try: - for t, s, e in self.scan_string(instring, debug=debug): - out.append(instring[lastE:s]) - if t: - if isinstance(t, ParseResults): - out += t.as_list() - elif isinstance(t, Iterable) and not isinstance(t, str_type): - out.extend(t) - else: - out.append(t) - lastE = e - out.append(instring[lastE:]) - out = [o for o in out if o] - return "".join([str(s) for s in _flatten(out)]) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def search_string( - self, - instring: str, - max_matches: int = _MAX_INT, - *, - debug: bool = False, - maxMatches: int = _MAX_INT, - ) -> ParseResults: - """ - Another extension to :class:`scan_string`, simplifying the access to the tokens found - to match the given parse expression. May be called with optional - ``max_matches`` argument, to clip searching after 'n' matches are found. - - Example:: - - # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters - cap_word = Word(alphas.upper(), alphas.lower()) - - print(cap_word.search_string("More than Iron, more than Lead, more than Gold I need Electricity")) - - # the sum() builtin can be used to merge results into a single ParseResults object - print(sum(cap_word.search_string("More than Iron, more than Lead, more than Gold I need Electricity"))) - - prints:: - - [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] - ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] - """ - maxMatches = min(maxMatches, max_matches) - try: - return ParseResults( - [t for t, s, e in self.scan_string(instring, maxMatches, debug=debug)] - ) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def split( - self, - instring: str, - maxsplit: int = _MAX_INT, - include_separators: bool = False, - *, - includeSeparators=False, - ) -> Generator[str, None, None]: - """ - Generator method to split a string using the given expression as a separator. - May be called with optional ``maxsplit`` argument, to limit the number of splits; - and the optional ``include_separators`` argument (default= ``False``), if the separating - matching text should be included in the split results. - - Example:: - - punc = one_of(list(".,;:/-!?")) - print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) - - prints:: - - ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] - """ - includeSeparators = includeSeparators or include_separators - last = 0 - for t, s, e in self.scan_string(instring, max_matches=maxsplit): - yield instring[last:s] - if includeSeparators: - yield t[0] - last = e - yield instring[last:] - - def __add__(self, other) -> "ParserElement": - """ - Implementation of ``+`` operator - returns :class:`And`. Adding strings to a :class:`ParserElement` - converts them to :class:`Literal`s by default. - - Example:: - - greet = Word(alphas) + "," + Word(alphas) + "!" - hello = "Hello, World!" - print(hello, "->", greet.parse_string(hello)) - - prints:: - - Hello, World! -> ['Hello', ',', 'World', '!'] - - ``...`` may be used as a parse expression as a short form of :class:`SkipTo`. - - Literal('start') + ... + Literal('end') - - is equivalent to: - - Literal('start') + SkipTo('end')("_skipped*") + Literal('end') - - Note that the skipped text is returned with '_skipped' as a results name, - and to support having multiple skips in the same parser, the value returned is - a list of all skipped text. - """ - if other is Ellipsis: - return _PendingSkip(self) - - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return And([self, other]) - - def __radd__(self, other) -> "ParserElement": - """ - Implementation of ``+`` operator when left operand is not a :class:`ParserElement` - """ - if other is Ellipsis: - return SkipTo(self)("_skipped*") + self - - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other + self - - def __sub__(self, other) -> "ParserElement": - """ - Implementation of ``-`` operator, returns :class:`And` with error stop - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return self + And._ErrorStop() + other - - def __rsub__(self, other) -> "ParserElement": - """ - Implementation of ``-`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other - self - - def __mul__(self, other) -> "ParserElement": - """ - Implementation of ``*`` operator, allows use of ``expr * 3`` in place of - ``expr + expr + expr``. Expressions may also be multiplied by a 2-integer - tuple, similar to ``{min, max}`` multipliers in regular expressions. Tuples - may also include ``None`` as in: - - ``expr*(n, None)`` or ``expr*(n, )`` is equivalent - to ``expr*n + ZeroOrMore(expr)`` - (read as "at least n instances of ``expr``") - - ``expr*(None, n)`` is equivalent to ``expr*(0, n)`` - (read as "0 to n instances of ``expr``") - - ``expr*(None, None)`` is equivalent to ``ZeroOrMore(expr)`` - - ``expr*(1, None)`` is equivalent to ``OneOrMore(expr)`` - - Note that ``expr*(None, n)`` does not raise an exception if - more than n exprs exist in the input stream; that is, - ``expr*(None, n)`` does not enforce a maximum number of expr - occurrences. If this behavior is desired, then write - ``expr*(None, n) + ~expr`` - """ - if other is Ellipsis: - other = (0, None) - elif isinstance(other, tuple) and other[:1] == (Ellipsis,): - other = ((0,) + other[1:] + (None,))[:2] - - if isinstance(other, int): - minElements, optElements = other, 0 - elif isinstance(other, tuple): - other = tuple(o if o is not Ellipsis else None for o in other) - other = (other + (None, None))[:2] - if other[0] is None: - other = (0, other[1]) - if isinstance(other[0], int) and other[1] is None: - if other[0] == 0: - return ZeroOrMore(self) - if other[0] == 1: - return OneOrMore(self) - else: - return self * other[0] + ZeroOrMore(self) - elif isinstance(other[0], int) and isinstance(other[1], int): - minElements, optElements = other - optElements -= minElements - else: - raise TypeError( - "cannot multiply ParserElement and ({}) objects".format( - ",".join(type(item).__name__ for item in other) - ) - ) - else: - raise TypeError( - "cannot multiply ParserElement and {} objects".format( - type(other).__name__ - ) - ) - - if minElements < 0: - raise ValueError("cannot multiply ParserElement by negative value") - if optElements < 0: - raise ValueError( - "second tuple value must be greater or equal to first tuple value" - ) - if minElements == optElements == 0: - return And([]) - - if optElements: - - def makeOptionalList(n): - if n > 1: - return Opt(self + makeOptionalList(n - 1)) - else: - return Opt(self) - - if minElements: - if minElements == 1: - ret = self + makeOptionalList(optElements) - else: - ret = And([self] * minElements) + makeOptionalList(optElements) - else: - ret = makeOptionalList(optElements) - else: - if minElements == 1: - ret = self - else: - ret = And([self] * minElements) - return ret - - def __rmul__(self, other) -> "ParserElement": - return self.__mul__(other) - - def __or__(self, other) -> "ParserElement": - """ - Implementation of ``|`` operator - returns :class:`MatchFirst` - """ - if other is Ellipsis: - return _PendingSkip(self, must_skip=True) - - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return MatchFirst([self, other]) - - def __ror__(self, other) -> "ParserElement": - """ - Implementation of ``|`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other | self - - def __xor__(self, other) -> "ParserElement": - """ - Implementation of ``^`` operator - returns :class:`Or` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return Or([self, other]) - - def __rxor__(self, other) -> "ParserElement": - """ - Implementation of ``^`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other ^ self - - def __and__(self, other) -> "ParserElement": - """ - Implementation of ``&`` operator - returns :class:`Each` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return Each([self, other]) - - def __rand__(self, other) -> "ParserElement": - """ - Implementation of ``&`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other & self - - def __invert__(self) -> "ParserElement": - """ - Implementation of ``~`` operator - returns :class:`NotAny` - """ - return NotAny(self) - - # disable __iter__ to override legacy use of sequential access to __getitem__ to - # iterate over a sequence - __iter__ = None - - def __getitem__(self, key): - """ - use ``[]`` indexing notation as a short form for expression repetition: - - - ``expr[n]`` is equivalent to ``expr*n`` - - ``expr[m, n]`` is equivalent to ``expr*(m, n)`` - - ``expr[n, ...]`` or ``expr[n,]`` is equivalent - to ``expr*n + ZeroOrMore(expr)`` - (read as "at least n instances of ``expr``") - - ``expr[..., n]`` is equivalent to ``expr*(0, n)`` - (read as "0 to n instances of ``expr``") - - ``expr[...]`` and ``expr[0, ...]`` are equivalent to ``ZeroOrMore(expr)`` - - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)`` - - ``None`` may be used in place of ``...``. - - Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception - if more than ``n`` ``expr``s exist in the input stream. If this behavior is - desired, then write ``expr[..., n] + ~expr``. - """ - - # convert single arg keys to tuples - try: - if isinstance(key, str_type): - key = (key,) - iter(key) - except TypeError: - key = (key, key) - - if len(key) > 2: - raise TypeError( - "only 1 or 2 index arguments supported ({}{})".format( - key[:5], "... [{}]".format(len(key)) if len(key) > 5 else "" - ) - ) - - # clip to 2 elements - ret = self * tuple(key[:2]) - return ret - - def __call__(self, name: str = None) -> "ParserElement": - """ - Shortcut for :class:`set_results_name`, with ``list_all_matches=False``. - - If ``name`` is given with a trailing ``'*'`` character, then ``list_all_matches`` will be - passed as ``True``. - - If ``name` is omitted, same as calling :class:`copy`. - - Example:: - - # these are equivalent - userdata = Word(alphas).set_results_name("name") + Word(nums + "-").set_results_name("socsecno") - userdata = Word(alphas)("name") + Word(nums + "-")("socsecno") - """ - if name is not None: - return self._setResultsName(name) - else: - return self.copy() - - def suppress(self) -> "ParserElement": - """ - Suppresses the output of this :class:`ParserElement`; useful to keep punctuation from - cluttering up returned output. - """ - return Suppress(self) - - def ignore_whitespace(self, recursive: bool = True) -> "ParserElement": - """ - Enables the skipping of whitespace before matching the characters in the - :class:`ParserElement`'s defined pattern. - - :param recursive: If ``True`` (the default), also enable whitespace skipping in child elements (if any) - """ - self.skipWhitespace = True - return self - - def leave_whitespace(self, recursive: bool = True) -> "ParserElement": - """ - Disables the skipping of whitespace before matching the characters in the - :class:`ParserElement`'s defined pattern. This is normally only used internally by - the pyparsing module, but may be needed in some whitespace-sensitive grammars. - - :param recursive: If true (the default), also disable whitespace skipping in child elements (if any) - """ - self.skipWhitespace = False - return self - - def set_whitespace_chars( - self, chars: Union[Set[str], str], copy_defaults: bool = False - ) -> "ParserElement": - """ - Overrides the default whitespace chars - """ - self.skipWhitespace = True - self.whiteChars = set(chars) - self.copyDefaultWhiteChars = copy_defaults - return self - - def parse_with_tabs(self) -> "ParserElement": - """ - Overrides default behavior to expand ```` s to spaces before parsing the input string. - Must be called before ``parse_string`` when the input grammar contains elements that - match ```` characters. - """ - self.keepTabs = True - return self - - def ignore(self, other: "ParserElement") -> "ParserElement": - """ - Define expression to be ignored (e.g., comments) while doing pattern - matching; may be called repeatedly, to define multiple comment or other - ignorable patterns. - - Example:: - - patt = Word(alphas)[1, ...] - patt.parse_string('ablaj /* comment */ lskjd') - # -> ['ablaj'] - - patt.ignore(c_style_comment) - patt.parse_string('ablaj /* comment */ lskjd') - # -> ['ablaj', 'lskjd'] - """ - import typing - - if isinstance(other, str_type): - other = Suppress(other) - - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - self.ignoreExprs.append(other) - else: - self.ignoreExprs.append(Suppress(other.copy())) - return self - - def set_debug_actions( - self, - start_action: DebugStartAction, - success_action: DebugSuccessAction, - exception_action: DebugExceptionAction, - ) -> "ParserElement": - """ - Customize display of debugging messages while doing pattern matching: - - - ``start_action`` - method to be called when an expression is about to be parsed; - should have the signature ``fn(input_string: str, location: int, expression: ParserElement, cache_hit: bool)`` - - - ``success_action`` - method to be called when an expression has successfully parsed; - should have the signature ``fn(input_string: str, start_location: int, end_location: int, expression: ParserELement, parsed_tokens: ParseResults, cache_hit: bool)`` - - - ``exception_action`` - method to be called when expression fails to parse; - should have the signature ``fn(input_string: str, location: int, expression: ParserElement, exception: Exception, cache_hit: bool)`` - """ - self.debugActions = self.DebugActions( - start_action or _default_start_debug_action, - success_action or _default_success_debug_action, - exception_action or _default_exception_debug_action, - ) - self.debug = True - return self - - def set_debug(self, flag: bool = True) -> "ParserElement": - """ - Enable display of debugging messages while doing pattern matching. - Set ``flag`` to ``True`` to enable, ``False`` to disable. - - Example:: - - wd = Word(alphas).set_name("alphaword") - integer = Word(nums).set_name("numword") - term = wd | integer - - # turn on debugging for wd - wd.set_debug() - - term[1, ...].parse_string("abc 123 xyz 890") - - prints:: - - Match alphaword at loc 0(1,1) - Matched alphaword -> ['abc'] - Match alphaword at loc 3(1,4) - Exception raised:Expected alphaword (at char 4), (line:1, col:5) - Match alphaword at loc 7(1,8) - Matched alphaword -> ['xyz'] - Match alphaword at loc 11(1,12) - Exception raised:Expected alphaword (at char 12), (line:1, col:13) - Match alphaword at loc 15(1,16) - Exception raised:Expected alphaword (at char 15), (line:1, col:16) - - The output shown is that produced by the default debug actions - custom debug actions can be - specified using :class:`set_debug_actions`. Prior to attempting - to match the ``wd`` expression, the debugging message ``"Match at loc (,)"`` - is shown. Then if the parse succeeds, a ``"Matched"`` message is shown, or an ``"Exception raised"`` - message is shown. Also note the use of :class:`set_name` to assign a human-readable name to the expression, - which makes debugging and exception messages easier to understand - for instance, the default - name created for the :class:`Word` expression without calling ``set_name`` is ``"W:(A-Za-z)"``. - """ - if flag: - self.set_debug_actions( - _default_start_debug_action, - _default_success_debug_action, - _default_exception_debug_action, - ) - else: - self.debug = False - return self - - @property - def default_name(self) -> str: - if self._defaultName is None: - self._defaultName = self._generateDefaultName() - return self._defaultName - - @abstractmethod - def _generateDefaultName(self): - """ - Child classes must define this method, which defines how the ``default_name`` is set. - """ - - def set_name(self, name: str) -> "ParserElement": - """ - Define name for this expression, makes debugging and exception messages clearer. - Example:: - Word(nums).parse_string("ABC") # -> Exception: Expected W:(0-9) (at char 0), (line:1, col:1) - Word(nums).set_name("integer").parse_string("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) - """ - self.customName = name - self.errmsg = "Expected " + self.name - if __diag__.enable_debug_on_named_expressions: - self.set_debug() - return self - - @property - def name(self) -> str: - # This will use a user-defined name if available, but otherwise defaults back to the auto-generated name - return self.customName if self.customName is not None else self.default_name - - def __str__(self) -> str: - return self.name - - def __repr__(self) -> str: - return str(self) - - def streamline(self) -> "ParserElement": - self.streamlined = True - self._defaultName = None - return self - - def recurse(self) -> Sequence["ParserElement"]: - return [] - - def _checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.recurse(): - e._checkRecursion(subRecCheckList) - - def validate(self, validateTrace=None) -> None: - """ - Check defined expressions for valid structure, check for infinite recursive definitions. - """ - self._checkRecursion([]) - - def parse_file( - self, - file_or_filename: Union[str, Path, TextIO], - encoding: str = "utf-8", - parse_all: bool = False, - *, - parseAll: bool = False, - ) -> ParseResults: - """ - Execute the parse expression on the given file or filename. - If a filename is specified (instead of a file object), - the entire file is opened, read, and closed before parsing. - """ - parseAll = parseAll or parse_all - try: - file_contents = file_or_filename.read() - except AttributeError: - with open(file_or_filename, "r", encoding=encoding) as f: - file_contents = f.read() - try: - return self.parse_string(file_contents, parseAll) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def __eq__(self, other): - if self is other: - return True - elif isinstance(other, str_type): - return self.matches(other, parse_all=True) - elif isinstance(other, ParserElement): - return vars(self) == vars(other) - return False - - def __hash__(self): - return id(self) - - def matches( - self, test_string: str, parse_all: bool = True, *, parseAll: bool = True - ) -> bool: - """ - Method for quick testing of a parser against a test string. Good for simple - inline microtests of sub expressions while building up larger parser. - - Parameters: - - ``test_string`` - to test against this expression for a match - - ``parse_all`` - (default= ``True``) - flag to pass to :class:`parse_string` when running tests - - Example:: - - expr = Word(nums) - assert expr.matches("100") - """ - parseAll = parseAll and parse_all - try: - self.parse_string(str(test_string), parse_all=parseAll) - return True - except ParseBaseException: - return False - - def run_tests( - self, - tests: Union[str, List[str]], - parse_all: bool = True, - comment: typing.Optional[Union["ParserElement", str]] = "#", - full_dump: bool = True, - print_results: bool = True, - failure_tests: bool = False, - post_parse: Callable[[str, ParseResults], str] = None, - file: typing.Optional[TextIO] = None, - with_line_numbers: bool = False, - *, - parseAll: bool = True, - fullDump: bool = True, - printResults: bool = True, - failureTests: bool = False, - postParse: Callable[[str, ParseResults], str] = None, - ) -> Tuple[bool, List[Tuple[str, Union[ParseResults, Exception]]]]: - """ - Execute the parse expression on a series of test strings, showing each - test, the parsed results or where the parse failed. Quick and easy way to - run a parse expression against a list of sample strings. - - Parameters: - - ``tests`` - a list of separate test strings, or a multiline string of test strings - - ``parse_all`` - (default= ``True``) - flag to pass to :class:`parse_string` when running tests - - ``comment`` - (default= ``'#'``) - expression for indicating embedded comments in the test - string; pass None to disable comment filtering - - ``full_dump`` - (default= ``True``) - dump results as list followed by results names in nested outline; - if False, only dump nested list - - ``print_results`` - (default= ``True``) prints test output to stdout - - ``failure_tests`` - (default= ``False``) indicates if these tests are expected to fail parsing - - ``post_parse`` - (default= ``None``) optional callback for successful parse results; called as - `fn(test_string, parse_results)` and returns a string to be added to the test output - - ``file`` - (default= ``None``) optional file-like object to which test output will be written; - if None, will default to ``sys.stdout`` - - ``with_line_numbers`` - default= ``False``) show test strings with line and column numbers - - Returns: a (success, results) tuple, where success indicates that all tests succeeded - (or failed if ``failure_tests`` is True), and the results contain a list of lines of each - test's output - - Example:: - - number_expr = pyparsing_common.number.copy() - - result = number_expr.run_tests(''' - # unsigned integer - 100 - # negative integer - -100 - # float with scientific notation - 6.02e23 - # integer with scientific notation - 1e-12 - ''') - print("Success" if result[0] else "Failed!") - - result = number_expr.run_tests(''' - # stray character - 100Z - # missing leading digit before '.' - -.100 - # too many '.' - 3.14.159 - ''', failure_tests=True) - print("Success" if result[0] else "Failed!") - - prints:: - - # unsigned integer - 100 - [100] - - # negative integer - -100 - [-100] - - # float with scientific notation - 6.02e23 - [6.02e+23] - - # integer with scientific notation - 1e-12 - [1e-12] - - Success - - # stray character - 100Z - ^ - FAIL: Expected end of text (at char 3), (line:1, col:4) - - # missing leading digit before '.' - -.100 - ^ - FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) - - # too many '.' - 3.14.159 - ^ - FAIL: Expected end of text (at char 4), (line:1, col:5) - - Success - - Each test string must be on a single line. If you want to test a string that spans multiple - lines, create a test like this:: - - expr.run_tests(r"this is a test\\n of strings that spans \\n 3 lines") - - (Note that this is a raw string literal, you must include the leading ``'r'``.) - """ - from .testing import pyparsing_test - - parseAll = parseAll and parse_all - fullDump = fullDump and full_dump - printResults = printResults and print_results - failureTests = failureTests or failure_tests - postParse = postParse or post_parse - if isinstance(tests, str_type): - line_strip = type(tests).strip - tests = [line_strip(test_line) for test_line in tests.rstrip().splitlines()] - if isinstance(comment, str_type): - comment = Literal(comment) - if file is None: - file = sys.stdout - print_ = file.write - - result: Union[ParseResults, Exception] - allResults = [] - comments = [] - success = True - NL = Literal(r"\n").add_parse_action(replace_with("\n")).ignore(quoted_string) - BOM = "\ufeff" - for t in tests: - if comment is not None and comment.matches(t, False) or comments and not t: - comments.append( - pyparsing_test.with_line_numbers(t) if with_line_numbers else t - ) - continue - if not t: - continue - out = [ - "\n" + "\n".join(comments) if comments else "", - pyparsing_test.with_line_numbers(t) if with_line_numbers else t, - ] - comments = [] - try: - # convert newline marks to actual newlines, and strip leading BOM if present - t = NL.transform_string(t.lstrip(BOM)) - result = self.parse_string(t, parse_all=parseAll) - except ParseBaseException as pe: - fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" - out.append(pe.explain()) - out.append("FAIL: " + str(pe)) - if ParserElement.verbose_stacktrace: - out.extend(traceback.format_tb(pe.__traceback__)) - success = success and failureTests - result = pe - except Exception as exc: - out.append("FAIL-EXCEPTION: {}: {}".format(type(exc).__name__, exc)) - if ParserElement.verbose_stacktrace: - out.extend(traceback.format_tb(exc.__traceback__)) - success = success and failureTests - result = exc - else: - success = success and not failureTests - if postParse is not None: - try: - pp_value = postParse(t, result) - if pp_value is not None: - if isinstance(pp_value, ParseResults): - out.append(pp_value.dump()) - else: - out.append(str(pp_value)) - else: - out.append(result.dump()) - except Exception as e: - out.append(result.dump(full=fullDump)) - out.append( - "{} failed: {}: {}".format( - postParse.__name__, type(e).__name__, e - ) - ) - else: - out.append(result.dump(full=fullDump)) - out.append("") - - if printResults: - print_("\n".join(out)) - - allResults.append((t, result)) - - return success, allResults - - def create_diagram( - self, - output_html: Union[TextIO, Path, str], - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, - **kwargs, - ) -> None: - """ - Create a railroad diagram for the parser. - - Parameters: - - output_html (str or file-like object) - output target for generated - diagram HTML - - vertical (int) - threshold for formatting multiple alternatives vertically - instead of horizontally (default=3) - - show_results_names - bool flag whether diagram should show annotations for - defined results names - - show_groups - bool flag whether groups should be highlighted with an unlabeled surrounding box - Additional diagram-formatting keyword arguments can also be included; - see railroad.Diagram class. - """ - - try: - from .diagram import to_railroad, railroad_to_html - except ImportError as ie: - raise Exception( - "must ``pip install pyparsing[diagrams]`` to generate parser railroad diagrams" - ) from ie - - self.streamline() - - railroad = to_railroad( - self, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - diagram_kwargs=kwargs, - ) - if isinstance(output_html, (str, Path)): - with open(output_html, "w", encoding="utf-8") as diag_file: - diag_file.write(railroad_to_html(railroad)) - else: - # we were passed a file-like object, just write to it - output_html.write(railroad_to_html(railroad)) - - setDefaultWhitespaceChars = set_default_whitespace_chars - inlineLiteralsUsing = inline_literals_using - setResultsName = set_results_name - setBreak = set_break - setParseAction = set_parse_action - addParseAction = add_parse_action - addCondition = add_condition - setFailAction = set_fail_action - tryParse = try_parse - canParseNext = can_parse_next - resetCache = reset_cache - enableLeftRecursion = enable_left_recursion - enablePackrat = enable_packrat - parseString = parse_string - scanString = scan_string - searchString = search_string - transformString = transform_string - setWhitespaceChars = set_whitespace_chars - parseWithTabs = parse_with_tabs - setDebugActions = set_debug_actions - setDebug = set_debug - defaultName = default_name - setName = set_name - parseFile = parse_file - runTests = run_tests - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class _PendingSkip(ParserElement): - # internal placeholder class to hold a place were '...' is added to a parser element, - # once another ParserElement is added, this placeholder will be replaced with a SkipTo - def __init__(self, expr: ParserElement, must_skip: bool = False): - super().__init__() - self.anchor = expr - self.must_skip = must_skip - - def _generateDefaultName(self): - return str(self.anchor + Empty()).replace("Empty", "...") - - def __add__(self, other) -> "ParserElement": - skipper = SkipTo(other).set_name("...")("_skipped*") - if self.must_skip: - - def must_skip(t): - if not t._skipped or t._skipped.as_list() == [""]: - del t[0] - t.pop("_skipped", None) - - def show_skip(t): - if t._skipped.as_list()[-1:] == [""]: - t.pop("_skipped") - t["_skipped"] = "missing <" + repr(self.anchor) + ">" - - return ( - self.anchor + skipper().add_parse_action(must_skip) - | skipper().add_parse_action(show_skip) - ) + other - - return self.anchor + skipper + other - - def __repr__(self): - return self.defaultName - - def parseImpl(self, *args): - raise Exception( - "use of `...` expression without following SkipTo target expression" - ) - - -class Token(ParserElement): - """Abstract :class:`ParserElement` subclass, for defining atomic - matching patterns. - """ - - def __init__(self): - super().__init__(savelist=False) - - def _generateDefaultName(self): - return type(self).__name__ - - -class Empty(Token): - """ - An empty token, will always match. - """ - - def __init__(self): - super().__init__() - self.mayReturnEmpty = True - self.mayIndexError = False - - -class NoMatch(Token): - """ - A token that will never match. - """ - - def __init__(self): - super().__init__() - self.mayReturnEmpty = True - self.mayIndexError = False - self.errmsg = "Unmatchable token" - - def parseImpl(self, instring, loc, doActions=True): - raise ParseException(instring, loc, self.errmsg, self) - - -class Literal(Token): - """ - Token to exactly match a specified string. - - Example:: - - Literal('blah').parse_string('blah') # -> ['blah'] - Literal('blah').parse_string('blahfooblah') # -> ['blah'] - Literal('blah').parse_string('bla') # -> Exception: Expected "blah" - - For case-insensitive matching, use :class:`CaselessLiteral`. - - For keyword matching (force word break before and after the matched string), - use :class:`Keyword` or :class:`CaselessKeyword`. - """ - - def __init__(self, match_string: str = "", *, matchString: str = ""): - super().__init__() - match_string = matchString or match_string - self.match = match_string - self.matchLen = len(match_string) - try: - self.firstMatchChar = match_string[0] - except IndexError: - raise ValueError("null string passed to Literal; use Empty() instead") - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = False - self.mayIndexError = False - - # Performance tuning: modify __class__ to select - # a parseImpl optimized for single-character check - if self.matchLen == 1 and type(self) is Literal: - self.__class__ = _SingleCharLiteral - - def _generateDefaultName(self): - return repr(self.match) - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] == self.firstMatchChar and instring.startswith( - self.match, loc - ): - return loc + self.matchLen, self.match - raise ParseException(instring, loc, self.errmsg, self) - - -class _SingleCharLiteral(Literal): - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] == self.firstMatchChar: - return loc + 1, self.match - raise ParseException(instring, loc, self.errmsg, self) - - -ParserElement._literalStringClass = Literal - - -class Keyword(Token): - """ - Token to exactly match a specified string as a keyword, that is, - it must be immediately followed by a non-keyword character. Compare - with :class:`Literal`: - - - ``Literal("if")`` will match the leading ``'if'`` in - ``'ifAndOnlyIf'``. - - ``Keyword("if")`` will not; it will only match the leading - ``'if'`` in ``'if x=1'``, or ``'if(y==2)'`` - - Accepts two optional constructor arguments in addition to the - keyword string: - - - ``identChars`` is a string of characters that would be valid - identifier characters, defaulting to all alphanumerics + "_" and - "$" - - ``caseless`` allows case-insensitive matching, default is ``False``. - - Example:: - - Keyword("start").parse_string("start") # -> ['start'] - Keyword("start").parse_string("starting") # -> Exception - - For case-insensitive matching, use :class:`CaselessKeyword`. - """ - - DEFAULT_KEYWORD_CHARS = alphanums + "_$" - - def __init__( - self, - match_string: str = "", - ident_chars: typing.Optional[str] = None, - caseless: bool = False, - *, - matchString: str = "", - identChars: typing.Optional[str] = None, - ): - super().__init__() - identChars = identChars or ident_chars - if identChars is None: - identChars = Keyword.DEFAULT_KEYWORD_CHARS - match_string = matchString or match_string - self.match = match_string - self.matchLen = len(match_string) - try: - self.firstMatchChar = match_string[0] - except IndexError: - raise ValueError("null string passed to Keyword; use Empty() instead") - self.errmsg = "Expected {} {}".format(type(self).__name__, self.name) - self.mayReturnEmpty = False - self.mayIndexError = False - self.caseless = caseless - if caseless: - self.caselessmatch = match_string.upper() - identChars = identChars.upper() - self.identChars = set(identChars) - - def _generateDefaultName(self): - return repr(self.match) - - def parseImpl(self, instring, loc, doActions=True): - errmsg = self.errmsg - errloc = loc - if self.caseless: - if instring[loc : loc + self.matchLen].upper() == self.caselessmatch: - if loc == 0 or instring[loc - 1].upper() not in self.identChars: - if ( - loc >= len(instring) - self.matchLen - or instring[loc + self.matchLen].upper() not in self.identChars - ): - return loc + self.matchLen, self.match - else: - # followed by keyword char - errmsg += ", was immediately followed by keyword character" - errloc = loc + self.matchLen - else: - # preceded by keyword char - errmsg += ", keyword was immediately preceded by keyword character" - errloc = loc - 1 - # else no match just raise plain exception - - else: - if ( - instring[loc] == self.firstMatchChar - and self.matchLen == 1 - or instring.startswith(self.match, loc) - ): - if loc == 0 or instring[loc - 1] not in self.identChars: - if ( - loc >= len(instring) - self.matchLen - or instring[loc + self.matchLen] not in self.identChars - ): - return loc + self.matchLen, self.match - else: - # followed by keyword char - errmsg += ( - ", keyword was immediately followed by keyword character" - ) - errloc = loc + self.matchLen - else: - # preceded by keyword char - errmsg += ", keyword was immediately preceded by keyword character" - errloc = loc - 1 - # else no match just raise plain exception - - raise ParseException(instring, errloc, errmsg, self) - - @staticmethod - def set_default_keyword_chars(chars) -> None: - """ - Overrides the default characters used by :class:`Keyword` expressions. - """ - Keyword.DEFAULT_KEYWORD_CHARS = chars - - setDefaultKeywordChars = set_default_keyword_chars - - -class CaselessLiteral(Literal): - """ - Token to match a specified string, ignoring case of letters. - Note: the matched results will always be in the case of the given - match string, NOT the case of the input text. - - Example:: - - CaselessLiteral("CMD")[1, ...].parse_string("cmd CMD Cmd10") - # -> ['CMD', 'CMD', 'CMD'] - - (Contrast with example for :class:`CaselessKeyword`.) - """ - - def __init__(self, match_string: str = "", *, matchString: str = ""): - match_string = matchString or match_string - super().__init__(match_string.upper()) - # Preserve the defining literal. - self.returnString = match_string - self.errmsg = "Expected " + self.name - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc : loc + self.matchLen].upper() == self.match: - return loc + self.matchLen, self.returnString - raise ParseException(instring, loc, self.errmsg, self) - - -class CaselessKeyword(Keyword): - """ - Caseless version of :class:`Keyword`. - - Example:: - - CaselessKeyword("CMD")[1, ...].parse_string("cmd CMD Cmd10") - # -> ['CMD', 'CMD'] - - (Contrast with example for :class:`CaselessLiteral`.) - """ - - def __init__( - self, - match_string: str = "", - ident_chars: typing.Optional[str] = None, - *, - matchString: str = "", - identChars: typing.Optional[str] = None, - ): - identChars = identChars or ident_chars - match_string = matchString or match_string - super().__init__(match_string, identChars, caseless=True) - - -class CloseMatch(Token): - """A variation on :class:`Literal` which matches "close" matches, - that is, strings with at most 'n' mismatching characters. - :class:`CloseMatch` takes parameters: - - - ``match_string`` - string to be matched - - ``caseless`` - a boolean indicating whether to ignore casing when comparing characters - - ``max_mismatches`` - (``default=1``) maximum number of - mismatches allowed to count as a match - - The results from a successful parse will contain the matched text - from the input string and the following named results: - - - ``mismatches`` - a list of the positions within the - match_string where mismatches were found - - ``original`` - the original match_string used to compare - against the input string - - If ``mismatches`` is an empty list, then the match was an exact - match. - - Example:: - - patt = CloseMatch("ATCATCGAATGGA") - patt.parse_string("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) - patt.parse_string("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) - - # exact match - patt.parse_string("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) - - # close match allowing up to 2 mismatches - patt = CloseMatch("ATCATCGAATGGA", max_mismatches=2) - patt.parse_string("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) - """ - - def __init__( - self, - match_string: str, - max_mismatches: int = None, - *, - maxMismatches: int = 1, - caseless=False, - ): - maxMismatches = max_mismatches if max_mismatches is not None else maxMismatches - super().__init__() - self.match_string = match_string - self.maxMismatches = maxMismatches - self.errmsg = "Expected {!r} (with up to {} mismatches)".format( - self.match_string, self.maxMismatches - ) - self.caseless = caseless - self.mayIndexError = False - self.mayReturnEmpty = False - - def _generateDefaultName(self): - return "{}:{!r}".format(type(self).__name__, self.match_string) - - def parseImpl(self, instring, loc, doActions=True): - start = loc - instrlen = len(instring) - maxloc = start + len(self.match_string) - - if maxloc <= instrlen: - match_string = self.match_string - match_stringloc = 0 - mismatches = [] - maxMismatches = self.maxMismatches - - for match_stringloc, s_m in enumerate( - zip(instring[loc:maxloc], match_string) - ): - src, mat = s_m - if self.caseless: - src, mat = src.lower(), mat.lower() - - if src != mat: - mismatches.append(match_stringloc) - if len(mismatches) > maxMismatches: - break - else: - loc = start + match_stringloc + 1 - results = ParseResults([instring[start:loc]]) - results["original"] = match_string - results["mismatches"] = mismatches - return loc, results - - raise ParseException(instring, loc, self.errmsg, self) - - -class Word(Token): - """Token for matching words composed of allowed character sets. - Parameters: - - ``init_chars`` - string of all characters that should be used to - match as a word; "ABC" will match "AAA", "ABAB", "CBAC", etc.; - if ``body_chars`` is also specified, then this is the string of - initial characters - - ``body_chars`` - string of characters that - can be used for matching after a matched initial character as - given in ``init_chars``; if omitted, same as the initial characters - (default=``None``) - - ``min`` - minimum number of characters to match (default=1) - - ``max`` - maximum number of characters to match (default=0) - - ``exact`` - exact number of characters to match (default=0) - - ``as_keyword`` - match as a keyword (default=``False``) - - ``exclude_chars`` - characters that might be - found in the input ``body_chars`` string but which should not be - accepted for matching ;useful to define a word of all - printables except for one or two characters, for instance - (default=``None``) - - :class:`srange` is useful for defining custom character set strings - for defining :class:`Word` expressions, using range notation from - regular expression character sets. - - A common mistake is to use :class:`Word` to match a specific literal - string, as in ``Word("Address")``. Remember that :class:`Word` - uses the string argument to define *sets* of matchable characters. - This expression would match "Add", "AAA", "dAred", or any other word - made up of the characters 'A', 'd', 'r', 'e', and 's'. To match an - exact literal string, use :class:`Literal` or :class:`Keyword`. - - pyparsing includes helper strings for building Words: - - - :class:`alphas` - - :class:`nums` - - :class:`alphanums` - - :class:`hexnums` - - :class:`alphas8bit` (alphabetic characters in ASCII range 128-255 - - accented, tilded, umlauted, etc.) - - :class:`punc8bit` (non-alphabetic characters in ASCII range - 128-255 - currency, symbols, superscripts, diacriticals, etc.) - - :class:`printables` (any non-whitespace character) - - ``alphas``, ``nums``, and ``printables`` are also defined in several - Unicode sets - see :class:`pyparsing_unicode``. - - Example:: - - # a word composed of digits - integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) - - # a word with a leading capital, and zero or more lowercase - capital_word = Word(alphas.upper(), alphas.lower()) - - # hostnames are alphanumeric, with leading alpha, and '-' - hostname = Word(alphas, alphanums + '-') - - # roman numeral (not a strict parser, accepts invalid mix of characters) - roman = Word("IVXLCDM") - - # any string of non-whitespace characters, except for ',' - csv_value = Word(printables, exclude_chars=",") - """ - - def __init__( - self, - init_chars: str = "", - body_chars: typing.Optional[str] = None, - min: int = 1, - max: int = 0, - exact: int = 0, - as_keyword: bool = False, - exclude_chars: typing.Optional[str] = None, - *, - initChars: typing.Optional[str] = None, - bodyChars: typing.Optional[str] = None, - asKeyword: bool = False, - excludeChars: typing.Optional[str] = None, - ): - initChars = initChars or init_chars - bodyChars = bodyChars or body_chars - asKeyword = asKeyword or as_keyword - excludeChars = excludeChars or exclude_chars - super().__init__() - if not initChars: - raise ValueError( - "invalid {}, initChars cannot be empty string".format( - type(self).__name__ - ) - ) - - initChars = set(initChars) - self.initChars = initChars - if excludeChars: - excludeChars = set(excludeChars) - initChars -= excludeChars - if bodyChars: - bodyChars = set(bodyChars) - excludeChars - self.initCharsOrig = "".join(sorted(initChars)) - - if bodyChars: - self.bodyCharsOrig = "".join(sorted(bodyChars)) - self.bodyChars = set(bodyChars) - else: - self.bodyCharsOrig = "".join(sorted(initChars)) - self.bodyChars = set(initChars) - - self.maxSpecified = max > 0 - - if min < 1: - raise ValueError( - "cannot specify a minimum length < 1; use Opt(Word()) if zero-length word is permitted" - ) - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.asKeyword = asKeyword - - # see if we can make a regex for this Word - if " " not in self.initChars | self.bodyChars and (min == 1 and exact == 0): - if self.bodyChars == self.initChars: - if max == 0: - repeat = "+" - elif max == 1: - repeat = "" - else: - repeat = "{{{},{}}}".format( - self.minLen, "" if self.maxLen == _MAX_INT else self.maxLen - ) - self.reString = "[{}]{}".format( - _collapse_string_to_ranges(self.initChars), - repeat, - ) - elif len(self.initChars) == 1: - if max == 0: - repeat = "*" - else: - repeat = "{{0,{}}}".format(max - 1) - self.reString = "{}[{}]{}".format( - re.escape(self.initCharsOrig), - _collapse_string_to_ranges(self.bodyChars), - repeat, - ) - else: - if max == 0: - repeat = "*" - elif max == 2: - repeat = "" - else: - repeat = "{{0,{}}}".format(max - 1) - self.reString = "[{}][{}]{}".format( - _collapse_string_to_ranges(self.initChars), - _collapse_string_to_ranges(self.bodyChars), - repeat, - ) - if self.asKeyword: - self.reString = r"\b" + self.reString + r"\b" - - try: - self.re = re.compile(self.reString) - except re.error: - self.re = None - else: - self.re_match = self.re.match - self.__class__ = _WordRegex - - def _generateDefaultName(self): - def charsAsStr(s): - max_repr_len = 16 - s = _collapse_string_to_ranges(s, re_escape=False) - if len(s) > max_repr_len: - return s[: max_repr_len - 3] + "..." - else: - return s - - if self.initChars != self.bodyChars: - base = "W:({}, {})".format( - charsAsStr(self.initChars), charsAsStr(self.bodyChars) - ) - else: - base = "W:({})".format(charsAsStr(self.initChars)) - - # add length specification - if self.minLen > 1 or self.maxLen != _MAX_INT: - if self.minLen == self.maxLen: - if self.minLen == 1: - return base[2:] - else: - return base + "{{{}}}".format(self.minLen) - elif self.maxLen == _MAX_INT: - return base + "{{{},...}}".format(self.minLen) - else: - return base + "{{{},{}}}".format(self.minLen, self.maxLen) - return base - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] not in self.initChars: - raise ParseException(instring, loc, self.errmsg, self) - - start = loc - loc += 1 - instrlen = len(instring) - bodychars = self.bodyChars - maxloc = start + self.maxLen - maxloc = min(maxloc, instrlen) - while loc < maxloc and instring[loc] in bodychars: - loc += 1 - - throwException = False - if loc - start < self.minLen: - throwException = True - elif self.maxSpecified and loc < instrlen and instring[loc] in bodychars: - throwException = True - elif self.asKeyword: - if ( - start > 0 - and instring[start - 1] in bodychars - or loc < instrlen - and instring[loc] in bodychars - ): - throwException = True - - if throwException: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class _WordRegex(Word): - def parseImpl(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - return loc, result.group() - - -class Char(_WordRegex): - """A short-cut class for defining :class:`Word` ``(characters, exact=1)``, - when defining a match of any single character in a string of - characters. - """ - - def __init__( - self, - charset: str, - as_keyword: bool = False, - exclude_chars: typing.Optional[str] = None, - *, - asKeyword: bool = False, - excludeChars: typing.Optional[str] = None, - ): - asKeyword = asKeyword or as_keyword - excludeChars = excludeChars or exclude_chars - super().__init__( - charset, exact=1, asKeyword=asKeyword, excludeChars=excludeChars - ) - self.reString = "[{}]".format(_collapse_string_to_ranges(self.initChars)) - if asKeyword: - self.reString = r"\b{}\b".format(self.reString) - self.re = re.compile(self.reString) - self.re_match = self.re.match - - -class Regex(Token): - r"""Token for matching strings that match a given regular - expression. Defined with string specifying the regular expression in - a form recognized by the stdlib Python `re module `_. - If the given regex contains named groups (defined using ``(?P...)``), - these will be preserved as named :class:`ParseResults`. - - If instead of the Python stdlib ``re`` module you wish to use a different RE module - (such as the ``regex`` module), you can do so by building your ``Regex`` object with - a compiled RE that was compiled using ``regex``. - - Example:: - - realnum = Regex(r"[+-]?\d+\.\d*") - # ref: https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression - roman = Regex(r"M{0,4}(CM|CD|D?{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") - - # named fields in a regex will be returned as named results - date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)') - - # the Regex class will accept re's compiled using the regex module - import regex - parser = pp.Regex(regex.compile(r'[0-9]')) - """ - - def __init__( - self, - pattern: Any, - flags: Union[re.RegexFlag, int] = 0, - as_group_list: bool = False, - as_match: bool = False, - *, - asGroupList: bool = False, - asMatch: bool = False, - ): - """The parameters ``pattern`` and ``flags`` are passed - to the ``re.compile()`` function as-is. See the Python - `re module `_ module for an - explanation of the acceptable patterns and flags. - """ - super().__init__() - asGroupList = asGroupList or as_group_list - asMatch = asMatch or as_match - - if isinstance(pattern, str_type): - if not pattern: - raise ValueError("null string passed to Regex; use Empty() instead") - - self._re = None - self.reString = self.pattern = pattern - self.flags = flags - - elif hasattr(pattern, "pattern") and hasattr(pattern, "match"): - self._re = pattern - self.pattern = self.reString = pattern.pattern - self.flags = flags - - else: - raise TypeError( - "Regex may only be constructed with a string or a compiled RE object" - ) - - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.asGroupList = asGroupList - self.asMatch = asMatch - if self.asGroupList: - self.parseImpl = self.parseImplAsGroupList - if self.asMatch: - self.parseImpl = self.parseImplAsMatch - - @cached_property - def re(self): - if self._re: - return self._re - else: - try: - return re.compile(self.pattern, self.flags) - except re.error: - raise ValueError( - "invalid pattern ({!r}) passed to Regex".format(self.pattern) - ) - - @cached_property - def re_match(self): - return self.re.match - - @cached_property - def mayReturnEmpty(self): - return self.re_match("") is not None - - def _generateDefaultName(self): - return "Re:({})".format(repr(self.pattern).replace("\\\\", "\\")) - - def parseImpl(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = ParseResults(result.group()) - d = result.groupdict() - if d: - for k, v in d.items(): - ret[k] = v - return loc, ret - - def parseImplAsGroupList(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result.groups() - return loc, ret - - def parseImplAsMatch(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result - return loc, ret - - def sub(self, repl: str) -> ParserElement: - r""" - Return :class:`Regex` with an attached parse action to transform the parsed - result as if called using `re.sub(expr, repl, string) `_. - - Example:: - - make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2") - print(make_html.transform_string("h1:main title:")) - # prints "

      main title

      " - """ - if self.asGroupList: - raise TypeError("cannot use sub() with Regex(asGroupList=True)") - - if self.asMatch and callable(repl): - raise TypeError("cannot use sub() with a callable with Regex(asMatch=True)") - - if self.asMatch: - - def pa(tokens): - return tokens[0].expand(repl) - - else: - - def pa(tokens): - return self.re.sub(repl, tokens[0]) - - return self.add_parse_action(pa) - - -class QuotedString(Token): - r""" - Token for matching strings that are delimited by quoting characters. - - Defined with the following parameters: - - - ``quote_char`` - string of one or more characters defining the - quote delimiting string - - ``esc_char`` - character to re_escape quotes, typically backslash - (default= ``None``) - - ``esc_quote`` - special quote sequence to re_escape an embedded quote - string (such as SQL's ``""`` to re_escape an embedded ``"``) - (default= ``None``) - - ``multiline`` - boolean indicating whether quotes can span - multiple lines (default= ``False``) - - ``unquote_results`` - boolean indicating whether the matched text - should be unquoted (default= ``True``) - - ``end_quote_char`` - string of one or more characters defining the - end of the quote delimited string (default= ``None`` => same as - quote_char) - - ``convert_whitespace_escapes`` - convert escaped whitespace - (``'\t'``, ``'\n'``, etc.) to actual whitespace - (default= ``True``) - - Example:: - - qs = QuotedString('"') - print(qs.search_string('lsjdf "This is the quote" sldjf')) - complex_qs = QuotedString('{{', end_quote_char='}}') - print(complex_qs.search_string('lsjdf {{This is the "quote"}} sldjf')) - sql_qs = QuotedString('"', esc_quote='""') - print(sql_qs.search_string('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) - - prints:: - - [['This is the quote']] - [['This is the "quote"']] - [['This is the quote with "embedded" quotes']] - """ - ws_map = ((r"\t", "\t"), (r"\n", "\n"), (r"\f", "\f"), (r"\r", "\r")) - - def __init__( - self, - quote_char: str = "", - esc_char: typing.Optional[str] = None, - esc_quote: typing.Optional[str] = None, - multiline: bool = False, - unquote_results: bool = True, - end_quote_char: typing.Optional[str] = None, - convert_whitespace_escapes: bool = True, - *, - quoteChar: str = "", - escChar: typing.Optional[str] = None, - escQuote: typing.Optional[str] = None, - unquoteResults: bool = True, - endQuoteChar: typing.Optional[str] = None, - convertWhitespaceEscapes: bool = True, - ): - super().__init__() - escChar = escChar or esc_char - escQuote = escQuote or esc_quote - unquoteResults = unquoteResults and unquote_results - endQuoteChar = endQuoteChar or end_quote_char - convertWhitespaceEscapes = ( - convertWhitespaceEscapes and convert_whitespace_escapes - ) - quote_char = quoteChar or quote_char - - # remove white space from quote chars - wont work anyway - quote_char = quote_char.strip() - if not quote_char: - raise ValueError("quote_char cannot be the empty string") - - if endQuoteChar is None: - endQuoteChar = quote_char - else: - endQuoteChar = endQuoteChar.strip() - if not endQuoteChar: - raise ValueError("endQuoteChar cannot be the empty string") - - self.quoteChar = quote_char - self.quoteCharLen = len(quote_char) - self.firstQuoteChar = quote_char[0] - self.endQuoteChar = endQuoteChar - self.endQuoteCharLen = len(endQuoteChar) - self.escChar = escChar - self.escQuote = escQuote - self.unquoteResults = unquoteResults - self.convertWhitespaceEscapes = convertWhitespaceEscapes - - sep = "" - inner_pattern = "" - - if escQuote: - inner_pattern += r"{}(?:{})".format(sep, re.escape(escQuote)) - sep = "|" - - if escChar: - inner_pattern += r"{}(?:{}.)".format(sep, re.escape(escChar)) - sep = "|" - self.escCharReplacePattern = re.escape(self.escChar) + "(.)" - - if len(self.endQuoteChar) > 1: - inner_pattern += ( - "{}(?:".format(sep) - + "|".join( - "(?:{}(?!{}))".format( - re.escape(self.endQuoteChar[:i]), - re.escape(self.endQuoteChar[i:]), - ) - for i in range(len(self.endQuoteChar) - 1, 0, -1) - ) - + ")" - ) - sep = "|" - - if multiline: - self.flags = re.MULTILINE | re.DOTALL - inner_pattern += r"{}(?:[^{}{}])".format( - sep, - _escape_regex_range_chars(self.endQuoteChar[0]), - (_escape_regex_range_chars(escChar) if escChar is not None else ""), - ) - else: - self.flags = 0 - inner_pattern += r"{}(?:[^{}\n\r{}])".format( - sep, - _escape_regex_range_chars(self.endQuoteChar[0]), - (_escape_regex_range_chars(escChar) if escChar is not None else ""), - ) - - self.pattern = "".join( - [ - re.escape(self.quoteChar), - "(?:", - inner_pattern, - ")*", - re.escape(self.endQuoteChar), - ] - ) - - try: - self.re = re.compile(self.pattern, self.flags) - self.reString = self.pattern - self.re_match = self.re.match - except re.error: - raise ValueError( - "invalid pattern {!r} passed to Regex".format(self.pattern) - ) - - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.mayReturnEmpty = True - - def _generateDefaultName(self): - if self.quoteChar == self.endQuoteChar and isinstance(self.quoteChar, str_type): - return "string enclosed in {!r}".format(self.quoteChar) - - return "quoted string, starting with {} ending with {}".format( - self.quoteChar, self.endQuoteChar - ) - - def parseImpl(self, instring, loc, doActions=True): - result = ( - instring[loc] == self.firstQuoteChar - and self.re_match(instring, loc) - or None - ) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result.group() - - if self.unquoteResults: - - # strip off quotes - ret = ret[self.quoteCharLen : -self.endQuoteCharLen] - - if isinstance(ret, str_type): - # replace escaped whitespace - if "\\" in ret and self.convertWhitespaceEscapes: - for wslit, wschar in self.ws_map: - ret = ret.replace(wslit, wschar) - - # replace escaped characters - if self.escChar: - ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret) - - # replace escaped quotes - if self.escQuote: - ret = ret.replace(self.escQuote, self.endQuoteChar) - - return loc, ret - - -class CharsNotIn(Token): - """Token for matching words composed of characters *not* in a given - set (will include whitespace in matched characters if not listed in - the provided exclusion set - see example). Defined with string - containing all disallowed characters, and an optional minimum, - maximum, and/or exact length. The default value for ``min`` is - 1 (a minimum value < 1 is not valid); the default values for - ``max`` and ``exact`` are 0, meaning no maximum or exact - length restriction. - - Example:: - - # define a comma-separated-value as anything that is not a ',' - csv_value = CharsNotIn(',') - print(delimited_list(csv_value).parse_string("dkls,lsdkjf,s12 34,@!#,213")) - - prints:: - - ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] - """ - - def __init__( - self, - not_chars: str = "", - min: int = 1, - max: int = 0, - exact: int = 0, - *, - notChars: str = "", - ): - super().__init__() - self.skipWhitespace = False - self.notChars = not_chars or notChars - self.notCharsSet = set(self.notChars) - - if min < 1: - raise ValueError( - "cannot specify a minimum length < 1; use " - "Opt(CharsNotIn()) if zero-length char group is permitted" - ) - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = self.minLen == 0 - self.mayIndexError = False - - def _generateDefaultName(self): - not_chars_str = _collapse_string_to_ranges(self.notChars) - if len(not_chars_str) > 16: - return "!W:({}...)".format(self.notChars[: 16 - 3]) - else: - return "!W:({})".format(self.notChars) - - def parseImpl(self, instring, loc, doActions=True): - notchars = self.notCharsSet - if instring[loc] in notchars: - raise ParseException(instring, loc, self.errmsg, self) - - start = loc - loc += 1 - maxlen = min(start + self.maxLen, len(instring)) - while loc < maxlen and instring[loc] not in notchars: - loc += 1 - - if loc - start < self.minLen: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class White(Token): - """Special matching class for matching whitespace. Normally, - whitespace is ignored by pyparsing grammars. This class is included - when some whitespace structures are significant. Define with - a string containing the whitespace characters to be matched; default - is ``" \\t\\r\\n"``. Also takes optional ``min``, - ``max``, and ``exact`` arguments, as defined for the - :class:`Word` class. - """ - - whiteStrs = { - " ": "", - "\t": "", - "\n": "", - "\r": "", - "\f": "", - "\u00A0": "", - "\u1680": "", - "\u180E": "", - "\u2000": "", - "\u2001": "", - "\u2002": "", - "\u2003": "", - "\u2004": "", - "\u2005": "", - "\u2006": "", - "\u2007": "", - "\u2008": "", - "\u2009": "", - "\u200A": "", - "\u200B": "", - "\u202F": "", - "\u205F": "", - "\u3000": "", - } - - def __init__(self, ws: str = " \t\r\n", min: int = 1, max: int = 0, exact: int = 0): - super().__init__() - self.matchWhite = ws - self.set_whitespace_chars( - "".join(c for c in self.whiteStrs if c not in self.matchWhite), - copy_defaults=True, - ) - # self.leave_whitespace() - self.mayReturnEmpty = True - self.errmsg = "Expected " + self.name - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - def _generateDefaultName(self): - return "".join(White.whiteStrs[c] for c in self.matchWhite) - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] not in self.matchWhite: - raise ParseException(instring, loc, self.errmsg, self) - start = loc - loc += 1 - maxloc = start + self.maxLen - maxloc = min(maxloc, len(instring)) - while loc < maxloc and instring[loc] in self.matchWhite: - loc += 1 - - if loc - start < self.minLen: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class PositionToken(Token): - def __init__(self): - super().__init__() - self.mayReturnEmpty = True - self.mayIndexError = False - - -class GoToColumn(PositionToken): - """Token to advance to a specific column of input text; useful for - tabular report scraping. - """ - - def __init__(self, colno: int): - super().__init__() - self.col = colno - - def preParse(self, instring, loc): - if col(loc, instring) != self.col: - instrlen = len(instring) - if self.ignoreExprs: - loc = self._skipIgnorables(instring, loc) - while ( - loc < instrlen - and instring[loc].isspace() - and col(loc, instring) != self.col - ): - loc += 1 - return loc - - def parseImpl(self, instring, loc, doActions=True): - thiscol = col(loc, instring) - if thiscol > self.col: - raise ParseException(instring, loc, "Text not in expected column", self) - newloc = loc + self.col - thiscol - ret = instring[loc:newloc] - return newloc, ret - - -class LineStart(PositionToken): - r"""Matches if current position is at the beginning of a line within - the parse string - - Example:: - - test = '''\ - AAA this line - AAA and this line - AAA but not this one - B AAA and definitely not this one - ''' - - for t in (LineStart() + 'AAA' + restOfLine).search_string(test): - print(t) - - prints:: - - ['AAA', ' this line'] - ['AAA', ' and this line'] - - """ - - def __init__(self): - super().__init__() - self.leave_whitespace() - self.orig_whiteChars = set() | self.whiteChars - self.whiteChars.discard("\n") - self.skipper = Empty().set_whitespace_chars(self.whiteChars) - self.errmsg = "Expected start of line" - - def preParse(self, instring, loc): - if loc == 0: - return loc - else: - ret = self.skipper.preParse(instring, loc) - if "\n" in self.orig_whiteChars: - while instring[ret : ret + 1] == "\n": - ret = self.skipper.preParse(instring, ret + 1) - return ret - - def parseImpl(self, instring, loc, doActions=True): - if col(loc, instring) == 1: - return loc, [] - raise ParseException(instring, loc, self.errmsg, self) - - -class LineEnd(PositionToken): - """Matches if current position is at the end of a line within the - parse string - """ - - def __init__(self): - super().__init__() - self.whiteChars.discard("\n") - self.set_whitespace_chars(self.whiteChars, copy_defaults=False) - self.errmsg = "Expected end of line" - - def parseImpl(self, instring, loc, doActions=True): - if loc < len(instring): - if instring[loc] == "\n": - return loc + 1, "\n" - else: - raise ParseException(instring, loc, self.errmsg, self) - elif loc == len(instring): - return loc + 1, [] - else: - raise ParseException(instring, loc, self.errmsg, self) - - -class StringStart(PositionToken): - """Matches if current position is at the beginning of the parse - string - """ - - def __init__(self): - super().__init__() - self.errmsg = "Expected start of text" - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - # see if entire string up to here is just whitespace and ignoreables - if loc != self.preParse(instring, 0): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class StringEnd(PositionToken): - """ - Matches if current position is at the end of the parse string - """ - - def __init__(self): - super().__init__() - self.errmsg = "Expected end of text" - - def parseImpl(self, instring, loc, doActions=True): - if loc < len(instring): - raise ParseException(instring, loc, self.errmsg, self) - elif loc == len(instring): - return loc + 1, [] - elif loc > len(instring): - return loc, [] - else: - raise ParseException(instring, loc, self.errmsg, self) - - -class WordStart(PositionToken): - """Matches if the current position is at the beginning of a - :class:`Word`, and is not preceded by any character in a given - set of ``word_chars`` (default= ``printables``). To emulate the - ``\b`` behavior of regular expressions, use - ``WordStart(alphanums)``. ``WordStart`` will also match at - the beginning of the string being parsed, or at the beginning of - a line. - """ - - def __init__(self, word_chars: str = printables, *, wordChars: str = printables): - wordChars = word_chars if wordChars == printables else wordChars - super().__init__() - self.wordChars = set(wordChars) - self.errmsg = "Not at the start of a word" - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - if ( - instring[loc - 1] in self.wordChars - or instring[loc] not in self.wordChars - ): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class WordEnd(PositionToken): - """Matches if the current position is at the end of a :class:`Word`, - and is not followed by any character in a given set of ``word_chars`` - (default= ``printables``). To emulate the ``\b`` behavior of - regular expressions, use ``WordEnd(alphanums)``. ``WordEnd`` - will also match at the end of the string being parsed, or at the end - of a line. - """ - - def __init__(self, word_chars: str = printables, *, wordChars: str = printables): - wordChars = word_chars if wordChars == printables else wordChars - super().__init__() - self.wordChars = set(wordChars) - self.skipWhitespace = False - self.errmsg = "Not at the end of a word" - - def parseImpl(self, instring, loc, doActions=True): - instrlen = len(instring) - if instrlen > 0 and loc < instrlen: - if ( - instring[loc] in self.wordChars - or instring[loc - 1] not in self.wordChars - ): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class ParseExpression(ParserElement): - """Abstract subclass of ParserElement, for combining and - post-processing parsed tokens. - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = False): - super().__init__(savelist) - self.exprs: List[ParserElement] - if isinstance(exprs, _generatorType): - exprs = list(exprs) - - if isinstance(exprs, str_type): - self.exprs = [self._literalStringClass(exprs)] - elif isinstance(exprs, ParserElement): - self.exprs = [exprs] - elif isinstance(exprs, Iterable): - exprs = list(exprs) - # if sequence of strings provided, wrap with Literal - if any(isinstance(expr, str_type) for expr in exprs): - exprs = ( - self._literalStringClass(e) if isinstance(e, str_type) else e - for e in exprs - ) - self.exprs = list(exprs) - else: - try: - self.exprs = list(exprs) - except TypeError: - self.exprs = [exprs] - self.callPreparse = False - - def recurse(self) -> Sequence[ParserElement]: - return self.exprs[:] - - def append(self, other) -> ParserElement: - self.exprs.append(other) - self._defaultName = None - return self - - def leave_whitespace(self, recursive: bool = True) -> ParserElement: - """ - Extends ``leave_whitespace`` defined in base class, and also invokes ``leave_whitespace`` on - all contained expressions. - """ - super().leave_whitespace(recursive) - - if recursive: - self.exprs = [e.copy() for e in self.exprs] - for e in self.exprs: - e.leave_whitespace(recursive) - return self - - def ignore_whitespace(self, recursive: bool = True) -> ParserElement: - """ - Extends ``ignore_whitespace`` defined in base class, and also invokes ``leave_whitespace`` on - all contained expressions. - """ - super().ignore_whitespace(recursive) - if recursive: - self.exprs = [e.copy() for e in self.exprs] - for e in self.exprs: - e.ignore_whitespace(recursive) - return self - - def ignore(self, other) -> ParserElement: - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - super().ignore(other) - for e in self.exprs: - e.ignore(self.ignoreExprs[-1]) - else: - super().ignore(other) - for e in self.exprs: - e.ignore(self.ignoreExprs[-1]) - return self - - def _generateDefaultName(self): - return "{}:({})".format(self.__class__.__name__, str(self.exprs)) - - def streamline(self) -> ParserElement: - if self.streamlined: - return self - - super().streamline() - - for e in self.exprs: - e.streamline() - - # collapse nested :class:`And`'s of the form ``And(And(And(a, b), c), d)`` to ``And(a, b, c, d)`` - # but only if there are no parse actions or resultsNames on the nested And's - # (likewise for :class:`Or`'s and :class:`MatchFirst`'s) - if len(self.exprs) == 2: - other = self.exprs[0] - if ( - isinstance(other, self.__class__) - and not other.parseAction - and other.resultsName is None - and not other.debug - ): - self.exprs = other.exprs[:] + [self.exprs[1]] - self._defaultName = None - self.mayReturnEmpty |= other.mayReturnEmpty - self.mayIndexError |= other.mayIndexError - - other = self.exprs[-1] - if ( - isinstance(other, self.__class__) - and not other.parseAction - and other.resultsName is None - and not other.debug - ): - self.exprs = self.exprs[:-1] + other.exprs[:] - self._defaultName = None - self.mayReturnEmpty |= other.mayReturnEmpty - self.mayIndexError |= other.mayIndexError - - self.errmsg = "Expected " + str(self) - - return self - - def validate(self, validateTrace=None) -> None: - tmp = (validateTrace if validateTrace is not None else [])[:] + [self] - for e in self.exprs: - e.validate(tmp) - self._checkRecursion([]) - - def copy(self) -> ParserElement: - ret = super().copy() - ret.exprs = [e.copy() for e in self.exprs] - return ret - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_ungrouped_named_tokens_in_collection - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in self.suppress_warnings_ - ): - for e in self.exprs: - if ( - isinstance(e, ParserElement) - and e.resultsName - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in e.suppress_warnings_ - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "collides with {!r} on contained expression".format( - "warn_ungrouped_named_tokens_in_collection", - name, - type(self).__name__, - e.resultsName, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class And(ParseExpression): - """ - Requires all given :class:`ParseExpression` s to be found in the given order. - Expressions may be separated by whitespace. - May be constructed using the ``'+'`` operator. - May also be constructed using the ``'-'`` operator, which will - suppress backtracking. - - Example:: - - integer = Word(nums) - name_expr = Word(alphas)[1, ...] - - expr = And([integer("id"), name_expr("name"), integer("age")]) - # more easily written as: - expr = integer("id") + name_expr("name") + integer("age") - """ - - class _ErrorStop(Empty): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.leave_whitespace() - - def _generateDefaultName(self): - return "-" - - def __init__( - self, exprs_arg: typing.Iterable[ParserElement], savelist: bool = True - ): - exprs: List[ParserElement] = list(exprs_arg) - if exprs and Ellipsis in exprs: - tmp = [] - for i, expr in enumerate(exprs): - if expr is Ellipsis: - if i < len(exprs) - 1: - skipto_arg: ParserElement = (Empty() + exprs[i + 1]).exprs[-1] - tmp.append(SkipTo(skipto_arg)("_skipped*")) - else: - raise Exception( - "cannot construct And with sequence ending in ..." - ) - else: - tmp.append(expr) - exprs[:] = tmp - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - if not isinstance(self.exprs[0], White): - self.set_whitespace_chars( - self.exprs[0].whiteChars, - copy_defaults=self.exprs[0].copyDefaultWhiteChars, - ) - self.skipWhitespace = self.exprs[0].skipWhitespace - else: - self.skipWhitespace = False - else: - self.mayReturnEmpty = True - self.callPreparse = True - - def streamline(self) -> ParserElement: - # collapse any _PendingSkip's - if self.exprs: - if any( - isinstance(e, ParseExpression) - and e.exprs - and isinstance(e.exprs[-1], _PendingSkip) - for e in self.exprs[:-1] - ): - for i, e in enumerate(self.exprs[:-1]): - if e is None: - continue - if ( - isinstance(e, ParseExpression) - and e.exprs - and isinstance(e.exprs[-1], _PendingSkip) - ): - e.exprs[-1] = e.exprs[-1] + self.exprs[i + 1] - self.exprs[i + 1] = None - self.exprs = [e for e in self.exprs if e is not None] - - super().streamline() - - # link any IndentedBlocks to the prior expression - for prev, cur in zip(self.exprs, self.exprs[1:]): - # traverse cur or any first embedded expr of cur looking for an IndentedBlock - # (but watch out for recursive grammar) - seen = set() - while cur: - if id(cur) in seen: - break - seen.add(id(cur)) - if isinstance(cur, IndentedBlock): - prev.add_parse_action( - lambda s, l, t, cur_=cur: setattr( - cur_, "parent_anchor", col(l, s) - ) - ) - break - subs = cur.recurse() - cur = next(iter(subs), None) - - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - return self - - def parseImpl(self, instring, loc, doActions=True): - # pass False as callPreParse arg to _parse for first element, since we already - # pre-parsed the string as part of our And pre-parsing - loc, resultlist = self.exprs[0]._parse( - instring, loc, doActions, callPreParse=False - ) - errorStop = False - for e in self.exprs[1:]: - # if isinstance(e, And._ErrorStop): - if type(e) is And._ErrorStop: - errorStop = True - continue - if errorStop: - try: - loc, exprtokens = e._parse(instring, loc, doActions) - except ParseSyntaxException: - raise - except ParseBaseException as pe: - pe.__traceback__ = None - raise ParseSyntaxException._from_exception(pe) - except IndexError: - raise ParseSyntaxException( - instring, len(instring), self.errmsg, self - ) - else: - loc, exprtokens = e._parse(instring, loc, doActions) - if exprtokens or exprtokens.haskeys(): - resultlist += exprtokens - return loc, resultlist - - def __iadd__(self, other): - if isinstance(other, str_type): - other = self._literalStringClass(other) - return self.append(other) # And([self, other]) - - def _checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.exprs: - e._checkRecursion(subRecCheckList) - if not e.mayReturnEmpty: - break - - def _generateDefaultName(self): - inner = " ".join(str(e) for e in self.exprs) - # strip off redundant inner {}'s - while len(inner) > 1 and inner[0 :: len(inner) - 1] == "{}": - inner = inner[1:-1] - return "{" + inner + "}" - - -class Or(ParseExpression): - """Requires that at least one :class:`ParseExpression` is found. If - two expressions match, the expression that matches the longest - string will be used. May be constructed using the ``'^'`` - operator. - - Example:: - - # construct Or using '^' operator - - number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) - print(number.search_string("123 3.1416 789")) - - prints:: - - [['123'], ['3.1416'], ['789']] - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = False): - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all(e.skipWhitespace for e in self.exprs) - else: - self.mayReturnEmpty = True - - def streamline(self) -> ParserElement: - super().streamline() - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.saveAsList = any(e.saveAsList for e in self.exprs) - self.skipWhitespace = all( - e.skipWhitespace and not isinstance(e, White) for e in self.exprs - ) - else: - self.saveAsList = False - return self - - def parseImpl(self, instring, loc, doActions=True): - maxExcLoc = -1 - maxException = None - matches = [] - fatals = [] - if all(e.callPreparse for e in self.exprs): - loc = self.preParse(instring, loc) - for e in self.exprs: - try: - loc2 = e.try_parse(instring, loc, raise_fatal=True) - except ParseFatalException as pfe: - pfe.__traceback__ = None - pfe.parserElement = e - fatals.append(pfe) - maxException = None - maxExcLoc = -1 - except ParseException as err: - if not fatals: - err.__traceback__ = None - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException( - instring, len(instring), e.errmsg, self - ) - maxExcLoc = len(instring) - else: - # save match among all matches, to retry longest to shortest - matches.append((loc2, e)) - - if matches: - # re-evaluate all matches in descending order of length of match, in case attached actions - # might change whether or how much they match of the input. - matches.sort(key=itemgetter(0), reverse=True) - - if not doActions: - # no further conditions or parse actions to change the selection of - # alternative, so the first match will be the best match - best_expr = matches[0][1] - return best_expr._parse(instring, loc, doActions) - - longest = -1, None - for loc1, expr1 in matches: - if loc1 <= longest[0]: - # already have a longer match than this one will deliver, we are done - return longest - - try: - loc2, toks = expr1._parse(instring, loc, doActions) - except ParseException as err: - err.__traceback__ = None - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - else: - if loc2 >= loc1: - return loc2, toks - # didn't match as much as before - elif loc2 > longest[0]: - longest = loc2, toks - - if longest != (-1, None): - return longest - - if fatals: - if len(fatals) > 1: - fatals.sort(key=lambda e: -e.loc) - if fatals[0].loc == fatals[1].loc: - fatals.sort(key=lambda e: (-e.loc, -len(str(e.parserElement)))) - max_fatal = fatals[0] - raise max_fatal - - if maxException is not None: - maxException.msg = self.errmsg - raise maxException - else: - raise ParseException( - instring, loc, "no defined alternatives to match", self - ) - - def __ixor__(self, other): - if isinstance(other, str_type): - other = self._literalStringClass(other) - return self.append(other) # Or([self, other]) - - def _generateDefaultName(self): - return "{" + " ^ ".join(str(e) for e in self.exprs) + "}" - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_multiple_tokens_in_named_alternation - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in self.suppress_warnings_ - ): - if any( - isinstance(e, And) - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in e.suppress_warnings_ - for e in self.exprs - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "will return a list of all parsed tokens in an And alternative, " - "in prior versions only the first token was returned; enclose " - "contained argument in Group".format( - "warn_multiple_tokens_in_named_alternation", - name, - type(self).__name__, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - -class MatchFirst(ParseExpression): - """Requires that at least one :class:`ParseExpression` is found. If - more than one expression matches, the first one listed is the one that will - match. May be constructed using the ``'|'`` operator. - - Example:: - - # construct MatchFirst using '|' operator - - # watch the order of expressions to match - number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) - print(number.search_string("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] - - # put more selective expression first - number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) - print(number.search_string("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = False): - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all(e.skipWhitespace for e in self.exprs) - else: - self.mayReturnEmpty = True - - def streamline(self) -> ParserElement: - if self.streamlined: - return self - - super().streamline() - if self.exprs: - self.saveAsList = any(e.saveAsList for e in self.exprs) - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all( - e.skipWhitespace and not isinstance(e, White) for e in self.exprs - ) - else: - self.saveAsList = False - self.mayReturnEmpty = True - return self - - def parseImpl(self, instring, loc, doActions=True): - maxExcLoc = -1 - maxException = None - - for e in self.exprs: - try: - return e._parse( - instring, - loc, - doActions, - ) - except ParseFatalException as pfe: - pfe.__traceback__ = None - pfe.parserElement = e - raise - except ParseException as err: - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException( - instring, len(instring), e.errmsg, self - ) - maxExcLoc = len(instring) - - if maxException is not None: - maxException.msg = self.errmsg - raise maxException - else: - raise ParseException( - instring, loc, "no defined alternatives to match", self - ) - - def __ior__(self, other): - if isinstance(other, str_type): - other = self._literalStringClass(other) - return self.append(other) # MatchFirst([self, other]) - - def _generateDefaultName(self): - return "{" + " | ".join(str(e) for e in self.exprs) + "}" - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_multiple_tokens_in_named_alternation - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in self.suppress_warnings_ - ): - if any( - isinstance(e, And) - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in e.suppress_warnings_ - for e in self.exprs - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "will return a list of all parsed tokens in an And alternative, " - "in prior versions only the first token was returned; enclose " - "contained argument in Group".format( - "warn_multiple_tokens_in_named_alternation", - name, - type(self).__name__, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - -class Each(ParseExpression): - """Requires all given :class:`ParseExpression` s to be found, but in - any order. Expressions may be separated by whitespace. - - May be constructed using the ``'&'`` operator. - - Example:: - - color = one_of("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") - shape_type = one_of("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") - integer = Word(nums) - shape_attr = "shape:" + shape_type("shape") - posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") - color_attr = "color:" + color("color") - size_attr = "size:" + integer("size") - - # use Each (using operator '&') to accept attributes in any order - # (shape and posn are required, color and size are optional) - shape_spec = shape_attr & posn_attr & Opt(color_attr) & Opt(size_attr) - - shape_spec.run_tests(''' - shape: SQUARE color: BLACK posn: 100, 120 - shape: CIRCLE size: 50 color: BLUE posn: 50,80 - color:GREEN size:20 shape:TRIANGLE posn:20,40 - ''' - ) - - prints:: - - shape: SQUARE color: BLACK posn: 100, 120 - ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] - - color: BLACK - - posn: ['100', ',', '120'] - - x: 100 - - y: 120 - - shape: SQUARE - - - shape: CIRCLE size: 50 color: BLUE posn: 50,80 - ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] - - color: BLUE - - posn: ['50', ',', '80'] - - x: 50 - - y: 80 - - shape: CIRCLE - - size: 50 - - - color: GREEN size: 20 shape: TRIANGLE posn: 20,40 - ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] - - color: GREEN - - posn: ['20', ',', '40'] - - x: 20 - - y: 40 - - shape: TRIANGLE - - size: 20 - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = True): - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - else: - self.mayReturnEmpty = True - self.skipWhitespace = True - self.initExprGroups = True - self.saveAsList = True - - def streamline(self) -> ParserElement: - super().streamline() - if self.exprs: - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - else: - self.mayReturnEmpty = True - return self - - def parseImpl(self, instring, loc, doActions=True): - if self.initExprGroups: - self.opt1map = dict( - (id(e.expr), e) for e in self.exprs if isinstance(e, Opt) - ) - opt1 = [e.expr for e in self.exprs if isinstance(e, Opt)] - opt2 = [ - e - for e in self.exprs - if e.mayReturnEmpty and not isinstance(e, (Opt, Regex, ZeroOrMore)) - ] - self.optionals = opt1 + opt2 - self.multioptionals = [ - e.expr.set_results_name(e.resultsName, list_all_matches=True) - for e in self.exprs - if isinstance(e, _MultipleMatch) - ] - self.multirequired = [ - e.expr.set_results_name(e.resultsName, list_all_matches=True) - for e in self.exprs - if isinstance(e, OneOrMore) - ] - self.required = [ - e for e in self.exprs if not isinstance(e, (Opt, ZeroOrMore, OneOrMore)) - ] - self.required += self.multirequired - self.initExprGroups = False - - tmpLoc = loc - tmpReqd = self.required[:] - tmpOpt = self.optionals[:] - multis = self.multioptionals[:] - matchOrder = [] - - keepMatching = True - failed = [] - fatals = [] - while keepMatching: - tmpExprs = tmpReqd + tmpOpt + multis - failed.clear() - fatals.clear() - for e in tmpExprs: - try: - tmpLoc = e.try_parse(instring, tmpLoc, raise_fatal=True) - except ParseFatalException as pfe: - pfe.__traceback__ = None - pfe.parserElement = e - fatals.append(pfe) - failed.append(e) - except ParseException: - failed.append(e) - else: - matchOrder.append(self.opt1map.get(id(e), e)) - if e in tmpReqd: - tmpReqd.remove(e) - elif e in tmpOpt: - tmpOpt.remove(e) - if len(failed) == len(tmpExprs): - keepMatching = False - - # look for any ParseFatalExceptions - if fatals: - if len(fatals) > 1: - fatals.sort(key=lambda e: -e.loc) - if fatals[0].loc == fatals[1].loc: - fatals.sort(key=lambda e: (-e.loc, -len(str(e.parserElement)))) - max_fatal = fatals[0] - raise max_fatal - - if tmpReqd: - missing = ", ".join([str(e) for e in tmpReqd]) - raise ParseException( - instring, - loc, - "Missing one or more required elements ({})".format(missing), - ) - - # add any unmatched Opts, in case they have default values defined - matchOrder += [e for e in self.exprs if isinstance(e, Opt) and e.expr in tmpOpt] - - total_results = ParseResults([]) - for e in matchOrder: - loc, results = e._parse(instring, loc, doActions) - total_results += results - - return loc, total_results - - def _generateDefaultName(self): - return "{" + " & ".join(str(e) for e in self.exprs) + "}" - - -class ParseElementEnhance(ParserElement): - """Abstract subclass of :class:`ParserElement`, for combining and - post-processing parsed tokens. - """ - - def __init__(self, expr: Union[ParserElement, str], savelist: bool = False): - super().__init__(savelist) - if isinstance(expr, str_type): - if issubclass(self._literalStringClass, Token): - expr = self._literalStringClass(expr) - elif issubclass(type(self), self._literalStringClass): - expr = Literal(expr) - else: - expr = self._literalStringClass(Literal(expr)) - self.expr = expr - if expr is not None: - self.mayIndexError = expr.mayIndexError - self.mayReturnEmpty = expr.mayReturnEmpty - self.set_whitespace_chars( - expr.whiteChars, copy_defaults=expr.copyDefaultWhiteChars - ) - self.skipWhitespace = expr.skipWhitespace - self.saveAsList = expr.saveAsList - self.callPreparse = expr.callPreparse - self.ignoreExprs.extend(expr.ignoreExprs) - - def recurse(self) -> Sequence[ParserElement]: - return [self.expr] if self.expr is not None else [] - - def parseImpl(self, instring, loc, doActions=True): - if self.expr is not None: - return self.expr._parse(instring, loc, doActions, callPreParse=False) - else: - raise ParseException(instring, loc, "No expression defined", self) - - def leave_whitespace(self, recursive: bool = True) -> ParserElement: - super().leave_whitespace(recursive) - - if recursive: - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.leave_whitespace(recursive) - return self - - def ignore_whitespace(self, recursive: bool = True) -> ParserElement: - super().ignore_whitespace(recursive) - - if recursive: - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.ignore_whitespace(recursive) - return self - - def ignore(self, other) -> ParserElement: - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - super().ignore(other) - if self.expr is not None: - self.expr.ignore(self.ignoreExprs[-1]) - else: - super().ignore(other) - if self.expr is not None: - self.expr.ignore(self.ignoreExprs[-1]) - return self - - def streamline(self) -> ParserElement: - super().streamline() - if self.expr is not None: - self.expr.streamline() - return self - - def _checkRecursion(self, parseElementList): - if self in parseElementList: - raise RecursiveGrammarException(parseElementList + [self]) - subRecCheckList = parseElementList[:] + [self] - if self.expr is not None: - self.expr._checkRecursion(subRecCheckList) - - def validate(self, validateTrace=None) -> None: - if validateTrace is None: - validateTrace = [] - tmp = validateTrace[:] + [self] - if self.expr is not None: - self.expr.validate(tmp) - self._checkRecursion([]) - - def _generateDefaultName(self): - return "{}:({})".format(self.__class__.__name__, str(self.expr)) - - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class IndentedBlock(ParseElementEnhance): - """ - Expression to match one or more expressions at a given indentation level. - Useful for parsing text where structure is implied by indentation (like Python source code). - """ - - class _Indent(Empty): - def __init__(self, ref_col: int): - super().__init__() - self.errmsg = "expected indent at column {}".format(ref_col) - self.add_condition(lambda s, l, t: col(l, s) == ref_col) - - class _IndentGreater(Empty): - def __init__(self, ref_col: int): - super().__init__() - self.errmsg = "expected indent at column greater than {}".format(ref_col) - self.add_condition(lambda s, l, t: col(l, s) > ref_col) - - def __init__( - self, expr: ParserElement, *, recursive: bool = False, grouped: bool = True - ): - super().__init__(expr, savelist=True) - # if recursive: - # raise NotImplementedError("IndentedBlock with recursive is not implemented") - self._recursive = recursive - self._grouped = grouped - self.parent_anchor = 1 - - def parseImpl(self, instring, loc, doActions=True): - # advance parse position to non-whitespace by using an Empty() - # this should be the column to be used for all subsequent indented lines - anchor_loc = Empty().preParse(instring, loc) - - # see if self.expr matches at the current location - if not it will raise an exception - # and no further work is necessary - self.expr.try_parse(instring, anchor_loc, doActions) - - indent_col = col(anchor_loc, instring) - peer_detect_expr = self._Indent(indent_col) - - inner_expr = Empty() + peer_detect_expr + self.expr - if self._recursive: - sub_indent = self._IndentGreater(indent_col) - nested_block = IndentedBlock( - self.expr, recursive=self._recursive, grouped=self._grouped - ) - nested_block.set_debug(self.debug) - nested_block.parent_anchor = indent_col - inner_expr += Opt(sub_indent + nested_block) - - inner_expr.set_name(f"inner {hex(id(inner_expr))[-4:].upper()}@{indent_col}") - block = OneOrMore(inner_expr) - - trailing_undent = self._Indent(self.parent_anchor) | StringEnd() - - if self._grouped: - wrapper = Group - else: - wrapper = lambda expr: expr - return (wrapper(block) + Optional(trailing_undent)).parseImpl( - instring, anchor_loc, doActions - ) - - -class AtStringStart(ParseElementEnhance): - """Matches if expression matches at the beginning of the parse - string:: - - AtStringStart(Word(nums)).parse_string("123") - # prints ["123"] - - AtStringStart(Word(nums)).parse_string(" 123") - # raises ParseException - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - self.callPreparse = False - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - raise ParseException(instring, loc, "not found at string start") - return super().parseImpl(instring, loc, doActions) - - -class AtLineStart(ParseElementEnhance): - r"""Matches if an expression matches at the beginning of a line within - the parse string - - Example:: - - test = '''\ - AAA this line - AAA and this line - AAA but not this one - B AAA and definitely not this one - ''' - - for t in (AtLineStart('AAA') + restOfLine).search_string(test): - print(t) - - prints:: - - ['AAA', ' this line'] - ['AAA', ' and this line'] - - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - self.callPreparse = False - - def parseImpl(self, instring, loc, doActions=True): - if col(loc, instring) != 1: - raise ParseException(instring, loc, "not found at line start") - return super().parseImpl(instring, loc, doActions) - - -class FollowedBy(ParseElementEnhance): - """Lookahead matching of the given parse expression. - ``FollowedBy`` does *not* advance the parsing position within - the input string, it only verifies that the specified parse - expression matches at the current position. ``FollowedBy`` - always returns a null token list. If any results names are defined - in the lookahead expression, those *will* be returned for access by - name. - - Example:: - - # use FollowedBy to match a label only if it is followed by a ':' - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - - attr_expr[1, ...].parse_string("shape: SQUARE color: BLACK posn: upper left").pprint() - - prints:: - - [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - # by using self._expr.parse and deleting the contents of the returned ParseResults list - # we keep any named results that were defined in the FollowedBy expression - _, ret = self.expr._parse(instring, loc, doActions=doActions) - del ret[:] - - return loc, ret - - -class PrecededBy(ParseElementEnhance): - """Lookbehind matching of the given parse expression. - ``PrecededBy`` does not advance the parsing position within the - input string, it only verifies that the specified parse expression - matches prior to the current position. ``PrecededBy`` always - returns a null token list, but if a results name is defined on the - given expression, it is returned. - - Parameters: - - - expr - expression that must match prior to the current parse - location - - retreat - (default= ``None``) - (int) maximum number of characters - to lookbehind prior to the current parse location - - If the lookbehind expression is a string, :class:`Literal`, - :class:`Keyword`, or a :class:`Word` or :class:`CharsNotIn` - with a specified exact or maximum length, then the retreat - parameter is not required. Otherwise, retreat must be specified to - give a maximum number of characters to look back from - the current parse position for a lookbehind match. - - Example:: - - # VB-style variable names with type prefixes - int_var = PrecededBy("#") + pyparsing_common.identifier - str_var = PrecededBy("$") + pyparsing_common.identifier - - """ - - def __init__( - self, expr: Union[ParserElement, str], retreat: typing.Optional[int] = None - ): - super().__init__(expr) - self.expr = self.expr().leave_whitespace() - self.mayReturnEmpty = True - self.mayIndexError = False - self.exact = False - if isinstance(expr, str_type): - retreat = len(expr) - self.exact = True - elif isinstance(expr, (Literal, Keyword)): - retreat = expr.matchLen - self.exact = True - elif isinstance(expr, (Word, CharsNotIn)) and expr.maxLen != _MAX_INT: - retreat = expr.maxLen - self.exact = True - elif isinstance(expr, PositionToken): - retreat = 0 - self.exact = True - self.retreat = retreat - self.errmsg = "not preceded by " + str(expr) - self.skipWhitespace = False - self.parseAction.append(lambda s, l, t: t.__delitem__(slice(None, None))) - - def parseImpl(self, instring, loc=0, doActions=True): - if self.exact: - if loc < self.retreat: - raise ParseException(instring, loc, self.errmsg) - start = loc - self.retreat - _, ret = self.expr._parse(instring, start) - else: - # retreat specified a maximum lookbehind window, iterate - test_expr = self.expr + StringEnd() - instring_slice = instring[max(0, loc - self.retreat) : loc] - last_expr = ParseException(instring, loc, self.errmsg) - for offset in range(1, min(loc, self.retreat + 1) + 1): - try: - # print('trying', offset, instring_slice, repr(instring_slice[loc - offset:])) - _, ret = test_expr._parse( - instring_slice, len(instring_slice) - offset - ) - except ParseBaseException as pbe: - last_expr = pbe - else: - break - else: - raise last_expr - return loc, ret - - -class Located(ParseElementEnhance): - """ - Decorates a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parse_with_tabs` - - Example:: - - wd = Word(alphas) - for match in Located(wd).search_string("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [0, ['ljsdf'], 5] - [8, ['lksdjjf'], 15] - [18, ['lkkjj'], 23] - - """ - - def parseImpl(self, instring, loc, doActions=True): - start = loc - loc, tokens = self.expr._parse(instring, start, doActions, callPreParse=False) - ret_tokens = ParseResults([start, tokens, loc]) - ret_tokens["locn_start"] = start - ret_tokens["value"] = tokens - ret_tokens["locn_end"] = loc - if self.resultsName: - # must return as a list, so that the name will be attached to the complete group - return loc, [ret_tokens] - else: - return loc, ret_tokens - - -class NotAny(ParseElementEnhance): - """ - Lookahead to disallow matching with the given parse expression. - ``NotAny`` does *not* advance the parsing position within the - input string, it only verifies that the specified parse expression - does *not* match at the current position. Also, ``NotAny`` does - *not* skip over leading whitespace. ``NotAny`` always returns - a null token list. May be constructed using the ``'~'`` operator. - - Example:: - - AND, OR, NOT = map(CaselessKeyword, "AND OR NOT".split()) - - # take care not to mistake keywords for identifiers - ident = ~(AND | OR | NOT) + Word(alphas) - boolean_term = Opt(NOT) + ident - - # very crude boolean expression - to support parenthesis groups and - # operation hierarchy, use infix_notation - boolean_expr = boolean_term + ((AND | OR) + boolean_term)[...] - - # integers that are followed by "." are actually floats - integer = Word(nums) + ~Char(".") - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - # do NOT use self.leave_whitespace(), don't want to propagate to exprs - # self.leave_whitespace() - self.skipWhitespace = False - - self.mayReturnEmpty = True - self.errmsg = "Found unwanted token, " + str(self.expr) - - def parseImpl(self, instring, loc, doActions=True): - if self.expr.can_parse_next(instring, loc): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - def _generateDefaultName(self): - return "~{" + str(self.expr) + "}" - - -class _MultipleMatch(ParseElementEnhance): - def __init__( - self, - expr: ParserElement, - stop_on: typing.Optional[Union[ParserElement, str]] = None, - *, - stopOn: typing.Optional[Union[ParserElement, str]] = None, - ): - super().__init__(expr) - stopOn = stopOn or stop_on - self.saveAsList = True - ender = stopOn - if isinstance(ender, str_type): - ender = self._literalStringClass(ender) - self.stopOn(ender) - - def stopOn(self, ender) -> ParserElement: - if isinstance(ender, str_type): - ender = self._literalStringClass(ender) - self.not_ender = ~ender if ender is not None else None - return self - - def parseImpl(self, instring, loc, doActions=True): - self_expr_parse = self.expr._parse - self_skip_ignorables = self._skipIgnorables - check_ender = self.not_ender is not None - if check_ender: - try_not_ender = self.not_ender.tryParse - - # must be at least one (but first see if we are the stopOn sentinel; - # if so, fail) - if check_ender: - try_not_ender(instring, loc) - loc, tokens = self_expr_parse(instring, loc, doActions) - try: - hasIgnoreExprs = not not self.ignoreExprs - while 1: - if check_ender: - try_not_ender(instring, loc) - if hasIgnoreExprs: - preloc = self_skip_ignorables(instring, loc) - else: - preloc = loc - loc, tmptokens = self_expr_parse(instring, preloc, doActions) - if tmptokens or tmptokens.haskeys(): - tokens += tmptokens - except (ParseException, IndexError): - pass - - return loc, tokens - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_ungrouped_named_tokens_in_collection - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in self.suppress_warnings_ - ): - for e in [self.expr] + self.expr.recurse(): - if ( - isinstance(e, ParserElement) - and e.resultsName - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in e.suppress_warnings_ - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "collides with {!r} on contained expression".format( - "warn_ungrouped_named_tokens_in_collection", - name, - type(self).__name__, - e.resultsName, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - -class OneOrMore(_MultipleMatch): - """ - Repetition of one or more of the given expression. - - Parameters: - - expr - expression that must match one or more times - - stop_on - (default= ``None``) - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - - Example:: - - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).set_parse_action(' '.join)) - - text = "shape: SQUARE posn: upper left color: BLACK" - attr_expr[1, ...].parse_string(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] - - # use stop_on attribute for OneOrMore to avoid reading label string as part of the data - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - OneOrMore(attr_expr).parse_string(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] - - # could also be written as - (attr_expr * (1,)).parse_string(text).pprint() - """ - - def _generateDefaultName(self): - return "{" + str(self.expr) + "}..." - - -class ZeroOrMore(_MultipleMatch): - """ - Optional repetition of zero or more of the given expression. - - Parameters: - - ``expr`` - expression that must match zero or more times - - ``stop_on`` - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - (default= ``None``) - - Example: similar to :class:`OneOrMore` - """ - - def __init__( - self, - expr: ParserElement, - stop_on: typing.Optional[Union[ParserElement, str]] = None, - *, - stopOn: typing.Optional[Union[ParserElement, str]] = None, - ): - super().__init__(expr, stopOn=stopOn or stop_on) - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - try: - return super().parseImpl(instring, loc, doActions) - except (ParseException, IndexError): - return loc, ParseResults([], name=self.resultsName) - - def _generateDefaultName(self): - return "[" + str(self.expr) + "]..." - - -class _NullToken: - def __bool__(self): - return False - - def __str__(self): - return "" - - -class Opt(ParseElementEnhance): - """ - Optional matching of the given expression. - - Parameters: - - ``expr`` - expression that must match zero or more times - - ``default`` (optional) - value to be returned if the optional expression is not found. - - Example:: - - # US postal code can be a 5-digit zip, plus optional 4-digit qualifier - zip = Combine(Word(nums, exact=5) + Opt('-' + Word(nums, exact=4))) - zip.run_tests(''' - # traditional ZIP code - 12345 - - # ZIP+4 form - 12101-0001 - - # invalid ZIP - 98765- - ''') - - prints:: - - # traditional ZIP code - 12345 - ['12345'] - - # ZIP+4 form - 12101-0001 - ['12101-0001'] - - # invalid ZIP - 98765- - ^ - FAIL: Expected end of text (at char 5), (line:1, col:6) - """ - - __optionalNotMatched = _NullToken() - - def __init__( - self, expr: Union[ParserElement, str], default: Any = __optionalNotMatched - ): - super().__init__(expr, savelist=False) - self.saveAsList = self.expr.saveAsList - self.defaultValue = default - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - self_expr = self.expr - try: - loc, tokens = self_expr._parse(instring, loc, doActions, callPreParse=False) - except (ParseException, IndexError): - default_value = self.defaultValue - if default_value is not self.__optionalNotMatched: - if self_expr.resultsName: - tokens = ParseResults([default_value]) - tokens[self_expr.resultsName] = default_value - else: - tokens = [default_value] - else: - tokens = [] - return loc, tokens - - def _generateDefaultName(self): - inner = str(self.expr) - # strip off redundant inner {}'s - while len(inner) > 1 and inner[0 :: len(inner) - 1] == "{}": - inner = inner[1:-1] - return "[" + inner + "]" - - -Optional = Opt - - -class SkipTo(ParseElementEnhance): - """ - Token for skipping over all undefined text until the matched - expression is found. - - Parameters: - - ``expr`` - target expression marking the end of the data to be skipped - - ``include`` - if ``True``, the target expression is also parsed - (the skipped text and target expression are returned as a 2-element - list) (default= ``False``). - - ``ignore`` - (default= ``None``) used to define grammars (typically quoted strings and - comments) that might contain false matches to the target expression - - ``fail_on`` - (default= ``None``) define expressions that are not allowed to be - included in the skipped test; if found before the target expression is found, - the :class:`SkipTo` is not a match - - Example:: - - report = ''' - Outstanding Issues Report - 1 Jan 2000 - - # | Severity | Description | Days Open - -----+----------+-------------------------------------------+----------- - 101 | Critical | Intermittent system crash | 6 - 94 | Cosmetic | Spelling error on Login ('log|n') | 14 - 79 | Minor | System slow when running too many reports | 47 - ''' - integer = Word(nums) - SEP = Suppress('|') - # use SkipTo to simply match everything up until the next SEP - # - ignore quoted strings, so that a '|' character inside a quoted string does not match - # - parse action will call token.strip() for each matched token, i.e., the description body - string_data = SkipTo(SEP, ignore=quoted_string) - string_data.set_parse_action(token_map(str.strip)) - ticket_expr = (integer("issue_num") + SEP - + string_data("sev") + SEP - + string_data("desc") + SEP - + integer("days_open")) - - for tkt in ticket_expr.search_string(report): - print tkt.dump() - - prints:: - - ['101', 'Critical', 'Intermittent system crash', '6'] - - days_open: '6' - - desc: 'Intermittent system crash' - - issue_num: '101' - - sev: 'Critical' - ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] - - days_open: '14' - - desc: "Spelling error on Login ('log|n')" - - issue_num: '94' - - sev: 'Cosmetic' - ['79', 'Minor', 'System slow when running too many reports', '47'] - - days_open: '47' - - desc: 'System slow when running too many reports' - - issue_num: '79' - - sev: 'Minor' - """ - - def __init__( - self, - other: Union[ParserElement, str], - include: bool = False, - ignore: bool = None, - fail_on: typing.Optional[Union[ParserElement, str]] = None, - *, - failOn: Union[ParserElement, str] = None, - ): - super().__init__(other) - failOn = failOn or fail_on - self.ignoreExpr = ignore - self.mayReturnEmpty = True - self.mayIndexError = False - self.includeMatch = include - self.saveAsList = False - if isinstance(failOn, str_type): - self.failOn = self._literalStringClass(failOn) - else: - self.failOn = failOn - self.errmsg = "No match found for " + str(self.expr) - - def parseImpl(self, instring, loc, doActions=True): - startloc = loc - instrlen = len(instring) - self_expr_parse = self.expr._parse - self_failOn_canParseNext = ( - self.failOn.canParseNext if self.failOn is not None else None - ) - self_ignoreExpr_tryParse = ( - self.ignoreExpr.tryParse if self.ignoreExpr is not None else None - ) - - tmploc = loc - while tmploc <= instrlen: - if self_failOn_canParseNext is not None: - # break if failOn expression matches - if self_failOn_canParseNext(instring, tmploc): - break - - if self_ignoreExpr_tryParse is not None: - # advance past ignore expressions - while 1: - try: - tmploc = self_ignoreExpr_tryParse(instring, tmploc) - except ParseBaseException: - break - - try: - self_expr_parse(instring, tmploc, doActions=False, callPreParse=False) - except (ParseException, IndexError): - # no match, advance loc in string - tmploc += 1 - else: - # matched skipto expr, done - break - - else: - # ran off the end of the input string without matching skipto expr, fail - raise ParseException(instring, loc, self.errmsg, self) - - # build up return values - loc = tmploc - skiptext = instring[startloc:loc] - skipresult = ParseResults(skiptext) - - if self.includeMatch: - loc, mat = self_expr_parse(instring, loc, doActions, callPreParse=False) - skipresult += mat - - return loc, skipresult - - -class Forward(ParseElementEnhance): - """ - Forward declaration of an expression to be defined later - - used for recursive grammars, such as algebraic infix notation. - When the expression is known, it is assigned to the ``Forward`` - variable using the ``'<<'`` operator. - - Note: take care when assigning to ``Forward`` not to overlook - precedence of operators. - - Specifically, ``'|'`` has a lower precedence than ``'<<'``, so that:: - - fwd_expr << a | b | c - - will actually be evaluated as:: - - (fwd_expr << a) | b | c - - thereby leaving b and c out as parseable alternatives. It is recommended that you - explicitly group the values inserted into the ``Forward``:: - - fwd_expr << (a | b | c) - - Converting to use the ``'<<='`` operator instead will avoid this problem. - - See :class:`ParseResults.pprint` for an example of a recursive - parser created using ``Forward``. - """ - - def __init__(self, other: typing.Optional[Union[ParserElement, str]] = None): - self.caller_frame = traceback.extract_stack(limit=2)[0] - super().__init__(other, savelist=False) - self.lshift_line = None - - def __lshift__(self, other): - if hasattr(self, "caller_frame"): - del self.caller_frame - if isinstance(other, str_type): - other = self._literalStringClass(other) - self.expr = other - self.mayIndexError = self.expr.mayIndexError - self.mayReturnEmpty = self.expr.mayReturnEmpty - self.set_whitespace_chars( - self.expr.whiteChars, copy_defaults=self.expr.copyDefaultWhiteChars - ) - self.skipWhitespace = self.expr.skipWhitespace - self.saveAsList = self.expr.saveAsList - self.ignoreExprs.extend(self.expr.ignoreExprs) - self.lshift_line = traceback.extract_stack(limit=2)[-2] - return self - - def __ilshift__(self, other): - return self << other - - def __or__(self, other): - caller_line = traceback.extract_stack(limit=2)[-2] - if ( - __diag__.warn_on_match_first_with_lshift_operator - and caller_line == self.lshift_line - and Diagnostics.warn_on_match_first_with_lshift_operator - not in self.suppress_warnings_ - ): - warnings.warn( - "using '<<' operator with '|' is probably an error, use '<<='", - stacklevel=2, - ) - ret = super().__or__(other) - return ret - - def __del__(self): - # see if we are getting dropped because of '=' reassignment of var instead of '<<=' or '<<' - if ( - self.expr is None - and __diag__.warn_on_assignment_to_Forward - and Diagnostics.warn_on_assignment_to_Forward not in self.suppress_warnings_ - ): - warnings.warn_explicit( - "Forward defined here but no expression attached later using '<<=' or '<<'", - UserWarning, - filename=self.caller_frame.filename, - lineno=self.caller_frame.lineno, - ) - - def parseImpl(self, instring, loc, doActions=True): - if ( - self.expr is None - and __diag__.warn_on_parse_using_empty_Forward - and Diagnostics.warn_on_parse_using_empty_Forward - not in self.suppress_warnings_ - ): - # walk stack until parse_string, scan_string, search_string, or transform_string is found - parse_fns = [ - "parse_string", - "scan_string", - "search_string", - "transform_string", - ] - tb = traceback.extract_stack(limit=200) - for i, frm in enumerate(reversed(tb), start=1): - if frm.name in parse_fns: - stacklevel = i + 1 - break - else: - stacklevel = 2 - warnings.warn( - "Forward expression was never assigned a value, will not parse any input", - stacklevel=stacklevel, - ) - if not ParserElement._left_recursion_enabled: - return super().parseImpl(instring, loc, doActions) - # ## Bounded Recursion algorithm ## - # Recursion only needs to be processed at ``Forward`` elements, since they are - # the only ones that can actually refer to themselves. The general idea is - # to handle recursion stepwise: We start at no recursion, then recurse once, - # recurse twice, ..., until more recursion offers no benefit (we hit the bound). - # - # The "trick" here is that each ``Forward`` gets evaluated in two contexts - # - to *match* a specific recursion level, and - # - to *search* the bounded recursion level - # and the two run concurrently. The *search* must *match* each recursion level - # to find the best possible match. This is handled by a memo table, which - # provides the previous match to the next level match attempt. - # - # See also "Left Recursion in Parsing Expression Grammars", Medeiros et al. - # - # There is a complication since we not only *parse* but also *transform* via - # actions: We do not want to run the actions too often while expanding. Thus, - # we expand using `doActions=False` and only run `doActions=True` if the next - # recursion level is acceptable. - with ParserElement.recursion_lock: - memo = ParserElement.recursion_memos - try: - # we are parsing at a specific recursion expansion - use it as-is - prev_loc, prev_result = memo[loc, self, doActions] - if isinstance(prev_result, Exception): - raise prev_result - return prev_loc, prev_result.copy() - except KeyError: - act_key = (loc, self, True) - peek_key = (loc, self, False) - # we are searching for the best recursion expansion - keep on improving - # both `doActions` cases must be tracked separately here! - prev_loc, prev_peek = memo[peek_key] = ( - loc - 1, - ParseException( - instring, loc, "Forward recursion without base case", self - ), - ) - if doActions: - memo[act_key] = memo[peek_key] - while True: - try: - new_loc, new_peek = super().parseImpl(instring, loc, False) - except ParseException: - # we failed before getting any match – do not hide the error - if isinstance(prev_peek, Exception): - raise - new_loc, new_peek = prev_loc, prev_peek - # the match did not get better: we are done - if new_loc <= prev_loc: - if doActions: - # replace the match for doActions=False as well, - # in case the action did backtrack - prev_loc, prev_result = memo[peek_key] = memo[act_key] - del memo[peek_key], memo[act_key] - return prev_loc, prev_result.copy() - del memo[peek_key] - return prev_loc, prev_peek.copy() - # the match did get better: see if we can improve further - else: - if doActions: - try: - memo[act_key] = super().parseImpl(instring, loc, True) - except ParseException as e: - memo[peek_key] = memo[act_key] = (new_loc, e) - raise - prev_loc, prev_peek = memo[peek_key] = new_loc, new_peek - - def leave_whitespace(self, recursive: bool = True) -> ParserElement: - self.skipWhitespace = False - return self - - def ignore_whitespace(self, recursive: bool = True) -> ParserElement: - self.skipWhitespace = True - return self - - def streamline(self) -> ParserElement: - if not self.streamlined: - self.streamlined = True - if self.expr is not None: - self.expr.streamline() - return self - - def validate(self, validateTrace=None) -> None: - if validateTrace is None: - validateTrace = [] - - if self not in validateTrace: - tmp = validateTrace[:] + [self] - if self.expr is not None: - self.expr.validate(tmp) - self._checkRecursion([]) - - def _generateDefaultName(self): - # Avoid infinite recursion by setting a temporary _defaultName - self._defaultName = ": ..." - - # Use the string representation of main expression. - retString = "..." - try: - if self.expr is not None: - retString = str(self.expr)[:1000] - else: - retString = "None" - finally: - return self.__class__.__name__ + ": " + retString - - def copy(self) -> ParserElement: - if self.expr is not None: - return super().copy() - else: - ret = Forward() - ret <<= self - return ret - - def _setResultsName(self, name, list_all_matches=False): - if ( - __diag__.warn_name_set_on_empty_Forward - and Diagnostics.warn_name_set_on_empty_Forward - not in self.suppress_warnings_ - ): - if self.expr is None: - warnings.warn( - "{}: setting results name {!r} on {} expression " - "that has no contained expression".format( - "warn_name_set_on_empty_Forward", name, type(self).__name__ - ), - stacklevel=3, - ) - - return super()._setResultsName(name, list_all_matches) - - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class TokenConverter(ParseElementEnhance): - """ - Abstract subclass of :class:`ParseExpression`, for converting parsed results. - """ - - def __init__(self, expr: Union[ParserElement, str], savelist=False): - super().__init__(expr) # , savelist) - self.saveAsList = False - - -class Combine(TokenConverter): - """Converter to concatenate all matching tokens to a single string. - By default, the matching patterns must also be contiguous in the - input string; this can be disabled by specifying - ``'adjacent=False'`` in the constructor. - - Example:: - - real = Word(nums) + '.' + Word(nums) - print(real.parse_string('3.1416')) # -> ['3', '.', '1416'] - # will also erroneously match the following - print(real.parse_string('3. 1416')) # -> ['3', '.', '1416'] - - real = Combine(Word(nums) + '.' + Word(nums)) - print(real.parse_string('3.1416')) # -> ['3.1416'] - # no match when there are internal spaces - print(real.parse_string('3. 1416')) # -> Exception: Expected W:(0123...) - """ - - def __init__( - self, - expr: ParserElement, - join_string: str = "", - adjacent: bool = True, - *, - joinString: typing.Optional[str] = None, - ): - super().__init__(expr) - joinString = joinString if joinString is not None else join_string - # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself - if adjacent: - self.leave_whitespace() - self.adjacent = adjacent - self.skipWhitespace = True - self.joinString = joinString - self.callPreparse = True - - def ignore(self, other) -> ParserElement: - if self.adjacent: - ParserElement.ignore(self, other) - else: - super().ignore(other) - return self - - def postParse(self, instring, loc, tokenlist): - retToks = tokenlist.copy() - del retToks[:] - retToks += ParseResults( - ["".join(tokenlist._asStringList(self.joinString))], modal=self.modalResults - ) - - if self.resultsName and retToks.haskeys(): - return [retToks] - else: - return retToks - - -class Group(TokenConverter): - """Converter to return the matched tokens as a list - useful for - returning tokens of :class:`ZeroOrMore` and :class:`OneOrMore` expressions. - - The optional ``aslist`` argument when set to True will return the - parsed tokens as a Python list instead of a pyparsing ParseResults. - - Example:: - - ident = Word(alphas) - num = Word(nums) - term = ident | num - func = ident + Opt(delimited_list(term)) - print(func.parse_string("fn a, b, 100")) - # -> ['fn', 'a', 'b', '100'] - - func = ident + Group(Opt(delimited_list(term))) - print(func.parse_string("fn a, b, 100")) - # -> ['fn', ['a', 'b', '100']] - """ - - def __init__(self, expr: ParserElement, aslist: bool = False): - super().__init__(expr) - self.saveAsList = True - self._asPythonList = aslist - - def postParse(self, instring, loc, tokenlist): - if self._asPythonList: - return ParseResults.List( - tokenlist.asList() - if isinstance(tokenlist, ParseResults) - else list(tokenlist) - ) - else: - return [tokenlist] - - -class Dict(TokenConverter): - """Converter to return a repetitive expression as a list, but also - as a dictionary. Each element can also be referenced using the first - token in the expression as its key. Useful for tabular report - scraping when the first column can be used as a item key. - - The optional ``asdict`` argument when set to True will return the - parsed tokens as a Python dict instead of a pyparsing ParseResults. - - Example:: - - data_word = Word(alphas) - label = data_word + FollowedBy(':') - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - - # print attributes as plain groups - print(attr_expr[1, ...].parse_string(text).dump()) - - # instead of OneOrMore(expr), parse using Dict(Group(expr)[1, ...]) - Dict will auto-assign names - result = Dict(Group(attr_expr)[1, ...]).parse_string(text) - print(result.dump()) - - # access named fields as dict entries, or output as dict - print(result['shape']) - print(result.as_dict()) - - prints:: - - ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} - - See more examples at :class:`ParseResults` of accessing fields by results name. - """ - - def __init__(self, expr: ParserElement, asdict: bool = False): - super().__init__(expr) - self.saveAsList = True - self._asPythonDict = asdict - - def postParse(self, instring, loc, tokenlist): - for i, tok in enumerate(tokenlist): - if len(tok) == 0: - continue - - ikey = tok[0] - if isinstance(ikey, int): - ikey = str(ikey).strip() - - if len(tok) == 1: - tokenlist[ikey] = _ParseResultsWithOffset("", i) - - elif len(tok) == 2 and not isinstance(tok[1], ParseResults): - tokenlist[ikey] = _ParseResultsWithOffset(tok[1], i) - - else: - try: - dictvalue = tok.copy() # ParseResults(i) - except Exception: - exc = TypeError( - "could not extract dict values from parsed results" - " - Dict expression must contain Grouped expressions" - ) - raise exc from None - - del dictvalue[0] - - if len(dictvalue) != 1 or ( - isinstance(dictvalue, ParseResults) and dictvalue.haskeys() - ): - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue, i) - else: - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0], i) - - if self._asPythonDict: - return [tokenlist.as_dict()] if self.resultsName else tokenlist.as_dict() - else: - return [tokenlist] if self.resultsName else tokenlist - - -class Suppress(TokenConverter): - """Converter for ignoring the results of a parsed expression. - - Example:: - - source = "a, b, c,d" - wd = Word(alphas) - wd_list1 = wd + (',' + wd)[...] - print(wd_list1.parse_string(source)) - - # often, delimiters that are useful during parsing are just in the - # way afterward - use Suppress to keep them out of the parsed output - wd_list2 = wd + (Suppress(',') + wd)[...] - print(wd_list2.parse_string(source)) - - # Skipped text (using '...') can be suppressed as well - source = "lead in START relevant text END trailing text" - start_marker = Keyword("START") - end_marker = Keyword("END") - find_body = Suppress(...) + start_marker + ... + end_marker - print(find_body.parse_string(source) - - prints:: - - ['a', ',', 'b', ',', 'c', ',', 'd'] - ['a', 'b', 'c', 'd'] - ['START', 'relevant text ', 'END'] - - (See also :class:`delimited_list`.) - """ - - def __init__(self, expr: Union[ParserElement, str], savelist: bool = False): - if expr is ...: - expr = _PendingSkip(NoMatch()) - super().__init__(expr) - - def __add__(self, other) -> "ParserElement": - if isinstance(self.expr, _PendingSkip): - return Suppress(SkipTo(other)) + other - else: - return super().__add__(other) - - def __sub__(self, other) -> "ParserElement": - if isinstance(self.expr, _PendingSkip): - return Suppress(SkipTo(other)) - other - else: - return super().__sub__(other) - - def postParse(self, instring, loc, tokenlist): - return [] - - def suppress(self) -> ParserElement: - return self - - -def trace_parse_action(f: ParseAction) -> ParseAction: - """Decorator for debugging parse actions. - - When the parse action is called, this decorator will print - ``">> entering method-name(line:, , )"``. - When the parse action completes, the decorator will print - ``"<<"`` followed by the returned value, or any exception that the parse action raised. - - Example:: - - wd = Word(alphas) - - @trace_parse_action - def remove_duplicate_chars(tokens): - return ''.join(sorted(set(''.join(tokens)))) - - wds = wd[1, ...].set_parse_action(remove_duplicate_chars) - print(wds.parse_string("slkdjs sld sldd sdlf sdljf")) - - prints:: - - >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) - < 3: - thisFunc = paArgs[0].__class__.__name__ + "." + thisFunc - sys.stderr.write( - ">>entering {}(line: {!r}, {}, {!r})\n".format(thisFunc, line(l, s), l, t) - ) - try: - ret = f(*paArgs) - except Exception as exc: - sys.stderr.write("< str: - r"""Helper to easily define string ranges for use in :class:`Word` - construction. Borrows syntax from regexp ``'[]'`` string range - definitions:: - - srange("[0-9]") -> "0123456789" - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - - The input string must be enclosed in []'s, and the returned string - is the expanded character set joined into a single string. The - values enclosed in the []'s may be: - - - a single character - - an escaped character with a leading backslash (such as ``\-`` - or ``\]``) - - an escaped hex character with a leading ``'\x'`` - (``\x21``, which is a ``'!'`` character) (``\0x##`` - is also supported for backwards compatibility) - - an escaped octal character with a leading ``'\0'`` - (``\041``, which is a ``'!'`` character) - - a range of any of the above, separated by a dash (``'a-z'``, - etc.) - - any combination of the above (``'aeiouy'``, - ``'a-zA-Z0-9_$'``, etc.) - """ - _expanded = ( - lambda p: p - if not isinstance(p, ParseResults) - else "".join(chr(c) for c in range(ord(p[0]), ord(p[1]) + 1)) - ) - try: - return "".join(_expanded(part) for part in _reBracketExpr.parse_string(s).body) - except Exception: - return "" - - -def token_map(func, *args) -> ParseAction: - """Helper to define a parse action by mapping a function to all - elements of a :class:`ParseResults` list. If any additional args are passed, - they are forwarded to the given function as additional arguments - after the token, as in - ``hex_integer = Word(hexnums).set_parse_action(token_map(int, 16))``, - which will convert the parsed data to an integer using base 16. - - Example (compare the last to example in :class:`ParserElement.transform_string`:: - - hex_ints = Word(hexnums)[1, ...].set_parse_action(token_map(int, 16)) - hex_ints.run_tests(''' - 00 11 22 aa FF 0a 0d 1a - ''') - - upperword = Word(alphas).set_parse_action(token_map(str.upper)) - upperword[1, ...].run_tests(''' - my kingdom for a horse - ''') - - wd = Word(alphas).set_parse_action(token_map(str.title)) - wd[1, ...].set_parse_action(' '.join).run_tests(''' - now is the winter of our discontent made glorious summer by this sun of york - ''') - - prints:: - - 00 11 22 aa FF 0a 0d 1a - [0, 17, 34, 170, 255, 10, 13, 26] - - my kingdom for a horse - ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] - - now is the winter of our discontent made glorious summer by this sun of york - ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] - """ - - def pa(s, l, t): - return [func(tokn, *args) for tokn in t] - - func_name = getattr(func, "__name__", getattr(func, "__class__").__name__) - pa.__name__ = func_name - - return pa - - -def autoname_elements() -> None: - """ - Utility to simplify mass-naming of parser elements, for - generating railroad diagram with named subdiagrams. - """ - for name, var in sys._getframe().f_back.f_locals.items(): - if isinstance(var, ParserElement) and not var.customName: - var.set_name(name) - - -dbl_quoted_string = Combine( - Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"' -).set_name("string enclosed in double quotes") - -sgl_quoted_string = Combine( - Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'" -).set_name("string enclosed in single quotes") - -quoted_string = Combine( - Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"' - | Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'" -).set_name("quotedString using single or double quotes") - -unicode_string = Combine("u" + quoted_string.copy()).set_name("unicode string literal") - - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - -# backward compatibility names -tokenMap = token_map -conditionAsParseAction = condition_as_parse_action -nullDebugAction = null_debug_action -sglQuotedString = sgl_quoted_string -dblQuotedString = dbl_quoted_string -quotedString = quoted_string -unicodeString = unicode_string -lineStart = line_start -lineEnd = line_end -stringStart = string_start -stringEnd = string_end -traceParseAction = trace_parse_action diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py deleted file mode 100644 index 898644755..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/diagram/__init__.py +++ /dev/null @@ -1,642 +0,0 @@ -import railroad -import pyparsing -import typing -from typing import ( - List, - NamedTuple, - Generic, - TypeVar, - Dict, - Callable, - Set, - Iterable, -) -from jinja2 import Template -from io import StringIO -import inspect - - -jinja2_template_source = """\ - - - - {% if not head %} - - {% else %} - {{ head | safe }} - {% endif %} - - -{{ body | safe }} -{% for diagram in diagrams %} -
      -

      {{ diagram.title }}

      -
      {{ diagram.text }}
      -
      - {{ diagram.svg }} -
      -
      -{% endfor %} - - -""" - -template = Template(jinja2_template_source) - -# Note: ideally this would be a dataclass, but we're supporting Python 3.5+ so we can't do this yet -NamedDiagram = NamedTuple( - "NamedDiagram", - [("name", str), ("diagram", typing.Optional[railroad.DiagramItem]), ("index", int)], -) -""" -A simple structure for associating a name with a railroad diagram -""" - -T = TypeVar("T") - - -class EachItem(railroad.Group): - """ - Custom railroad item to compose a: - - Group containing a - - OneOrMore containing a - - Choice of the elements in the Each - with the group label indicating that all must be matched - """ - - all_label = "[ALL]" - - def __init__(self, *items): - choice_item = railroad.Choice(len(items) - 1, *items) - one_or_more_item = railroad.OneOrMore(item=choice_item) - super().__init__(one_or_more_item, label=self.all_label) - - -class AnnotatedItem(railroad.Group): - """ - Simple subclass of Group that creates an annotation label - """ - - def __init__(self, label: str, item): - super().__init__(item=item, label="[{}]".format(label) if label else label) - - -class EditablePartial(Generic[T]): - """ - Acts like a functools.partial, but can be edited. In other words, it represents a type that hasn't yet been - constructed. - """ - - # We need this here because the railroad constructors actually transform the data, so can't be called until the - # entire tree is assembled - - def __init__(self, func: Callable[..., T], args: list, kwargs: dict): - self.func = func - self.args = args - self.kwargs = kwargs - - @classmethod - def from_call(cls, func: Callable[..., T], *args, **kwargs) -> "EditablePartial[T]": - """ - If you call this function in the same way that you would call the constructor, it will store the arguments - as you expect. For example EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3) - """ - return EditablePartial(func=func, args=list(args), kwargs=kwargs) - - @property - def name(self): - return self.kwargs["name"] - - def __call__(self) -> T: - """ - Evaluate the partial and return the result - """ - args = self.args.copy() - kwargs = self.kwargs.copy() - - # This is a helpful hack to allow you to specify varargs parameters (e.g. *args) as keyword args (e.g. - # args=['list', 'of', 'things']) - arg_spec = inspect.getfullargspec(self.func) - if arg_spec.varargs in self.kwargs: - args += kwargs.pop(arg_spec.varargs) - - return self.func(*args, **kwargs) - - -def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str: - """ - Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams - :params kwargs: kwargs to be passed in to the template - """ - data = [] - for diagram in diagrams: - if diagram.diagram is None: - continue - io = StringIO() - diagram.diagram.writeSvg(io.write) - title = diagram.name - if diagram.index == 0: - title += " (root)" - data.append({"title": title, "text": "", "svg": io.getvalue()}) - - return template.render(diagrams=data, **kwargs) - - -def resolve_partial(partial: "EditablePartial[T]") -> T: - """ - Recursively resolves a collection of Partials into whatever type they are - """ - if isinstance(partial, EditablePartial): - partial.args = resolve_partial(partial.args) - partial.kwargs = resolve_partial(partial.kwargs) - return partial() - elif isinstance(partial, list): - return [resolve_partial(x) for x in partial] - elif isinstance(partial, dict): - return {key: resolve_partial(x) for key, x in partial.items()} - else: - return partial - - -def to_railroad( - element: pyparsing.ParserElement, - diagram_kwargs: typing.Optional[dict] = None, - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, -) -> List[NamedDiagram]: - """ - Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram - creation if you want to access the Railroad tree before it is converted to HTML - :param element: base element of the parser being diagrammed - :param diagram_kwargs: kwargs to pass to the Diagram() constructor - :param vertical: (optional) - int - limit at which number of alternatives should be - shown vertically instead of horizontally - :param show_results_names - bool to indicate whether results name annotations should be - included in the diagram - :param show_groups - bool to indicate whether groups should be highlighted with an unlabeled - surrounding box - """ - # Convert the whole tree underneath the root - lookup = ConverterState(diagram_kwargs=diagram_kwargs or {}) - _to_diagram_element( - element, - lookup=lookup, - parent=None, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - root_id = id(element) - # Convert the root if it hasn't been already - if root_id in lookup: - if not element.customName: - lookup[root_id].name = "" - lookup[root_id].mark_for_extraction(root_id, lookup, force=True) - - # Now that we're finished, we can convert from intermediate structures into Railroad elements - diags = list(lookup.diagrams.values()) - if len(diags) > 1: - # collapse out duplicate diags with the same name - seen = set() - deduped_diags = [] - for d in diags: - # don't extract SkipTo elements, they are uninformative as subdiagrams - if d.name == "...": - continue - if d.name is not None and d.name not in seen: - seen.add(d.name) - deduped_diags.append(d) - resolved = [resolve_partial(partial) for partial in deduped_diags] - else: - # special case - if just one diagram, always display it, even if - # it has no name - resolved = [resolve_partial(partial) for partial in diags] - return sorted(resolved, key=lambda diag: diag.index) - - -def _should_vertical( - specification: int, exprs: Iterable[pyparsing.ParserElement] -) -> bool: - """ - Returns true if we should return a vertical list of elements - """ - if specification is None: - return False - else: - return len(_visible_exprs(exprs)) >= specification - - -class ElementState: - """ - State recorded for an individual pyparsing Element - """ - - # Note: this should be a dataclass, but we have to support Python 3.5 - def __init__( - self, - element: pyparsing.ParserElement, - converted: EditablePartial, - parent: EditablePartial, - number: int, - name: str = None, - parent_index: typing.Optional[int] = None, - ): - #: The pyparsing element that this represents - self.element: pyparsing.ParserElement = element - #: The name of the element - self.name: typing.Optional[str] = name - #: The output Railroad element in an unconverted state - self.converted: EditablePartial = converted - #: The parent Railroad element, which we store so that we can extract this if it's duplicated - self.parent: EditablePartial = parent - #: The order in which we found this element, used for sorting diagrams if this is extracted into a diagram - self.number: int = number - #: The index of this inside its parent - self.parent_index: typing.Optional[int] = parent_index - #: If true, we should extract this out into a subdiagram - self.extract: bool = False - #: If true, all of this element's children have been filled out - self.complete: bool = False - - def mark_for_extraction( - self, el_id: int, state: "ConverterState", name: str = None, force: bool = False - ): - """ - Called when this instance has been seen twice, and thus should eventually be extracted into a sub-diagram - :param el_id: id of the element - :param state: element/diagram state tracker - :param name: name to use for this element's text - :param force: If true, force extraction now, regardless of the state of this. Only useful for extracting the - root element when we know we're finished - """ - self.extract = True - - # Set the name - if not self.name: - if name: - # Allow forcing a custom name - self.name = name - elif self.element.customName: - self.name = self.element.customName - else: - self.name = "" - - # Just because this is marked for extraction doesn't mean we can do it yet. We may have to wait for children - # to be added - # Also, if this is just a string literal etc, don't bother extracting it - if force or (self.complete and _worth_extracting(self.element)): - state.extract_into_diagram(el_id) - - -class ConverterState: - """ - Stores some state that persists between recursions into the element tree - """ - - def __init__(self, diagram_kwargs: typing.Optional[dict] = None): - #: A dictionary mapping ParserElements to state relating to them - self._element_diagram_states: Dict[int, ElementState] = {} - #: A dictionary mapping ParserElement IDs to subdiagrams generated from them - self.diagrams: Dict[int, EditablePartial[NamedDiagram]] = {} - #: The index of the next unnamed element - self.unnamed_index: int = 1 - #: The index of the next element. This is used for sorting - self.index: int = 0 - #: Shared kwargs that are used to customize the construction of diagrams - self.diagram_kwargs: dict = diagram_kwargs or {} - self.extracted_diagram_names: Set[str] = set() - - def __setitem__(self, key: int, value: ElementState): - self._element_diagram_states[key] = value - - def __getitem__(self, key: int) -> ElementState: - return self._element_diagram_states[key] - - def __delitem__(self, key: int): - del self._element_diagram_states[key] - - def __contains__(self, key: int): - return key in self._element_diagram_states - - def generate_unnamed(self) -> int: - """ - Generate a number used in the name of an otherwise unnamed diagram - """ - self.unnamed_index += 1 - return self.unnamed_index - - def generate_index(self) -> int: - """ - Generate a number used to index a diagram - """ - self.index += 1 - return self.index - - def extract_into_diagram(self, el_id: int): - """ - Used when we encounter the same token twice in the same tree. When this - happens, we replace all instances of that token with a terminal, and - create a new subdiagram for the token - """ - position = self[el_id] - - # Replace the original definition of this element with a regular block - if position.parent: - ret = EditablePartial.from_call(railroad.NonTerminal, text=position.name) - if "item" in position.parent.kwargs: - position.parent.kwargs["item"] = ret - elif "items" in position.parent.kwargs: - position.parent.kwargs["items"][position.parent_index] = ret - - # If the element we're extracting is a group, skip to its content but keep the title - if position.converted.func == railroad.Group: - content = position.converted.kwargs["item"] - else: - content = position.converted - - self.diagrams[el_id] = EditablePartial.from_call( - NamedDiagram, - name=position.name, - diagram=EditablePartial.from_call( - railroad.Diagram, content, **self.diagram_kwargs - ), - index=position.number, - ) - - del self[el_id] - - -def _worth_extracting(element: pyparsing.ParserElement) -> bool: - """ - Returns true if this element is worth having its own sub-diagram. Simply, if any of its children - themselves have children, then its complex enough to extract - """ - children = element.recurse() - return any(child.recurse() for child in children) - - -def _apply_diagram_item_enhancements(fn): - """ - decorator to ensure enhancements to a diagram item (such as results name annotations) - get applied on return from _to_diagram_element (we do this since there are several - returns in _to_diagram_element) - """ - - def _inner( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, - ) -> typing.Optional[EditablePartial]: - - ret = fn( - element, - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ) - - # apply annotation for results name, if present - if show_results_names and ret is not None: - element_results_name = element.resultsName - if element_results_name: - # add "*" to indicate if this is a "list all results" name - element_results_name += "" if element.modalResults else "*" - ret = EditablePartial.from_call( - railroad.Group, item=ret, label=element_results_name - ) - - return ret - - return _inner - - -def _visible_exprs(exprs: Iterable[pyparsing.ParserElement]): - non_diagramming_exprs = ( - pyparsing.ParseElementEnhance, - pyparsing.PositionToken, - pyparsing.And._ErrorStop, - ) - return [ - e - for e in exprs - if not (e.customName or e.resultsName or isinstance(e, non_diagramming_exprs)) - ] - - -@_apply_diagram_item_enhancements -def _to_diagram_element( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, -) -> typing.Optional[EditablePartial]: - """ - Recursively converts a PyParsing Element to a railroad Element - :param lookup: The shared converter state that keeps track of useful things - :param index: The index of this element within the parent - :param parent: The parent of this element in the output tree - :param vertical: Controls at what point we make a list of elements vertical. If this is an integer (the default), - it sets the threshold of the number of items before we go vertical. If True, always go vertical, if False, never - do so - :param name_hint: If provided, this will override the generated name - :param show_results_names: bool flag indicating whether to add annotations for results names - :returns: The converted version of the input element, but as a Partial that hasn't yet been constructed - :param show_groups: bool flag indicating whether to show groups using bounding box - """ - exprs = element.recurse() - name = name_hint or element.customName or element.__class__.__name__ - - # Python's id() is used to provide a unique identifier for elements - el_id = id(element) - - element_results_name = element.resultsName - - # Here we basically bypass processing certain wrapper elements if they contribute nothing to the diagram - if not element.customName: - if isinstance( - element, - ( - # pyparsing.TokenConverter, - # pyparsing.Forward, - pyparsing.Located, - ), - ): - # However, if this element has a useful custom name, and its child does not, we can pass it on to the child - if exprs: - if not exprs[0].customName: - propagated_name = name - else: - propagated_name = None - - return _to_diagram_element( - element.expr, - parent=parent, - lookup=lookup, - vertical=vertical, - index=index, - name_hint=propagated_name, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # If the element isn't worth extracting, we always treat it as the first time we say it - if _worth_extracting(element): - if el_id in lookup: - # If we've seen this element exactly once before, we are only just now finding out that it's a duplicate, - # so we have to extract it into a new diagram. - looked_up = lookup[el_id] - looked_up.mark_for_extraction(el_id, lookup, name=name_hint) - ret = EditablePartial.from_call(railroad.NonTerminal, text=looked_up.name) - return ret - - elif el_id in lookup.diagrams: - # If we have seen the element at least twice before, and have already extracted it into a subdiagram, we - # just put in a marker element that refers to the sub-diagram - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - return ret - - # Recursively convert child elements - # Here we find the most relevant Railroad element for matching pyparsing Element - # We use ``items=[]`` here to hold the place for where the child elements will go once created - if isinstance(element, pyparsing.And): - # detect And's created with ``expr*N`` notation - for these use a OneOrMore with a repeat - # (all will have the same name, and resultsName) - if not exprs: - return None - if len(set((e.name, e.resultsName) for e in exprs)) == 1: - ret = EditablePartial.from_call( - railroad.OneOrMore, item="", repeat=str(len(exprs)) - ) - elif _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Stack, items=[]) - else: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)): - if not exprs: - return None - if _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Choice, 0, items=[]) - else: - ret = EditablePartial.from_call(railroad.HorizontalChoice, items=[]) - elif isinstance(element, pyparsing.Each): - if not exprs: - return None - ret = EditablePartial.from_call(EachItem, items=[]) - elif isinstance(element, pyparsing.NotAny): - ret = EditablePartial.from_call(AnnotatedItem, label="NOT", item="") - elif isinstance(element, pyparsing.FollowedBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKAHEAD", item="") - elif isinstance(element, pyparsing.PrecededBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKBEHIND", item="") - elif isinstance(element, pyparsing.Group): - if show_groups: - ret = EditablePartial.from_call(AnnotatedItem, label="", item="") - else: - ret = EditablePartial.from_call(railroad.Group, label="", item="") - elif isinstance(element, pyparsing.TokenConverter): - ret = EditablePartial.from_call( - AnnotatedItem, label=type(element).__name__.lower(), item="" - ) - elif isinstance(element, pyparsing.Opt): - ret = EditablePartial.from_call(railroad.Optional, item="") - elif isinstance(element, pyparsing.OneOrMore): - ret = EditablePartial.from_call(railroad.OneOrMore, item="") - elif isinstance(element, pyparsing.ZeroOrMore): - ret = EditablePartial.from_call(railroad.ZeroOrMore, item="") - elif isinstance(element, pyparsing.Group): - ret = EditablePartial.from_call( - railroad.Group, item=None, label=element_results_name - ) - elif isinstance(element, pyparsing.Empty) and not element.customName: - # Skip unnamed "Empty" elements - ret = None - elif len(exprs) > 1: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif len(exprs) > 0 and not element_results_name: - ret = EditablePartial.from_call(railroad.Group, item="", label=name) - else: - terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName) - ret = terminal - - if ret is None: - return - - # Indicate this element's position in the tree so we can extract it if necessary - lookup[el_id] = ElementState( - element=element, - converted=ret, - parent=parent, - parent_index=index, - number=lookup.generate_index(), - ) - if element.customName: - lookup[el_id].mark_for_extraction(el_id, lookup, element.customName) - - i = 0 - for expr in exprs: - # Add a placeholder index in case we have to extract the child before we even add it to the parent - if "items" in ret.kwargs: - ret.kwargs["items"].insert(i, None) - - item = _to_diagram_element( - expr, - parent=ret, - lookup=lookup, - vertical=vertical, - index=i, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # Some elements don't need to be shown in the diagram - if item is not None: - if "item" in ret.kwargs: - ret.kwargs["item"] = item - elif "items" in ret.kwargs: - # If we've already extracted the child, don't touch this index, since it's occupied by a nonterminal - ret.kwargs["items"][i] = item - i += 1 - elif "items" in ret.kwargs: - # If we're supposed to skip this element, remove it from the parent - del ret.kwargs["items"][i] - - # If all this items children are none, skip this item - if ret and ( - ("items" in ret.kwargs and len(ret.kwargs["items"]) == 0) - or ("item" in ret.kwargs and ret.kwargs["item"] is None) - ): - ret = EditablePartial.from_call(railroad.Terminal, name) - - # Mark this element as "complete", ie it has all of its children - if el_id in lookup: - lookup[el_id].complete = True - - if el_id in lookup and lookup[el_id].extract and lookup[el_id].complete: - lookup.extract_into_diagram(el_id) - if ret is not None: - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - - return ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py deleted file mode 100644 index a38447bb0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/exceptions.py +++ /dev/null @@ -1,267 +0,0 @@ -# exceptions.py - -import re -import sys -import typing - -from .util import col, line, lineno, _collapse_string_to_ranges -from .unicode import pyparsing_unicode as ppu - - -class ExceptionWordUnicode(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): - pass - - -_extract_alphanums = _collapse_string_to_ranges(ExceptionWordUnicode.alphanums) -_exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") - - -class ParseBaseException(Exception): - """base exception class for all parsing runtime exceptions""" - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, - pstr: str, - loc: int = 0, - msg: typing.Optional[str] = None, - elem=None, - ): - self.loc = loc - if msg is None: - self.msg = pstr - self.pstr = "" - else: - self.msg = msg - self.pstr = pstr - self.parser_element = self.parserElement = elem - self.args = (pstr, loc, msg) - - @staticmethod - def explain_exception(exc, depth=16): - """ - Method to take an exception and translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - exc - exception raised during parsing (need not be a ParseException, in support - of Python exceptions that might be raised in a parse action) - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - """ - import inspect - from .core import ParserElement - - if depth is None: - depth = sys.getrecursionlimit() - ret = [] - if isinstance(exc, ParseBaseException): - ret.append(exc.line) - ret.append(" " * (exc.column - 1) + "^") - ret.append("{}: {}".format(type(exc).__name__, exc)) - - if depth > 0: - callers = inspect.getinnerframes(exc.__traceback__, context=depth) - seen = set() - for i, ff in enumerate(callers[-depth:]): - frm = ff[0] - - f_self = frm.f_locals.get("self", None) - if isinstance(f_self, ParserElement): - if frm.f_code.co_name not in ("parseImpl", "_parseNoCache"): - continue - if id(f_self) in seen: - continue - seen.add(id(f_self)) - - self_type = type(f_self) - ret.append( - "{}.{} - {}".format( - self_type.__module__, self_type.__name__, f_self - ) - ) - - elif f_self is not None: - self_type = type(f_self) - ret.append("{}.{}".format(self_type.__module__, self_type.__name__)) - - else: - code = frm.f_code - if code.co_name in ("wrapper", ""): - continue - - ret.append("{}".format(code.co_name)) - - depth -= 1 - if not depth: - break - - return "\n".join(ret) - - @classmethod - def _from_exception(cls, pe): - """ - internal factory method to simplify creating one type of ParseException - from another - avoids having __init__ signature conflicts among subclasses - """ - return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) - - @property - def line(self) -> str: - """ - Return the line of text where the exception occurred. - """ - return line(self.loc, self.pstr) - - @property - def lineno(self) -> int: - """ - Return the 1-based line number of text where the exception occurred. - """ - return lineno(self.loc, self.pstr) - - @property - def col(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - @property - def column(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - def __str__(self) -> str: - if self.pstr: - if self.loc >= len(self.pstr): - foundstr = ", found end of text" - else: - # pull out next word at error location - found_match = _exception_word_extractor.match(self.pstr, self.loc) - if found_match is not None: - found = found_match.group(0) - else: - found = self.pstr[self.loc : self.loc + 1] - foundstr = (", found %r" % found).replace(r"\\", "\\") - else: - foundstr = "" - return "{}{} (at char {}), (line:{}, col:{})".format( - self.msg, foundstr, self.loc, self.lineno, self.column - ) - - def __repr__(self): - return str(self) - - def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str: - """ - Extracts the exception line from the input string, and marks - the location of the exception with a special symbol. - """ - markerString = marker_string if marker_string is not None else markerString - line_str = self.line - line_column = self.column - 1 - if markerString: - line_str = "".join( - (line_str[:line_column], markerString, line_str[line_column:]) - ) - return line_str.strip() - - def explain(self, depth=16) -> str: - """ - Method to translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - - Example:: - - expr = pp.Word(pp.nums) * 3 - try: - expr.parse_string("123 456 A789") - except pp.ParseException as pe: - print(pe.explain(depth=0)) - - prints:: - - 123 456 A789 - ^ - ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9) - - Note: the diagnostic output will include string representations of the expressions - that failed to parse. These representations will be more helpful if you use `set_name` to - give identifiable names to your expressions. Otherwise they will use the default string - forms, which may be cryptic to read. - - Note: pyparsing's default truncation of exception tracebacks may also truncate the - stack of expressions that are displayed in the ``explain`` output. To get the full listing - of parser expressions, you may have to set ``ParserElement.verbose_stacktrace = True`` - """ - return self.explain_exception(self, depth) - - markInputline = mark_input_line - - -class ParseException(ParseBaseException): - """ - Exception thrown when a parse expression doesn't match the input string - - Example:: - - try: - Word(nums).set_name("integer").parse_string("ABC") - except ParseException as pe: - print(pe) - print("column: {}".format(pe.column)) - - prints:: - - Expected integer (at char 0), (line:1, col:1) - column: 1 - - """ - - -class ParseFatalException(ParseBaseException): - """ - User-throwable exception thrown when inconsistent parse content - is found; stops all parsing immediately - """ - - -class ParseSyntaxException(ParseFatalException): - """ - Just like :class:`ParseFatalException`, but thrown internally - when an :class:`ErrorStop` ('-' operator) indicates - that parsing is to stop immediately because an unbacktrackable - syntax error has been found. - """ - - -class RecursiveGrammarException(Exception): - """ - Exception thrown by :class:`ParserElement.validate` if the - grammar could be left-recursive; parser may need to enable - left recursion using :class:`ParserElement.enable_left_recursion` - """ - - def __init__(self, parseElementList): - self.parseElementTrace = parseElementList - - def __str__(self) -> str: - return "RecursiveGrammarException: {}".format(self.parseElementTrace) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py deleted file mode 100644 index 9588b3b78..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/helpers.py +++ /dev/null @@ -1,1088 +0,0 @@ -# helpers.py -import html.entities -import re -import typing - -from . import __diag__ -from .core import * -from .util import _bslash, _flatten, _escape_regex_range_chars - - -# -# global helpers -# -def delimited_list( - expr: Union[str, ParserElement], - delim: Union[str, ParserElement] = ",", - combine: bool = False, - min: typing.Optional[int] = None, - max: typing.Optional[int] = None, - *, - allow_trailing_delim: bool = False, -) -> ParserElement: - """Helper to define a delimited list of expressions - the delimiter - defaults to ','. By default, the list elements and delimiters can - have intervening whitespace, and comments, but this can be - overridden by passing ``combine=True`` in the constructor. If - ``combine`` is set to ``True``, the matching tokens are - returned as a single token string, with the delimiters included; - otherwise, the matching tokens are returned as a list of tokens, - with the delimiters suppressed. - - If ``allow_trailing_delim`` is set to True, then the list may end with - a delimiter. - - Example:: - - delimited_list(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc'] - delimited_list(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] - """ - if isinstance(expr, str_type): - expr = ParserElement._literalStringClass(expr) - - dlName = "{expr} [{delim} {expr}]...{end}".format( - expr=str(expr.copy().streamline()), - delim=str(delim), - end=" [{}]".format(str(delim)) if allow_trailing_delim else "", - ) - - if not combine: - delim = Suppress(delim) - - if min is not None: - if min < 1: - raise ValueError("min must be greater than 0") - min -= 1 - if max is not None: - if min is not None and max <= min: - raise ValueError("max must be greater than, or equal to min") - max -= 1 - delimited_list_expr = expr + (delim + expr)[min, max] - - if allow_trailing_delim: - delimited_list_expr += Opt(delim) - - if combine: - return Combine(delimited_list_expr).set_name(dlName) - else: - return delimited_list_expr.set_name(dlName) - - -def counted_array( - expr: ParserElement, - int_expr: typing.Optional[ParserElement] = None, - *, - intExpr: typing.Optional[ParserElement] = None, -) -> ParserElement: - """Helper to define a counted list of expressions. - - This helper defines a pattern of the form:: - - integer expr expr expr... - - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the - leading count token is suppressed. - - If ``int_expr`` is specified, it should be a pyparsing expression - that produces an integer value. - - Example:: - - counted_array(Word(alphas)).parse_string('2 ab cd ef') # -> ['ab', 'cd'] - - # in this parser, the leading integer value is given in binary, - # '10' indicating that 2 values are in the array - binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2)) - counted_array(Word(alphas), int_expr=binary_constant).parse_string('10 ab cd ef') # -> ['ab', 'cd'] - - # if other fields must be parsed after the count but before the - # list items, give the fields results names and they will - # be preserved in the returned ParseResults: - count_with_metadata = integer + Word(alphas)("type") - typed_array = counted_array(Word(alphanums), int_expr=count_with_metadata)("items") - result = typed_array.parse_string("3 bool True True False") - print(result.dump()) - - # prints - # ['True', 'True', 'False'] - # - items: ['True', 'True', 'False'] - # - type: 'bool' - """ - intExpr = intExpr or int_expr - array_expr = Forward() - - def count_field_parse_action(s, l, t): - nonlocal array_expr - n = t[0] - array_expr <<= (expr * n) if n else Empty() - # clear list contents, but keep any named results - del t[:] - - if intExpr is None: - intExpr = Word(nums).set_parse_action(lambda t: int(t[0])) - else: - intExpr = intExpr.copy() - intExpr.set_name("arrayLen") - intExpr.add_parse_action(count_field_parse_action, call_during_try=True) - return (intExpr + array_expr).set_name("(len) " + str(expr) + "...") - - -def match_previous_literal(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_literal(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches a previous literal, will also match the leading - ``"1:1"`` in ``"1:10"``. If this is not desired, use - :class:`match_previous_expr`. Do *not* use with packrat parsing - enabled. - """ - rep = Forward() - - def copy_token_to_repeater(s, l, t): - if t: - if len(t) == 1: - rep << t[0] - else: - # flatten t tokens - tflat = _flatten(t.as_list()) - rep << And(Literal(tt) for tt in tflat) - else: - rep << Empty() - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def match_previous_expr(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_expr(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches by expressions, will *not* match the leading ``"1:1"`` - in ``"1:10"``; the expressions are evaluated first, and then - compared, so ``"1"`` is compared with ``"10"``. Do *not* use - with packrat parsing enabled. - """ - rep = Forward() - e2 = expr.copy() - rep <<= e2 - - def copy_token_to_repeater(s, l, t): - matchTokens = _flatten(t.as_list()) - - def must_match_these_tokens(s, l, t): - theseTokens = _flatten(t.as_list()) - if theseTokens != matchTokens: - raise ParseException( - s, l, "Expected {}, found{}".format(matchTokens, theseTokens) - ) - - rep.set_parse_action(must_match_these_tokens, callDuringTry=True) - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def one_of( - strs: Union[typing.Iterable[str], str], - caseless: bool = False, - use_regex: bool = True, - as_keyword: bool = False, - *, - useRegex: bool = True, - asKeyword: bool = False, -) -> ParserElement: - """Helper to quickly define a set of alternative :class:`Literal` s, - and makes sure to do longest-first testing when there is a conflict, - regardless of the input order, but returns - a :class:`MatchFirst` for best performance. - - Parameters: - - - ``strs`` - a string of space-delimited literals, or a collection of - string literals - - ``caseless`` - treat all literals as caseless - (default= ``False``) - - ``use_regex`` - as an optimization, will - generate a :class:`Regex` object; otherwise, will generate - a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if - creating a :class:`Regex` raises an exception) - (default= ``True``) - - ``as_keyword`` - enforce :class:`Keyword`-style matching on the - generated expressions - (default= ``False``) - - ``asKeyword`` and ``useRegex`` are retained for pre-PEP8 compatibility, - but will be removed in a future release - - Example:: - - comp_oper = one_of("< = > <= >= !=") - var = Word(alphas) - number = Word(nums) - term = var | number - comparison_expr = term + comp_oper + term - print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12")) - - prints:: - - [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] - """ - asKeyword = asKeyword or as_keyword - useRegex = useRegex and use_regex - - if ( - isinstance(caseless, str_type) - and __diag__.warn_on_multiple_string_args_to_oneof - ): - warnings.warn( - "More than one string argument passed to one_of, pass" - " choices as a list or space-delimited string", - stacklevel=2, - ) - - if caseless: - isequal = lambda a, b: a.upper() == b.upper() - masks = lambda a, b: b.upper().startswith(a.upper()) - parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral - else: - isequal = lambda a, b: a == b - masks = lambda a, b: b.startswith(a) - parseElementClass = Keyword if asKeyword else Literal - - symbols: List[str] = [] - if isinstance(strs, str_type): - symbols = strs.split() - elif isinstance(strs, Iterable): - symbols = list(strs) - else: - raise TypeError("Invalid argument to one_of, expected string or iterable") - if not symbols: - return NoMatch() - - # reorder given symbols to take care to avoid masking longer choices with shorter ones - # (but only if the given symbols are not just single characters) - if any(len(sym) > 1 for sym in symbols): - i = 0 - while i < len(symbols) - 1: - cur = symbols[i] - for j, other in enumerate(symbols[i + 1 :]): - if isequal(other, cur): - del symbols[i + j + 1] - break - elif masks(cur, other): - del symbols[i + j + 1] - symbols.insert(i, other) - break - else: - i += 1 - - if useRegex: - re_flags: int = re.IGNORECASE if caseless else 0 - - try: - if all(len(sym) == 1 for sym in symbols): - # symbols are just single characters, create range regex pattern - patt = "[{}]".format( - "".join(_escape_regex_range_chars(sym) for sym in symbols) - ) - else: - patt = "|".join(re.escape(sym) for sym in symbols) - - # wrap with \b word break markers if defining as keywords - if asKeyword: - patt = r"\b(?:{})\b".format(patt) - - ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols)) - - if caseless: - # add parse action to return symbols as specified, not in random - # casing as found in input string - symbol_map = {sym.lower(): sym for sym in symbols} - ret.add_parse_action(lambda s, l, t: symbol_map[t[0].lower()]) - - return ret - - except re.error: - warnings.warn( - "Exception creating Regex for one_of, building MatchFirst", stacklevel=2 - ) - - # last resort, just use MatchFirst - return MatchFirst(parseElementClass(sym) for sym in symbols).set_name( - " | ".join(symbols) - ) - - -def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: - """Helper to easily and clearly define a dictionary by specifying - the respective patterns for the key and value. Takes care of - defining the :class:`Dict`, :class:`ZeroOrMore`, and - :class:`Group` tokens in the proper order. The key pattern - can include delimiting markers or punctuation, as long as they are - suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the :class:`Dict` results - can include named token fields. - - Example:: - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - print(attr_expr[1, ...].parse_string(text).dump()) - - attr_label = label - attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join) - - # similar to Dict, but simpler call format - result = dict_of(attr_label, attr_value).parse_string(text) - print(result.dump()) - print(result['shape']) - print(result.shape) # object attribute access works too - print(result.as_dict()) - - prints:: - - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - SQUARE - {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} - """ - return Dict(OneOrMore(Group(key + value))) - - -def original_text_for( - expr: ParserElement, as_string: bool = True, *, asString: bool = True -) -> ParserElement: - """Helper to return the original, untokenized text for a given - expression. Useful to restore the parsed fields of an HTML start - tag into the raw tag text itself, or to revert separate tokens with - intervening whitespace back to the original matching input text. By - default, returns astring containing the original parsed text. - - If the optional ``as_string`` argument is passed as - ``False``, then the return value is - a :class:`ParseResults` containing any results names that - were originally matched, and a single token containing the original - matched text from the input string. So if the expression passed to - :class:`original_text_for` contains expressions with defined - results names, you must set ``as_string`` to ``False`` if you - want to preserve those results name values. - - The ``asString`` pre-PEP8 argument is retained for compatibility, - but will be removed in a future release. - - Example:: - - src = "this is test bold text normal text " - for tag in ("b", "i"): - opener, closer = make_html_tags(tag) - patt = original_text_for(opener + SkipTo(closer) + closer) - print(patt.search_string(src)[0]) - - prints:: - - [' bold text '] - ['text'] - """ - asString = asString and as_string - - locMarker = Empty().set_parse_action(lambda s, loc, t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s, l, t: s[t._original_start : t._original_end] - else: - - def extractText(s, l, t): - t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]] - - matchExpr.set_parse_action(extractText) - matchExpr.ignoreExprs = expr.ignoreExprs - matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection) - return matchExpr - - -def ungroup(expr: ParserElement) -> ParserElement: - """Helper to undo pyparsing's default grouping of And expressions, - even if all but one are non-empty. - """ - return TokenConverter(expr).add_parse_action(lambda t: t[0]) - - -def locatedExpr(expr: ParserElement) -> ParserElement: - """ - (DEPRECATED - future code should use the Located class) - Helper to decorate a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parseWithTabs` - - Example:: - - wd = Word(alphas) - for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [[0, 'ljsdf', 5]] - [[8, 'lksdjjf', 15]] - [[18, 'lkkjj', 23]] - """ - locator = Empty().set_parse_action(lambda ss, ll, tt: ll) - return Group( - locator("locn_start") - + expr("value") - + locator.copy().leaveWhitespace()("locn_end") - ) - - -def nested_expr( - opener: Union[str, ParserElement] = "(", - closer: Union[str, ParserElement] = ")", - content: typing.Optional[ParserElement] = None, - ignore_expr: ParserElement = quoted_string(), - *, - ignoreExpr: ParserElement = quoted_string(), -) -> ParserElement: - """Helper method for defining nested lists enclosed in opening and - closing delimiters (``"("`` and ``")"`` are the default). - - Parameters: - - ``opener`` - opening character for a nested list - (default= ``"("``); can also be a pyparsing expression - - ``closer`` - closing character for a nested list - (default= ``")"``); can also be a pyparsing expression - - ``content`` - expression for items within the nested lists - (default= ``None``) - - ``ignore_expr`` - expression for ignoring opening and closing delimiters - (default= :class:`quoted_string`) - - ``ignoreExpr`` - this pre-PEP8 argument is retained for compatibility - but will be removed in a future release - - If an expression is not provided for the content argument, the - nested expression will capture all whitespace-delimited content - between delimiters as a list of separate values. - - Use the ``ignore_expr`` argument to define expressions that may - contain opening or closing characters that should not be treated as - opening or closing characters for nesting, such as quoted_string or - a comment expression. Specify multiple expressions using an - :class:`Or` or :class:`MatchFirst`. The default is - :class:`quoted_string`, but if no expressions are to be ignored, then - pass ``None`` for this argument. - - Example:: - - data_type = one_of("void int short long char float double") - decl_data_type = Combine(data_type + Opt(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR, RPAR = map(Suppress, "()") - - code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Opt(delimited_list(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(c_style_comment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.search_string(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - - prints:: - - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if ignoreExpr != ignore_expr: - ignoreExpr = ignore_expr if ignoreExpr == quoted_string() else ignoreExpr - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener, str_type) and isinstance(closer, str_type): - if len(opener) == 1 and len(closer) == 1: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS, - exact=1, - ) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = empty.copy() + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS - ).set_parse_action(lambda t: t[0].strip()) - else: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = Combine( - OneOrMore( - ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - raise ValueError( - "opening and closing arguments must be strings if no content expression is given" - ) - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( - Suppress(opener) + ZeroOrMore(ignoreExpr | ret | content) + Suppress(closer) - ) - else: - ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.set_name("nested %s%s expression" % (opener, closer)) - return ret - - -def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr, str_type): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas, alphanums + "_-:") - if xml: - tagAttrValue = dbl_quoted_string.copy().set_parse_action(remove_quotes) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - else: - tagAttrValue = quoted_string.copy().set_parse_action(remove_quotes) | Word( - printables, exclude_chars=">" - ) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict( - ZeroOrMore( - Group( - tagAttrName.set_parse_action(lambda t: t[0].lower()) - + Opt(Suppress("=") + tagAttrValue) - ) - ) - ) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - closeTag = Combine(Literal("", adjacent=False) - - openTag.set_name("<%s>" % resname) - # add start results name in parse action now that ungrouped names are not reported at two levels - openTag.add_parse_action( - lambda t: t.__setitem__( - "start" + "".join(resname.replace(":", " ").title().split()), t.copy() - ) - ) - closeTag = closeTag( - "end" + "".join(resname.replace(":", " ").title().split()) - ).set_name("" % resname) - openTag.tag = resname - closeTag.tag = resname - openTag.tag_body = SkipTo(closeTag()) - return openTag, closeTag - - -def make_html_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for HTML, - given a tag name. Matches tags in either upper or lower case, - attributes with namespaces and with quoted or unquoted values. - - Example:: - - text = 'More info at the pyparsing wiki page' - # make_html_tags returns pyparsing expressions for the opening and - # closing tags as a 2-tuple - a, a_end = make_html_tags("A") - link_expr = a + SkipTo(a_end)("link_text") + a_end - - for link in link_expr.search_string(text): - # attributes in the tag (like "href" shown here) are - # also accessible as named results - print(link.link_text, '->', link.href) - - prints:: - - pyparsing -> https://github.com/pyparsing/pyparsing/wiki - """ - return _makeTags(tag_str, False) - - -def make_xml_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for XML, - given a tag name. Matches tags only in the given upper/lower case. - - Example: similar to :class:`make_html_tags` - """ - return _makeTags(tag_str, True) - - -any_open_tag: ParserElement -any_close_tag: ParserElement -any_open_tag, any_close_tag = make_html_tags( - Word(alphas, alphanums + "_:").set_name("any tag") -) - -_htmlEntityMap = {k.rstrip(";"): v for k, v in html.entities.html5.items()} -common_html_entity = Regex("&(?P" + "|".join(_htmlEntityMap) + ");").set_name( - "common HTML entity" -) - - -def replace_html_entity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - - -class OpAssoc(Enum): - LEFT = 1 - RIGHT = 2 - - -InfixNotationOperatorArgType = Union[ - ParserElement, str, Tuple[Union[ParserElement, str], Union[ParserElement, str]] -] -InfixNotationOperatorSpec = Union[ - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - typing.Optional[ParseAction], - ], - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - ], -] - - -def infix_notation( - base_expr: ParserElement, - op_list: List[InfixNotationOperatorSpec], - lpar: Union[str, ParserElement] = Suppress("("), - rpar: Union[str, ParserElement] = Suppress(")"), -) -> ParserElement: - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary - or binary, left- or right-associative. Parse actions can also be - attached to operator expressions. The generated parser will also - recognize the use of parentheses to override operator precedences - (see example below). - - Note: if you define a deep operator list, you may see performance - issues when using infix_notation. See - :class:`ParserElement.enable_packrat` for a mechanism to potentially - improve your parser performance. - - Parameters: - - ``base_expr`` - expression representing the most basic operand to - be used in the expression - - ``op_list`` - list of tuples, one for each operator precedence level - in the expression grammar; each tuple is of the form ``(op_expr, - num_operands, right_left_assoc, (optional)parse_action)``, where: - - - ``op_expr`` is the pyparsing expression for the operator; may also - be a string, which will be converted to a Literal; if ``num_operands`` - is 3, ``op_expr`` is a tuple of two expressions, for the two - operators separating the 3 terms - - ``num_operands`` is the number of terms for this operator (must be 1, - 2, or 3) - - ``right_left_assoc`` is the indicator whether the operator is right - or left associative, using the pyparsing-defined constants - ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``. - - ``parse_action`` is the parse action to be associated with - expressions matching this operator expression (the parse action - tuple member may be omitted); if the parse action is passed - a tuple or list of functions, this is equivalent to calling - ``set_parse_action(*fn)`` - (:class:`ParserElement.set_parse_action`) - - ``lpar`` - expression for matching left-parentheses; if passed as a - str, then will be parsed as Suppress(lpar). If lpar is passed as - an expression (such as ``Literal('(')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress('(')``) - - ``rpar`` - expression for matching right-parentheses; if passed as a - str, then will be parsed as Suppress(rpar). If rpar is passed as - an expression (such as ``Literal(')')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress(')')``) - - Example:: - - # simple example of four-function arithmetic with ints and - # variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infix_notation(integer | varname, - [ - ('-', 1, OpAssoc.RIGHT), - (one_of('* /'), 2, OpAssoc.LEFT), - (one_of('+ -'), 2, OpAssoc.LEFT), - ]) - - arith_expr.run_tests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', full_dump=False) - - prints:: - - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - # captive version of FollowedBy that does not do parse actions or capture results names - class _FB(FollowedBy): - def parseImpl(self, instring, loc, doActions=True): - self.expr.try_parse(instring, loc) - return loc, [] - - _FB.__name__ = "FollowedBy>" - - ret = Forward() - if isinstance(lpar, str): - lpar = Suppress(lpar) - if isinstance(rpar, str): - rpar = Suppress(rpar) - - # if lpar and rpar are not suppressed, wrap in group - if not (isinstance(rpar, Suppress) and isinstance(rpar, Suppress)): - lastExpr = base_expr | Group(lpar + ret + rpar) - else: - lastExpr = base_expr | (lpar + ret + rpar) - - for i, operDef in enumerate(op_list): - opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] - if isinstance(opExpr, str_type): - opExpr = ParserElement._literalStringClass(opExpr) - if arity == 3: - if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2: - raise ValueError( - "if numterms=3, opExpr must be a tuple or list of two expressions" - ) - opExpr1, opExpr2 = opExpr - term_name = "{}{} term".format(opExpr1, opExpr2) - else: - term_name = "{} term".format(opExpr) - - if not 1 <= arity <= 3: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT): - raise ValueError("operator must indicate right or left associativity") - - thisExpr: Forward = Forward().set_name(term_name) - if rightLeftAssoc is OpAssoc.LEFT: - if arity == 1: - matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...]) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group( - lastExpr + (opExpr + lastExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr[2, ...]) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr - ) + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr)) - elif rightLeftAssoc is OpAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Opt): - opExpr = Opt(opExpr) - matchExpr = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group( - lastExpr + (opExpr + thisExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + thisExpr) + Group( - lastExpr + thisExpr[1, ...] - ) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr - ) + Group(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.set_parse_action(*pa) - else: - matchExpr.set_parse_action(pa) - thisExpr <<= (matchExpr | lastExpr).setName(term_name) - lastExpr = thisExpr - ret <<= lastExpr - return ret - - -def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]): - """ - (DEPRECATED - use IndentedBlock class instead) - Helper method for defining space-delimited indentation blocks, - such as those used to define block statements in Python source code. - - Parameters: - - - ``blockStatementExpr`` - expression defining syntax of statement that - is repeated within the indented block - - ``indentStack`` - list created by caller to manage indentation stack - (multiple ``statementWithIndentedBlock`` expressions within a single - grammar should share a common ``indentStack``) - - ``indent`` - boolean indicating whether block must be indented beyond - the current level; set to ``False`` for block of left-most statements - (default= ``True``) - - A valid block must contain at least one ``blockStatement``. - - (Note that indentedBlock uses internal parse actions which make it - incompatible with packrat parsing.) - - Example:: - - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group(funcDecl + func_body) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << (funcDef | assignment | identifier) - - module_body = stmt[1, ...] - - parseTree = module_body.parseString(data) - parseTree.pprint() - - prints:: - - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - backup_stacks.append(indentStack[:]) - - def reset_stack(): - indentStack[:] = backup_stacks[-1] - - def checkPeerIndent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseException(s, l, "illegal nesting") - raise ParseException(s, l, "not a peer entry") - - def checkSubIndent(s, l, t): - curCol = col(l, s) - if curCol > indentStack[-1]: - indentStack.append(curCol) - else: - raise ParseException(s, l, "not a subentry") - - def checkUnindent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if not (indentStack and curCol in indentStack): - raise ParseException(s, l, "not an unindent") - if curCol < indentStack[-1]: - indentStack.pop() - - NL = OneOrMore(LineEnd().set_whitespace_chars("\t ").suppress()) - INDENT = (Empty() + Empty().set_parse_action(checkSubIndent)).set_name("INDENT") - PEER = Empty().set_parse_action(checkPeerIndent).set_name("") - UNDENT = Empty().set_parse_action(checkUnindent).set_name("UNINDENT") - if indent: - smExpr = Group( - Opt(NL) - + INDENT - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + UNDENT - ) - else: - smExpr = Group( - Opt(NL) - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + Opt(UNDENT) - ) - - # add a parse action to remove backup_stack from list of backups - smExpr.add_parse_action( - lambda: backup_stacks.pop(-1) and None if backup_stacks else None - ) - smExpr.set_fail_action(lambda a, b, c, d: reset_stack()) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.set_name("indented block") - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -c_style_comment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/").set_name( - "C style comment" -) -"Comment of the form ``/* ... */``" - -html_comment = Regex(r"").set_name("HTML comment") -"Comment of the form ````" - -rest_of_line = Regex(r".*").leave_whitespace().set_name("rest of line") -dbl_slash_comment = Regex(r"//(?:\\\n|[^\n])*").set_name("// comment") -"Comment of the form ``// ... (to end of line)``" - -cpp_style_comment = Combine( - Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/" | dbl_slash_comment -).set_name("C++ style comment") -"Comment of either form :class:`c_style_comment` or :class:`dbl_slash_comment`" - -java_style_comment = cpp_style_comment -"Same as :class:`cpp_style_comment`" - -python_style_comment = Regex(r"#.*").set_name("Python style comment") -"Comment of the form ``# ... (to end of line)``" - - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - - -# pre-PEP8 compatible names -delimitedList = delimited_list -countedArray = counted_array -matchPreviousLiteral = match_previous_literal -matchPreviousExpr = match_previous_expr -oneOf = one_of -dictOf = dict_of -originalTextFor = original_text_for -nestedExpr = nested_expr -makeHTMLTags = make_html_tags -makeXMLTags = make_xml_tags -anyOpenTag, anyCloseTag = any_open_tag, any_close_tag -commonHTMLEntity = common_html_entity -replaceHTMLEntity = replace_html_entity -opAssoc = OpAssoc -infixNotation = infix_notation -cStyleComment = c_style_comment -htmlComment = html_comment -restOfLine = rest_of_line -dblSlashComment = dbl_slash_comment -cppStyleComment = cpp_style_comment -javaStyleComment = java_style_comment -pythonStyleComment = python_style_comment diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py deleted file mode 100644 index 00c9421d3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/results.py +++ /dev/null @@ -1,760 +0,0 @@ -# results.py -from collections.abc import MutableMapping, Mapping, MutableSequence, Iterator -import pprint -from weakref import ref as wkref -from typing import Tuple, Any - -str_type: Tuple[type, ...] = (str, bytes) -_generator_type = type((_ for _ in ())) - - -class _ParseResultsWithOffset: - __slots__ = ["tup"] - - def __init__(self, p1, p2): - self.tup = (p1, p2) - - def __getitem__(self, i): - return self.tup[i] - - def __getstate__(self): - return self.tup - - def __setstate__(self, *args): - self.tup = args[0] - - -class ParseResults: - """Structured parse results, to provide multiple means of access to - the parsed data: - - - as a list (``len(results)``) - - by list index (``results[0], results[1]``, etc.) - - by attribute (``results.`` - see :class:`ParserElement.set_results_name`) - - Example:: - - integer = Word(nums) - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - # equivalent form: - # date_str = (integer("year") + '/' - # + integer("month") + '/' - # + integer("day")) - - # parse_string returns a ParseResults object - result = date_str.parse_string("1999/12/31") - - def test(s, fn=repr): - print("{} -> {}".format(s, fn(eval(s)))) - test("list(result)") - test("result[0]") - test("result['month']") - test("result.day") - test("'month' in result") - test("'minutes' in result") - test("result.dump()", str) - - prints:: - - list(result) -> ['1999', '/', '12', '/', '31'] - result[0] -> '1999' - result['month'] -> '12' - result.day -> '31' - 'month' in result -> True - 'minutes' in result -> False - result.dump() -> ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - - _null_values: Tuple[Any, ...] = (None, [], "", ()) - - __slots__ = [ - "_name", - "_parent", - "_all_names", - "_modal", - "_toklist", - "_tokdict", - "__weakref__", - ] - - class List(list): - """ - Simple wrapper class to distinguish parsed list results that should be preserved - as actual Python lists, instead of being converted to :class:`ParseResults`: - - LBRACK, RBRACK = map(pp.Suppress, "[]") - element = pp.Forward() - item = ppc.integer - element_list = LBRACK + pp.delimited_list(element) + RBRACK - - # add parse actions to convert from ParseResults to actual Python collection types - def as_python_list(t): - return pp.ParseResults.List(t.as_list()) - element_list.add_parse_action(as_python_list) - - element <<= item | element_list - - element.run_tests(''' - 100 - [2,3,4] - [[2, 1],3,4] - [(2, 1),3,4] - (2,3,4) - ''', post_parse=lambda s, r: (r[0], type(r[0]))) - - prints: - - 100 - (100, ) - - [2,3,4] - ([2, 3, 4], ) - - [[2, 1],3,4] - ([[2, 1], 3, 4], ) - - (Used internally by :class:`Group` when `aslist=True`.) - """ - - def __new__(cls, contained=None): - if contained is None: - contained = [] - - if not isinstance(contained, list): - raise TypeError( - "{} may only be constructed with a list," - " not {}".format(cls.__name__, type(contained).__name__) - ) - - return list.__new__(cls) - - def __new__(cls, toklist=None, name=None, **kwargs): - if isinstance(toklist, ParseResults): - return toklist - self = object.__new__(cls) - self._name = None - self._parent = None - self._all_names = set() - - if toklist is None: - self._toklist = [] - elif isinstance(toklist, (list, _generator_type)): - self._toklist = ( - [toklist[:]] - if isinstance(toklist, ParseResults.List) - else list(toklist) - ) - else: - self._toklist = [toklist] - self._tokdict = dict() - return self - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance - ): - self._modal = modal - if name is not None and name != "": - if isinstance(name, int): - name = str(name) - if not modal: - self._all_names = {name} - self._name = name - if toklist not in self._null_values: - if isinstance(toklist, (str_type, type)): - toklist = [toklist] - if asList: - if isinstance(toklist, ParseResults): - self[name] = _ParseResultsWithOffset( - ParseResults(toklist._toklist), 0 - ) - else: - self[name] = _ParseResultsWithOffset( - ParseResults(toklist[0]), 0 - ) - self[name]._name = name - else: - try: - self[name] = toklist[0] - except (KeyError, TypeError, IndexError): - if toklist is not self: - self[name] = toklist - else: - self._name = name - - def __getitem__(self, i): - if isinstance(i, (int, slice)): - return self._toklist[i] - else: - if i not in self._all_names: - return self._tokdict[i][-1][0] - else: - return ParseResults([v[0] for v in self._tokdict[i]]) - - def __setitem__(self, k, v, isinstance=isinstance): - if isinstance(v, _ParseResultsWithOffset): - self._tokdict[k] = self._tokdict.get(k, list()) + [v] - sub = v[0] - elif isinstance(k, (int, slice)): - self._toklist[k] = v - sub = v - else: - self._tokdict[k] = self._tokdict.get(k, list()) + [ - _ParseResultsWithOffset(v, 0) - ] - sub = v - if isinstance(sub, ParseResults): - sub._parent = wkref(self) - - def __delitem__(self, i): - if isinstance(i, (int, slice)): - mylen = len(self._toklist) - del self._toklist[i] - - # convert int to slice - if isinstance(i, int): - if i < 0: - i += mylen - i = slice(i, i + 1) - # get removed indices - removed = list(range(*i.indices(mylen))) - removed.reverse() - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for j in removed: - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position - (position > j) - ) - else: - del self._tokdict[i] - - def __contains__(self, k) -> bool: - return k in self._tokdict - - def __len__(self) -> int: - return len(self._toklist) - - def __bool__(self) -> bool: - return not not (self._toklist or self._tokdict) - - def __iter__(self) -> Iterator: - return iter(self._toklist) - - def __reversed__(self) -> Iterator: - return iter(self._toklist[::-1]) - - def keys(self): - return iter(self._tokdict) - - def values(self): - return (self[k] for k in self.keys()) - - def items(self): - return ((k, self[k]) for k in self.keys()) - - def haskeys(self) -> bool: - """ - Since ``keys()`` returns an iterator, this method is helpful in bypassing - code that looks for the existence of any defined results names.""" - return bool(self._tokdict) - - def pop(self, *args, **kwargs): - """ - Removes and returns item at specified index (default= ``last``). - Supports both ``list`` and ``dict`` semantics for ``pop()``. If - passed no argument or an integer argument, it will use ``list`` - semantics and pop tokens from the list of parsed tokens. If passed - a non-integer argument (most likely a string), it will use ``dict`` - semantics and pop the corresponding value from any defined results - names. A second default return value argument is supported, just as in - ``dict.pop()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - def remove_first(tokens): - tokens.pop(0) - numlist.add_parse_action(remove_first) - print(numlist.parse_string("0 123 321")) # -> ['123', '321'] - - label = Word(alphas) - patt = label("LABEL") + Word(nums)[1, ...] - print(patt.parse_string("AAB 123 321").dump()) - - # Use pop() in a parse action to remove named result (note that corresponding value is not - # removed from list form of results) - def remove_LABEL(tokens): - tokens.pop("LABEL") - return tokens - patt.add_parse_action(remove_LABEL) - print(patt.parse_string("AAB 123 321").dump()) - - prints:: - - ['AAB', '123', '321'] - - LABEL: 'AAB' - - ['AAB', '123', '321'] - """ - if not args: - args = [-1] - for k, v in kwargs.items(): - if k == "default": - args = (args[0], v) - else: - raise TypeError( - "pop() got an unexpected keyword argument {!r}".format(k) - ) - if isinstance(args[0], int) or len(args) == 1 or args[0] in self: - index = args[0] - ret = self[index] - del self[index] - return ret - else: - defaultvalue = args[1] - return defaultvalue - - def get(self, key, default_value=None): - """ - Returns named result matching the given key, or if there is no - such name, then returns the given ``default_value`` or ``None`` if no - ``default_value`` is specified. - - Similar to ``dict.get()``. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string("1999/12/31") - print(result.get("year")) # -> '1999' - print(result.get("hour", "not specified")) # -> 'not specified' - print(result.get("hour")) # -> None - """ - if key in self: - return self[key] - else: - return default_value - - def insert(self, index, ins_string): - """ - Inserts new element at location index in the list of parsed tokens. - - Similar to ``list.insert()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to insert the parse location in the front of the parsed results - def insert_locn(locn, tokens): - tokens.insert(0, locn) - numlist.add_parse_action(insert_locn) - print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321'] - """ - self._toklist.insert(index, ins_string) - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position + (position > index) - ) - - def append(self, item): - """ - Add single element to end of ``ParseResults`` list of elements. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to compute the sum of the parsed integers, and add it to the end - def append_sum(tokens): - tokens.append(sum(map(int, tokens))) - numlist.add_parse_action(append_sum) - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444] - """ - self._toklist.append(item) - - def extend(self, itemseq): - """ - Add sequence of elements to end of ``ParseResults`` list of elements. - - Example:: - - patt = Word(alphas)[1, ...] - - # use a parse action to append the reverse of the matched strings, to make a palindrome - def make_palindrome(tokens): - tokens.extend(reversed([t[::-1] for t in tokens])) - return ''.join(tokens) - patt.add_parse_action(make_palindrome) - print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' - """ - if isinstance(itemseq, ParseResults): - self.__iadd__(itemseq) - else: - self._toklist.extend(itemseq) - - def clear(self): - """ - Clear all elements and results names. - """ - del self._toklist[:] - self._tokdict.clear() - - def __getattr__(self, name): - try: - return self[name] - except KeyError: - if name.startswith("__"): - raise AttributeError(name) - return "" - - def __add__(self, other) -> "ParseResults": - ret = self.copy() - ret += other - return ret - - def __iadd__(self, other) -> "ParseResults": - if other._tokdict: - offset = len(self._toklist) - addoffset = lambda a: offset if a < 0 else a + offset - otheritems = other._tokdict.items() - otherdictitems = [ - (k, _ParseResultsWithOffset(v[0], addoffset(v[1]))) - for k, vlist in otheritems - for v in vlist - ] - for k, v in otherdictitems: - self[k] = v - if isinstance(v[0], ParseResults): - v[0]._parent = wkref(self) - - self._toklist += other._toklist - self._all_names |= other._all_names - return self - - def __radd__(self, other) -> "ParseResults": - if isinstance(other, int) and other == 0: - # useful for merging many ParseResults using sum() builtin - return self.copy() - else: - # this may raise a TypeError - so be it - return other + self - - def __repr__(self) -> str: - return "{}({!r}, {})".format(type(self).__name__, self._toklist, self.as_dict()) - - def __str__(self) -> str: - return ( - "[" - + ", ".join( - [ - str(i) if isinstance(i, ParseResults) else repr(i) - for i in self._toklist - ] - ) - + "]" - ) - - def _asStringList(self, sep=""): - out = [] - for item in self._toklist: - if out and sep: - out.append(sep) - if isinstance(item, ParseResults): - out += item._asStringList() - else: - out.append(str(item)) - return out - - def as_list(self) -> list: - """ - Returns the parse results as a nested list of matching tokens, all converted to strings. - - Example:: - - patt = Word(alphas)[1, ...] - result = patt.parse_string("sldkj lsdkj sldkj") - # even though the result prints in string-like form, it is actually a pyparsing ParseResults - print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] - - # Use as_list() to create an actual list - result_list = result.as_list() - print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] - """ - return [ - res.as_list() if isinstance(res, ParseResults) else res - for res in self._toklist - ] - - def as_dict(self) -> dict: - """ - Returns the named parse results as a nested dictionary. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('12/31/1999') - print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) - - result_dict = result.as_dict() - print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} - - # even though a ParseResults supports dict-like access, sometime you just need to have a dict - import json - print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable - print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"} - """ - - def to_item(obj): - if isinstance(obj, ParseResults): - return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj] - else: - return obj - - return dict((k, to_item(v)) for k, v in self.items()) - - def copy(self) -> "ParseResults": - """ - Returns a new copy of a :class:`ParseResults` object. - """ - ret = ParseResults(self._toklist) - ret._tokdict = self._tokdict.copy() - ret._parent = self._parent - ret._all_names |= self._all_names - ret._name = self._name - return ret - - def get_name(self): - r""" - Returns the results name for this token expression. Useful when several - different expressions might match at a particular location. - - Example:: - - integer = Word(nums) - ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") - house_number_expr = Suppress('#') + Word(nums, alphanums) - user_data = (Group(house_number_expr)("house_number") - | Group(ssn_expr)("ssn") - | Group(integer)("age")) - user_info = user_data[1, ...] - - result = user_info.parse_string("22 111-22-3333 #221B") - for item in result: - print(item.get_name(), ':', item[0]) - - prints:: - - age : 22 - ssn : 111-22-3333 - house_number : 221B - """ - if self._name: - return self._name - elif self._parent: - par = self._parent() - - def find_in_parent(sub): - return next( - ( - k - for k, vlist in par._tokdict.items() - for v, loc in vlist - if sub is v - ), - None, - ) - - return find_in_parent(self) if par else None - elif ( - len(self) == 1 - and len(self._tokdict) == 1 - and next(iter(self._tokdict.values()))[0][1] in (0, -1) - ): - return next(iter(self._tokdict.keys())) - else: - return None - - def dump(self, indent="", full=True, include_list=True, _depth=0) -> str: - """ - Diagnostic method for listing out the contents of - a :class:`ParseResults`. Accepts an optional ``indent`` argument so - that this string can be embedded in a nested display of other data. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('1999/12/31') - print(result.dump()) - - prints:: - - ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - out = [] - NL = "\n" - out.append(indent + str(self.as_list()) if include_list else "") - - if full: - if self.haskeys(): - items = sorted((str(k), v) for k, v in self.items()) - for k, v in items: - if out: - out.append(NL) - out.append("{}{}- {}: ".format(indent, (" " * _depth), k)) - if isinstance(v, ParseResults): - if v: - out.append( - v.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ) - ) - else: - out.append(str(v)) - else: - out.append(repr(v)) - if any(isinstance(vv, ParseResults) for vv in self): - v = self - for i, vv in enumerate(v): - if isinstance(vv, ParseResults): - out.append( - "\n{}{}[{}]:\n{}{}{}".format( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - vv.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ), - ) - ) - else: - out.append( - "\n%s%s[%d]:\n%s%s%s" - % ( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - str(vv), - ) - ) - - return "".join(out) - - def pprint(self, *args, **kwargs): - """ - Pretty-printer for parsed results as a list, using the - `pprint `_ module. - Accepts additional positional or keyword args as defined for - `pprint.pprint `_ . - - Example:: - - ident = Word(alphas, alphanums) - num = Word(nums) - func = Forward() - term = ident | num | Group('(' + func + ')') - func <<= ident + Group(Optional(delimited_list(term))) - result = func.parse_string("fna a,b,(fnb c,d,200),100") - result.pprint(width=40) - - prints:: - - ['fna', - ['a', - 'b', - ['(', 'fnb', ['c', 'd', '200'], ')'], - '100']] - """ - pprint.pprint(self.as_list(), *args, **kwargs) - - # add support for pickle protocol - def __getstate__(self): - return ( - self._toklist, - ( - self._tokdict.copy(), - self._parent is not None and self._parent() or None, - self._all_names, - self._name, - ), - ) - - def __setstate__(self, state): - self._toklist, (self._tokdict, par, inAccumNames, self._name) = state - self._all_names = set(inAccumNames) - if par is not None: - self._parent = wkref(par) - else: - self._parent = None - - def __getnewargs__(self): - return self._toklist, self._name - - def __dir__(self): - return dir(type(self)) + list(self.keys()) - - @classmethod - def from_dict(cls, other, name=None) -> "ParseResults": - """ - Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the - name-value relations as results names. If an optional ``name`` argument is - given, a nested ``ParseResults`` will be returned. - """ - - def is_iterable(obj): - try: - iter(obj) - except Exception: - return False - else: - return not isinstance(obj, str_type) - - ret = cls([]) - for k, v in other.items(): - if isinstance(v, Mapping): - ret += cls.from_dict(v, name=k) - else: - ret += cls([v], name=k, asList=is_iterable(v)) - if name is not None: - ret = cls([ret], name=name) - return ret - - asList = as_list - asDict = as_dict - getName = get_name - - -MutableMapping.register(ParseResults) -MutableSequence.register(ParseResults) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py deleted file mode 100644 index 84a0ef170..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/testing.py +++ /dev/null @@ -1,331 +0,0 @@ -# testing.py - -from contextlib import contextmanager -import typing - -from .core import ( - ParserElement, - ParseException, - Keyword, - __diag__, - __compat__, -) - - -class pyparsing_test: - """ - namespace class for classes useful in writing unit tests - """ - - class reset_pyparsing_context: - """ - Context manager to be used when writing unit tests that modify pyparsing config values: - - packrat parsing - - bounded recursion parsing - - default whitespace characters. - - default keyword characters - - literal string auto-conversion class - - __diag__ settings - - Example:: - - with reset_pyparsing_context(): - # test that literals used to construct a grammar are automatically suppressed - ParserElement.inlineLiteralsUsing(Suppress) - - term = Word(alphas) | Word(nums) - group = Group('(' + term[...] + ')') - - # assert that the '()' characters are not included in the parsed tokens - self.assertParseAndCheckList(group, "(abc 123 def)", ['abc', '123', 'def']) - - # after exiting context manager, literals are converted to Literal expressions again - """ - - def __init__(self): - self._save_context = {} - - def save(self): - self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS - self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS - - self._save_context[ - "literal_string_class" - ] = ParserElement._literalStringClass - - self._save_context["verbose_stacktrace"] = ParserElement.verbose_stacktrace - - self._save_context["packrat_enabled"] = ParserElement._packratEnabled - if ParserElement._packratEnabled: - self._save_context[ - "packrat_cache_size" - ] = ParserElement.packrat_cache.size - else: - self._save_context["packrat_cache_size"] = None - self._save_context["packrat_parse"] = ParserElement._parse - self._save_context[ - "recursion_enabled" - ] = ParserElement._left_recursion_enabled - - self._save_context["__diag__"] = { - name: getattr(__diag__, name) for name in __diag__._all_names - } - - self._save_context["__compat__"] = { - "collect_all_And_tokens": __compat__.collect_all_And_tokens - } - - return self - - def restore(self): - # reset pyparsing global state - if ( - ParserElement.DEFAULT_WHITE_CHARS - != self._save_context["default_whitespace"] - ): - ParserElement.set_default_whitespace_chars( - self._save_context["default_whitespace"] - ) - - ParserElement.verbose_stacktrace = self._save_context["verbose_stacktrace"] - - Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] - ParserElement.inlineLiteralsUsing( - self._save_context["literal_string_class"] - ) - - for name, value in self._save_context["__diag__"].items(): - (__diag__.enable if value else __diag__.disable)(name) - - ParserElement._packratEnabled = False - if self._save_context["packrat_enabled"]: - ParserElement.enable_packrat(self._save_context["packrat_cache_size"]) - else: - ParserElement._parse = self._save_context["packrat_parse"] - ParserElement._left_recursion_enabled = self._save_context[ - "recursion_enabled" - ] - - __compat__.collect_all_And_tokens = self._save_context["__compat__"] - - return self - - def copy(self): - ret = type(self)() - ret._save_context.update(self._save_context) - return ret - - def __enter__(self): - return self.save() - - def __exit__(self, *args): - self.restore() - - class TestParseResultsAsserts: - """ - A mixin class to add parse results assertion methods to normal unittest.TestCase classes. - """ - - def assertParseResultsEquals( - self, result, expected_list=None, expected_dict=None, msg=None - ): - """ - Unit test assertion to compare a :class:`ParseResults` object with an optional ``expected_list``, - and compare any defined results names with an optional ``expected_dict``. - """ - if expected_list is not None: - self.assertEqual(expected_list, result.as_list(), msg=msg) - if expected_dict is not None: - self.assertEqual(expected_dict, result.as_dict(), msg=msg) - - def assertParseAndCheckList( - self, expr, test_string, expected_list, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asList()`` is equal to the ``expected_list``. - """ - result = expr.parse_string(test_string, parse_all=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) - - def assertParseAndCheckDict( - self, expr, test_string, expected_dict, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asDict()`` is equal to the ``expected_dict``. - """ - result = expr.parse_string(test_string, parseAll=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) - - def assertRunTestResults( - self, run_tests_report, expected_parse_results=None, msg=None - ): - """ - Unit test assertion to evaluate output of ``ParserElement.runTests()``. If a list of - list-dict tuples is given as the ``expected_parse_results`` argument, then these are zipped - with the report tuples returned by ``runTests`` and evaluated using ``assertParseResultsEquals``. - Finally, asserts that the overall ``runTests()`` success value is ``True``. - - :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests - :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] - """ - run_test_success, run_test_results = run_tests_report - - if expected_parse_results is not None: - merged = [ - (*rpt, expected) - for rpt, expected in zip(run_test_results, expected_parse_results) - ] - for test_string, result, expected in merged: - # expected should be a tuple containing a list and/or a dict or an exception, - # and optional failure message string - # an empty tuple will skip any result validation - fail_msg = next( - (exp for exp in expected if isinstance(exp, str)), None - ) - expected_exception = next( - ( - exp - for exp in expected - if isinstance(exp, type) and issubclass(exp, Exception) - ), - None, - ) - if expected_exception is not None: - with self.assertRaises( - expected_exception=expected_exception, msg=fail_msg or msg - ): - if isinstance(result, Exception): - raise result - else: - expected_list = next( - (exp for exp in expected if isinstance(exp, list)), None - ) - expected_dict = next( - (exp for exp in expected if isinstance(exp, dict)), None - ) - if (expected_list, expected_dict) != (None, None): - self.assertParseResultsEquals( - result, - expected_list=expected_list, - expected_dict=expected_dict, - msg=fail_msg or msg, - ) - else: - # warning here maybe? - print("no validation for {!r}".format(test_string)) - - # do this last, in case some specific test results can be reported instead - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - - @contextmanager - def assertRaisesParseException(self, exc_type=ParseException, msg=None): - with self.assertRaises(exc_type, msg=msg): - yield - - @staticmethod - def with_line_numbers( - s: str, - start_line: typing.Optional[int] = None, - end_line: typing.Optional[int] = None, - expand_tabs: bool = True, - eol_mark: str = "|", - mark_spaces: typing.Optional[str] = None, - mark_control: typing.Optional[str] = None, - ) -> str: - """ - Helpful method for debugging a parser - prints a string with line and column numbers. - (Line and column numbers are 1-based.) - - :param s: tuple(bool, str - string to be printed with line and column numbers - :param start_line: int - (optional) starting line number in s to print (default=1) - :param end_line: int - (optional) ending line number in s to print (default=len(s)) - :param expand_tabs: bool - (optional) expand tabs to spaces, to match the pyparsing default - :param eol_mark: str - (optional) string to mark the end of lines, helps visualize trailing spaces (default="|") - :param mark_spaces: str - (optional) special character to display in place of spaces - :param mark_control: str - (optional) convert non-printing control characters to a placeholding - character; valid values: - - "unicode" - replaces control chars with Unicode symbols, such as "␍" and "␊" - - any single character string - replace control characters with given string - - None (default) - string is displayed as-is - - :return: str - input string with leading line numbers and column number headers - """ - if expand_tabs: - s = s.expandtabs() - if mark_control is not None: - if mark_control == "unicode": - tbl = str.maketrans( - {c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433))} - | {127: 0x2421} - ) - eol_mark = "" - else: - tbl = str.maketrans( - {c: mark_control for c in list(range(0, 32)) + [127]} - ) - s = s.translate(tbl) - if mark_spaces is not None and mark_spaces != " ": - if mark_spaces == "unicode": - tbl = str.maketrans({9: 0x2409, 32: 0x2423}) - s = s.translate(tbl) - else: - s = s.replace(" ", mark_spaces) - if start_line is None: - start_line = 1 - if end_line is None: - end_line = len(s) - end_line = min(end_line, len(s)) - start_line = min(max(1, start_line), end_line) - - if mark_control != "unicode": - s_lines = s.splitlines()[start_line - 1 : end_line] - else: - s_lines = [line + "␊" for line in s.split("␊")[start_line - 1 : end_line]] - if not s_lines: - return "" - - lineno_width = len(str(end_line)) - max_line_len = max(len(line) for line in s_lines) - lead = " " * (lineno_width + 1) - if max_line_len >= 99: - header0 = ( - lead - + "".join( - "{}{}".format(" " * 99, (i + 1) % 100) - for i in range(max(max_line_len // 100, 1)) - ) - + "\n" - ) - else: - header0 = "" - header1 = ( - header0 - + lead - + "".join( - " {}".format((i + 1) % 10) - for i in range(-(-max_line_len // 10)) - ) - + "\n" - ) - header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n" - return ( - header1 - + header2 - + "\n".join( - "{:{}d}:{}{}".format(i, lineno_width, line, eol_mark) - for i, line in enumerate(s_lines, start=start_line) - ) - + "\n" - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py deleted file mode 100644 index 065262039..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/unicode.py +++ /dev/null @@ -1,352 +0,0 @@ -# unicode.py - -import sys -from itertools import filterfalse -from typing import List, Tuple, Union - - -class _lazyclassproperty: - def __init__(self, fn): - self.fn = fn - self.__doc__ = fn.__doc__ - self.__name__ = fn.__name__ - - def __get__(self, obj, cls): - if cls is None: - cls = type(obj) - if not hasattr(cls, "_intern") or any( - cls._intern is getattr(superclass, "_intern", []) - for superclass in cls.__mro__[1:] - ): - cls._intern = {} - attrname = self.fn.__name__ - if attrname not in cls._intern: - cls._intern[attrname] = self.fn(cls) - return cls._intern[attrname] - - -UnicodeRangeList = List[Union[Tuple[int, int], Tuple[int]]] - - -class unicode_set: - """ - A set of Unicode characters, for language-specific strings for - ``alphas``, ``nums``, ``alphanums``, and ``printables``. - A unicode_set is defined by a list of ranges in the Unicode character - set, in a class attribute ``_ranges``. Ranges can be specified using - 2-tuples or a 1-tuple, such as:: - - _ranges = [ - (0x0020, 0x007e), - (0x00a0, 0x00ff), - (0x0100,), - ] - - Ranges are left- and right-inclusive. A 1-tuple of (x,) is treated as (x, x). - - A unicode set can also be defined using multiple inheritance of other unicode sets:: - - class CJK(Chinese, Japanese, Korean): - pass - """ - - _ranges: UnicodeRangeList = [] - - @_lazyclassproperty - def _chars_for_ranges(cls): - ret = [] - for cc in cls.__mro__: - if cc is unicode_set: - break - for rr in getattr(cc, "_ranges", ()): - ret.extend(range(rr[0], rr[-1] + 1)) - return [chr(c) for c in sorted(set(ret))] - - @_lazyclassproperty - def printables(cls): - "all non-whitespace characters in this range" - return "".join(filterfalse(str.isspace, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphas(cls): - "all alphabetic characters in this range" - return "".join(filter(str.isalpha, cls._chars_for_ranges)) - - @_lazyclassproperty - def nums(cls): - "all numeric digit characters in this range" - return "".join(filter(str.isdigit, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphanums(cls): - "all alphanumeric characters in this range" - return cls.alphas + cls.nums - - @_lazyclassproperty - def identchars(cls): - "all characters in this range that are valid identifier characters, plus underscore '_'" - return "".join( - sorted( - set( - "".join(filter(str.isidentifier, cls._chars_for_ranges)) - + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµº" - + "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" - + "_" - ) - ) - ) - - @_lazyclassproperty - def identbodychars(cls): - """ - all characters in this range that are valid identifier body characters, - plus the digits 0-9 - """ - return "".join( - sorted( - set( - cls.identchars - + "0123456789" - + "".join( - [c for c in cls._chars_for_ranges if ("_" + c).isidentifier()] - ) - ) - ) - ) - - -class pyparsing_unicode(unicode_set): - """ - A namespace class for defining common language unicode_sets. - """ - - # fmt: off - - # define ranges in language character sets - _ranges: UnicodeRangeList = [ - (0x0020, sys.maxunicode), - ] - - class BasicMultilingualPlane(unicode_set): - "Unicode set for the Basic Multilingual Plane" - _ranges: UnicodeRangeList = [ - (0x0020, 0xFFFF), - ] - - class Latin1(unicode_set): - "Unicode set for Latin-1 Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0020, 0x007E), - (0x00A0, 0x00FF), - ] - - class LatinA(unicode_set): - "Unicode set for Latin-A Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0100, 0x017F), - ] - - class LatinB(unicode_set): - "Unicode set for Latin-B Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0180, 0x024F), - ] - - class Greek(unicode_set): - "Unicode set for Greek Unicode Character Ranges" - _ranges: UnicodeRangeList = [ - (0x0342, 0x0345), - (0x0370, 0x0377), - (0x037A, 0x037F), - (0x0384, 0x038A), - (0x038C,), - (0x038E, 0x03A1), - (0x03A3, 0x03E1), - (0x03F0, 0x03FF), - (0x1D26, 0x1D2A), - (0x1D5E,), - (0x1D60,), - (0x1D66, 0x1D6A), - (0x1F00, 0x1F15), - (0x1F18, 0x1F1D), - (0x1F20, 0x1F45), - (0x1F48, 0x1F4D), - (0x1F50, 0x1F57), - (0x1F59,), - (0x1F5B,), - (0x1F5D,), - (0x1F5F, 0x1F7D), - (0x1F80, 0x1FB4), - (0x1FB6, 0x1FC4), - (0x1FC6, 0x1FD3), - (0x1FD6, 0x1FDB), - (0x1FDD, 0x1FEF), - (0x1FF2, 0x1FF4), - (0x1FF6, 0x1FFE), - (0x2129,), - (0x2719, 0x271A), - (0xAB65,), - (0x10140, 0x1018D), - (0x101A0,), - (0x1D200, 0x1D245), - (0x1F7A1, 0x1F7A7), - ] - - class Cyrillic(unicode_set): - "Unicode set for Cyrillic Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0400, 0x052F), - (0x1C80, 0x1C88), - (0x1D2B,), - (0x1D78,), - (0x2DE0, 0x2DFF), - (0xA640, 0xA672), - (0xA674, 0xA69F), - (0xFE2E, 0xFE2F), - ] - - class Chinese(unicode_set): - "Unicode set for Chinese Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x2E80, 0x2E99), - (0x2E9B, 0x2EF3), - (0x31C0, 0x31E3), - (0x3400, 0x4DB5), - (0x4E00, 0x9FEF), - (0xA700, 0xA707), - (0xF900, 0xFA6D), - (0xFA70, 0xFAD9), - (0x16FE2, 0x16FE3), - (0x1F210, 0x1F212), - (0x1F214, 0x1F23B), - (0x1F240, 0x1F248), - (0x20000, 0x2A6D6), - (0x2A700, 0x2B734), - (0x2B740, 0x2B81D), - (0x2B820, 0x2CEA1), - (0x2CEB0, 0x2EBE0), - (0x2F800, 0x2FA1D), - ] - - class Japanese(unicode_set): - "Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges" - _ranges: UnicodeRangeList = [] - - class Kanji(unicode_set): - "Unicode set for Kanji Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x4E00, 0x9FBF), - (0x3000, 0x303F), - ] - - class Hiragana(unicode_set): - "Unicode set for Hiragana Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x3041, 0x3096), - (0x3099, 0x30A0), - (0x30FC,), - (0xFF70,), - (0x1B001,), - (0x1B150, 0x1B152), - (0x1F200,), - ] - - class Katakana(unicode_set): - "Unicode set for Katakana Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x3099, 0x309C), - (0x30A0, 0x30FF), - (0x31F0, 0x31FF), - (0x32D0, 0x32FE), - (0xFF65, 0xFF9F), - (0x1B000,), - (0x1B164, 0x1B167), - (0x1F201, 0x1F202), - (0x1F213,), - ] - - class Hangul(unicode_set): - "Unicode set for Hangul (Korean) Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x1100, 0x11FF), - (0x302E, 0x302F), - (0x3131, 0x318E), - (0x3200, 0x321C), - (0x3260, 0x327B), - (0x327E,), - (0xA960, 0xA97C), - (0xAC00, 0xD7A3), - (0xD7B0, 0xD7C6), - (0xD7CB, 0xD7FB), - (0xFFA0, 0xFFBE), - (0xFFC2, 0xFFC7), - (0xFFCA, 0xFFCF), - (0xFFD2, 0xFFD7), - (0xFFDA, 0xFFDC), - ] - - Korean = Hangul - - class CJK(Chinese, Japanese, Hangul): - "Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range" - - class Thai(unicode_set): - "Unicode set for Thai Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0E01, 0x0E3A), - (0x0E3F, 0x0E5B) - ] - - class Arabic(unicode_set): - "Unicode set for Arabic Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0600, 0x061B), - (0x061E, 0x06FF), - (0x0700, 0x077F), - ] - - class Hebrew(unicode_set): - "Unicode set for Hebrew Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0591, 0x05C7), - (0x05D0, 0x05EA), - (0x05EF, 0x05F4), - (0xFB1D, 0xFB36), - (0xFB38, 0xFB3C), - (0xFB3E,), - (0xFB40, 0xFB41), - (0xFB43, 0xFB44), - (0xFB46, 0xFB4F), - ] - - class Devanagari(unicode_set): - "Unicode set for Devanagari Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0900, 0x097F), - (0xA8E0, 0xA8FF) - ] - - # fmt: on - - -pyparsing_unicode.Japanese._ranges = ( - pyparsing_unicode.Japanese.Kanji._ranges - + pyparsing_unicode.Japanese.Hiragana._ranges - + pyparsing_unicode.Japanese.Katakana._ranges -) - -pyparsing_unicode.BMP = pyparsing_unicode.BasicMultilingualPlane - -# add language identifiers using language Unicode -pyparsing_unicode.العربية = pyparsing_unicode.Arabic -pyparsing_unicode.中文 = pyparsing_unicode.Chinese -pyparsing_unicode.кириллица = pyparsing_unicode.Cyrillic -pyparsing_unicode.Ελληνικά = pyparsing_unicode.Greek -pyparsing_unicode.עִברִית = pyparsing_unicode.Hebrew -pyparsing_unicode.日本語 = pyparsing_unicode.Japanese -pyparsing_unicode.Japanese.漢字 = pyparsing_unicode.Japanese.Kanji -pyparsing_unicode.Japanese.カタカナ = pyparsing_unicode.Japanese.Katakana -pyparsing_unicode.Japanese.ひらがな = pyparsing_unicode.Japanese.Hiragana -pyparsing_unicode.한국어 = pyparsing_unicode.Korean -pyparsing_unicode.ไทย = pyparsing_unicode.Thai -pyparsing_unicode.देवनागरी = pyparsing_unicode.Devanagari diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py deleted file mode 100644 index 34ce092c6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/pyparsing/util.py +++ /dev/null @@ -1,235 +0,0 @@ -# util.py -import warnings -import types -import collections -import itertools -from functools import lru_cache -from typing import List, Union, Iterable - -_bslash = chr(92) - - -class __config_flags: - """Internal class for defining compatibility and debugging flags""" - - _all_names: List[str] = [] - _fixed_names: List[str] = [] - _type_desc = "configuration" - - @classmethod - def _set(cls, dname, value): - if dname in cls._fixed_names: - warnings.warn( - "{}.{} {} is {} and cannot be overridden".format( - cls.__name__, - dname, - cls._type_desc, - str(getattr(cls, dname)).upper(), - ) - ) - return - if dname in cls._all_names: - setattr(cls, dname, value) - else: - raise ValueError("no such {} {!r}".format(cls._type_desc, dname)) - - enable = classmethod(lambda cls, name: cls._set(name, True)) - disable = classmethod(lambda cls, name: cls._set(name, False)) - - -@lru_cache(maxsize=128) -def col(loc: int, strg: str) -> int: - """ - Returns current column within a string, counting newlines as line separators. - The first column is number 1. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See - :class:`ParserElement.parseString` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - """ - s = strg - return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc) - - -@lru_cache(maxsize=128) -def lineno(loc: int, strg: str) -> int: - """Returns current line number within a string, counting newlines as line separators. - The first line is number 1. - - Note - the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`ParserElement.parseString` - for more information on parsing strings containing ```` s, and - suggested methods to maintain a consistent view of the parsed string, the - parse location, and line and column positions within the parsed string. - """ - return strg.count("\n", 0, loc) + 1 - - -@lru_cache(maxsize=128) -def line(loc: int, strg: str) -> str: - """ - Returns the line of text containing loc within a string, counting newlines as line separators. - """ - last_cr = strg.rfind("\n", 0, loc) - next_cr = strg.find("\n", loc) - return strg[last_cr + 1 : next_cr] if next_cr >= 0 else strg[last_cr + 1 :] - - -class _UnboundedCache: - def __init__(self): - cache = {} - cache_get = cache.get - self.not_in_cache = not_in_cache = object() - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - - def clear(_): - cache.clear() - - self.size = None - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class _FifoCache: - def __init__(self, size): - self.not_in_cache = not_in_cache = object() - cache = collections.OrderedDict() - cache_get = cache.get - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - while len(cache) > size: - cache.popitem(last=False) - - def clear(_): - cache.clear() - - self.size = size - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class LRUMemo: - """ - A memoizing mapping that retains `capacity` deleted items - - The memo tracks retained items by their access order; once `capacity` items - are retained, the least recently used item is discarded. - """ - - def __init__(self, capacity): - self._capacity = capacity - self._active = {} - self._memory = collections.OrderedDict() - - def __getitem__(self, key): - try: - return self._active[key] - except KeyError: - self._memory.move_to_end(key) - return self._memory[key] - - def __setitem__(self, key, value): - self._memory.pop(key, None) - self._active[key] = value - - def __delitem__(self, key): - try: - value = self._active.pop(key) - except KeyError: - pass - else: - while len(self._memory) >= self._capacity: - self._memory.popitem(last=False) - self._memory[key] = value - - def clear(self): - self._active.clear() - self._memory.clear() - - -class UnboundedMemo(dict): - """ - A memoizing mapping that retains all deleted items - """ - - def __delitem__(self, key): - pass - - -def _escape_regex_range_chars(s: str) -> str: - # escape these chars: ^-[] - for c in r"\^-[]": - s = s.replace(c, _bslash + c) - s = s.replace("\n", r"\n") - s = s.replace("\t", r"\t") - return str(s) - - -def _collapse_string_to_ranges( - s: Union[str, Iterable[str]], re_escape: bool = True -) -> str: - def is_consecutive(c): - c_int = ord(c) - is_consecutive.prev, prev = c_int, is_consecutive.prev - if c_int - prev > 1: - is_consecutive.value = next(is_consecutive.counter) - return is_consecutive.value - - is_consecutive.prev = 0 - is_consecutive.counter = itertools.count() - is_consecutive.value = -1 - - def escape_re_range_char(c): - return "\\" + c if c in r"\^-][" else c - - def no_escape_re_range_char(c): - return c - - if not re_escape: - escape_re_range_char = no_escape_re_range_char - - ret = [] - s = "".join(sorted(set(s))) - if len(s) > 3: - for _, chars in itertools.groupby(s, key=is_consecutive): - first = last = next(chars) - last = collections.deque( - itertools.chain(iter([last]), chars), maxlen=1 - ).pop() - if first == last: - ret.append(escape_re_range_char(first)) - else: - sep = "" if ord(last) == ord(first) + 1 else "-" - ret.append( - "{}{}{}".format( - escape_re_range_char(first), sep, escape_re_range_char(last) - ) - ) - else: - ret = [escape_re_range_char(c) for c in s] - - return "".join(ret) - - -def _flatten(ll: list) -> list: - ret = [] - for i in ll: - if isinstance(i, list): - ret.extend(_flatten(i)) - else: - ret.append(i) - return ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py new file mode 100644 index 000000000..ef42417c2 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/_vendor/typing_extensions.py @@ -0,0 +1,2209 @@ +import abc +import collections +import collections.abc +import functools +import operator +import sys +import types as _types +import typing + + +__all__ = [ + # Super-special typing primitives. + 'Any', + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'ParamSpecArgs', + 'ParamSpecKwargs', + 'Self', + 'Type', + 'TypeVar', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'NamedTuple', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsIndex', + + # One-off things. + 'Annotated', + 'assert_never', + 'assert_type', + 'clear_overloads', + 'dataclass_transform', + 'get_overloads', + 'final', + 'get_args', + 'get_origin', + 'get_type_hints', + 'IntVar', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'override', + 'Protocol', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeGuard', + 'TYPE_CHECKING', + 'Never', + 'NoReturn', + 'Required', + 'NotRequired', +] + +# for backward compatibility +PEP_560 = True +GenericMeta = type + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + +_marker = object() + + +def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" + f" actual {alen}, expected {elen}") + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + for t in types: + if ( + isinstance(t, typevar_types) and + t not in tvars and + not _is_unpack(t) + ): + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + +NoReturn = typing.NoReturn + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +if sys.version_info >= (3, 11): + from typing import Any +else: + + class _AnyMeta(type): + def __instancecheck__(self, obj): + if self is Any: + raise TypeError("typing_extensions.Any cannot be used with isinstance()") + return super().__instancecheck__(obj) + + def __repr__(self): + if self is Any: + return "typing_extensions.Any" + return super().__repr__() + + class Any(metaclass=_AnyMeta): + """Special type indicating an unconstrained type. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + checks. + """ + def __new__(cls, *args, **kwargs): + if cls is Any: + raise TypeError("Any cannot be instantiated") + return super().__new__(cls, *args, **kwargs) + + +ClassVar = typing.ClassVar + +# On older versions of typing there is an internal class named "Final". +# 3.8+ +if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): + Final = typing.Final +# 3.7 +else: + class _FinalForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Final = _FinalForm('Final', + doc="""A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties.""") + +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +def IntVar(name): + return typing.TypeVar(name) + + +# 3.8+: +if hasattr(typing, 'Literal'): + Literal = typing.Literal +# 3.7: +else: + class _LiteralForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return typing._GenericAlias(self, parameters) + + Literal = _LiteralForm('Literal', + doc="""A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") + + +_overload_dummy = typing._overload_dummy # noqa + + +if hasattr(typing, "get_overloads"): # 3.11+ + overload = typing.overload + get_overloads = typing.get_overloads + clear_overloads = typing.clear_overloads +else: + # {module: {qualname: {firstlineno: func}}} + _overload_registry = collections.defaultdict( + functools.partial(collections.defaultdict, dict) + ) + + def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + + The overloads for a function can be retrieved at runtime using the + get_overloads() function. + """ + # classmethod and staticmethod + f = getattr(func, "__func__", func) + try: + _overload_registry[f.__module__][f.__qualname__][ + f.__code__.co_firstlineno + ] = func + except AttributeError: + # Not a normal function; ignore. + pass + return _overload_dummy + + def get_overloads(func): + """Return all defined overloads for *func* as a sequence.""" + # classmethod and staticmethod + f = getattr(func, "__func__", func) + if f.__module__ not in _overload_registry: + return [] + mod_dict = _overload_registry[f.__module__] + if f.__qualname__ not in mod_dict: + return [] + return list(mod_dict[f.__qualname__].values()) + + def clear_overloads(): + """Clear all overloads in the registry.""" + _overload_registry.clear() + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. + + +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +Deque = typing.Deque +ContextManager = typing.ContextManager +AsyncContextManager = typing.AsyncContextManager +DefaultDict = typing.DefaultDict + +# 3.7.2+ +if hasattr(typing, 'OrderedDict'): + OrderedDict = typing.OrderedDict +# 3.7.0-3.7.2 +else: + OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) + +Counter = typing.Counter +ChainMap = typing.ChainMap +AsyncGenerator = typing.AsyncGenerator +NewType = typing.NewType +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +_PROTO_WHITELIST = ['Callable', 'Awaitable', + 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', + 'ContextManager', 'AsyncContextManager'] + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in ('Protocol', 'Generic'): + continue + annotations = getattr(base, '__annotations__', {}) + for attr in list(base.__dict__.keys()) + list(annotations.keys()): + if (not attr.startswith('_abc_') and attr not in ( + '__abstractmethods__', '__annotations__', '__weakref__', + '_is_protocol', '_is_runtime_protocol', '__dict__', + '__args__', '__slots__', + '__next_in_mro__', '__parameters__', '__origin__', + '__orig_bases__', '__extra__', '__tree_hash__', + '__doc__', '__subclasshook__', '__init__', '__new__', + '__module__', '_MutableMapping__marker', '_gorg')): + attrs.add(attr) + return attrs + + +def _is_callable_members_only(cls): + return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) + + +def _maybe_adjust_parameters(cls): + """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__. + + The contents of this function are very similar + to logic found in typing.Generic.__init_subclass__ + on the CPython main branch. + """ + tvars = [] + if '__orig_bases__' in cls.__dict__: + tvars = typing._collect_type_vars(cls.__orig_bases__) + # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. + # If found, tvars must be a subset of it. + # If not found, tvars is it. + # Also check for and reject plain Generic, + # and reject multiple Generic[...] and/or Protocol[...]. + gvars = None + for base in cls.__orig_bases__: + if (isinstance(base, typing._GenericAlias) and + base.__origin__ in (typing.Generic, Protocol)): + # for error messages + the_base = base.__origin__.__name__ + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...]" + " and/or Protocol[...] multiple types.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) + s_args = ', '.join(str(g) for g in gvars) + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {the_base}[{s_args}]") + tvars = gvars + cls.__parameters__ = tuple(tvars) + + +# 3.8+ +if hasattr(typing, 'Protocol'): + Protocol = typing.Protocol +# 3.7 +else: + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + class _ProtocolMeta(abc.ABCMeta): # noqa: B024 + # This metaclass is a bit unfortunate and exists only because of the lack + # of __instancehook__. + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if ((not getattr(cls, '_is_protocol', False) or + _is_callable_members_only(cls)) and + issubclass(instance.__class__, cls)): + return True + if cls._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(cls, attr, None)) or + getattr(instance, attr) is not None) + for attr in _get_protocol_attrs(cls)): + return True + return super().__instancecheck__(instance) + + class Protocol(metaclass=_ProtocolMeta): + # There is quite a lot of overlapping code with typing.Generic. + # Unfortunately it is hard to avoid this while these live in two different + # modules. The duplicated code will be removed when Protocol is moved to typing. + """Base class for protocol classes. Protocol classes are defined as:: + + class Proto(Protocol): + def meth(self) -> int: + ... + + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: + + class C: + def meth(self) -> int: + return 0 + + def func(x: Proto) -> int: + return x.meth() + + func(C()) # Passes static type check + + See PEP 544 for details. Protocol classes decorated with + @typing_extensions.runtime act as simple-minded runtime protocol that checks + only the presence of given attributes, ignoring their type signatures. + + Protocol classes can be generic, they are defined as:: + + class GenProto(Protocol[T]): + def meth(self) -> T: + ... + """ + __slots__ = () + _is_protocol = True + + def __new__(cls, *args, **kwds): + if cls is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can only be used as a base class") + return super().__new__(cls) + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple): + params = (params,) + if not params and cls is not typing.Tuple: + raise TypeError( + f"Parameter list to {cls.__qualname__}[...] cannot be empty") + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) # noqa + if cls is Protocol: + # Generic can only be subscripted with unique type variables. + if not all(isinstance(p, typing.TypeVar) for p in params): + i = 0 + while isinstance(params[i], typing.TypeVar): + i += 1 + raise TypeError( + "Parameters to Protocol[...] must all be type variables." + f" Parameter {i + 1} is {params[i]}") + if len(set(params)) != len(params): + raise TypeError( + "Parameters to Protocol[...] must all be unique") + else: + # Subscripting a regular Generic subclass. + _check_generic(cls, params, len(cls.__parameters__)) + return typing._GenericAlias(cls, params) + + def __init_subclass__(cls, *args, **kwargs): + if '__orig_bases__' in cls.__dict__: + error = typing.Generic in cls.__orig_bases__ + else: + error = typing.Generic in cls.__bases__ + if error: + raise TypeError("Cannot inherit from plain Generic") + _maybe_adjust_parameters(cls) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + def _proto_hook(other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not getattr(cls, '_is_runtime_protocol', False): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Instance and class checks can only be used with" + " @runtime protocols") + if not _is_callable_members_only(cls): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + if not isinstance(other, type): + # Same error as for issubclass(1, int) + raise TypeError('issubclass() arg 1 must be a class') + for attr in _get_protocol_attrs(cls): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + annotations = getattr(base, '__annotations__', {}) + if (isinstance(annotations, typing.Mapping) and + attr in annotations and + isinstance(other, _ProtocolMeta) and + other._is_protocol): + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # We have nothing more to do for non-protocols. + if not cls._is_protocol: + return + + # Check consistency of bases. + for base in cls.__bases__: + if not (base in (object, typing.Generic) or + base.__module__ == 'collections.abc' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, _ProtocolMeta) and base._is_protocol): + raise TypeError('Protocols can only inherit from other' + f' protocols, got {repr(base)}') + cls.__init__ = _no_init + + +# 3.8+ +if hasattr(typing, 'runtime_checkable'): + runtime_checkable = typing.runtime_checkable +# 3.7 +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol, so that it + can be used with isinstance() and issubclass(). Raise TypeError + if applied to a non-protocol class. + + This allows a simple-minded structural check very similar to the + one-offs in collections.abc such as Hashable. + """ + if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + f' got {cls!r}') + cls._is_runtime_protocol = True + return cls + + +# Exists for backwards compatibility. +runtime = runtime_checkable + + +# 3.8+ +if hasattr(typing, 'SupportsIndex'): + SupportsIndex = typing.SupportsIndex +# 3.7 +else: + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + +if hasattr(typing, "Required"): + # The standard library TypedDict in Python 3.8 does not store runtime information + # about which (if any) keys are optional. See https://bugs.python.org/issue38834 + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + def _check_fails(cls, other): + try: + if sys._getframe(1).f_globals['__name__'] not in ['abc', + 'functools', + 'typing']: + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + except (AttributeError, ValueError): + pass + return False + + def _dict_new(*args, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + return dict(*args, **kwargs) + + _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)' + + def _typeddict_new(*args, total=True, **kwargs): + if not args: + raise TypeError('TypedDict.__new__(): not enough arguments') + _, args = args[0], args[1:] # allow the "cls" keyword be passed + if args: + typename, args = args[0], args[1:] # allow the "_typename" keyword be passed + elif '_typename' in kwargs: + typename = kwargs.pop('_typename') + import warnings + warnings.warn("Passing '_typename' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + raise TypeError("TypedDict.__new__() missing 1 required positional " + "argument: '_typename'") + if args: + try: + fields, = args # allow the "_fields" keyword be passed + except ValueError: + raise TypeError('TypedDict.__new__() takes from 2 to 3 ' + f'positional arguments but {len(args) + 2} ' + 'were given') + elif '_fields' in kwargs and len(kwargs) == 1: + fields = kwargs.pop('_fields') + import warnings + warnings.warn("Passing '_fields' as keyword argument is deprecated", + DeprecationWarning, stacklevel=2) + else: + fields = None + + if fields is None: + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + + ns = {'__annotations__': dict(fields)} + try: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return _TypedDictMeta(typename, (), ns, total=total) + + _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,' + ' /, *, total=True, **kwargs)') + + class _TypedDictMeta(type): + def __init__(cls, name, bases, ns, total=True): + super().__init__(name, bases, ns) + + def __new__(cls, name, bases, ns, total=True): + # Create new typed dict class object. + # This method is called directly when TypedDict is subclassed, + # or via _typeddict_new when TypedDict is instantiated. This way + # TypedDict supports all three syntaxes described in its docstring. + # Subclasses and instances of TypedDict return actual dictionaries + # via _dict_new. + ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new + # Don't insert typing.Generic into __bases__ here, + # or Generic.__init_subclass__ will raise TypeError + # in the super().__new__() call. + # Instead, monkey-patch __bases__ onto the class after it's been created. + tp_dict = super().__new__(cls, name, (dict,), ns) + + if any(issubclass(base, typing.Generic) for base in bases): + tp_dict.__bases__ = (typing.Generic, dict) + _maybe_adjust_parameters(tp_dict) + + annotations = {} + own_annotations = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + own_annotations = { + n: typing._type_check(tp, msg) for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + + for base in bases: + annotations.update(base.__dict__.get('__annotations__', {})) + required_keys.update(base.__dict__.get('__required_keys__', ())) + optional_keys.update(base.__dict__.get('__optional_keys__', ())) + + annotations.update(own_annotations) + for annotation_key, annotation_type in own_annotations.items(): + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + annotation_origin = get_origin(annotation_type) + + if annotation_origin is Required: + required_keys.add(annotation_key) + elif annotation_origin is NotRequired: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + return tp_dict + + __instancecheck__ = __subclasscheck__ = _check_fails + + TypedDict = _TypedDictMeta('TypedDict', (dict,), {}) + TypedDict.__module__ = __name__ + TypedDict.__doc__ = \ + """A simple typed name space. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type that expects all of its + instances to have a certain set of keys, with each key + associated with a value of a consistent type. This expectation + is not checked at runtime but is only enforced by type checkers. + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports two additional equivalent forms:: + + Point2D = TypedDict('Point2D', x=int, y=int, label=str) + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + The class syntax is only supported in Python 3.6+, while two other + syntax forms work for Python 2.7 and 3.2+ + """ + + if hasattr(typing, "_TypedDictMeta"): + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + else: + _TYPEDDICT_TYPES = (_TypedDictMeta,) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + return isinstance(tp, tuple(_TYPEDDICT_TYPES)) + + +if hasattr(typing, "assert_type"): + assert_type = typing.assert_type + +else: + def assert_type(__val, __typ): + """Assert (to the type checker) that the value is of the given type. + + When the type checker encounters a call to assert_type(), it + emits an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # ok + assert_type(name, int) # type checker error + + At runtime this returns the first argument unchanged and otherwise + does nothing. + """ + return __val + + +if hasattr(typing, "Required"): + get_type_hints = typing.get_type_hints +else: + import functools + import types + + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, _AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return types.GenericAlias(t.__origin__, stripped_args) + if hasattr(types, "UnionType") and isinstance(t, types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + if hasattr(typing, "Annotated"): + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + else: + hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + +# Python 3.9+ has PEP 593 (Annotated) +if hasattr(typing, 'Annotated'): + Annotated = typing.Annotated + # Not exported and not a public API, but needed for get_origin() and get_args() + # to work. + _AnnotatedAlias = typing._AnnotatedAlias +# 3.7-3.8 +else: + class _AnnotatedAlias(typing._GenericAlias, _root=True): + """Runtime representation of an annotated type. + + At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' + with extra annotations. The alias behaves like a normal typing alias, + instantiating is the same as instantiating the underlying type, binding + it to types is also the same. + """ + def __init__(self, origin, metadata): + if isinstance(origin, _AnnotatedAlias): + metadata = origin.__metadata__ + metadata + origin = origin.__origin__ + super().__init__(origin, origin) + self.__metadata__ = metadata + + def copy_with(self, params): + assert len(params) == 1 + new_type = params[0] + return _AnnotatedAlias(new_type, self.__metadata__) + + def __repr__(self): + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") + + def __reduce__(self): + return operator.getitem, ( + Annotated, (self.__origin__,) + self.__metadata__ + ) + + def __eq__(self, other): + if not isinstance(other, _AnnotatedAlias): + return NotImplemented + if self.__origin__ != other.__origin__: + return False + return self.__metadata__ == other.__metadata__ + + def __hash__(self): + return hash((self.__origin__, self.__metadata__)) + + class Annotated: + """Add context specific metadata to a type. + + Example: Annotated[int, runtime_check.Unsigned] indicates to the + hypothetical runtime_check module that this type is an unsigned int. + Every other consumer of this type can ignore this metadata and treat + this type as int. + + The first argument to Annotated must be a valid type (and will be in + the __origin__ field), the remaining arguments are kept as a tuple in + the __extra__ field. + + Details: + + - It's an error to call `Annotated` with less than two arguments. + - Nested Annotated are flattened:: + + Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] + + - Instantiating an annotated type is equivalent to instantiating the + underlying type:: + + Annotated[C, Ann1](5) == C(5) + + - Annotated can be used as a generic type alias:: + + Optimized = Annotated[T, runtime.Optimize()] + Optimized[int] == Annotated[int, runtime.Optimize()] + + OptimizedList = Annotated[List[T], runtime.Optimize()] + OptimizedList[int] == Annotated[List[int], runtime.Optimize()] + """ + + __slots__ = () + + def __new__(cls, *args, **kwargs): + raise TypeError("Type Annotated cannot be instantiated.") + + @typing._tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple) or len(params) < 2: + raise TypeError("Annotated[...] should be used " + "with at least two arguments (a type and an " + "annotation).") + allowed_special_forms = (ClassVar, Final) + if get_origin(params[0]) in allowed_special_forms: + origin = params[0] + else: + msg = "Annotated[t, ...]: t must be a type." + origin = typing._type_check(params[0], msg) + metadata = tuple(params[1:]) + return _AnnotatedAlias(origin, metadata) + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + f"Cannot subclass {cls.__module__}.Annotated" + ) + +# Python 3.8 has get_origin() and get_args() but those implementations aren't +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +if sys.version_info[:2] >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.7-3.9 +else: + try: + # 3.9+ + from typing import _BaseGenericAlias + except ImportError: + _BaseGenericAlias = typing._GenericAlias + try: + # 3.9+ + from typing import GenericAlias as _typing_GenericAlias + except ImportError: + _typing_GenericAlias = typing._GenericAlias + + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, _AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, _AnnotatedAlias): + return (tp.__origin__,) + tp.__metadata__ + if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)): + if getattr(tp, "_special", False): + return () + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeAliasForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") +# 3.7-3.8 +else: + class _TypeAliasForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + TypeAlias = _TypeAliasForm('TypeAlias', + doc="""Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example + above.""") + + +class _DefaultMixin: + """Mixin for TypeVarLike defaults.""" + + __slots__ = () + + def __init__(self, default): + if isinstance(default, (tuple, list)): + self.__default__ = tuple((typing._type_check(d, "Default must be a type") + for d in default)) + elif default: + self.__default__ = typing._type_check(default, "Default must be a type") + else: + self.__default__ = None + + +# Add default and infer_variance parameters from PEP 696 and 695 +class TypeVar(typing.TypeVar, _DefaultMixin, _root=True): + """Type variable.""" + + __module__ = 'typing' + + def __init__(self, name, *constraints, bound=None, + covariant=False, contravariant=False, + default=None, infer_variance=False): + super().__init__(name, *constraints, bound=bound, covariant=covariant, + contravariant=contravariant) + _DefaultMixin.__init__(self, default) + self.__infer_variance__ = infer_variance + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.7-3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + +# 3.10+ +if hasattr(typing, 'ParamSpec'): + + # Add default Parameter - PEP 696 + class ParamSpec(typing.ParamSpec, _DefaultMixin, _root=True): + """Parameter specification variable.""" + + __module__ = 'typing' + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + default=None): + super().__init__(name, bound=bound, covariant=covariant, + contravariant=contravariant) + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + +# 3.7-3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list, _DefaultMixin): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + default=None): + super().__init__([self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + +# 3.7-3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + __class__ = typing._GenericAlias + + # Flag in 3.8. + _special = False + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + +# 3.7-3.9 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not isinstance(parameters[-1], ParamSpec): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = tuple(typing._type_check(p, msg) for p in parameters) + return _ConcatenateGenericAlias(self, parameters) + + +# 3.10+ +if hasattr(typing, 'Concatenate'): + Concatenate = typing.Concatenate + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa +# 3.9 +elif sys.version_info[:2] >= (3, 9): + @_TypeAliasForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) +# 3.7-8 +else: + class _ConcatenateForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + return _concatenate_getitem(self, parameters) + + Concatenate = _ConcatenateForm( + 'Concatenate', + doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """) + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +elif sys.version_info[:2] >= (3, 9): + class _TypeGuardForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_TypeGuardForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) +# 3.7-3.8 +else: + class _TypeGuardForm(typing._SpecialForm, _root=True): + + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type') + return typing._GenericAlias(self, (item,)) + + TypeGuard = _TypeGuardForm( + 'TypeGuard', + doc="""Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """) + + +# Vendored from cpython typing._SpecialFrom +class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +if hasattr(typing, "LiteralString"): + LiteralString = typing.LiteralString +else: + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Self"): + Self = typing.Self +else: + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Never"): + Never = typing.Never +else: + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, 'Required'): + Required = typing.Required + NotRequired = typing.NotRequired +elif sys.version_info[:2] >= (3, 9): + class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + +else: + class _RequiredForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + Required = _RequiredForm( + 'Required', + doc="""A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """) + NotRequired = _RequiredForm( + 'NotRequired', + doc="""A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """) + + +if hasattr(typing, "Unpack"): # 3.11+ + Unpack = typing.Unpack +elif sys.version_info[:2] >= (3, 9): + class _UnpackSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @_UnpackSpecialForm + def Unpack(self, parameters): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + +else: + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm( + 'Unpack', + doc="""A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... + + """) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + + +if hasattr(typing, "TypeVarTuple"): # 3.11+ + + # Add default Parameter - PEP 696 + class TypeVarTuple(typing.TypeVarTuple, _DefaultMixin, _root=True): + """Type variable tuple.""" + + def __init__(self, name, *, default=None): + super().__init__(name) + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + +else: + class TypeVarTuple(_DefaultMixin): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* + type such as ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, default=None): + self.__name__ = name + _DefaultMixin.__init__(self, default) + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + +if hasattr(typing, "reveal_type"): + reveal_type = typing.reveal_type +else: + def reveal_type(__obj: T) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr) + return __obj + + +if hasattr(typing, "assert_never"): + assert_never = typing.assert_never +else: + def assert_never(__arg: Never) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + raise AssertionError("Expected code to be unreachable") + + +if hasattr(typing, 'dataclass_transform'): + dataclass_transform = typing.dataclass_transform +else: + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_specifiers: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + **kwargs: typing.Any, + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_specifiers`` specifies a static list of supported classes + or functions that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "field_specifiers": field_specifiers, + "kwargs": kwargs, + } + return cls_or_fn + return decorator + + +if hasattr(typing, "override"): + override = typing.override +else: + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(__arg: _F) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: ... + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + See PEP 698 for details. + + """ + return __arg + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + typing._collect_type_vars = _collect_type_vars + typing._check_generic = _check_generic + + +# Backport typing.NamedTuple as it exists in Python 3.11. +# In 3.11, the ability to define generic `NamedTuple`s was supported. +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. +if sys.version_info >= (3, 11): + NamedTuple = typing.NamedTuple +else: + def _caller(): + try: + return sys._getframe(2).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): # For platforms without _getframe() + return None + + def _make_nmtuple(name, types, module, defaults=()): + fields = [n for n, t in types] + annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") + for n, t in types} + nm_tpl = collections.namedtuple(name, fields, + defaults=defaults, module=module) + nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations + # The `_field_types` attribute was removed in 3.9; + # in earlier versions, it is the same as the `__annotations__` attribute + if sys.version_info < (3, 9): + nm_tpl._field_types = annotations + return nm_tpl + + _prohibited_namedtuple_fields = typing._prohibited + _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) + + class _NamedTupleMeta(type): + def __new__(cls, typename, bases, ns): + assert _NamedTuple in bases + for base in bases: + if base is not _NamedTuple and base is not typing.Generic: + raise TypeError( + 'can only inherit from a NamedTuple type and Generic') + bases = tuple(tuple if base is _NamedTuple else base for base in bases) + types = ns.get('__annotations__', {}) + default_names = [] + for field_name in types: + if field_name in ns: + default_names.append(field_name) + elif default_names: + raise TypeError(f"Non-default namedtuple field {field_name} " + f"cannot follow default field" + f"{'s' if len(default_names) > 1 else ''} " + f"{', '.join(default_names)}") + nm_tpl = _make_nmtuple( + typename, types.items(), + defaults=[ns[n] for n in default_names], + module=ns['__module__'] + ) + nm_tpl.__bases__ = bases + if typing.Generic in bases: + class_getitem = typing.Generic.__class_getitem__.__func__ + nm_tpl.__class_getitem__ = classmethod(class_getitem) + # update from user namespace without overriding special namedtuple attributes + for key in ns: + if key in _prohibited_namedtuple_fields: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special_namedtuple_fields and key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + if typing.Generic in bases: + nm_tpl.__init_subclass__() + return nm_tpl + + def NamedTuple(__typename, __fields=None, **kwargs): + if __fields is None: + __fields = kwargs.items() + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + return _make_nmtuple(__typename, __fields, module=_caller()) + + NamedTuple.__doc__ = typing.NamedTuple.__doc__ + _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) + + # On 3.8+, alter the signature so that it matches typing.NamedTuple. + # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7, + # so just leave the signature as it is on 3.7. + if sys.version_info >= (3, 8): + NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)' + + def _namedtuple_mro_entries(bases): + assert NamedTuple in bases + return (_NamedTuple,) + + NamedTuple.__mro_entries__ = _namedtuple_mro_entries diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/extern/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/extern/__init__.py index 70897eea6..948bcc609 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/extern/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/pkg_resources/extern/__init__.py @@ -58,7 +58,8 @@ def find_spec(self, fullname, path=None, target=None): """Return a module spec for vendored names.""" return ( importlib.util.spec_from_loader(fullname, self) - if self._module_matches_namespace(fullname) else None + if self._module_matches_namespace(fullname) + else None ) def install(self): @@ -70,7 +71,10 @@ def install(self): names = ( - 'packaging', 'pyparsing', 'appdirs', 'jaraco', 'importlib_resources', + 'packaging', + 'platformdirs', + 'jaraco', + 'importlib_resources', 'more_itertools', ) VendorImporter(__name__, names).install() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE deleted file mode 100644 index ea215f2db..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -pycparser -- A C parser in Python - -Copyright (c) 2008-2020, Eli Bendersky -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -* Neither the name of Eli Bendersky nor the names of its contributors may - be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT -OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA deleted file mode 100644 index 1d0fbd651..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/METADATA +++ /dev/null @@ -1,31 +0,0 @@ -Metadata-Version: 2.1 -Name: pycparser -Version: 2.21 -Summary: C parser in Python -Home-page: https://github.com/eliben/pycparser -Author: Eli Bendersky -Author-email: eliben@gmail.com -Maintainer: Eli Bendersky -License: BSD -Platform: Cross Platform -Classifier: Development Status :: 5 - Production/Stable -Classifier: License :: OSI Approved :: BSD License -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* - - -pycparser is a complete parser of the C language, written in -pure Python using the PLY parsing library. -It parses C code into an AST and can serve as a front-end for -C compilers or analysis tools. - - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD deleted file mode 100644 index 2de17f9e2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/RECORD +++ /dev/null @@ -1,41 +0,0 @@ -pycparser-2.21.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -pycparser-2.21.dist-info/LICENSE,sha256=Pn3yW437ZYyakVAZMNTZQ7BQh6g0fH4rQyVhavU1BHs,1536 -pycparser-2.21.dist-info/METADATA,sha256=GvTEQA9yKj0nvP4mknfoGpMvjaJXCQjQANcQHrRrAxc,1108 -pycparser-2.21.dist-info/RECORD,, -pycparser-2.21.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110 -pycparser-2.21.dist-info/top_level.txt,sha256=c-lPcS74L_8KoH7IE6PQF5ofyirRQNV4VhkbSFIPeWM,10 -pycparser/__init__.py,sha256=WUEp5D0fuHBH9Q8c1fYvR2eKWfj-CNghLf2MMlQLI1I,2815 -pycparser/__pycache__/__init__.cpython-310.pyc,, -pycparser/__pycache__/_ast_gen.cpython-310.pyc,, -pycparser/__pycache__/_build_tables.cpython-310.pyc,, -pycparser/__pycache__/ast_transforms.cpython-310.pyc,, -pycparser/__pycache__/c_ast.cpython-310.pyc,, -pycparser/__pycache__/c_generator.cpython-310.pyc,, -pycparser/__pycache__/c_lexer.cpython-310.pyc,, -pycparser/__pycache__/c_parser.cpython-310.pyc,, -pycparser/__pycache__/lextab.cpython-310.pyc,, -pycparser/__pycache__/plyparser.cpython-310.pyc,, -pycparser/__pycache__/yacctab.cpython-310.pyc,, -pycparser/_ast_gen.py,sha256=0JRVnDW-Jw-3IjVlo8je9rbAcp6Ko7toHAnB5zi7h0Q,10555 -pycparser/_build_tables.py,sha256=oZCd3Plhq-vkV-QuEsaahcf-jUI6-HgKsrAL9gvFzuU,1039 -pycparser/_c_ast.cfg,sha256=ld5ezE9yzIJFIVAUfw7ezJSlMi4nXKNCzfmqjOyQTNo,4255 -pycparser/ast_transforms.py,sha256=GTMYlUgWmXd5wJVyovXY1qzzAqjxzCpVVg0664dKGBs,5691 -pycparser/c_ast.py,sha256=HWeOrfYdCY0u5XaYhE1i60uVyE3yMWdcxzECUX-DqJw,31445 -pycparser/c_generator.py,sha256=yi6Mcqxv88J5ue8k5-mVGxh3iJ37iD4QyF-sWcGjC-8,17772 -pycparser/c_lexer.py,sha256=xCpjIb6vOUebBJpdifidb08y7XgAsO3T1gNGXJT93-w,17167 -pycparser/c_parser.py,sha256=_8y3i52bL6SUK21KmEEl0qzHxe-0eZRzjZGkWg8gQ4A,73680 -pycparser/lextab.py,sha256=fIxBAHYRC418oKF52M7xb8_KMj3K-tHx0TzZiKwxjPM,8504 -pycparser/ply/__init__.py,sha256=q4s86QwRsYRa20L9ueSxfh-hPihpftBjDOvYa2_SS2Y,102 -pycparser/ply/__pycache__/__init__.cpython-310.pyc,, -pycparser/ply/__pycache__/cpp.cpython-310.pyc,, -pycparser/ply/__pycache__/ctokens.cpython-310.pyc,, -pycparser/ply/__pycache__/lex.cpython-310.pyc,, -pycparser/ply/__pycache__/yacc.cpython-310.pyc,, -pycparser/ply/__pycache__/ygen.cpython-310.pyc,, -pycparser/ply/cpp.py,sha256=UtC3ylTWp5_1MKA-PLCuwKQR8zSOnlGuGGIdzj8xS98,33282 -pycparser/ply/ctokens.py,sha256=MKksnN40TehPhgVfxCJhjj_BjL943apreABKYz-bl0Y,3177 -pycparser/ply/lex.py,sha256=7Qol57x702HZwjA3ZLp-84CUEWq1EehW-N67Wzghi-M,42918 -pycparser/ply/yacc.py,sha256=eatSDkRLgRr6X3-hoDk_SQQv065R0BdL2K7fQ54CgVM,137323 -pycparser/ply/ygen.py,sha256=2JYNeYtrPz1JzLSLO3d4GsS8zJU8jY_I_CR1VI9gWrA,2251 -pycparser/plyparser.py,sha256=8tLOoEytcapvWrr1JfCf7Dog-wulBtS1YrDs8S7JfMo,4875 -pycparser/yacctab.py,sha256=j_fVNIyDWDRVk7eWMqQtlBw2AwUSV5JTrtT58l7zis0,205652 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL deleted file mode 100644 index ef99c6cf3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.34.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt deleted file mode 100644 index dc1c9e101..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser-2.21.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -pycparser diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py deleted file mode 100644 index d82eb2d6f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/__init__.py +++ /dev/null @@ -1,90 +0,0 @@ -#----------------------------------------------------------------- -# pycparser: __init__.py -# -# This package file exports some convenience functions for -# interacting with pycparser -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- -__all__ = ['c_lexer', 'c_parser', 'c_ast'] -__version__ = '2.21' - -import io -from subprocess import check_output -from .c_parser import CParser - - -def preprocess_file(filename, cpp_path='cpp', cpp_args=''): - """ Preprocess a file using cpp. - - filename: - Name of the file you want to preprocess. - - cpp_path: - cpp_args: - Refer to the documentation of parse_file for the meaning of these - arguments. - - When successful, returns the preprocessed file's contents. - Errors from cpp will be printed out. - """ - path_list = [cpp_path] - if isinstance(cpp_args, list): - path_list += cpp_args - elif cpp_args != '': - path_list += [cpp_args] - path_list += [filename] - - try: - # Note the use of universal_newlines to treat all newlines - # as \n for Python's purpose - text = check_output(path_list, universal_newlines=True) - except OSError as e: - raise RuntimeError("Unable to invoke 'cpp'. " + - 'Make sure its path was passed correctly\n' + - ('Original error: %s' % e)) - - return text - - -def parse_file(filename, use_cpp=False, cpp_path='cpp', cpp_args='', - parser=None): - """ Parse a C file using pycparser. - - filename: - Name of the file you want to parse. - - use_cpp: - Set to True if you want to execute the C pre-processor - on the file prior to parsing it. - - cpp_path: - If use_cpp is True, this is the path to 'cpp' on your - system. If no path is provided, it attempts to just - execute 'cpp', so it must be in your PATH. - - cpp_args: - If use_cpp is True, set this to the command line arguments strings - to cpp. Be careful with quotes - it's best to pass a raw string - (r'') here. For example: - r'-I../utils/fake_libc_include' - If several arguments are required, pass a list of strings. - - parser: - Optional parser object to be used instead of the default CParser - - When successful, an AST is returned. ParseError can be - thrown if the file doesn't parse successfully. - - Errors from cpp will be printed out. - """ - if use_cpp: - text = preprocess_file(filename, cpp_path, cpp_args) - else: - with io.open(filename) as f: - text = f.read() - - if parser is None: - parser = CParser() - return parser.parse(text, filename) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py deleted file mode 100644 index 0f7d330ba..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_ast_gen.py +++ /dev/null @@ -1,336 +0,0 @@ -#----------------------------------------------------------------- -# _ast_gen.py -# -# Generates the AST Node classes from a specification given in -# a configuration file -# -# The design of this module was inspired by astgen.py from the -# Python 2.5 code-base. -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- -from string import Template - - -class ASTCodeGenerator(object): - def __init__(self, cfg_filename='_c_ast.cfg'): - """ Initialize the code generator from a configuration - file. - """ - self.cfg_filename = cfg_filename - self.node_cfg = [NodeCfg(name, contents) - for (name, contents) in self.parse_cfgfile(cfg_filename)] - - def generate(self, file=None): - """ Generates the code into file, an open file buffer. - """ - src = Template(_PROLOGUE_COMMENT).substitute( - cfg_filename=self.cfg_filename) - - src += _PROLOGUE_CODE - for node_cfg in self.node_cfg: - src += node_cfg.generate_source() + '\n\n' - - file.write(src) - - def parse_cfgfile(self, filename): - """ Parse the configuration file and yield pairs of - (name, contents) for each node. - """ - with open(filename, "r") as f: - for line in f: - line = line.strip() - if not line or line.startswith('#'): - continue - colon_i = line.find(':') - lbracket_i = line.find('[') - rbracket_i = line.find(']') - if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i: - raise RuntimeError("Invalid line in %s:\n%s\n" % (filename, line)) - - name = line[:colon_i] - val = line[lbracket_i + 1:rbracket_i] - vallist = [v.strip() for v in val.split(',')] if val else [] - yield name, vallist - - -class NodeCfg(object): - """ Node configuration. - - name: node name - contents: a list of contents - attributes and child nodes - See comment at the top of the configuration file for details. - """ - - def __init__(self, name, contents): - self.name = name - self.all_entries = [] - self.attr = [] - self.child = [] - self.seq_child = [] - - for entry in contents: - clean_entry = entry.rstrip('*') - self.all_entries.append(clean_entry) - - if entry.endswith('**'): - self.seq_child.append(clean_entry) - elif entry.endswith('*'): - self.child.append(clean_entry) - else: - self.attr.append(entry) - - def generate_source(self): - src = self._gen_init() - src += '\n' + self._gen_children() - src += '\n' + self._gen_iter() - src += '\n' + self._gen_attr_names() - return src - - def _gen_init(self): - src = "class %s(Node):\n" % self.name - - if self.all_entries: - args = ', '.join(self.all_entries) - slots = ', '.join("'{0}'".format(e) for e in self.all_entries) - slots += ", 'coord', '__weakref__'" - arglist = '(self, %s, coord=None)' % args - else: - slots = "'coord', '__weakref__'" - arglist = '(self, coord=None)' - - src += " __slots__ = (%s)\n" % slots - src += " def __init__%s:\n" % arglist - - for name in self.all_entries + ['coord']: - src += " self.%s = %s\n" % (name, name) - - return src - - def _gen_children(self): - src = ' def children(self):\n' - - if self.all_entries: - src += ' nodelist = []\n' - - for child in self.child: - src += ( - ' if self.%(child)s is not None:' + - ' nodelist.append(("%(child)s", self.%(child)s))\n') % ( - dict(child=child)) - - for seq_child in self.seq_child: - src += ( - ' for i, child in enumerate(self.%(child)s or []):\n' - ' nodelist.append(("%(child)s[%%d]" %% i, child))\n') % ( - dict(child=seq_child)) - - src += ' return tuple(nodelist)\n' - else: - src += ' return ()\n' - - return src - - def _gen_iter(self): - src = ' def __iter__(self):\n' - - if self.all_entries: - for child in self.child: - src += ( - ' if self.%(child)s is not None:\n' + - ' yield self.%(child)s\n') % (dict(child=child)) - - for seq_child in self.seq_child: - src += ( - ' for child in (self.%(child)s or []):\n' - ' yield child\n') % (dict(child=seq_child)) - - if not (self.child or self.seq_child): - # Empty generator - src += ( - ' return\n' + - ' yield\n') - else: - # Empty generator - src += ( - ' return\n' + - ' yield\n') - - return src - - def _gen_attr_names(self): - src = " attr_names = (" + ''.join("%r, " % nm for nm in self.attr) + ')' - return src - - -_PROLOGUE_COMMENT = \ -r'''#----------------------------------------------------------------- -# ** ATTENTION ** -# This code was automatically generated from the file: -# $cfg_filename -# -# Do not modify it directly. Modify the configuration file and -# run the generator again. -# ** ** *** ** ** -# -# pycparser: c_ast.py -# -# AST Node classes. -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- - -''' - -_PROLOGUE_CODE = r''' -import sys - -def _repr(obj): - """ - Get the representation of an object, with dedicated pprint-like format for lists. - """ - if isinstance(obj, list): - return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]' - else: - return repr(obj) - -class Node(object): - __slots__ = () - """ Abstract base class for AST nodes. - """ - def __repr__(self): - """ Generates a python representation of the current node - """ - result = self.__class__.__name__ + '(' - - indent = '' - separator = '' - for name in self.__slots__[:-2]: - result += separator - result += indent - result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__))))) - - separator = ',' - indent = '\n ' + (' ' * len(self.__class__.__name__)) - - result += indent + ')' - - return result - - def children(self): - """ A sequence of all children that are Nodes - """ - pass - - def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None): - """ Pretty print the Node and all its attributes and - children (recursively) to a buffer. - - buf: - Open IO buffer into which the Node is printed. - - offset: - Initial offset (amount of leading spaces) - - attrnames: - True if you want to see the attribute names in - name=value pairs. False to only see the values. - - nodenames: - True if you want to see the actual node names - within their parents. - - showcoord: - Do you want the coordinates of each Node to be - displayed. - """ - lead = ' ' * offset - if nodenames and _my_node_name is not None: - buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ') - else: - buf.write(lead + self.__class__.__name__+ ': ') - - if self.attr_names: - if attrnames: - nvlist = [(n, getattr(self,n)) for n in self.attr_names] - attrstr = ', '.join('%s=%s' % nv for nv in nvlist) - else: - vlist = [getattr(self, n) for n in self.attr_names] - attrstr = ', '.join('%s' % v for v in vlist) - buf.write(attrstr) - - if showcoord: - buf.write(' (at %s)' % self.coord) - buf.write('\n') - - for (child_name, child) in self.children(): - child.show( - buf, - offset=offset + 2, - attrnames=attrnames, - nodenames=nodenames, - showcoord=showcoord, - _my_node_name=child_name) - - -class NodeVisitor(object): - """ A base NodeVisitor class for visiting c_ast nodes. - Subclass it and define your own visit_XXX methods, where - XXX is the class name you want to visit with these - methods. - - For example: - - class ConstantVisitor(NodeVisitor): - def __init__(self): - self.values = [] - - def visit_Constant(self, node): - self.values.append(node.value) - - Creates a list of values of all the constant nodes - encountered below the given node. To use it: - - cv = ConstantVisitor() - cv.visit(node) - - Notes: - - * generic_visit() will be called for AST nodes for which - no visit_XXX method was defined. - * The children of nodes for which a visit_XXX was - defined will not be visited - if you need this, call - generic_visit() on the node. - You can use: - NodeVisitor.generic_visit(self, node) - * Modeled after Python's own AST visiting facilities - (the ast module of Python 3.0) - """ - - _method_cache = None - - def visit(self, node): - """ Visit a node. - """ - - if self._method_cache is None: - self._method_cache = {} - - visitor = self._method_cache.get(node.__class__.__name__, None) - if visitor is None: - method = 'visit_' + node.__class__.__name__ - visitor = getattr(self, method, self.generic_visit) - self._method_cache[node.__class__.__name__] = visitor - - return visitor(node) - - def generic_visit(self, node): - """ Called if no explicit visitor function exists for a - node. Implements preorder visiting of the node. - """ - for c in node: - self.visit(c) - -''' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py deleted file mode 100644 index 958381ad0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_build_tables.py +++ /dev/null @@ -1,37 +0,0 @@ -#----------------------------------------------------------------- -# pycparser: _build_tables.py -# -# A dummy for generating the lexing/parsing tables and and -# compiling them into .pyc for faster execution in optimized mode. -# Also generates AST code from the configuration file. -# Should be called from the pycparser directory. -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- - -# Insert '.' and '..' as first entries to the search path for modules. -# Restricted environments like embeddable python do not include the -# current working directory on startup. -import sys -sys.path[0:0] = ['.', '..'] - -# Generate c_ast.py -from _ast_gen import ASTCodeGenerator -ast_gen = ASTCodeGenerator('_c_ast.cfg') -ast_gen.generate(open('c_ast.py', 'w')) - -from pycparser import c_parser - -# Generates the tables -# -c_parser.CParser( - lex_optimize=True, - yacc_debug=False, - yacc_optimize=True) - -# Load to compile into .pyc -# -import lextab -import yacctab -import c_ast diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg deleted file mode 100644 index 0626533e8..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/_c_ast.cfg +++ /dev/null @@ -1,195 +0,0 @@ -#----------------------------------------------------------------- -# pycparser: _c_ast.cfg -# -# Defines the AST Node classes used in pycparser. -# -# Each entry is a Node sub-class name, listing the attributes -# and child nodes of the class: -# * - a child node -# ** - a sequence of child nodes -# - an attribute -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- - -# ArrayDecl is a nested declaration of an array with the given type. -# dim: the dimension (for example, constant 42) -# dim_quals: list of dimension qualifiers, to support C99's allowing 'const' -# and 'static' within the array dimension in function declarations. -ArrayDecl: [type*, dim*, dim_quals] - -ArrayRef: [name*, subscript*] - -# op: =, +=, /= etc. -# -Assignment: [op, lvalue*, rvalue*] - -Alignas: [alignment*] - -BinaryOp: [op, left*, right*] - -Break: [] - -Case: [expr*, stmts**] - -Cast: [to_type*, expr*] - -# Compound statement in C99 is a list of block items (declarations or -# statements). -# -Compound: [block_items**] - -# Compound literal (anonymous aggregate) for C99. -# (type-name) {initializer_list} -# type: the typename -# init: InitList for the initializer list -# -CompoundLiteral: [type*, init*] - -# type: int, char, float, string, etc. -# -Constant: [type, value] - -Continue: [] - -# name: the variable being declared -# quals: list of qualifiers (const, volatile) -# funcspec: list function specifiers (i.e. inline in C99) -# storage: list of storage specifiers (extern, register, etc.) -# type: declaration type (probably nested with all the modifiers) -# init: initialization value, or None -# bitsize: bit field size, or None -# -Decl: [name, quals, align, storage, funcspec, type*, init*, bitsize*] - -DeclList: [decls**] - -Default: [stmts**] - -DoWhile: [cond*, stmt*] - -# Represents the ellipsis (...) parameter in a function -# declaration -# -EllipsisParam: [] - -# An empty statement (a semicolon ';' on its own) -# -EmptyStatement: [] - -# Enumeration type specifier -# name: an optional ID -# values: an EnumeratorList -# -Enum: [name, values*] - -# A name/value pair for enumeration values -# -Enumerator: [name, value*] - -# A list of enumerators -# -EnumeratorList: [enumerators**] - -# A list of expressions separated by the comma operator. -# -ExprList: [exprs**] - -# This is the top of the AST, representing a single C file (a -# translation unit in K&R jargon). It contains a list of -# "external-declaration"s, which is either declarations (Decl), -# Typedef or function definitions (FuncDef). -# -FileAST: [ext**] - -# for (init; cond; next) stmt -# -For: [init*, cond*, next*, stmt*] - -# name: Id -# args: ExprList -# -FuncCall: [name*, args*] - -# type (args) -# -FuncDecl: [args*, type*] - -# Function definition: a declarator for the function name and -# a body, which is a compound statement. -# There's an optional list of parameter declarations for old -# K&R-style definitions -# -FuncDef: [decl*, param_decls**, body*] - -Goto: [name] - -ID: [name] - -# Holder for types that are a simple identifier (e.g. the built -# ins void, char etc. and typedef-defined types) -# -IdentifierType: [names] - -If: [cond*, iftrue*, iffalse*] - -# An initialization list used for compound literals. -# -InitList: [exprs**] - -Label: [name, stmt*] - -# A named initializer for C99. -# The name of a NamedInitializer is a sequence of Nodes, because -# names can be hierarchical and contain constant expressions. -# -NamedInitializer: [name**, expr*] - -# a list of comma separated function parameter declarations -# -ParamList: [params**] - -PtrDecl: [quals, type*] - -Return: [expr*] - -StaticAssert: [cond*, message*] - -# name: struct tag name -# decls: declaration of members -# -Struct: [name, decls**] - -# type: . or -> -# name.field or name->field -# -StructRef: [name*, type, field*] - -Switch: [cond*, stmt*] - -# cond ? iftrue : iffalse -# -TernaryOp: [cond*, iftrue*, iffalse*] - -# A base type declaration -# -TypeDecl: [declname, quals, align, type*] - -# A typedef declaration. -# Very similar to Decl, but without some attributes -# -Typedef: [name, quals, storage, type*] - -Typename: [name, quals, align, type*] - -UnaryOp: [op, expr*] - -# name: union tag name -# decls: declaration of members -# -Union: [name, decls**] - -While: [cond*, stmt*] - -Pragma: [string] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py deleted file mode 100644 index 367dcf54c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ast_transforms.py +++ /dev/null @@ -1,164 +0,0 @@ -#------------------------------------------------------------------------------ -# pycparser: ast_transforms.py -# -# Some utilities used by the parser to create a friendlier AST. -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#------------------------------------------------------------------------------ - -from . import c_ast - - -def fix_switch_cases(switch_node): - """ The 'case' statements in a 'switch' come out of parsing with one - child node, so subsequent statements are just tucked to the parent - Compound. Additionally, consecutive (fall-through) case statements - come out messy. This is a peculiarity of the C grammar. The following: - - switch (myvar) { - case 10: - k = 10; - p = k + 1; - return 10; - case 20: - case 30: - return 20; - default: - break; - } - - Creates this tree (pseudo-dump): - - Switch - ID: myvar - Compound: - Case 10: - k = 10 - p = k + 1 - return 10 - Case 20: - Case 30: - return 20 - Default: - break - - The goal of this transform is to fix this mess, turning it into the - following: - - Switch - ID: myvar - Compound: - Case 10: - k = 10 - p = k + 1 - return 10 - Case 20: - Case 30: - return 20 - Default: - break - - A fixed AST node is returned. The argument may be modified. - """ - assert isinstance(switch_node, c_ast.Switch) - if not isinstance(switch_node.stmt, c_ast.Compound): - return switch_node - - # The new Compound child for the Switch, which will collect children in the - # correct order - new_compound = c_ast.Compound([], switch_node.stmt.coord) - - # The last Case/Default node - last_case = None - - # Goes over the children of the Compound below the Switch, adding them - # either directly below new_compound or below the last Case as appropriate - # (for `switch(cond) {}`, block_items would have been None) - for child in (switch_node.stmt.block_items or []): - if isinstance(child, (c_ast.Case, c_ast.Default)): - # If it's a Case/Default: - # 1. Add it to the Compound and mark as "last case" - # 2. If its immediate child is also a Case or Default, promote it - # to a sibling. - new_compound.block_items.append(child) - _extract_nested_case(child, new_compound.block_items) - last_case = new_compound.block_items[-1] - else: - # Other statements are added as children to the last case, if it - # exists. - if last_case is None: - new_compound.block_items.append(child) - else: - last_case.stmts.append(child) - - switch_node.stmt = new_compound - return switch_node - - -def _extract_nested_case(case_node, stmts_list): - """ Recursively extract consecutive Case statements that are made nested - by the parser and add them to the stmts_list. - """ - if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)): - stmts_list.append(case_node.stmts.pop()) - _extract_nested_case(stmts_list[-1], stmts_list) - - -def fix_atomic_specifiers(decl): - """ Atomic specifiers like _Atomic(type) are unusually structured, - conferring a qualifier upon the contained type. - - This function fixes a decl with atomic specifiers to have a sane AST - structure, by removing spurious Typename->TypeDecl pairs and attaching - the _Atomic qualifier in the right place. - """ - # There can be multiple levels of _Atomic in a decl; fix them until a - # fixed point is reached. - while True: - decl, found = _fix_atomic_specifiers_once(decl) - if not found: - break - - # Make sure to add an _Atomic qual on the topmost decl if needed. Also - # restore the declname on the innermost TypeDecl (it gets placed in the - # wrong place during construction). - typ = decl - while not isinstance(typ, c_ast.TypeDecl): - try: - typ = typ.type - except AttributeError: - return decl - if '_Atomic' in typ.quals and '_Atomic' not in decl.quals: - decl.quals.append('_Atomic') - if typ.declname is None: - typ.declname = decl.name - - return decl - - -def _fix_atomic_specifiers_once(decl): - """ Performs one 'fix' round of atomic specifiers. - Returns (modified_decl, found) where found is True iff a fix was made. - """ - parent = decl - grandparent = None - node = decl.type - while node is not None: - if isinstance(node, c_ast.Typename) and '_Atomic' in node.quals: - break - try: - grandparent = parent - parent = node - node = node.type - except AttributeError: - # If we've reached a node without a `type` field, it means we won't - # find what we're looking for at this point; give up the search - # and return the original decl unmodified. - return decl, False - - assert isinstance(parent, c_ast.TypeDecl) - grandparent.type = node.type - if '_Atomic' not in node.type.quals: - node.type.quals.append('_Atomic') - return decl, True diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py deleted file mode 100644 index 6575a2ad3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_ast.py +++ /dev/null @@ -1,1125 +0,0 @@ -#----------------------------------------------------------------- -# ** ATTENTION ** -# This code was automatically generated from the file: -# _c_ast.cfg -# -# Do not modify it directly. Modify the configuration file and -# run the generator again. -# ** ** *** ** ** -# -# pycparser: c_ast.py -# -# AST Node classes. -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- - - -import sys - -def _repr(obj): - """ - Get the representation of an object, with dedicated pprint-like format for lists. - """ - if isinstance(obj, list): - return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]' - else: - return repr(obj) - -class Node(object): - __slots__ = () - """ Abstract base class for AST nodes. - """ - def __repr__(self): - """ Generates a python representation of the current node - """ - result = self.__class__.__name__ + '(' - - indent = '' - separator = '' - for name in self.__slots__[:-2]: - result += separator - result += indent - result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n ' + (' ' * (len(name) + len(self.__class__.__name__))))) - - separator = ',' - indent = '\n ' + (' ' * len(self.__class__.__name__)) - - result += indent + ')' - - return result - - def children(self): - """ A sequence of all children that are Nodes - """ - pass - - def show(self, buf=sys.stdout, offset=0, attrnames=False, nodenames=False, showcoord=False, _my_node_name=None): - """ Pretty print the Node and all its attributes and - children (recursively) to a buffer. - - buf: - Open IO buffer into which the Node is printed. - - offset: - Initial offset (amount of leading spaces) - - attrnames: - True if you want to see the attribute names in - name=value pairs. False to only see the values. - - nodenames: - True if you want to see the actual node names - within their parents. - - showcoord: - Do you want the coordinates of each Node to be - displayed. - """ - lead = ' ' * offset - if nodenames and _my_node_name is not None: - buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ') - else: - buf.write(lead + self.__class__.__name__+ ': ') - - if self.attr_names: - if attrnames: - nvlist = [(n, getattr(self,n)) for n in self.attr_names] - attrstr = ', '.join('%s=%s' % nv for nv in nvlist) - else: - vlist = [getattr(self, n) for n in self.attr_names] - attrstr = ', '.join('%s' % v for v in vlist) - buf.write(attrstr) - - if showcoord: - buf.write(' (at %s)' % self.coord) - buf.write('\n') - - for (child_name, child) in self.children(): - child.show( - buf, - offset=offset + 2, - attrnames=attrnames, - nodenames=nodenames, - showcoord=showcoord, - _my_node_name=child_name) - - -class NodeVisitor(object): - """ A base NodeVisitor class for visiting c_ast nodes. - Subclass it and define your own visit_XXX methods, where - XXX is the class name you want to visit with these - methods. - - For example: - - class ConstantVisitor(NodeVisitor): - def __init__(self): - self.values = [] - - def visit_Constant(self, node): - self.values.append(node.value) - - Creates a list of values of all the constant nodes - encountered below the given node. To use it: - - cv = ConstantVisitor() - cv.visit(node) - - Notes: - - * generic_visit() will be called for AST nodes for which - no visit_XXX method was defined. - * The children of nodes for which a visit_XXX was - defined will not be visited - if you need this, call - generic_visit() on the node. - You can use: - NodeVisitor.generic_visit(self, node) - * Modeled after Python's own AST visiting facilities - (the ast module of Python 3.0) - """ - - _method_cache = None - - def visit(self, node): - """ Visit a node. - """ - - if self._method_cache is None: - self._method_cache = {} - - visitor = self._method_cache.get(node.__class__.__name__, None) - if visitor is None: - method = 'visit_' + node.__class__.__name__ - visitor = getattr(self, method, self.generic_visit) - self._method_cache[node.__class__.__name__] = visitor - - return visitor(node) - - def generic_visit(self, node): - """ Called if no explicit visitor function exists for a - node. Implements preorder visiting of the node. - """ - for c in node: - self.visit(c) - -class ArrayDecl(Node): - __slots__ = ('type', 'dim', 'dim_quals', 'coord', '__weakref__') - def __init__(self, type, dim, dim_quals, coord=None): - self.type = type - self.dim = dim - self.dim_quals = dim_quals - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - if self.dim is not None: nodelist.append(("dim", self.dim)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - if self.dim is not None: - yield self.dim - - attr_names = ('dim_quals', ) - -class ArrayRef(Node): - __slots__ = ('name', 'subscript', 'coord', '__weakref__') - def __init__(self, name, subscript, coord=None): - self.name = name - self.subscript = subscript - self.coord = coord - - def children(self): - nodelist = [] - if self.name is not None: nodelist.append(("name", self.name)) - if self.subscript is not None: nodelist.append(("subscript", self.subscript)) - return tuple(nodelist) - - def __iter__(self): - if self.name is not None: - yield self.name - if self.subscript is not None: - yield self.subscript - - attr_names = () - -class Assignment(Node): - __slots__ = ('op', 'lvalue', 'rvalue', 'coord', '__weakref__') - def __init__(self, op, lvalue, rvalue, coord=None): - self.op = op - self.lvalue = lvalue - self.rvalue = rvalue - self.coord = coord - - def children(self): - nodelist = [] - if self.lvalue is not None: nodelist.append(("lvalue", self.lvalue)) - if self.rvalue is not None: nodelist.append(("rvalue", self.rvalue)) - return tuple(nodelist) - - def __iter__(self): - if self.lvalue is not None: - yield self.lvalue - if self.rvalue is not None: - yield self.rvalue - - attr_names = ('op', ) - -class Alignas(Node): - __slots__ = ('alignment', 'coord', '__weakref__') - def __init__(self, alignment, coord=None): - self.alignment = alignment - self.coord = coord - - def children(self): - nodelist = [] - if self.alignment is not None: nodelist.append(("alignment", self.alignment)) - return tuple(nodelist) - - def __iter__(self): - if self.alignment is not None: - yield self.alignment - - attr_names = () - -class BinaryOp(Node): - __slots__ = ('op', 'left', 'right', 'coord', '__weakref__') - def __init__(self, op, left, right, coord=None): - self.op = op - self.left = left - self.right = right - self.coord = coord - - def children(self): - nodelist = [] - if self.left is not None: nodelist.append(("left", self.left)) - if self.right is not None: nodelist.append(("right", self.right)) - return tuple(nodelist) - - def __iter__(self): - if self.left is not None: - yield self.left - if self.right is not None: - yield self.right - - attr_names = ('op', ) - -class Break(Node): - __slots__ = ('coord', '__weakref__') - def __init__(self, coord=None): - self.coord = coord - - def children(self): - return () - - def __iter__(self): - return - yield - - attr_names = () - -class Case(Node): - __slots__ = ('expr', 'stmts', 'coord', '__weakref__') - def __init__(self, expr, stmts, coord=None): - self.expr = expr - self.stmts = stmts - self.coord = coord - - def children(self): - nodelist = [] - if self.expr is not None: nodelist.append(("expr", self.expr)) - for i, child in enumerate(self.stmts or []): - nodelist.append(("stmts[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - if self.expr is not None: - yield self.expr - for child in (self.stmts or []): - yield child - - attr_names = () - -class Cast(Node): - __slots__ = ('to_type', 'expr', 'coord', '__weakref__') - def __init__(self, to_type, expr, coord=None): - self.to_type = to_type - self.expr = expr - self.coord = coord - - def children(self): - nodelist = [] - if self.to_type is not None: nodelist.append(("to_type", self.to_type)) - if self.expr is not None: nodelist.append(("expr", self.expr)) - return tuple(nodelist) - - def __iter__(self): - if self.to_type is not None: - yield self.to_type - if self.expr is not None: - yield self.expr - - attr_names = () - -class Compound(Node): - __slots__ = ('block_items', 'coord', '__weakref__') - def __init__(self, block_items, coord=None): - self.block_items = block_items - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.block_items or []): - nodelist.append(("block_items[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.block_items or []): - yield child - - attr_names = () - -class CompoundLiteral(Node): - __slots__ = ('type', 'init', 'coord', '__weakref__') - def __init__(self, type, init, coord=None): - self.type = type - self.init = init - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - if self.init is not None: nodelist.append(("init", self.init)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - if self.init is not None: - yield self.init - - attr_names = () - -class Constant(Node): - __slots__ = ('type', 'value', 'coord', '__weakref__') - def __init__(self, type, value, coord=None): - self.type = type - self.value = value - self.coord = coord - - def children(self): - nodelist = [] - return tuple(nodelist) - - def __iter__(self): - return - yield - - attr_names = ('type', 'value', ) - -class Continue(Node): - __slots__ = ('coord', '__weakref__') - def __init__(self, coord=None): - self.coord = coord - - def children(self): - return () - - def __iter__(self): - return - yield - - attr_names = () - -class Decl(Node): - __slots__ = ('name', 'quals', 'align', 'storage', 'funcspec', 'type', 'init', 'bitsize', 'coord', '__weakref__') - def __init__(self, name, quals, align, storage, funcspec, type, init, bitsize, coord=None): - self.name = name - self.quals = quals - self.align = align - self.storage = storage - self.funcspec = funcspec - self.type = type - self.init = init - self.bitsize = bitsize - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - if self.init is not None: nodelist.append(("init", self.init)) - if self.bitsize is not None: nodelist.append(("bitsize", self.bitsize)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - if self.init is not None: - yield self.init - if self.bitsize is not None: - yield self.bitsize - - attr_names = ('name', 'quals', 'align', 'storage', 'funcspec', ) - -class DeclList(Node): - __slots__ = ('decls', 'coord', '__weakref__') - def __init__(self, decls, coord=None): - self.decls = decls - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.decls or []): - nodelist.append(("decls[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.decls or []): - yield child - - attr_names = () - -class Default(Node): - __slots__ = ('stmts', 'coord', '__weakref__') - def __init__(self, stmts, coord=None): - self.stmts = stmts - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.stmts or []): - nodelist.append(("stmts[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.stmts or []): - yield child - - attr_names = () - -class DoWhile(Node): - __slots__ = ('cond', 'stmt', 'coord', '__weakref__') - def __init__(self, cond, stmt, coord=None): - self.cond = cond - self.stmt = stmt - self.coord = coord - - def children(self): - nodelist = [] - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.stmt is not None: nodelist.append(("stmt", self.stmt)) - return tuple(nodelist) - - def __iter__(self): - if self.cond is not None: - yield self.cond - if self.stmt is not None: - yield self.stmt - - attr_names = () - -class EllipsisParam(Node): - __slots__ = ('coord', '__weakref__') - def __init__(self, coord=None): - self.coord = coord - - def children(self): - return () - - def __iter__(self): - return - yield - - attr_names = () - -class EmptyStatement(Node): - __slots__ = ('coord', '__weakref__') - def __init__(self, coord=None): - self.coord = coord - - def children(self): - return () - - def __iter__(self): - return - yield - - attr_names = () - -class Enum(Node): - __slots__ = ('name', 'values', 'coord', '__weakref__') - def __init__(self, name, values, coord=None): - self.name = name - self.values = values - self.coord = coord - - def children(self): - nodelist = [] - if self.values is not None: nodelist.append(("values", self.values)) - return tuple(nodelist) - - def __iter__(self): - if self.values is not None: - yield self.values - - attr_names = ('name', ) - -class Enumerator(Node): - __slots__ = ('name', 'value', 'coord', '__weakref__') - def __init__(self, name, value, coord=None): - self.name = name - self.value = value - self.coord = coord - - def children(self): - nodelist = [] - if self.value is not None: nodelist.append(("value", self.value)) - return tuple(nodelist) - - def __iter__(self): - if self.value is not None: - yield self.value - - attr_names = ('name', ) - -class EnumeratorList(Node): - __slots__ = ('enumerators', 'coord', '__weakref__') - def __init__(self, enumerators, coord=None): - self.enumerators = enumerators - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.enumerators or []): - nodelist.append(("enumerators[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.enumerators or []): - yield child - - attr_names = () - -class ExprList(Node): - __slots__ = ('exprs', 'coord', '__weakref__') - def __init__(self, exprs, coord=None): - self.exprs = exprs - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.exprs or []): - nodelist.append(("exprs[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.exprs or []): - yield child - - attr_names = () - -class FileAST(Node): - __slots__ = ('ext', 'coord', '__weakref__') - def __init__(self, ext, coord=None): - self.ext = ext - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.ext or []): - nodelist.append(("ext[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.ext or []): - yield child - - attr_names = () - -class For(Node): - __slots__ = ('init', 'cond', 'next', 'stmt', 'coord', '__weakref__') - def __init__(self, init, cond, next, stmt, coord=None): - self.init = init - self.cond = cond - self.next = next - self.stmt = stmt - self.coord = coord - - def children(self): - nodelist = [] - if self.init is not None: nodelist.append(("init", self.init)) - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.next is not None: nodelist.append(("next", self.next)) - if self.stmt is not None: nodelist.append(("stmt", self.stmt)) - return tuple(nodelist) - - def __iter__(self): - if self.init is not None: - yield self.init - if self.cond is not None: - yield self.cond - if self.next is not None: - yield self.next - if self.stmt is not None: - yield self.stmt - - attr_names = () - -class FuncCall(Node): - __slots__ = ('name', 'args', 'coord', '__weakref__') - def __init__(self, name, args, coord=None): - self.name = name - self.args = args - self.coord = coord - - def children(self): - nodelist = [] - if self.name is not None: nodelist.append(("name", self.name)) - if self.args is not None: nodelist.append(("args", self.args)) - return tuple(nodelist) - - def __iter__(self): - if self.name is not None: - yield self.name - if self.args is not None: - yield self.args - - attr_names = () - -class FuncDecl(Node): - __slots__ = ('args', 'type', 'coord', '__weakref__') - def __init__(self, args, type, coord=None): - self.args = args - self.type = type - self.coord = coord - - def children(self): - nodelist = [] - if self.args is not None: nodelist.append(("args", self.args)) - if self.type is not None: nodelist.append(("type", self.type)) - return tuple(nodelist) - - def __iter__(self): - if self.args is not None: - yield self.args - if self.type is not None: - yield self.type - - attr_names = () - -class FuncDef(Node): - __slots__ = ('decl', 'param_decls', 'body', 'coord', '__weakref__') - def __init__(self, decl, param_decls, body, coord=None): - self.decl = decl - self.param_decls = param_decls - self.body = body - self.coord = coord - - def children(self): - nodelist = [] - if self.decl is not None: nodelist.append(("decl", self.decl)) - if self.body is not None: nodelist.append(("body", self.body)) - for i, child in enumerate(self.param_decls or []): - nodelist.append(("param_decls[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - if self.decl is not None: - yield self.decl - if self.body is not None: - yield self.body - for child in (self.param_decls or []): - yield child - - attr_names = () - -class Goto(Node): - __slots__ = ('name', 'coord', '__weakref__') - def __init__(self, name, coord=None): - self.name = name - self.coord = coord - - def children(self): - nodelist = [] - return tuple(nodelist) - - def __iter__(self): - return - yield - - attr_names = ('name', ) - -class ID(Node): - __slots__ = ('name', 'coord', '__weakref__') - def __init__(self, name, coord=None): - self.name = name - self.coord = coord - - def children(self): - nodelist = [] - return tuple(nodelist) - - def __iter__(self): - return - yield - - attr_names = ('name', ) - -class IdentifierType(Node): - __slots__ = ('names', 'coord', '__weakref__') - def __init__(self, names, coord=None): - self.names = names - self.coord = coord - - def children(self): - nodelist = [] - return tuple(nodelist) - - def __iter__(self): - return - yield - - attr_names = ('names', ) - -class If(Node): - __slots__ = ('cond', 'iftrue', 'iffalse', 'coord', '__weakref__') - def __init__(self, cond, iftrue, iffalse, coord=None): - self.cond = cond - self.iftrue = iftrue - self.iffalse = iffalse - self.coord = coord - - def children(self): - nodelist = [] - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.iftrue is not None: nodelist.append(("iftrue", self.iftrue)) - if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse)) - return tuple(nodelist) - - def __iter__(self): - if self.cond is not None: - yield self.cond - if self.iftrue is not None: - yield self.iftrue - if self.iffalse is not None: - yield self.iffalse - - attr_names = () - -class InitList(Node): - __slots__ = ('exprs', 'coord', '__weakref__') - def __init__(self, exprs, coord=None): - self.exprs = exprs - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.exprs or []): - nodelist.append(("exprs[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.exprs or []): - yield child - - attr_names = () - -class Label(Node): - __slots__ = ('name', 'stmt', 'coord', '__weakref__') - def __init__(self, name, stmt, coord=None): - self.name = name - self.stmt = stmt - self.coord = coord - - def children(self): - nodelist = [] - if self.stmt is not None: nodelist.append(("stmt", self.stmt)) - return tuple(nodelist) - - def __iter__(self): - if self.stmt is not None: - yield self.stmt - - attr_names = ('name', ) - -class NamedInitializer(Node): - __slots__ = ('name', 'expr', 'coord', '__weakref__') - def __init__(self, name, expr, coord=None): - self.name = name - self.expr = expr - self.coord = coord - - def children(self): - nodelist = [] - if self.expr is not None: nodelist.append(("expr", self.expr)) - for i, child in enumerate(self.name or []): - nodelist.append(("name[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - if self.expr is not None: - yield self.expr - for child in (self.name or []): - yield child - - attr_names = () - -class ParamList(Node): - __slots__ = ('params', 'coord', '__weakref__') - def __init__(self, params, coord=None): - self.params = params - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.params or []): - nodelist.append(("params[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.params or []): - yield child - - attr_names = () - -class PtrDecl(Node): - __slots__ = ('quals', 'type', 'coord', '__weakref__') - def __init__(self, quals, type, coord=None): - self.quals = quals - self.type = type - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - - attr_names = ('quals', ) - -class Return(Node): - __slots__ = ('expr', 'coord', '__weakref__') - def __init__(self, expr, coord=None): - self.expr = expr - self.coord = coord - - def children(self): - nodelist = [] - if self.expr is not None: nodelist.append(("expr", self.expr)) - return tuple(nodelist) - - def __iter__(self): - if self.expr is not None: - yield self.expr - - attr_names = () - -class StaticAssert(Node): - __slots__ = ('cond', 'message', 'coord', '__weakref__') - def __init__(self, cond, message, coord=None): - self.cond = cond - self.message = message - self.coord = coord - - def children(self): - nodelist = [] - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.message is not None: nodelist.append(("message", self.message)) - return tuple(nodelist) - - def __iter__(self): - if self.cond is not None: - yield self.cond - if self.message is not None: - yield self.message - - attr_names = () - -class Struct(Node): - __slots__ = ('name', 'decls', 'coord', '__weakref__') - def __init__(self, name, decls, coord=None): - self.name = name - self.decls = decls - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.decls or []): - nodelist.append(("decls[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.decls or []): - yield child - - attr_names = ('name', ) - -class StructRef(Node): - __slots__ = ('name', 'type', 'field', 'coord', '__weakref__') - def __init__(self, name, type, field, coord=None): - self.name = name - self.type = type - self.field = field - self.coord = coord - - def children(self): - nodelist = [] - if self.name is not None: nodelist.append(("name", self.name)) - if self.field is not None: nodelist.append(("field", self.field)) - return tuple(nodelist) - - def __iter__(self): - if self.name is not None: - yield self.name - if self.field is not None: - yield self.field - - attr_names = ('type', ) - -class Switch(Node): - __slots__ = ('cond', 'stmt', 'coord', '__weakref__') - def __init__(self, cond, stmt, coord=None): - self.cond = cond - self.stmt = stmt - self.coord = coord - - def children(self): - nodelist = [] - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.stmt is not None: nodelist.append(("stmt", self.stmt)) - return tuple(nodelist) - - def __iter__(self): - if self.cond is not None: - yield self.cond - if self.stmt is not None: - yield self.stmt - - attr_names = () - -class TernaryOp(Node): - __slots__ = ('cond', 'iftrue', 'iffalse', 'coord', '__weakref__') - def __init__(self, cond, iftrue, iffalse, coord=None): - self.cond = cond - self.iftrue = iftrue - self.iffalse = iffalse - self.coord = coord - - def children(self): - nodelist = [] - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.iftrue is not None: nodelist.append(("iftrue", self.iftrue)) - if self.iffalse is not None: nodelist.append(("iffalse", self.iffalse)) - return tuple(nodelist) - - def __iter__(self): - if self.cond is not None: - yield self.cond - if self.iftrue is not None: - yield self.iftrue - if self.iffalse is not None: - yield self.iffalse - - attr_names = () - -class TypeDecl(Node): - __slots__ = ('declname', 'quals', 'align', 'type', 'coord', '__weakref__') - def __init__(self, declname, quals, align, type, coord=None): - self.declname = declname - self.quals = quals - self.align = align - self.type = type - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - - attr_names = ('declname', 'quals', 'align', ) - -class Typedef(Node): - __slots__ = ('name', 'quals', 'storage', 'type', 'coord', '__weakref__') - def __init__(self, name, quals, storage, type, coord=None): - self.name = name - self.quals = quals - self.storage = storage - self.type = type - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - - attr_names = ('name', 'quals', 'storage', ) - -class Typename(Node): - __slots__ = ('name', 'quals', 'align', 'type', 'coord', '__weakref__') - def __init__(self, name, quals, align, type, coord=None): - self.name = name - self.quals = quals - self.align = align - self.type = type - self.coord = coord - - def children(self): - nodelist = [] - if self.type is not None: nodelist.append(("type", self.type)) - return tuple(nodelist) - - def __iter__(self): - if self.type is not None: - yield self.type - - attr_names = ('name', 'quals', 'align', ) - -class UnaryOp(Node): - __slots__ = ('op', 'expr', 'coord', '__weakref__') - def __init__(self, op, expr, coord=None): - self.op = op - self.expr = expr - self.coord = coord - - def children(self): - nodelist = [] - if self.expr is not None: nodelist.append(("expr", self.expr)) - return tuple(nodelist) - - def __iter__(self): - if self.expr is not None: - yield self.expr - - attr_names = ('op', ) - -class Union(Node): - __slots__ = ('name', 'decls', 'coord', '__weakref__') - def __init__(self, name, decls, coord=None): - self.name = name - self.decls = decls - self.coord = coord - - def children(self): - nodelist = [] - for i, child in enumerate(self.decls or []): - nodelist.append(("decls[%d]" % i, child)) - return tuple(nodelist) - - def __iter__(self): - for child in (self.decls or []): - yield child - - attr_names = ('name', ) - -class While(Node): - __slots__ = ('cond', 'stmt', 'coord', '__weakref__') - def __init__(self, cond, stmt, coord=None): - self.cond = cond - self.stmt = stmt - self.coord = coord - - def children(self): - nodelist = [] - if self.cond is not None: nodelist.append(("cond", self.cond)) - if self.stmt is not None: nodelist.append(("stmt", self.stmt)) - return tuple(nodelist) - - def __iter__(self): - if self.cond is not None: - yield self.cond - if self.stmt is not None: - yield self.stmt - - attr_names = () - -class Pragma(Node): - __slots__ = ('string', 'coord', '__weakref__') - def __init__(self, string, coord=None): - self.string = string - self.coord = coord - - def children(self): - nodelist = [] - return tuple(nodelist) - - def __iter__(self): - return - yield - - attr_names = ('string', ) - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py deleted file mode 100644 index 1057b2c62..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_generator.py +++ /dev/null @@ -1,502 +0,0 @@ -#------------------------------------------------------------------------------ -# pycparser: c_generator.py -# -# C code generator from pycparser AST nodes. -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#------------------------------------------------------------------------------ -from . import c_ast - - -class CGenerator(object): - """ Uses the same visitor pattern as c_ast.NodeVisitor, but modified to - return a value from each visit method, using string accumulation in - generic_visit. - """ - def __init__(self, reduce_parentheses=False): - """ Constructs C-code generator - - reduce_parentheses: - if True, eliminates needless parentheses on binary operators - """ - # Statements start with indentation of self.indent_level spaces, using - # the _make_indent method. - self.indent_level = 0 - self.reduce_parentheses = reduce_parentheses - - def _make_indent(self): - return ' ' * self.indent_level - - def visit(self, node): - method = 'visit_' + node.__class__.__name__ - return getattr(self, method, self.generic_visit)(node) - - def generic_visit(self, node): - if node is None: - return '' - else: - return ''.join(self.visit(c) for c_name, c in node.children()) - - def visit_Constant(self, n): - return n.value - - def visit_ID(self, n): - return n.name - - def visit_Pragma(self, n): - ret = '#pragma' - if n.string: - ret += ' ' + n.string - return ret - - def visit_ArrayRef(self, n): - arrref = self._parenthesize_unless_simple(n.name) - return arrref + '[' + self.visit(n.subscript) + ']' - - def visit_StructRef(self, n): - sref = self._parenthesize_unless_simple(n.name) - return sref + n.type + self.visit(n.field) - - def visit_FuncCall(self, n): - fref = self._parenthesize_unless_simple(n.name) - return fref + '(' + self.visit(n.args) + ')' - - def visit_UnaryOp(self, n): - if n.op == 'sizeof': - # Always parenthesize the argument of sizeof since it can be - # a name. - return 'sizeof(%s)' % self.visit(n.expr) - else: - operand = self._parenthesize_unless_simple(n.expr) - if n.op == 'p++': - return '%s++' % operand - elif n.op == 'p--': - return '%s--' % operand - else: - return '%s%s' % (n.op, operand) - - # Precedence map of binary operators: - precedence_map = { - # Should be in sync with c_parser.CParser.precedence - # Higher numbers are stronger binding - '||': 0, # weakest binding - '&&': 1, - '|': 2, - '^': 3, - '&': 4, - '==': 5, '!=': 5, - '>': 6, '>=': 6, '<': 6, '<=': 6, - '>>': 7, '<<': 7, - '+': 8, '-': 8, - '*': 9, '/': 9, '%': 9 # strongest binding - } - - def visit_BinaryOp(self, n): - # Note: all binary operators are left-to-right associative - # - # If `n.left.op` has a stronger or equally binding precedence in - # comparison to `n.op`, no parenthesis are needed for the left: - # e.g., `(a*b) + c` is equivalent to `a*b + c`, as well as - # `(a+b) - c` is equivalent to `a+b - c` (same precedence). - # If the left operator is weaker binding than the current, then - # parentheses are necessary: - # e.g., `(a+b) * c` is NOT equivalent to `a+b * c`. - lval_str = self._parenthesize_if( - n.left, - lambda d: not (self._is_simple_node(d) or - self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and - self.precedence_map[d.op] >= self.precedence_map[n.op])) - # If `n.right.op` has a stronger -but not equal- binding precedence, - # parenthesis can be omitted on the right: - # e.g., `a + (b*c)` is equivalent to `a + b*c`. - # If the right operator is weaker or equally binding, then parentheses - # are necessary: - # e.g., `a * (b+c)` is NOT equivalent to `a * b+c` and - # `a - (b+c)` is NOT equivalent to `a - b+c` (same precedence). - rval_str = self._parenthesize_if( - n.right, - lambda d: not (self._is_simple_node(d) or - self.reduce_parentheses and isinstance(d, c_ast.BinaryOp) and - self.precedence_map[d.op] > self.precedence_map[n.op])) - return '%s %s %s' % (lval_str, n.op, rval_str) - - def visit_Assignment(self, n): - rval_str = self._parenthesize_if( - n.rvalue, - lambda n: isinstance(n, c_ast.Assignment)) - return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str) - - def visit_IdentifierType(self, n): - return ' '.join(n.names) - - def _visit_expr(self, n): - if isinstance(n, c_ast.InitList): - return '{' + self.visit(n) + '}' - elif isinstance(n, c_ast.ExprList): - return '(' + self.visit(n) + ')' - else: - return self.visit(n) - - def visit_Decl(self, n, no_type=False): - # no_type is used when a Decl is part of a DeclList, where the type is - # explicitly only for the first declaration in a list. - # - s = n.name if no_type else self._generate_decl(n) - if n.bitsize: s += ' : ' + self.visit(n.bitsize) - if n.init: - s += ' = ' + self._visit_expr(n.init) - return s - - def visit_DeclList(self, n): - s = self.visit(n.decls[0]) - if len(n.decls) > 1: - s += ', ' + ', '.join(self.visit_Decl(decl, no_type=True) - for decl in n.decls[1:]) - return s - - def visit_Typedef(self, n): - s = '' - if n.storage: s += ' '.join(n.storage) + ' ' - s += self._generate_type(n.type) - return s - - def visit_Cast(self, n): - s = '(' + self._generate_type(n.to_type, emit_declname=False) + ')' - return s + ' ' + self._parenthesize_unless_simple(n.expr) - - def visit_ExprList(self, n): - visited_subexprs = [] - for expr in n.exprs: - visited_subexprs.append(self._visit_expr(expr)) - return ', '.join(visited_subexprs) - - def visit_InitList(self, n): - visited_subexprs = [] - for expr in n.exprs: - visited_subexprs.append(self._visit_expr(expr)) - return ', '.join(visited_subexprs) - - def visit_Enum(self, n): - return self._generate_struct_union_enum(n, name='enum') - - def visit_Alignas(self, n): - return '_Alignas({})'.format(self.visit(n.alignment)) - - def visit_Enumerator(self, n): - if not n.value: - return '{indent}{name},\n'.format( - indent=self._make_indent(), - name=n.name, - ) - else: - return '{indent}{name} = {value},\n'.format( - indent=self._make_indent(), - name=n.name, - value=self.visit(n.value), - ) - - def visit_FuncDef(self, n): - decl = self.visit(n.decl) - self.indent_level = 0 - body = self.visit(n.body) - if n.param_decls: - knrdecls = ';\n'.join(self.visit(p) for p in n.param_decls) - return decl + '\n' + knrdecls + ';\n' + body + '\n' - else: - return decl + '\n' + body + '\n' - - def visit_FileAST(self, n): - s = '' - for ext in n.ext: - if isinstance(ext, c_ast.FuncDef): - s += self.visit(ext) - elif isinstance(ext, c_ast.Pragma): - s += self.visit(ext) + '\n' - else: - s += self.visit(ext) + ';\n' - return s - - def visit_Compound(self, n): - s = self._make_indent() + '{\n' - self.indent_level += 2 - if n.block_items: - s += ''.join(self._generate_stmt(stmt) for stmt in n.block_items) - self.indent_level -= 2 - s += self._make_indent() + '}\n' - return s - - def visit_CompoundLiteral(self, n): - return '(' + self.visit(n.type) + '){' + self.visit(n.init) + '}' - - - def visit_EmptyStatement(self, n): - return ';' - - def visit_ParamList(self, n): - return ', '.join(self.visit(param) for param in n.params) - - def visit_Return(self, n): - s = 'return' - if n.expr: s += ' ' + self.visit(n.expr) - return s + ';' - - def visit_Break(self, n): - return 'break;' - - def visit_Continue(self, n): - return 'continue;' - - def visit_TernaryOp(self, n): - s = '(' + self._visit_expr(n.cond) + ') ? ' - s += '(' + self._visit_expr(n.iftrue) + ') : ' - s += '(' + self._visit_expr(n.iffalse) + ')' - return s - - def visit_If(self, n): - s = 'if (' - if n.cond: s += self.visit(n.cond) - s += ')\n' - s += self._generate_stmt(n.iftrue, add_indent=True) - if n.iffalse: - s += self._make_indent() + 'else\n' - s += self._generate_stmt(n.iffalse, add_indent=True) - return s - - def visit_For(self, n): - s = 'for (' - if n.init: s += self.visit(n.init) - s += ';' - if n.cond: s += ' ' + self.visit(n.cond) - s += ';' - if n.next: s += ' ' + self.visit(n.next) - s += ')\n' - s += self._generate_stmt(n.stmt, add_indent=True) - return s - - def visit_While(self, n): - s = 'while (' - if n.cond: s += self.visit(n.cond) - s += ')\n' - s += self._generate_stmt(n.stmt, add_indent=True) - return s - - def visit_DoWhile(self, n): - s = 'do\n' - s += self._generate_stmt(n.stmt, add_indent=True) - s += self._make_indent() + 'while (' - if n.cond: s += self.visit(n.cond) - s += ');' - return s - - def visit_StaticAssert(self, n): - s = '_Static_assert(' - s += self.visit(n.cond) - if n.message: - s += ',' - s += self.visit(n.message) - s += ')' - return s - - def visit_Switch(self, n): - s = 'switch (' + self.visit(n.cond) + ')\n' - s += self._generate_stmt(n.stmt, add_indent=True) - return s - - def visit_Case(self, n): - s = 'case ' + self.visit(n.expr) + ':\n' - for stmt in n.stmts: - s += self._generate_stmt(stmt, add_indent=True) - return s - - def visit_Default(self, n): - s = 'default:\n' - for stmt in n.stmts: - s += self._generate_stmt(stmt, add_indent=True) - return s - - def visit_Label(self, n): - return n.name + ':\n' + self._generate_stmt(n.stmt) - - def visit_Goto(self, n): - return 'goto ' + n.name + ';' - - def visit_EllipsisParam(self, n): - return '...' - - def visit_Struct(self, n): - return self._generate_struct_union_enum(n, 'struct') - - def visit_Typename(self, n): - return self._generate_type(n.type) - - def visit_Union(self, n): - return self._generate_struct_union_enum(n, 'union') - - def visit_NamedInitializer(self, n): - s = '' - for name in n.name: - if isinstance(name, c_ast.ID): - s += '.' + name.name - else: - s += '[' + self.visit(name) + ']' - s += ' = ' + self._visit_expr(n.expr) - return s - - def visit_FuncDecl(self, n): - return self._generate_type(n) - - def visit_ArrayDecl(self, n): - return self._generate_type(n, emit_declname=False) - - def visit_TypeDecl(self, n): - return self._generate_type(n, emit_declname=False) - - def visit_PtrDecl(self, n): - return self._generate_type(n, emit_declname=False) - - def _generate_struct_union_enum(self, n, name): - """ Generates code for structs, unions, and enums. name should be - 'struct', 'union', or 'enum'. - """ - if name in ('struct', 'union'): - members = n.decls - body_function = self._generate_struct_union_body - else: - assert name == 'enum' - members = None if n.values is None else n.values.enumerators - body_function = self._generate_enum_body - s = name + ' ' + (n.name or '') - if members is not None: - # None means no members - # Empty sequence means an empty list of members - s += '\n' - s += self._make_indent() - self.indent_level += 2 - s += '{\n' - s += body_function(members) - self.indent_level -= 2 - s += self._make_indent() + '}' - return s - - def _generate_struct_union_body(self, members): - return ''.join(self._generate_stmt(decl) for decl in members) - - def _generate_enum_body(self, members): - # `[:-2] + '\n'` removes the final `,` from the enumerator list - return ''.join(self.visit(value) for value in members)[:-2] + '\n' - - def _generate_stmt(self, n, add_indent=False): - """ Generation from a statement node. This method exists as a wrapper - for individual visit_* methods to handle different treatment of - some statements in this context. - """ - typ = type(n) - if add_indent: self.indent_level += 2 - indent = self._make_indent() - if add_indent: self.indent_level -= 2 - - if typ in ( - c_ast.Decl, c_ast.Assignment, c_ast.Cast, c_ast.UnaryOp, - c_ast.BinaryOp, c_ast.TernaryOp, c_ast.FuncCall, c_ast.ArrayRef, - c_ast.StructRef, c_ast.Constant, c_ast.ID, c_ast.Typedef, - c_ast.ExprList): - # These can also appear in an expression context so no semicolon - # is added to them automatically - # - return indent + self.visit(n) + ';\n' - elif typ in (c_ast.Compound,): - # No extra indentation required before the opening brace of a - # compound - because it consists of multiple lines it has to - # compute its own indentation. - # - return self.visit(n) - elif typ in (c_ast.If,): - return indent + self.visit(n) - else: - return indent + self.visit(n) + '\n' - - def _generate_decl(self, n): - """ Generation from a Decl node. - """ - s = '' - if n.funcspec: s = ' '.join(n.funcspec) + ' ' - if n.storage: s += ' '.join(n.storage) + ' ' - if n.align: s += self.visit(n.align[0]) + ' ' - s += self._generate_type(n.type) - return s - - def _generate_type(self, n, modifiers=[], emit_declname = True): - """ Recursive generation from a type node. n is the type node. - modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers - encountered on the way down to a TypeDecl, to allow proper - generation from it. - """ - typ = type(n) - #~ print(n, modifiers) - - if typ == c_ast.TypeDecl: - s = '' - if n.quals: s += ' '.join(n.quals) + ' ' - s += self.visit(n.type) - - nstr = n.declname if n.declname and emit_declname else '' - # Resolve modifiers. - # Wrap in parens to distinguish pointer to array and pointer to - # function syntax. - # - for i, modifier in enumerate(modifiers): - if isinstance(modifier, c_ast.ArrayDecl): - if (i != 0 and - isinstance(modifiers[i - 1], c_ast.PtrDecl)): - nstr = '(' + nstr + ')' - nstr += '[' - if modifier.dim_quals: - nstr += ' '.join(modifier.dim_quals) + ' ' - nstr += self.visit(modifier.dim) + ']' - elif isinstance(modifier, c_ast.FuncDecl): - if (i != 0 and - isinstance(modifiers[i - 1], c_ast.PtrDecl)): - nstr = '(' + nstr + ')' - nstr += '(' + self.visit(modifier.args) + ')' - elif isinstance(modifier, c_ast.PtrDecl): - if modifier.quals: - nstr = '* %s%s' % (' '.join(modifier.quals), - ' ' + nstr if nstr else '') - else: - nstr = '*' + nstr - if nstr: s += ' ' + nstr - return s - elif typ == c_ast.Decl: - return self._generate_decl(n.type) - elif typ == c_ast.Typename: - return self._generate_type(n.type, emit_declname = emit_declname) - elif typ == c_ast.IdentifierType: - return ' '.join(n.names) + ' ' - elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl): - return self._generate_type(n.type, modifiers + [n], - emit_declname = emit_declname) - else: - return self.visit(n) - - def _parenthesize_if(self, n, condition): - """ Visits 'n' and returns its string representation, parenthesized - if the condition function applied to the node returns True. - """ - s = self._visit_expr(n) - if condition(n): - return '(' + s + ')' - else: - return s - - def _parenthesize_unless_simple(self, n): - """ Common use case for _parenthesize_if - """ - return self._parenthesize_if(n, lambda d: not self._is_simple_node(d)) - - def _is_simple_node(self, n): - """ Returns True for nodes that are "simple" - i.e. nodes that always - have higher precedence than operators. - """ - return isinstance(n, (c_ast.Constant, c_ast.ID, c_ast.ArrayRef, - c_ast.StructRef, c_ast.FuncCall)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py deleted file mode 100644 index d68d8ebfa..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_lexer.py +++ /dev/null @@ -1,554 +0,0 @@ -#------------------------------------------------------------------------------ -# pycparser: c_lexer.py -# -# CLexer class: lexer for the C language -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#------------------------------------------------------------------------------ -import re - -from .ply import lex -from .ply.lex import TOKEN - - -class CLexer(object): - """ A lexer for the C language. After building it, set the - input text with input(), and call token() to get new - tokens. - - The public attribute filename can be set to an initial - filename, but the lexer will update it upon #line - directives. - """ - def __init__(self, error_func, on_lbrace_func, on_rbrace_func, - type_lookup_func): - """ Create a new Lexer. - - error_func: - An error function. Will be called with an error - message, line and column as arguments, in case of - an error during lexing. - - on_lbrace_func, on_rbrace_func: - Called when an LBRACE or RBRACE is encountered - (likely to push/pop type_lookup_func's scope) - - type_lookup_func: - A type lookup function. Given a string, it must - return True IFF this string is a name of a type - that was defined with a typedef earlier. - """ - self.error_func = error_func - self.on_lbrace_func = on_lbrace_func - self.on_rbrace_func = on_rbrace_func - self.type_lookup_func = type_lookup_func - self.filename = '' - - # Keeps track of the last token returned from self.token() - self.last_token = None - - # Allow either "# line" or "# " to support GCC's - # cpp output - # - self.line_pattern = re.compile(r'([ \t]*line\W)|([ \t]*\d+)') - self.pragma_pattern = re.compile(r'[ \t]*pragma\W') - - def build(self, **kwargs): - """ Builds the lexer from the specification. Must be - called after the lexer object is created. - - This method exists separately, because the PLY - manual warns against calling lex.lex inside - __init__ - """ - self.lexer = lex.lex(object=self, **kwargs) - - def reset_lineno(self): - """ Resets the internal line number counter of the lexer. - """ - self.lexer.lineno = 1 - - def input(self, text): - self.lexer.input(text) - - def token(self): - self.last_token = self.lexer.token() - return self.last_token - - def find_tok_column(self, token): - """ Find the column of the token in its line. - """ - last_cr = self.lexer.lexdata.rfind('\n', 0, token.lexpos) - return token.lexpos - last_cr - - ######################-- PRIVATE --###################### - - ## - ## Internal auxiliary methods - ## - def _error(self, msg, token): - location = self._make_tok_location(token) - self.error_func(msg, location[0], location[1]) - self.lexer.skip(1) - - def _make_tok_location(self, token): - return (token.lineno, self.find_tok_column(token)) - - ## - ## Reserved keywords - ## - keywords = ( - 'AUTO', 'BREAK', 'CASE', 'CHAR', 'CONST', - 'CONTINUE', 'DEFAULT', 'DO', 'DOUBLE', 'ELSE', 'ENUM', 'EXTERN', - 'FLOAT', 'FOR', 'GOTO', 'IF', 'INLINE', 'INT', 'LONG', - 'REGISTER', 'OFFSETOF', - 'RESTRICT', 'RETURN', 'SHORT', 'SIGNED', 'SIZEOF', 'STATIC', 'STRUCT', - 'SWITCH', 'TYPEDEF', 'UNION', 'UNSIGNED', 'VOID', - 'VOLATILE', 'WHILE', '__INT128', - ) - - keywords_new = ( - '_BOOL', '_COMPLEX', - '_NORETURN', '_THREAD_LOCAL', '_STATIC_ASSERT', - '_ATOMIC', '_ALIGNOF', '_ALIGNAS', - ) - - keyword_map = {} - - for keyword in keywords: - keyword_map[keyword.lower()] = keyword - - for keyword in keywords_new: - keyword_map[keyword[:2].upper() + keyword[2:].lower()] = keyword - - ## - ## All the tokens recognized by the lexer - ## - tokens = keywords + keywords_new + ( - # Identifiers - 'ID', - - # Type identifiers (identifiers previously defined as - # types with typedef) - 'TYPEID', - - # constants - 'INT_CONST_DEC', 'INT_CONST_OCT', 'INT_CONST_HEX', 'INT_CONST_BIN', 'INT_CONST_CHAR', - 'FLOAT_CONST', 'HEX_FLOAT_CONST', - 'CHAR_CONST', - 'WCHAR_CONST', - 'U8CHAR_CONST', - 'U16CHAR_CONST', - 'U32CHAR_CONST', - - # String literals - 'STRING_LITERAL', - 'WSTRING_LITERAL', - 'U8STRING_LITERAL', - 'U16STRING_LITERAL', - 'U32STRING_LITERAL', - - # Operators - 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MOD', - 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', - 'LOR', 'LAND', 'LNOT', - 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', - - # Assignment - 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', - 'PLUSEQUAL', 'MINUSEQUAL', - 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', - 'OREQUAL', - - # Increment/decrement - 'PLUSPLUS', 'MINUSMINUS', - - # Structure dereference (->) - 'ARROW', - - # Conditional operator (?) - 'CONDOP', - - # Delimiters - 'LPAREN', 'RPAREN', # ( ) - 'LBRACKET', 'RBRACKET', # [ ] - 'LBRACE', 'RBRACE', # { } - 'COMMA', 'PERIOD', # . , - 'SEMI', 'COLON', # ; : - - # Ellipsis (...) - 'ELLIPSIS', - - # pre-processor - 'PPHASH', # '#' - 'PPPRAGMA', # 'pragma' - 'PPPRAGMASTR', - ) - - ## - ## Regexes for use in tokens - ## - ## - - # valid C identifiers (K&R2: A.2.3), plus '$' (supported by some compilers) - identifier = r'[a-zA-Z_$][0-9a-zA-Z_$]*' - - hex_prefix = '0[xX]' - hex_digits = '[0-9a-fA-F]+' - bin_prefix = '0[bB]' - bin_digits = '[01]+' - - # integer constants (K&R2: A.2.5.1) - integer_suffix_opt = r'(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?' - decimal_constant = '(0'+integer_suffix_opt+')|([1-9][0-9]*'+integer_suffix_opt+')' - octal_constant = '0[0-7]*'+integer_suffix_opt - hex_constant = hex_prefix+hex_digits+integer_suffix_opt - bin_constant = bin_prefix+bin_digits+integer_suffix_opt - - bad_octal_constant = '0[0-7]*[89]' - - # character constants (K&R2: A.2.5.2) - # Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line - # directives with Windows paths as filenames (..\..\dir\file) - # For the same reason, decimal_escape allows all digit sequences. We want to - # parse all correct code, even if it means to sometimes parse incorrect - # code. - # - # The original regexes were taken verbatim from the C syntax definition, - # and were later modified to avoid worst-case exponential running time. - # - # simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])""" - # decimal_escape = r"""(\d+)""" - # hex_escape = r"""(x[0-9a-fA-F]+)""" - # bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])""" - # - # The following modifications were made to avoid the ambiguity that allowed backtracking: - # (https://github.com/eliben/pycparser/issues/61) - # - # - \x was removed from simple_escape, unless it was not followed by a hex digit, to avoid ambiguity with hex_escape. - # - hex_escape allows one or more hex characters, but requires that the next character(if any) is not hex - # - decimal_escape allows one or more decimal characters, but requires that the next character(if any) is not a decimal - # - bad_escape does not allow any decimals (8-9), to avoid conflicting with the permissive decimal_escape. - # - # Without this change, python's `re` module would recursively try parsing each ambiguous escape sequence in multiple ways. - # e.g. `\123` could be parsed as `\1`+`23`, `\12`+`3`, and `\123`. - - simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))""" - decimal_escape = r"""(\d+)(?!\d)""" - hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])""" - bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])""" - - escape_sequence = r"""(\\("""+simple_escape+'|'+decimal_escape+'|'+hex_escape+'))' - - # This complicated regex with lookahead might be slow for strings, so because all of the valid escapes (including \x) allowed - # 0 or more non-escaped characters after the first character, simple_escape+decimal_escape+hex_escape got simplified to - - escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])""" - - cconst_char = r"""([^'\\\n]|"""+escape_sequence+')' - char_const = "'"+cconst_char+"'" - wchar_const = 'L'+char_const - u8char_const = 'u8'+char_const - u16char_const = 'u'+char_const - u32char_const = 'U'+char_const - multicharacter_constant = "'"+cconst_char+"{2,4}'" - unmatched_quote = "('"+cconst_char+"*\\n)|('"+cconst_char+"*$)" - bad_char_const = r"""('"""+cconst_char+"""[^'\n]+')|('')|('"""+bad_escape+r"""[^'\n]*')""" - - # string literals (K&R2: A.2.6) - string_char = r"""([^"\\\n]|"""+escape_sequence_start_in_string+')' - string_literal = '"'+string_char+'*"' - wstring_literal = 'L'+string_literal - u8string_literal = 'u8'+string_literal - u16string_literal = 'u'+string_literal - u32string_literal = 'U'+string_literal - bad_string_literal = '"'+string_char+'*'+bad_escape+string_char+'*"' - - # floating constants (K&R2: A.2.5.3) - exponent_part = r"""([eE][-+]?[0-9]+)""" - fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)""" - floating_constant = '(((('+fractional_constant+')'+exponent_part+'?)|([0-9]+'+exponent_part+'))[FfLl]?)' - binary_exponent_part = r'''([pP][+-]?[0-9]+)''' - hex_fractional_constant = '((('+hex_digits+r""")?\."""+hex_digits+')|('+hex_digits+r"""\.))""" - hex_floating_constant = '('+hex_prefix+'('+hex_digits+'|'+hex_fractional_constant+')'+binary_exponent_part+'[FfLl]?)' - - ## - ## Lexer states: used for preprocessor \n-terminated directives - ## - states = ( - # ppline: preprocessor line directives - # - ('ppline', 'exclusive'), - - # pppragma: pragma - # - ('pppragma', 'exclusive'), - ) - - def t_PPHASH(self, t): - r'[ \t]*\#' - if self.line_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos): - t.lexer.begin('ppline') - self.pp_line = self.pp_filename = None - elif self.pragma_pattern.match(t.lexer.lexdata, pos=t.lexer.lexpos): - t.lexer.begin('pppragma') - else: - t.type = 'PPHASH' - return t - - ## - ## Rules for the ppline state - ## - @TOKEN(string_literal) - def t_ppline_FILENAME(self, t): - if self.pp_line is None: - self._error('filename before line number in #line', t) - else: - self.pp_filename = t.value.lstrip('"').rstrip('"') - - @TOKEN(decimal_constant) - def t_ppline_LINE_NUMBER(self, t): - if self.pp_line is None: - self.pp_line = t.value - else: - # Ignore: GCC's cpp sometimes inserts a numeric flag - # after the file name - pass - - def t_ppline_NEWLINE(self, t): - r'\n' - if self.pp_line is None: - self._error('line number missing in #line', t) - else: - self.lexer.lineno = int(self.pp_line) - - if self.pp_filename is not None: - self.filename = self.pp_filename - - t.lexer.begin('INITIAL') - - def t_ppline_PPLINE(self, t): - r'line' - pass - - t_ppline_ignore = ' \t' - - def t_ppline_error(self, t): - self._error('invalid #line directive', t) - - ## - ## Rules for the pppragma state - ## - def t_pppragma_NEWLINE(self, t): - r'\n' - t.lexer.lineno += 1 - t.lexer.begin('INITIAL') - - def t_pppragma_PPPRAGMA(self, t): - r'pragma' - return t - - t_pppragma_ignore = ' \t' - - def t_pppragma_STR(self, t): - '.+' - t.type = 'PPPRAGMASTR' - return t - - def t_pppragma_error(self, t): - self._error('invalid #pragma directive', t) - - ## - ## Rules for the normal state - ## - t_ignore = ' \t' - - # Newlines - def t_NEWLINE(self, t): - r'\n+' - t.lexer.lineno += t.value.count("\n") - - # Operators - t_PLUS = r'\+' - t_MINUS = r'-' - t_TIMES = r'\*' - t_DIVIDE = r'/' - t_MOD = r'%' - t_OR = r'\|' - t_AND = r'&' - t_NOT = r'~' - t_XOR = r'\^' - t_LSHIFT = r'<<' - t_RSHIFT = r'>>' - t_LOR = r'\|\|' - t_LAND = r'&&' - t_LNOT = r'!' - t_LT = r'<' - t_GT = r'>' - t_LE = r'<=' - t_GE = r'>=' - t_EQ = r'==' - t_NE = r'!=' - - # Assignment operators - t_EQUALS = r'=' - t_TIMESEQUAL = r'\*=' - t_DIVEQUAL = r'/=' - t_MODEQUAL = r'%=' - t_PLUSEQUAL = r'\+=' - t_MINUSEQUAL = r'-=' - t_LSHIFTEQUAL = r'<<=' - t_RSHIFTEQUAL = r'>>=' - t_ANDEQUAL = r'&=' - t_OREQUAL = r'\|=' - t_XOREQUAL = r'\^=' - - # Increment/decrement - t_PLUSPLUS = r'\+\+' - t_MINUSMINUS = r'--' - - # -> - t_ARROW = r'->' - - # ? - t_CONDOP = r'\?' - - # Delimiters - t_LPAREN = r'\(' - t_RPAREN = r'\)' - t_LBRACKET = r'\[' - t_RBRACKET = r'\]' - t_COMMA = r',' - t_PERIOD = r'\.' - t_SEMI = r';' - t_COLON = r':' - t_ELLIPSIS = r'\.\.\.' - - # Scope delimiters - # To see why on_lbrace_func is needed, consider: - # typedef char TT; - # void foo(int TT) { TT = 10; } - # TT x = 5; - # Outside the function, TT is a typedef, but inside (starting and ending - # with the braces) it's a parameter. The trouble begins with yacc's - # lookahead token. If we open a new scope in brace_open, then TT has - # already been read and incorrectly interpreted as TYPEID. So, we need - # to open and close scopes from within the lexer. - # Similar for the TT immediately outside the end of the function. - # - @TOKEN(r'\{') - def t_LBRACE(self, t): - self.on_lbrace_func() - return t - @TOKEN(r'\}') - def t_RBRACE(self, t): - self.on_rbrace_func() - return t - - t_STRING_LITERAL = string_literal - - # The following floating and integer constants are defined as - # functions to impose a strict order (otherwise, decimal - # is placed before the others because its regex is longer, - # and this is bad) - # - @TOKEN(floating_constant) - def t_FLOAT_CONST(self, t): - return t - - @TOKEN(hex_floating_constant) - def t_HEX_FLOAT_CONST(self, t): - return t - - @TOKEN(hex_constant) - def t_INT_CONST_HEX(self, t): - return t - - @TOKEN(bin_constant) - def t_INT_CONST_BIN(self, t): - return t - - @TOKEN(bad_octal_constant) - def t_BAD_CONST_OCT(self, t): - msg = "Invalid octal constant" - self._error(msg, t) - - @TOKEN(octal_constant) - def t_INT_CONST_OCT(self, t): - return t - - @TOKEN(decimal_constant) - def t_INT_CONST_DEC(self, t): - return t - - # Must come before bad_char_const, to prevent it from - # catching valid char constants as invalid - # - @TOKEN(multicharacter_constant) - def t_INT_CONST_CHAR(self, t): - return t - - @TOKEN(char_const) - def t_CHAR_CONST(self, t): - return t - - @TOKEN(wchar_const) - def t_WCHAR_CONST(self, t): - return t - - @TOKEN(u8char_const) - def t_U8CHAR_CONST(self, t): - return t - - @TOKEN(u16char_const) - def t_U16CHAR_CONST(self, t): - return t - - @TOKEN(u32char_const) - def t_U32CHAR_CONST(self, t): - return t - - @TOKEN(unmatched_quote) - def t_UNMATCHED_QUOTE(self, t): - msg = "Unmatched '" - self._error(msg, t) - - @TOKEN(bad_char_const) - def t_BAD_CHAR_CONST(self, t): - msg = "Invalid char constant %s" % t.value - self._error(msg, t) - - @TOKEN(wstring_literal) - def t_WSTRING_LITERAL(self, t): - return t - - @TOKEN(u8string_literal) - def t_U8STRING_LITERAL(self, t): - return t - - @TOKEN(u16string_literal) - def t_U16STRING_LITERAL(self, t): - return t - - @TOKEN(u32string_literal) - def t_U32STRING_LITERAL(self, t): - return t - - # unmatched string literals are caught by the preprocessor - - @TOKEN(bad_string_literal) - def t_BAD_STRING_LITERAL(self, t): - msg = "String contains invalid escape code" - self._error(msg, t) - - @TOKEN(identifier) - def t_ID(self, t): - t.type = self.keyword_map.get(t.value, "ID") - if t.type == 'ID' and self.type_lookup_func(t.value): - t.type = "TYPEID" - return t - - def t_error(self, t): - msg = 'Illegal character %s' % repr(t.value[0]) - self._error(msg, t) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py deleted file mode 100644 index 640a75940..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/c_parser.py +++ /dev/null @@ -1,1936 +0,0 @@ -#------------------------------------------------------------------------------ -# pycparser: c_parser.py -# -# CParser class: Parser and AST builder for the C language -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#------------------------------------------------------------------------------ -from .ply import yacc - -from . import c_ast -from .c_lexer import CLexer -from .plyparser import PLYParser, ParseError, parameterized, template -from .ast_transforms import fix_switch_cases, fix_atomic_specifiers - - -@template -class CParser(PLYParser): - def __init__( - self, - lex_optimize=True, - lexer=CLexer, - lextab='pycparser.lextab', - yacc_optimize=True, - yacctab='pycparser.yacctab', - yacc_debug=False, - taboutputdir=''): - """ Create a new CParser. - - Some arguments for controlling the debug/optimization - level of the parser are provided. The defaults are - tuned for release/performance mode. - The simple rules for using them are: - *) When tweaking CParser/CLexer, set these to False - *) When releasing a stable parser, set to True - - lex_optimize: - Set to False when you're modifying the lexer. - Otherwise, changes in the lexer won't be used, if - some lextab.py file exists. - When releasing with a stable lexer, set to True - to save the re-generation of the lexer table on - each run. - - lexer: - Set this parameter to define the lexer to use if - you're not using the default CLexer. - - lextab: - Points to the lex table that's used for optimized - mode. Only if you're modifying the lexer and want - some tests to avoid re-generating the table, make - this point to a local lex table file (that's been - earlier generated with lex_optimize=True) - - yacc_optimize: - Set to False when you're modifying the parser. - Otherwise, changes in the parser won't be used, if - some parsetab.py file exists. - When releasing with a stable parser, set to True - to save the re-generation of the parser table on - each run. - - yacctab: - Points to the yacc table that's used for optimized - mode. Only if you're modifying the parser, make - this point to a local yacc table file - - yacc_debug: - Generate a parser.out file that explains how yacc - built the parsing table from the grammar. - - taboutputdir: - Set this parameter to control the location of generated - lextab and yacctab files. - """ - self.clex = lexer( - error_func=self._lex_error_func, - on_lbrace_func=self._lex_on_lbrace_func, - on_rbrace_func=self._lex_on_rbrace_func, - type_lookup_func=self._lex_type_lookup_func) - - self.clex.build( - optimize=lex_optimize, - lextab=lextab, - outputdir=taboutputdir) - self.tokens = self.clex.tokens - - rules_with_opt = [ - 'abstract_declarator', - 'assignment_expression', - 'declaration_list', - 'declaration_specifiers_no_type', - 'designation', - 'expression', - 'identifier_list', - 'init_declarator_list', - 'id_init_declarator_list', - 'initializer_list', - 'parameter_type_list', - 'block_item_list', - 'type_qualifier_list', - 'struct_declarator_list' - ] - - for rule in rules_with_opt: - self._create_opt_rule(rule) - - self.cparser = yacc.yacc( - module=self, - start='translation_unit_or_empty', - debug=yacc_debug, - optimize=yacc_optimize, - tabmodule=yacctab, - outputdir=taboutputdir) - - # Stack of scopes for keeping track of symbols. _scope_stack[-1] is - # the current (topmost) scope. Each scope is a dictionary that - # specifies whether a name is a type. If _scope_stack[n][name] is - # True, 'name' is currently a type in the scope. If it's False, - # 'name' is used in the scope but not as a type (for instance, if we - # saw: int name; - # If 'name' is not a key in _scope_stack[n] then 'name' was not defined - # in this scope at all. - self._scope_stack = [dict()] - - # Keeps track of the last token given to yacc (the lookahead token) - self._last_yielded_token = None - - def parse(self, text, filename='', debug=False): - """ Parses C code and returns an AST. - - text: - A string containing the C source code - - filename: - Name of the file being parsed (for meaningful - error messages) - - debug: - Debug flag to YACC - """ - self.clex.filename = filename - self.clex.reset_lineno() - self._scope_stack = [dict()] - self._last_yielded_token = None - return self.cparser.parse( - input=text, - lexer=self.clex, - debug=debug) - - ######################-- PRIVATE --###################### - - def _push_scope(self): - self._scope_stack.append(dict()) - - def _pop_scope(self): - assert len(self._scope_stack) > 1 - self._scope_stack.pop() - - def _add_typedef_name(self, name, coord): - """ Add a new typedef name (ie a TYPEID) to the current scope - """ - if not self._scope_stack[-1].get(name, True): - self._parse_error( - "Typedef %r previously declared as non-typedef " - "in this scope" % name, coord) - self._scope_stack[-1][name] = True - - def _add_identifier(self, name, coord): - """ Add a new object, function, or enum member name (ie an ID) to the - current scope - """ - if self._scope_stack[-1].get(name, False): - self._parse_error( - "Non-typedef %r previously declared as typedef " - "in this scope" % name, coord) - self._scope_stack[-1][name] = False - - def _is_type_in_scope(self, name): - """ Is *name* a typedef-name in the current scope? - """ - for scope in reversed(self._scope_stack): - # If name is an identifier in this scope it shadows typedefs in - # higher scopes. - in_scope = scope.get(name) - if in_scope is not None: return in_scope - return False - - def _lex_error_func(self, msg, line, column): - self._parse_error(msg, self._coord(line, column)) - - def _lex_on_lbrace_func(self): - self._push_scope() - - def _lex_on_rbrace_func(self): - self._pop_scope() - - def _lex_type_lookup_func(self, name): - """ Looks up types that were previously defined with - typedef. - Passed to the lexer for recognizing identifiers that - are types. - """ - is_type = self._is_type_in_scope(name) - return is_type - - def _get_yacc_lookahead_token(self): - """ We need access to yacc's lookahead token in certain cases. - This is the last token yacc requested from the lexer, so we - ask the lexer. - """ - return self.clex.last_token - - # To understand what's going on here, read sections A.8.5 and - # A.8.6 of K&R2 very carefully. - # - # A C type consists of a basic type declaration, with a list - # of modifiers. For example: - # - # int *c[5]; - # - # The basic declaration here is 'int c', and the pointer and - # the array are the modifiers. - # - # Basic declarations are represented by TypeDecl (from module c_ast) and the - # modifiers are FuncDecl, PtrDecl and ArrayDecl. - # - # The standard states that whenever a new modifier is parsed, it should be - # added to the end of the list of modifiers. For example: - # - # K&R2 A.8.6.2: Array Declarators - # - # In a declaration T D where D has the form - # D1 [constant-expression-opt] - # and the type of the identifier in the declaration T D1 is - # "type-modifier T", the type of the - # identifier of D is "type-modifier array of T" - # - # This is what this method does. The declarator it receives - # can be a list of declarators ending with TypeDecl. It - # tacks the modifier to the end of this list, just before - # the TypeDecl. - # - # Additionally, the modifier may be a list itself. This is - # useful for pointers, that can come as a chain from the rule - # p_pointer. In this case, the whole modifier list is spliced - # into the new location. - def _type_modify_decl(self, decl, modifier): - """ Tacks a type modifier on a declarator, and returns - the modified declarator. - - Note: the declarator and modifier may be modified - """ - #~ print '****' - #~ decl.show(offset=3) - #~ modifier.show(offset=3) - #~ print '****' - - modifier_head = modifier - modifier_tail = modifier - - # The modifier may be a nested list. Reach its tail. - while modifier_tail.type: - modifier_tail = modifier_tail.type - - # If the decl is a basic type, just tack the modifier onto it. - if isinstance(decl, c_ast.TypeDecl): - modifier_tail.type = decl - return modifier - else: - # Otherwise, the decl is a list of modifiers. Reach - # its tail and splice the modifier onto the tail, - # pointing to the underlying basic type. - decl_tail = decl - - while not isinstance(decl_tail.type, c_ast.TypeDecl): - decl_tail = decl_tail.type - - modifier_tail.type = decl_tail.type - decl_tail.type = modifier_head - return decl - - # Due to the order in which declarators are constructed, - # they have to be fixed in order to look like a normal AST. - # - # When a declaration arrives from syntax construction, it has - # these problems: - # * The innermost TypeDecl has no type (because the basic - # type is only known at the uppermost declaration level) - # * The declaration has no variable name, since that is saved - # in the innermost TypeDecl - # * The typename of the declaration is a list of type - # specifiers, and not a node. Here, basic identifier types - # should be separated from more complex types like enums - # and structs. - # - # This method fixes these problems. - def _fix_decl_name_type(self, decl, typename): - """ Fixes a declaration. Modifies decl. - """ - # Reach the underlying basic type - # - type = decl - while not isinstance(type, c_ast.TypeDecl): - type = type.type - - decl.name = type.declname - type.quals = decl.quals[:] - - # The typename is a list of types. If any type in this - # list isn't an IdentifierType, it must be the only - # type in the list (it's illegal to declare "int enum ..") - # If all the types are basic, they're collected in the - # IdentifierType holder. - for tn in typename: - if not isinstance(tn, c_ast.IdentifierType): - if len(typename) > 1: - self._parse_error( - "Invalid multiple types specified", tn.coord) - else: - type.type = tn - return decl - - if not typename: - # Functions default to returning int - # - if not isinstance(decl.type, c_ast.FuncDecl): - self._parse_error( - "Missing type in declaration", decl.coord) - type.type = c_ast.IdentifierType( - ['int'], - coord=decl.coord) - else: - # At this point, we know that typename is a list of IdentifierType - # nodes. Concatenate all the names into a single list. - # - type.type = c_ast.IdentifierType( - [name for id in typename for name in id.names], - coord=typename[0].coord) - return decl - - def _add_declaration_specifier(self, declspec, newspec, kind, append=False): - """ Declaration specifiers are represented by a dictionary - with the entries: - * qual: a list of type qualifiers - * storage: a list of storage type qualifiers - * type: a list of type specifiers - * function: a list of function specifiers - * alignment: a list of alignment specifiers - - This method is given a declaration specifier, and a - new specifier of a given kind. - If `append` is True, the new specifier is added to the end of - the specifiers list, otherwise it's added at the beginning. - Returns the declaration specifier, with the new - specifier incorporated. - """ - spec = declspec or dict(qual=[], storage=[], type=[], function=[], alignment=[]) - - if append: - spec[kind].append(newspec) - else: - spec[kind].insert(0, newspec) - - return spec - - def _build_declarations(self, spec, decls, typedef_namespace=False): - """ Builds a list of declarations all sharing the given specifiers. - If typedef_namespace is true, each declared name is added - to the "typedef namespace", which also includes objects, - functions, and enum constants. - """ - is_typedef = 'typedef' in spec['storage'] - declarations = [] - - # Bit-fields are allowed to be unnamed. - if decls[0].get('bitsize') is not None: - pass - - # When redeclaring typedef names as identifiers in inner scopes, a - # problem can occur where the identifier gets grouped into - # spec['type'], leaving decl as None. This can only occur for the - # first declarator. - elif decls[0]['decl'] is None: - if len(spec['type']) < 2 or len(spec['type'][-1].names) != 1 or \ - not self._is_type_in_scope(spec['type'][-1].names[0]): - coord = '?' - for t in spec['type']: - if hasattr(t, 'coord'): - coord = t.coord - break - self._parse_error('Invalid declaration', coord) - - # Make this look as if it came from "direct_declarator:ID" - decls[0]['decl'] = c_ast.TypeDecl( - declname=spec['type'][-1].names[0], - type=None, - quals=None, - align=spec['alignment'], - coord=spec['type'][-1].coord) - # Remove the "new" type's name from the end of spec['type'] - del spec['type'][-1] - - # A similar problem can occur where the declaration ends up looking - # like an abstract declarator. Give it a name if this is the case. - elif not isinstance(decls[0]['decl'], ( - c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType)): - decls_0_tail = decls[0]['decl'] - while not isinstance(decls_0_tail, c_ast.TypeDecl): - decls_0_tail = decls_0_tail.type - if decls_0_tail.declname is None: - decls_0_tail.declname = spec['type'][-1].names[0] - del spec['type'][-1] - - for decl in decls: - assert decl['decl'] is not None - if is_typedef: - declaration = c_ast.Typedef( - name=None, - quals=spec['qual'], - storage=spec['storage'], - type=decl['decl'], - coord=decl['decl'].coord) - else: - declaration = c_ast.Decl( - name=None, - quals=spec['qual'], - align=spec['alignment'], - storage=spec['storage'], - funcspec=spec['function'], - type=decl['decl'], - init=decl.get('init'), - bitsize=decl.get('bitsize'), - coord=decl['decl'].coord) - - if isinstance(declaration.type, ( - c_ast.Enum, c_ast.Struct, c_ast.Union, - c_ast.IdentifierType)): - fixed_decl = declaration - else: - fixed_decl = self._fix_decl_name_type(declaration, spec['type']) - - # Add the type name defined by typedef to a - # symbol table (for usage in the lexer) - if typedef_namespace: - if is_typedef: - self._add_typedef_name(fixed_decl.name, fixed_decl.coord) - else: - self._add_identifier(fixed_decl.name, fixed_decl.coord) - - fixed_decl = fix_atomic_specifiers(fixed_decl) - declarations.append(fixed_decl) - - return declarations - - def _build_function_definition(self, spec, decl, param_decls, body): - """ Builds a function definition. - """ - if 'typedef' in spec['storage']: - self._parse_error("Invalid typedef", decl.coord) - - declaration = self._build_declarations( - spec=spec, - decls=[dict(decl=decl, init=None)], - typedef_namespace=True)[0] - - return c_ast.FuncDef( - decl=declaration, - param_decls=param_decls, - body=body, - coord=decl.coord) - - def _select_struct_union_class(self, token): - """ Given a token (either STRUCT or UNION), selects the - appropriate AST class. - """ - if token == 'struct': - return c_ast.Struct - else: - return c_ast.Union - - ## - ## Precedence and associativity of operators - ## - # If this changes, c_generator.CGenerator.precedence_map needs to change as - # well - precedence = ( - ('left', 'LOR'), - ('left', 'LAND'), - ('left', 'OR'), - ('left', 'XOR'), - ('left', 'AND'), - ('left', 'EQ', 'NE'), - ('left', 'GT', 'GE', 'LT', 'LE'), - ('left', 'RSHIFT', 'LSHIFT'), - ('left', 'PLUS', 'MINUS'), - ('left', 'TIMES', 'DIVIDE', 'MOD') - ) - - ## - ## Grammar productions - ## Implementation of the BNF defined in K&R2 A.13 - ## - - # Wrapper around a translation unit, to allow for empty input. - # Not strictly part of the C99 Grammar, but useful in practice. - def p_translation_unit_or_empty(self, p): - """ translation_unit_or_empty : translation_unit - | empty - """ - if p[1] is None: - p[0] = c_ast.FileAST([]) - else: - p[0] = c_ast.FileAST(p[1]) - - def p_translation_unit_1(self, p): - """ translation_unit : external_declaration - """ - # Note: external_declaration is already a list - p[0] = p[1] - - def p_translation_unit_2(self, p): - """ translation_unit : translation_unit external_declaration - """ - p[1].extend(p[2]) - p[0] = p[1] - - # Declarations always come as lists (because they can be - # several in one line), so we wrap the function definition - # into a list as well, to make the return value of - # external_declaration homogeneous. - def p_external_declaration_1(self, p): - """ external_declaration : function_definition - """ - p[0] = [p[1]] - - def p_external_declaration_2(self, p): - """ external_declaration : declaration - """ - p[0] = p[1] - - def p_external_declaration_3(self, p): - """ external_declaration : pp_directive - | pppragma_directive - """ - p[0] = [p[1]] - - def p_external_declaration_4(self, p): - """ external_declaration : SEMI - """ - p[0] = [] - - def p_external_declaration_5(self, p): - """ external_declaration : static_assert - """ - p[0] = p[1] - - def p_static_assert_declaration(self, p): - """ static_assert : _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN - | _STATIC_ASSERT LPAREN constant_expression RPAREN - """ - if len(p) == 5: - p[0] = [c_ast.StaticAssert(p[3], None, self._token_coord(p, 1))] - else: - p[0] = [c_ast.StaticAssert(p[3], p[5], self._token_coord(p, 1))] - - def p_pp_directive(self, p): - """ pp_directive : PPHASH - """ - self._parse_error('Directives not supported yet', - self._token_coord(p, 1)) - - def p_pppragma_directive(self, p): - """ pppragma_directive : PPPRAGMA - | PPPRAGMA PPPRAGMASTR - """ - if len(p) == 3: - p[0] = c_ast.Pragma(p[2], self._token_coord(p, 2)) - else: - p[0] = c_ast.Pragma("", self._token_coord(p, 1)) - - # In function definitions, the declarator can be followed by - # a declaration list, for old "K&R style" function definitios. - def p_function_definition_1(self, p): - """ function_definition : id_declarator declaration_list_opt compound_statement - """ - # no declaration specifiers - 'int' becomes the default type - spec = dict( - qual=[], - alignment=[], - storage=[], - type=[c_ast.IdentifierType(['int'], - coord=self._token_coord(p, 1))], - function=[]) - - p[0] = self._build_function_definition( - spec=spec, - decl=p[1], - param_decls=p[2], - body=p[3]) - - def p_function_definition_2(self, p): - """ function_definition : declaration_specifiers id_declarator declaration_list_opt compound_statement - """ - spec = p[1] - - p[0] = self._build_function_definition( - spec=spec, - decl=p[2], - param_decls=p[3], - body=p[4]) - - # Note, according to C18 A.2.2 6.7.10 static_assert-declaration _Static_assert - # is a declaration, not a statement. We additionally recognise it as a statement - # to fix parsing of _Static_assert inside the functions. - # - def p_statement(self, p): - """ statement : labeled_statement - | expression_statement - | compound_statement - | selection_statement - | iteration_statement - | jump_statement - | pppragma_directive - | static_assert - """ - p[0] = p[1] - - # A pragma is generally considered a decorator rather than an actual - # statement. Still, for the purposes of analyzing an abstract syntax tree of - # C code, pragma's should not be ignored and were previously treated as a - # statement. This presents a problem for constructs that take a statement - # such as labeled_statements, selection_statements, and - # iteration_statements, causing a misleading structure in the AST. For - # example, consider the following C code. - # - # for (int i = 0; i < 3; i++) - # #pragma omp critical - # sum += 1; - # - # This code will compile and execute "sum += 1;" as the body of the for - # loop. Previous implementations of PyCParser would render the AST for this - # block of code as follows: - # - # For: - # DeclList: - # Decl: i, [], [], [] - # TypeDecl: i, [] - # IdentifierType: ['int'] - # Constant: int, 0 - # BinaryOp: < - # ID: i - # Constant: int, 3 - # UnaryOp: p++ - # ID: i - # Pragma: omp critical - # Assignment: += - # ID: sum - # Constant: int, 1 - # - # This AST misleadingly takes the Pragma as the body of the loop and the - # assignment then becomes a sibling of the loop. - # - # To solve edge cases like these, the pragmacomp_or_statement rule groups - # a pragma and its following statement (which would otherwise be orphaned) - # using a compound block, effectively turning the above code into: - # - # for (int i = 0; i < 3; i++) { - # #pragma omp critical - # sum += 1; - # } - def p_pragmacomp_or_statement(self, p): - """ pragmacomp_or_statement : pppragma_directive statement - | statement - """ - if isinstance(p[1], c_ast.Pragma) and len(p) == 3: - p[0] = c_ast.Compound( - block_items=[p[1], p[2]], - coord=self._token_coord(p, 1)) - else: - p[0] = p[1] - - # In C, declarations can come several in a line: - # int x, *px, romulo = 5; - # - # However, for the AST, we will split them to separate Decl - # nodes. - # - # This rule splits its declarations and always returns a list - # of Decl nodes, even if it's one element long. - # - def p_decl_body(self, p): - """ decl_body : declaration_specifiers init_declarator_list_opt - | declaration_specifiers_no_type id_init_declarator_list_opt - """ - spec = p[1] - - # p[2] (init_declarator_list_opt) is either a list or None - # - if p[2] is None: - # By the standard, you must have at least one declarator unless - # declaring a structure tag, a union tag, or the members of an - # enumeration. - # - ty = spec['type'] - s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum) - if len(ty) == 1 and isinstance(ty[0], s_u_or_e): - decls = [c_ast.Decl( - name=None, - quals=spec['qual'], - align=spec['alignment'], - storage=spec['storage'], - funcspec=spec['function'], - type=ty[0], - init=None, - bitsize=None, - coord=ty[0].coord)] - - # However, this case can also occur on redeclared identifiers in - # an inner scope. The trouble is that the redeclared type's name - # gets grouped into declaration_specifiers; _build_declarations - # compensates for this. - # - else: - decls = self._build_declarations( - spec=spec, - decls=[dict(decl=None, init=None)], - typedef_namespace=True) - - else: - decls = self._build_declarations( - spec=spec, - decls=p[2], - typedef_namespace=True) - - p[0] = decls - - # The declaration has been split to a decl_body sub-rule and - # SEMI, because having them in a single rule created a problem - # for defining typedefs. - # - # If a typedef line was directly followed by a line using the - # type defined with the typedef, the type would not be - # recognized. This is because to reduce the declaration rule, - # the parser's lookahead asked for the token after SEMI, which - # was the type from the next line, and the lexer had no chance - # to see the updated type symbol table. - # - # Splitting solves this problem, because after seeing SEMI, - # the parser reduces decl_body, which actually adds the new - # type into the table to be seen by the lexer before the next - # line is reached. - def p_declaration(self, p): - """ declaration : decl_body SEMI - """ - p[0] = p[1] - - # Since each declaration is a list of declarations, this - # rule will combine all the declarations and return a single - # list - # - def p_declaration_list(self, p): - """ declaration_list : declaration - | declaration_list declaration - """ - p[0] = p[1] if len(p) == 2 else p[1] + p[2] - - # To know when declaration-specifiers end and declarators begin, - # we require declaration-specifiers to have at least one - # type-specifier, and disallow typedef-names after we've seen any - # type-specifier. These are both required by the spec. - # - def p_declaration_specifiers_no_type_1(self, p): - """ declaration_specifiers_no_type : type_qualifier declaration_specifiers_no_type_opt - """ - p[0] = self._add_declaration_specifier(p[2], p[1], 'qual') - - def p_declaration_specifiers_no_type_2(self, p): - """ declaration_specifiers_no_type : storage_class_specifier declaration_specifiers_no_type_opt - """ - p[0] = self._add_declaration_specifier(p[2], p[1], 'storage') - - def p_declaration_specifiers_no_type_3(self, p): - """ declaration_specifiers_no_type : function_specifier declaration_specifiers_no_type_opt - """ - p[0] = self._add_declaration_specifier(p[2], p[1], 'function') - - # Without this, `typedef _Atomic(T) U` will parse incorrectly because the - # _Atomic qualifier will match, instead of the specifier. - def p_declaration_specifiers_no_type_4(self, p): - """ declaration_specifiers_no_type : atomic_specifier declaration_specifiers_no_type_opt - """ - p[0] = self._add_declaration_specifier(p[2], p[1], 'type') - - def p_declaration_specifiers_no_type_5(self, p): - """ declaration_specifiers_no_type : alignment_specifier declaration_specifiers_no_type_opt - """ - p[0] = self._add_declaration_specifier(p[2], p[1], 'alignment') - - def p_declaration_specifiers_1(self, p): - """ declaration_specifiers : declaration_specifiers type_qualifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'qual', append=True) - - def p_declaration_specifiers_2(self, p): - """ declaration_specifiers : declaration_specifiers storage_class_specifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'storage', append=True) - - def p_declaration_specifiers_3(self, p): - """ declaration_specifiers : declaration_specifiers function_specifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'function', append=True) - - def p_declaration_specifiers_4(self, p): - """ declaration_specifiers : declaration_specifiers type_specifier_no_typeid - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True) - - def p_declaration_specifiers_5(self, p): - """ declaration_specifiers : type_specifier - """ - p[0] = self._add_declaration_specifier(None, p[1], 'type') - - def p_declaration_specifiers_6(self, p): - """ declaration_specifiers : declaration_specifiers_no_type type_specifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True) - - def p_declaration_specifiers_7(self, p): - """ declaration_specifiers : declaration_specifiers alignment_specifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'alignment', append=True) - - def p_storage_class_specifier(self, p): - """ storage_class_specifier : AUTO - | REGISTER - | STATIC - | EXTERN - | TYPEDEF - | _THREAD_LOCAL - """ - p[0] = p[1] - - def p_function_specifier(self, p): - """ function_specifier : INLINE - | _NORETURN - """ - p[0] = p[1] - - def p_type_specifier_no_typeid(self, p): - """ type_specifier_no_typeid : VOID - | _BOOL - | CHAR - | SHORT - | INT - | LONG - | FLOAT - | DOUBLE - | _COMPLEX - | SIGNED - | UNSIGNED - | __INT128 - """ - p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1)) - - def p_type_specifier(self, p): - """ type_specifier : typedef_name - | enum_specifier - | struct_or_union_specifier - | type_specifier_no_typeid - | atomic_specifier - """ - p[0] = p[1] - - # See section 6.7.2.4 of the C11 standard. - def p_atomic_specifier(self, p): - """ atomic_specifier : _ATOMIC LPAREN type_name RPAREN - """ - typ = p[3] - typ.quals.append('_Atomic') - p[0] = typ - - def p_type_qualifier(self, p): - """ type_qualifier : CONST - | RESTRICT - | VOLATILE - | _ATOMIC - """ - p[0] = p[1] - - def p_init_declarator_list(self, p): - """ init_declarator_list : init_declarator - | init_declarator_list COMMA init_declarator - """ - p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] - - # Returns a {decl= : init=} dictionary - # If there's no initializer, uses None - # - def p_init_declarator(self, p): - """ init_declarator : declarator - | declarator EQUALS initializer - """ - p[0] = dict(decl=p[1], init=(p[3] if len(p) > 2 else None)) - - def p_id_init_declarator_list(self, p): - """ id_init_declarator_list : id_init_declarator - | id_init_declarator_list COMMA init_declarator - """ - p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] - - def p_id_init_declarator(self, p): - """ id_init_declarator : id_declarator - | id_declarator EQUALS initializer - """ - p[0] = dict(decl=p[1], init=(p[3] if len(p) > 2 else None)) - - # Require at least one type specifier in a specifier-qualifier-list - # - def p_specifier_qualifier_list_1(self, p): - """ specifier_qualifier_list : specifier_qualifier_list type_specifier_no_typeid - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'type', append=True) - - def p_specifier_qualifier_list_2(self, p): - """ specifier_qualifier_list : specifier_qualifier_list type_qualifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'qual', append=True) - - def p_specifier_qualifier_list_3(self, p): - """ specifier_qualifier_list : type_specifier - """ - p[0] = self._add_declaration_specifier(None, p[1], 'type') - - def p_specifier_qualifier_list_4(self, p): - """ specifier_qualifier_list : type_qualifier_list type_specifier - """ - p[0] = dict(qual=p[1], alignment=[], storage=[], type=[p[2]], function=[]) - - def p_specifier_qualifier_list_5(self, p): - """ specifier_qualifier_list : alignment_specifier - """ - p[0] = dict(qual=[], alignment=[p[1]], storage=[], type=[], function=[]) - - def p_specifier_qualifier_list_6(self, p): - """ specifier_qualifier_list : specifier_qualifier_list alignment_specifier - """ - p[0] = self._add_declaration_specifier(p[1], p[2], 'alignment') - - # TYPEID is allowed here (and in other struct/enum related tag names), because - # struct/enum tags reside in their own namespace and can be named the same as types - # - def p_struct_or_union_specifier_1(self, p): - """ struct_or_union_specifier : struct_or_union ID - | struct_or_union TYPEID - """ - klass = self._select_struct_union_class(p[1]) - # None means no list of members - p[0] = klass( - name=p[2], - decls=None, - coord=self._token_coord(p, 2)) - - def p_struct_or_union_specifier_2(self, p): - """ struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close - | struct_or_union brace_open brace_close - """ - klass = self._select_struct_union_class(p[1]) - if len(p) == 4: - # Empty sequence means an empty list of members - p[0] = klass( - name=None, - decls=[], - coord=self._token_coord(p, 2)) - else: - p[0] = klass( - name=None, - decls=p[3], - coord=self._token_coord(p, 2)) - - - def p_struct_or_union_specifier_3(self, p): - """ struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close - | struct_or_union ID brace_open brace_close - | struct_or_union TYPEID brace_open struct_declaration_list brace_close - | struct_or_union TYPEID brace_open brace_close - """ - klass = self._select_struct_union_class(p[1]) - if len(p) == 5: - # Empty sequence means an empty list of members - p[0] = klass( - name=p[2], - decls=[], - coord=self._token_coord(p, 2)) - else: - p[0] = klass( - name=p[2], - decls=p[4], - coord=self._token_coord(p, 2)) - - def p_struct_or_union(self, p): - """ struct_or_union : STRUCT - | UNION - """ - p[0] = p[1] - - # Combine all declarations into a single list - # - def p_struct_declaration_list(self, p): - """ struct_declaration_list : struct_declaration - | struct_declaration_list struct_declaration - """ - if len(p) == 2: - p[0] = p[1] or [] - else: - p[0] = p[1] + (p[2] or []) - - def p_struct_declaration_1(self, p): - """ struct_declaration : specifier_qualifier_list struct_declarator_list_opt SEMI - """ - spec = p[1] - assert 'typedef' not in spec['storage'] - - if p[2] is not None: - decls = self._build_declarations( - spec=spec, - decls=p[2]) - - elif len(spec['type']) == 1: - # Anonymous struct/union, gcc extension, C1x feature. - # Although the standard only allows structs/unions here, I see no - # reason to disallow other types since some compilers have typedefs - # here, and pycparser isn't about rejecting all invalid code. - # - node = spec['type'][0] - if isinstance(node, c_ast.Node): - decl_type = node - else: - decl_type = c_ast.IdentifierType(node) - - decls = self._build_declarations( - spec=spec, - decls=[dict(decl=decl_type)]) - - else: - # Structure/union members can have the same names as typedefs. - # The trouble is that the member's name gets grouped into - # specifier_qualifier_list; _build_declarations compensates. - # - decls = self._build_declarations( - spec=spec, - decls=[dict(decl=None, init=None)]) - - p[0] = decls - - def p_struct_declaration_2(self, p): - """ struct_declaration : SEMI - """ - p[0] = None - - def p_struct_declaration_3(self, p): - """ struct_declaration : pppragma_directive - """ - p[0] = [p[1]] - - def p_struct_declarator_list(self, p): - """ struct_declarator_list : struct_declarator - | struct_declarator_list COMMA struct_declarator - """ - p[0] = p[1] + [p[3]] if len(p) == 4 else [p[1]] - - # struct_declarator passes up a dict with the keys: decl (for - # the underlying declarator) and bitsize (for the bitsize) - # - def p_struct_declarator_1(self, p): - """ struct_declarator : declarator - """ - p[0] = {'decl': p[1], 'bitsize': None} - - def p_struct_declarator_2(self, p): - """ struct_declarator : declarator COLON constant_expression - | COLON constant_expression - """ - if len(p) > 3: - p[0] = {'decl': p[1], 'bitsize': p[3]} - else: - p[0] = {'decl': c_ast.TypeDecl(None, None, None, None), 'bitsize': p[2]} - - def p_enum_specifier_1(self, p): - """ enum_specifier : ENUM ID - | ENUM TYPEID - """ - p[0] = c_ast.Enum(p[2], None, self._token_coord(p, 1)) - - def p_enum_specifier_2(self, p): - """ enum_specifier : ENUM brace_open enumerator_list brace_close - """ - p[0] = c_ast.Enum(None, p[3], self._token_coord(p, 1)) - - def p_enum_specifier_3(self, p): - """ enum_specifier : ENUM ID brace_open enumerator_list brace_close - | ENUM TYPEID brace_open enumerator_list brace_close - """ - p[0] = c_ast.Enum(p[2], p[4], self._token_coord(p, 1)) - - def p_enumerator_list(self, p): - """ enumerator_list : enumerator - | enumerator_list COMMA - | enumerator_list COMMA enumerator - """ - if len(p) == 2: - p[0] = c_ast.EnumeratorList([p[1]], p[1].coord) - elif len(p) == 3: - p[0] = p[1] - else: - p[1].enumerators.append(p[3]) - p[0] = p[1] - - def p_alignment_specifier(self, p): - """ alignment_specifier : _ALIGNAS LPAREN type_name RPAREN - | _ALIGNAS LPAREN constant_expression RPAREN - """ - p[0] = c_ast.Alignas(p[3], self._token_coord(p, 1)) - - def p_enumerator(self, p): - """ enumerator : ID - | ID EQUALS constant_expression - """ - if len(p) == 2: - enumerator = c_ast.Enumerator( - p[1], None, - self._token_coord(p, 1)) - else: - enumerator = c_ast.Enumerator( - p[1], p[3], - self._token_coord(p, 1)) - self._add_identifier(enumerator.name, enumerator.coord) - - p[0] = enumerator - - def p_declarator(self, p): - """ declarator : id_declarator - | typeid_declarator - """ - p[0] = p[1] - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_xxx_declarator_1(self, p): - """ xxx_declarator : direct_xxx_declarator - """ - p[0] = p[1] - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_xxx_declarator_2(self, p): - """ xxx_declarator : pointer direct_xxx_declarator - """ - p[0] = self._type_modify_decl(p[2], p[1]) - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_direct_xxx_declarator_1(self, p): - """ direct_xxx_declarator : yyy - """ - p[0] = c_ast.TypeDecl( - declname=p[1], - type=None, - quals=None, - align=None, - coord=self._token_coord(p, 1)) - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID')) - def p_direct_xxx_declarator_2(self, p): - """ direct_xxx_declarator : LPAREN xxx_declarator RPAREN - """ - p[0] = p[2] - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_direct_xxx_declarator_3(self, p): - """ direct_xxx_declarator : direct_xxx_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET - """ - quals = (p[3] if len(p) > 5 else []) or [] - # Accept dimension qualifiers - # Per C99 6.7.5.3 p7 - arr = c_ast.ArrayDecl( - type=None, - dim=p[4] if len(p) > 5 else p[3], - dim_quals=quals, - coord=p[1].coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=arr) - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_direct_xxx_declarator_4(self, p): - """ direct_xxx_declarator : direct_xxx_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET - | direct_xxx_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET - """ - # Using slice notation for PLY objects doesn't work in Python 3 for the - # version of PLY embedded with pycparser; see PLY Google Code issue 30. - # Work around that here by listing the two elements separately. - listed_quals = [item if isinstance(item, list) else [item] - for item in [p[3],p[4]]] - dim_quals = [qual for sublist in listed_quals for qual in sublist - if qual is not None] - arr = c_ast.ArrayDecl( - type=None, - dim=p[5], - dim_quals=dim_quals, - coord=p[1].coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=arr) - - # Special for VLAs - # - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_direct_xxx_declarator_5(self, p): - """ direct_xxx_declarator : direct_xxx_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET - """ - arr = c_ast.ArrayDecl( - type=None, - dim=c_ast.ID(p[4], self._token_coord(p, 4)), - dim_quals=p[3] if p[3] is not None else [], - coord=p[1].coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=arr) - - @parameterized(('id', 'ID'), ('typeid', 'TYPEID'), ('typeid_noparen', 'TYPEID')) - def p_direct_xxx_declarator_6(self, p): - """ direct_xxx_declarator : direct_xxx_declarator LPAREN parameter_type_list RPAREN - | direct_xxx_declarator LPAREN identifier_list_opt RPAREN - """ - func = c_ast.FuncDecl( - args=p[3], - type=None, - coord=p[1].coord) - - # To see why _get_yacc_lookahead_token is needed, consider: - # typedef char TT; - # void foo(int TT) { TT = 10; } - # Outside the function, TT is a typedef, but inside (starting and - # ending with the braces) it's a parameter. The trouble begins with - # yacc's lookahead token. We don't know if we're declaring or - # defining a function until we see LBRACE, but if we wait for yacc to - # trigger a rule on that token, then TT will have already been read - # and incorrectly interpreted as TYPEID. We need to add the - # parameters to the scope the moment the lexer sees LBRACE. - # - if self._get_yacc_lookahead_token().type == "LBRACE": - if func.args is not None: - for param in func.args.params: - if isinstance(param, c_ast.EllipsisParam): break - self._add_identifier(param.name, param.coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=func) - - def p_pointer(self, p): - """ pointer : TIMES type_qualifier_list_opt - | TIMES type_qualifier_list_opt pointer - """ - coord = self._token_coord(p, 1) - # Pointer decls nest from inside out. This is important when different - # levels have different qualifiers. For example: - # - # char * const * p; - # - # Means "pointer to const pointer to char" - # - # While: - # - # char ** const p; - # - # Means "const pointer to pointer to char" - # - # So when we construct PtrDecl nestings, the leftmost pointer goes in - # as the most nested type. - nested_type = c_ast.PtrDecl(quals=p[2] or [], type=None, coord=coord) - if len(p) > 3: - tail_type = p[3] - while tail_type.type is not None: - tail_type = tail_type.type - tail_type.type = nested_type - p[0] = p[3] - else: - p[0] = nested_type - - def p_type_qualifier_list(self, p): - """ type_qualifier_list : type_qualifier - | type_qualifier_list type_qualifier - """ - p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]] - - def p_parameter_type_list(self, p): - """ parameter_type_list : parameter_list - | parameter_list COMMA ELLIPSIS - """ - if len(p) > 2: - p[1].params.append(c_ast.EllipsisParam(self._token_coord(p, 3))) - - p[0] = p[1] - - def p_parameter_list(self, p): - """ parameter_list : parameter_declaration - | parameter_list COMMA parameter_declaration - """ - if len(p) == 2: # single parameter - p[0] = c_ast.ParamList([p[1]], p[1].coord) - else: - p[1].params.append(p[3]) - p[0] = p[1] - - # From ISO/IEC 9899:TC2, 6.7.5.3.11: - # "If, in a parameter declaration, an identifier can be treated either - # as a typedef name or as a parameter name, it shall be taken as a - # typedef name." - # - # Inside a parameter declaration, once we've reduced declaration specifiers, - # if we shift in an LPAREN and see a TYPEID, it could be either an abstract - # declarator or a declarator nested inside parens. This rule tells us to - # always treat it as an abstract declarator. Therefore, we only accept - # `id_declarator`s and `typeid_noparen_declarator`s. - def p_parameter_declaration_1(self, p): - """ parameter_declaration : declaration_specifiers id_declarator - | declaration_specifiers typeid_noparen_declarator - """ - spec = p[1] - if not spec['type']: - spec['type'] = [c_ast.IdentifierType(['int'], - coord=self._token_coord(p, 1))] - p[0] = self._build_declarations( - spec=spec, - decls=[dict(decl=p[2])])[0] - - def p_parameter_declaration_2(self, p): - """ parameter_declaration : declaration_specifiers abstract_declarator_opt - """ - spec = p[1] - if not spec['type']: - spec['type'] = [c_ast.IdentifierType(['int'], - coord=self._token_coord(p, 1))] - - # Parameters can have the same names as typedefs. The trouble is that - # the parameter's name gets grouped into declaration_specifiers, making - # it look like an old-style declaration; compensate. - # - if len(spec['type']) > 1 and len(spec['type'][-1].names) == 1 and \ - self._is_type_in_scope(spec['type'][-1].names[0]): - decl = self._build_declarations( - spec=spec, - decls=[dict(decl=p[2], init=None)])[0] - - # This truly is an old-style parameter declaration - # - else: - decl = c_ast.Typename( - name='', - quals=spec['qual'], - align=None, - type=p[2] or c_ast.TypeDecl(None, None, None, None), - coord=self._token_coord(p, 2)) - typename = spec['type'] - decl = self._fix_decl_name_type(decl, typename) - - p[0] = decl - - def p_identifier_list(self, p): - """ identifier_list : identifier - | identifier_list COMMA identifier - """ - if len(p) == 2: # single parameter - p[0] = c_ast.ParamList([p[1]], p[1].coord) - else: - p[1].params.append(p[3]) - p[0] = p[1] - - def p_initializer_1(self, p): - """ initializer : assignment_expression - """ - p[0] = p[1] - - def p_initializer_2(self, p): - """ initializer : brace_open initializer_list_opt brace_close - | brace_open initializer_list COMMA brace_close - """ - if p[2] is None: - p[0] = c_ast.InitList([], self._token_coord(p, 1)) - else: - p[0] = p[2] - - def p_initializer_list(self, p): - """ initializer_list : designation_opt initializer - | initializer_list COMMA designation_opt initializer - """ - if len(p) == 3: # single initializer - init = p[2] if p[1] is None else c_ast.NamedInitializer(p[1], p[2]) - p[0] = c_ast.InitList([init], p[2].coord) - else: - init = p[4] if p[3] is None else c_ast.NamedInitializer(p[3], p[4]) - p[1].exprs.append(init) - p[0] = p[1] - - def p_designation(self, p): - """ designation : designator_list EQUALS - """ - p[0] = p[1] - - # Designators are represented as a list of nodes, in the order in which - # they're written in the code. - # - def p_designator_list(self, p): - """ designator_list : designator - | designator_list designator - """ - p[0] = [p[1]] if len(p) == 2 else p[1] + [p[2]] - - def p_designator(self, p): - """ designator : LBRACKET constant_expression RBRACKET - | PERIOD identifier - """ - p[0] = p[2] - - def p_type_name(self, p): - """ type_name : specifier_qualifier_list abstract_declarator_opt - """ - typename = c_ast.Typename( - name='', - quals=p[1]['qual'][:], - align=None, - type=p[2] or c_ast.TypeDecl(None, None, None, None), - coord=self._token_coord(p, 2)) - - p[0] = self._fix_decl_name_type(typename, p[1]['type']) - - def p_abstract_declarator_1(self, p): - """ abstract_declarator : pointer - """ - dummytype = c_ast.TypeDecl(None, None, None, None) - p[0] = self._type_modify_decl( - decl=dummytype, - modifier=p[1]) - - def p_abstract_declarator_2(self, p): - """ abstract_declarator : pointer direct_abstract_declarator - """ - p[0] = self._type_modify_decl(p[2], p[1]) - - def p_abstract_declarator_3(self, p): - """ abstract_declarator : direct_abstract_declarator - """ - p[0] = p[1] - - # Creating and using direct_abstract_declarator_opt here - # instead of listing both direct_abstract_declarator and the - # lack of it in the beginning of _1 and _2 caused two - # shift/reduce errors. - # - def p_direct_abstract_declarator_1(self, p): - """ direct_abstract_declarator : LPAREN abstract_declarator RPAREN """ - p[0] = p[2] - - def p_direct_abstract_declarator_2(self, p): - """ direct_abstract_declarator : direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET - """ - arr = c_ast.ArrayDecl( - type=None, - dim=p[3], - dim_quals=[], - coord=p[1].coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=arr) - - def p_direct_abstract_declarator_3(self, p): - """ direct_abstract_declarator : LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET - """ - quals = (p[2] if len(p) > 4 else []) or [] - p[0] = c_ast.ArrayDecl( - type=c_ast.TypeDecl(None, None, None, None), - dim=p[3] if len(p) > 4 else p[2], - dim_quals=quals, - coord=self._token_coord(p, 1)) - - def p_direct_abstract_declarator_4(self, p): - """ direct_abstract_declarator : direct_abstract_declarator LBRACKET TIMES RBRACKET - """ - arr = c_ast.ArrayDecl( - type=None, - dim=c_ast.ID(p[3], self._token_coord(p, 3)), - dim_quals=[], - coord=p[1].coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=arr) - - def p_direct_abstract_declarator_5(self, p): - """ direct_abstract_declarator : LBRACKET TIMES RBRACKET - """ - p[0] = c_ast.ArrayDecl( - type=c_ast.TypeDecl(None, None, None, None), - dim=c_ast.ID(p[3], self._token_coord(p, 3)), - dim_quals=[], - coord=self._token_coord(p, 1)) - - def p_direct_abstract_declarator_6(self, p): - """ direct_abstract_declarator : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN - """ - func = c_ast.FuncDecl( - args=p[3], - type=None, - coord=p[1].coord) - - p[0] = self._type_modify_decl(decl=p[1], modifier=func) - - def p_direct_abstract_declarator_7(self, p): - """ direct_abstract_declarator : LPAREN parameter_type_list_opt RPAREN - """ - p[0] = c_ast.FuncDecl( - args=p[2], - type=c_ast.TypeDecl(None, None, None, None), - coord=self._token_coord(p, 1)) - - # declaration is a list, statement isn't. To make it consistent, block_item - # will always be a list - # - def p_block_item(self, p): - """ block_item : declaration - | statement - """ - p[0] = p[1] if isinstance(p[1], list) else [p[1]] - - # Since we made block_item a list, this just combines lists - # - def p_block_item_list(self, p): - """ block_item_list : block_item - | block_item_list block_item - """ - # Empty block items (plain ';') produce [None], so ignore them - p[0] = p[1] if (len(p) == 2 or p[2] == [None]) else p[1] + p[2] - - def p_compound_statement_1(self, p): - """ compound_statement : brace_open block_item_list_opt brace_close """ - p[0] = c_ast.Compound( - block_items=p[2], - coord=self._token_coord(p, 1)) - - def p_labeled_statement_1(self, p): - """ labeled_statement : ID COLON pragmacomp_or_statement """ - p[0] = c_ast.Label(p[1], p[3], self._token_coord(p, 1)) - - def p_labeled_statement_2(self, p): - """ labeled_statement : CASE constant_expression COLON pragmacomp_or_statement """ - p[0] = c_ast.Case(p[2], [p[4]], self._token_coord(p, 1)) - - def p_labeled_statement_3(self, p): - """ labeled_statement : DEFAULT COLON pragmacomp_or_statement """ - p[0] = c_ast.Default([p[3]], self._token_coord(p, 1)) - - def p_selection_statement_1(self, p): - """ selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement """ - p[0] = c_ast.If(p[3], p[5], None, self._token_coord(p, 1)) - - def p_selection_statement_2(self, p): - """ selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement """ - p[0] = c_ast.If(p[3], p[5], p[7], self._token_coord(p, 1)) - - def p_selection_statement_3(self, p): - """ selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement """ - p[0] = fix_switch_cases( - c_ast.Switch(p[3], p[5], self._token_coord(p, 1))) - - def p_iteration_statement_1(self, p): - """ iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement """ - p[0] = c_ast.While(p[3], p[5], self._token_coord(p, 1)) - - def p_iteration_statement_2(self, p): - """ iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI """ - p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1)) - - def p_iteration_statement_3(self, p): - """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ - p[0] = c_ast.For(p[3], p[5], p[7], p[9], self._token_coord(p, 1)) - - def p_iteration_statement_4(self, p): - """ iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ - p[0] = c_ast.For(c_ast.DeclList(p[3], self._token_coord(p, 1)), - p[4], p[6], p[8], self._token_coord(p, 1)) - - def p_jump_statement_1(self, p): - """ jump_statement : GOTO ID SEMI """ - p[0] = c_ast.Goto(p[2], self._token_coord(p, 1)) - - def p_jump_statement_2(self, p): - """ jump_statement : BREAK SEMI """ - p[0] = c_ast.Break(self._token_coord(p, 1)) - - def p_jump_statement_3(self, p): - """ jump_statement : CONTINUE SEMI """ - p[0] = c_ast.Continue(self._token_coord(p, 1)) - - def p_jump_statement_4(self, p): - """ jump_statement : RETURN expression SEMI - | RETURN SEMI - """ - p[0] = c_ast.Return(p[2] if len(p) == 4 else None, self._token_coord(p, 1)) - - def p_expression_statement(self, p): - """ expression_statement : expression_opt SEMI """ - if p[1] is None: - p[0] = c_ast.EmptyStatement(self._token_coord(p, 2)) - else: - p[0] = p[1] - - def p_expression(self, p): - """ expression : assignment_expression - | expression COMMA assignment_expression - """ - if len(p) == 2: - p[0] = p[1] - else: - if not isinstance(p[1], c_ast.ExprList): - p[1] = c_ast.ExprList([p[1]], p[1].coord) - - p[1].exprs.append(p[3]) - p[0] = p[1] - - def p_parenthesized_compound_expression(self, p): - """ assignment_expression : LPAREN compound_statement RPAREN """ - p[0] = p[2] - - def p_typedef_name(self, p): - """ typedef_name : TYPEID """ - p[0] = c_ast.IdentifierType([p[1]], coord=self._token_coord(p, 1)) - - def p_assignment_expression(self, p): - """ assignment_expression : conditional_expression - | unary_expression assignment_operator assignment_expression - """ - if len(p) == 2: - p[0] = p[1] - else: - p[0] = c_ast.Assignment(p[2], p[1], p[3], p[1].coord) - - # K&R2 defines these as many separate rules, to encode - # precedence and associativity. Why work hard ? I'll just use - # the built in precedence/associativity specification feature - # of PLY. (see precedence declaration above) - # - def p_assignment_operator(self, p): - """ assignment_operator : EQUALS - | XOREQUAL - | TIMESEQUAL - | DIVEQUAL - | MODEQUAL - | PLUSEQUAL - | MINUSEQUAL - | LSHIFTEQUAL - | RSHIFTEQUAL - | ANDEQUAL - | OREQUAL - """ - p[0] = p[1] - - def p_constant_expression(self, p): - """ constant_expression : conditional_expression """ - p[0] = p[1] - - def p_conditional_expression(self, p): - """ conditional_expression : binary_expression - | binary_expression CONDOP expression COLON conditional_expression - """ - if len(p) == 2: - p[0] = p[1] - else: - p[0] = c_ast.TernaryOp(p[1], p[3], p[5], p[1].coord) - - def p_binary_expression(self, p): - """ binary_expression : cast_expression - | binary_expression TIMES binary_expression - | binary_expression DIVIDE binary_expression - | binary_expression MOD binary_expression - | binary_expression PLUS binary_expression - | binary_expression MINUS binary_expression - | binary_expression RSHIFT binary_expression - | binary_expression LSHIFT binary_expression - | binary_expression LT binary_expression - | binary_expression LE binary_expression - | binary_expression GE binary_expression - | binary_expression GT binary_expression - | binary_expression EQ binary_expression - | binary_expression NE binary_expression - | binary_expression AND binary_expression - | binary_expression OR binary_expression - | binary_expression XOR binary_expression - | binary_expression LAND binary_expression - | binary_expression LOR binary_expression - """ - if len(p) == 2: - p[0] = p[1] - else: - p[0] = c_ast.BinaryOp(p[2], p[1], p[3], p[1].coord) - - def p_cast_expression_1(self, p): - """ cast_expression : unary_expression """ - p[0] = p[1] - - def p_cast_expression_2(self, p): - """ cast_expression : LPAREN type_name RPAREN cast_expression """ - p[0] = c_ast.Cast(p[2], p[4], self._token_coord(p, 1)) - - def p_unary_expression_1(self, p): - """ unary_expression : postfix_expression """ - p[0] = p[1] - - def p_unary_expression_2(self, p): - """ unary_expression : PLUSPLUS unary_expression - | MINUSMINUS unary_expression - | unary_operator cast_expression - """ - p[0] = c_ast.UnaryOp(p[1], p[2], p[2].coord) - - def p_unary_expression_3(self, p): - """ unary_expression : SIZEOF unary_expression - | SIZEOF LPAREN type_name RPAREN - | _ALIGNOF LPAREN type_name RPAREN - """ - p[0] = c_ast.UnaryOp( - p[1], - p[2] if len(p) == 3 else p[3], - self._token_coord(p, 1)) - - def p_unary_operator(self, p): - """ unary_operator : AND - | TIMES - | PLUS - | MINUS - | NOT - | LNOT - """ - p[0] = p[1] - - def p_postfix_expression_1(self, p): - """ postfix_expression : primary_expression """ - p[0] = p[1] - - def p_postfix_expression_2(self, p): - """ postfix_expression : postfix_expression LBRACKET expression RBRACKET """ - p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord) - - def p_postfix_expression_3(self, p): - """ postfix_expression : postfix_expression LPAREN argument_expression_list RPAREN - | postfix_expression LPAREN RPAREN - """ - p[0] = c_ast.FuncCall(p[1], p[3] if len(p) == 5 else None, p[1].coord) - - def p_postfix_expression_4(self, p): - """ postfix_expression : postfix_expression PERIOD ID - | postfix_expression PERIOD TYPEID - | postfix_expression ARROW ID - | postfix_expression ARROW TYPEID - """ - field = c_ast.ID(p[3], self._token_coord(p, 3)) - p[0] = c_ast.StructRef(p[1], p[2], field, p[1].coord) - - def p_postfix_expression_5(self, p): - """ postfix_expression : postfix_expression PLUSPLUS - | postfix_expression MINUSMINUS - """ - p[0] = c_ast.UnaryOp('p' + p[2], p[1], p[1].coord) - - def p_postfix_expression_6(self, p): - """ postfix_expression : LPAREN type_name RPAREN brace_open initializer_list brace_close - | LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close - """ - p[0] = c_ast.CompoundLiteral(p[2], p[5]) - - def p_primary_expression_1(self, p): - """ primary_expression : identifier """ - p[0] = p[1] - - def p_primary_expression_2(self, p): - """ primary_expression : constant """ - p[0] = p[1] - - def p_primary_expression_3(self, p): - """ primary_expression : unified_string_literal - | unified_wstring_literal - """ - p[0] = p[1] - - def p_primary_expression_4(self, p): - """ primary_expression : LPAREN expression RPAREN """ - p[0] = p[2] - - def p_primary_expression_5(self, p): - """ primary_expression : OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN - """ - coord = self._token_coord(p, 1) - p[0] = c_ast.FuncCall(c_ast.ID(p[1], coord), - c_ast.ExprList([p[3], p[5]], coord), - coord) - - def p_offsetof_member_designator(self, p): - """ offsetof_member_designator : identifier - | offsetof_member_designator PERIOD identifier - | offsetof_member_designator LBRACKET expression RBRACKET - """ - if len(p) == 2: - p[0] = p[1] - elif len(p) == 4: - p[0] = c_ast.StructRef(p[1], p[2], p[3], p[1].coord) - elif len(p) == 5: - p[0] = c_ast.ArrayRef(p[1], p[3], p[1].coord) - else: - raise NotImplementedError("Unexpected parsing state. len(p): %u" % len(p)) - - def p_argument_expression_list(self, p): - """ argument_expression_list : assignment_expression - | argument_expression_list COMMA assignment_expression - """ - if len(p) == 2: # single expr - p[0] = c_ast.ExprList([p[1]], p[1].coord) - else: - p[1].exprs.append(p[3]) - p[0] = p[1] - - def p_identifier(self, p): - """ identifier : ID """ - p[0] = c_ast.ID(p[1], self._token_coord(p, 1)) - - def p_constant_1(self, p): - """ constant : INT_CONST_DEC - | INT_CONST_OCT - | INT_CONST_HEX - | INT_CONST_BIN - | INT_CONST_CHAR - """ - uCount = 0 - lCount = 0 - for x in p[1][-3:]: - if x in ('l', 'L'): - lCount += 1 - elif x in ('u', 'U'): - uCount += 1 - t = '' - if uCount > 1: - raise ValueError('Constant cannot have more than one u/U suffix.') - elif lCount > 2: - raise ValueError('Constant cannot have more than two l/L suffix.') - prefix = 'unsigned ' * uCount + 'long ' * lCount - p[0] = c_ast.Constant( - prefix + 'int', p[1], self._token_coord(p, 1)) - - def p_constant_2(self, p): - """ constant : FLOAT_CONST - | HEX_FLOAT_CONST - """ - if 'x' in p[1].lower(): - t = 'float' - else: - if p[1][-1] in ('f', 'F'): - t = 'float' - elif p[1][-1] in ('l', 'L'): - t = 'long double' - else: - t = 'double' - - p[0] = c_ast.Constant( - t, p[1], self._token_coord(p, 1)) - - def p_constant_3(self, p): - """ constant : CHAR_CONST - | WCHAR_CONST - | U8CHAR_CONST - | U16CHAR_CONST - | U32CHAR_CONST - """ - p[0] = c_ast.Constant( - 'char', p[1], self._token_coord(p, 1)) - - # The "unified" string and wstring literal rules are for supporting - # concatenation of adjacent string literals. - # I.e. "hello " "world" is seen by the C compiler as a single string literal - # with the value "hello world" - # - def p_unified_string_literal(self, p): - """ unified_string_literal : STRING_LITERAL - | unified_string_literal STRING_LITERAL - """ - if len(p) == 2: # single literal - p[0] = c_ast.Constant( - 'string', p[1], self._token_coord(p, 1)) - else: - p[1].value = p[1].value[:-1] + p[2][1:] - p[0] = p[1] - - def p_unified_wstring_literal(self, p): - """ unified_wstring_literal : WSTRING_LITERAL - | U8STRING_LITERAL - | U16STRING_LITERAL - | U32STRING_LITERAL - | unified_wstring_literal WSTRING_LITERAL - | unified_wstring_literal U8STRING_LITERAL - | unified_wstring_literal U16STRING_LITERAL - | unified_wstring_literal U32STRING_LITERAL - """ - if len(p) == 2: # single literal - p[0] = c_ast.Constant( - 'string', p[1], self._token_coord(p, 1)) - else: - p[1].value = p[1].value.rstrip()[:-1] + p[2][2:] - p[0] = p[1] - - def p_brace_open(self, p): - """ brace_open : LBRACE - """ - p[0] = p[1] - p.set_lineno(0, p.lineno(1)) - - def p_brace_close(self, p): - """ brace_close : RBRACE - """ - p[0] = p[1] - p.set_lineno(0, p.lineno(1)) - - def p_empty(self, p): - 'empty : ' - p[0] = None - - def p_error(self, p): - # If error recovery is added here in the future, make sure - # _get_yacc_lookahead_token still works! - # - if p: - self._parse_error( - 'before: %s' % p.value, - self._coord(lineno=p.lineno, - column=self.clex.find_tok_column(p))) - else: - self._parse_error('At end of input', self.clex.filename) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py deleted file mode 100644 index 444b4656d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/lextab.py +++ /dev/null @@ -1,10 +0,0 @@ -# lextab.py. This file automatically created by PLY (version 3.10). Don't edit! -_tabversion = '3.10' -_lextokens = set(('INT_CONST_CHAR', 'VOID', 'LBRACKET', 'WCHAR_CONST', 'FLOAT_CONST', 'MINUS', 'RPAREN', 'STRUCT', 'LONG', 'PLUS', 'ELLIPSIS', 'U32STRING_LITERAL', 'GT', 'GOTO', 'ENUM', 'PERIOD', 'GE', 'INT_CONST_DEC', 'ARROW', '_STATIC_ASSERT', '__INT128', 'HEX_FLOAT_CONST', 'DOUBLE', 'MINUSEQUAL', 'INT_CONST_OCT', 'TIMESEQUAL', 'OR', 'SHORT', 'RETURN', 'RSHIFTEQUAL', '_ALIGNAS', 'RESTRICT', 'STATIC', 'SIZEOF', 'UNSIGNED', 'PLUSPLUS', 'COLON', 'WSTRING_LITERAL', 'DIVIDE', 'FOR', 'UNION', 'EQUALS', 'ELSE', 'ANDEQUAL', 'EQ', 'AND', 'TYPEID', 'LBRACE', 'PPHASH', 'INT', 'SIGNED', 'CONTINUE', 'NOT', 'OREQUAL', 'MOD', 'RSHIFT', 'DEFAULT', '_NORETURN', 'CHAR', 'WHILE', 'DIVEQUAL', '_ALIGNOF', 'EXTERN', 'LNOT', 'CASE', 'LAND', 'REGISTER', 'MODEQUAL', 'NE', 'SWITCH', 'INT_CONST_HEX', '_COMPLEX', 'PPPRAGMASTR', 'PLUSEQUAL', 'U32CHAR_CONST', 'CONDOP', 'U8STRING_LITERAL', 'BREAK', 'VOLATILE', 'PPPRAGMA', 'INLINE', 'INT_CONST_BIN', 'DO', 'U8CHAR_CONST', 'CONST', 'U16STRING_LITERAL', 'LOR', 'CHAR_CONST', 'LSHIFT', 'RBRACE', '_BOOL', 'LE', 'SEMI', '_THREAD_LOCAL', 'LT', 'COMMA', 'U16CHAR_CONST', 'OFFSETOF', '_ATOMIC', 'TYPEDEF', 'XOR', 'AUTO', 'TIMES', 'LPAREN', 'MINUSMINUS', 'ID', 'IF', 'STRING_LITERAL', 'FLOAT', 'XOREQUAL', 'LSHIFTEQUAL', 'RBRACKET')) -_lexreflags = 64 -_lexliterals = '' -_lexstateinfo = {'ppline': 'exclusive', 'pppragma': 'exclusive', 'INITIAL': 'inclusive'} -_lexstatere = {'ppline': [('(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P(0(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|([1-9][0-9]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?))|(?P\\n)|(?Pline)', [None, ('t_ppline_FILENAME', 'FILENAME'), None, None, ('t_ppline_LINE_NUMBER', 'LINE_NUMBER'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_ppline_NEWLINE', 'NEWLINE'), ('t_ppline_PPLINE', 'PPLINE')])], 'pppragma': [('(?P\\n)|(?Ppragma)|(?P.+)', [None, ('t_pppragma_NEWLINE', 'NEWLINE'), ('t_pppragma_PPPRAGMA', 'PPPRAGMA'), ('t_pppragma_STR', 'STR')])], 'INITIAL': [('(?P[ \\t]*\\#)|(?P\\n+)|(?P\\{)|(?P\\})|(?P((((([0-9]*\\.[0-9]+)|([0-9]+\\.))([eE][-+]?[0-9]+)?)|([0-9]+([eE][-+]?[0-9]+)))[FfLl]?))|(?P(0[xX]([0-9a-fA-F]+|((([0-9a-fA-F]+)?\\.[0-9a-fA-F]+)|([0-9a-fA-F]+\\.)))([pP][+-]?[0-9]+)[FfLl]?))|(?P0[xX][0-9a-fA-F]+(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P0[bB][01]+(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)', [None, ('t_PPHASH', 'PPHASH'), ('t_NEWLINE', 'NEWLINE'), ('t_LBRACE', 'LBRACE'), ('t_RBRACE', 'RBRACE'), ('t_FLOAT_CONST', 'FLOAT_CONST'), None, None, None, None, None, None, None, None, None, ('t_HEX_FLOAT_CONST', 'HEX_FLOAT_CONST'), None, None, None, None, None, None, None, ('t_INT_CONST_HEX', 'INT_CONST_HEX'), None, None, None, None, None, None, None, ('t_INT_CONST_BIN', 'INT_CONST_BIN')]), ('(?P0[0-7]*[89])|(?P0[0-7]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|(?P(0(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?)|([1-9][0-9]*(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?))|(?P\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F])))){2,4}\')|(?P\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?PL\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?Pu8\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?Pu\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')|(?PU\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))\')', [None, ('t_BAD_CONST_OCT', 'BAD_CONST_OCT'), ('t_INT_CONST_OCT', 'INT_CONST_OCT'), None, None, None, None, None, None, None, ('t_INT_CONST_DEC', 'INT_CONST_DEC'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_INT_CONST_CHAR', 'INT_CONST_CHAR'), None, None, None, None, None, None, ('t_CHAR_CONST', 'CHAR_CONST'), None, None, None, None, None, None, ('t_WCHAR_CONST', 'WCHAR_CONST'), None, None, None, None, None, None, ('t_U8CHAR_CONST', 'U8CHAR_CONST'), None, None, None, None, None, None, ('t_U16CHAR_CONST', 'U16CHAR_CONST'), None, None, None, None, None, None, ('t_U32CHAR_CONST', 'U32CHAR_CONST')]), ('(?P(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))*\\n)|(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))*$))|(?P(\'([^\'\\\\\\n]|(\\\\(([a-wyzA-Z._~!=&\\^\\-\\\\?\'"]|x(?![0-9a-fA-F]))|(\\d+)(?!\\d)|(x[0-9a-fA-F]+)(?![0-9a-fA-F]))))[^\'\n]+\')|(\'\')|(\'([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?\'"x0-9])[^\'\\n]*\'))|(?PL"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?Pu8"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?Pu"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?PU"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*([\\\\][^a-zA-Z._~^!=&\\^\\-\\\\?\'"x0-9])([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P[a-zA-Z_$][0-9a-zA-Z_$]*)|(?P"([^"\\\\\\n]|(\\\\[0-9a-zA-Z._~!=&\\^\\-\\\\?\'"]))*")|(?P\\.\\.\\.)|(?P\\+\\+)|(?P\\|\\|)|(?P\\^=)|(?P\\|=)|(?P<<=)|(?P>>=)|(?P\\+=)|(?P\\*=)', [None, ('t_UNMATCHED_QUOTE', 'UNMATCHED_QUOTE'), None, None, None, None, None, None, None, None, None, None, None, None, None, None, ('t_BAD_CHAR_CONST', 'BAD_CHAR_CONST'), None, None, None, None, None, None, None, None, None, None, ('t_WSTRING_LITERAL', 'WSTRING_LITERAL'), None, None, ('t_U8STRING_LITERAL', 'U8STRING_LITERAL'), None, None, ('t_U16STRING_LITERAL', 'U16STRING_LITERAL'), None, None, ('t_U32STRING_LITERAL', 'U32STRING_LITERAL'), None, None, ('t_BAD_STRING_LITERAL', 'BAD_STRING_LITERAL'), None, None, None, None, None, ('t_ID', 'ID'), (None, 'STRING_LITERAL'), None, None, (None, 'ELLIPSIS'), (None, 'PLUSPLUS'), (None, 'LOR'), (None, 'XOREQUAL'), (None, 'OREQUAL'), (None, 'LSHIFTEQUAL'), (None, 'RSHIFTEQUAL'), (None, 'PLUSEQUAL'), (None, 'TIMESEQUAL')]), ('(?P\\+)|(?P%=)|(?P/=)|(?P\\])|(?P\\?)|(?P\\^)|(?P<<)|(?P<=)|(?P\\()|(?P->)|(?P==)|(?P!=)|(?P--)|(?P\\|)|(?P\\*)|(?P\\[)|(?P>=)|(?P\\))|(?P&&)|(?P>>)|(?P-=)|(?P\\.)|(?P&=)|(?P=)|(?P<)|(?P,)|(?P/)|(?P&)|(?P%)|(?P;)|(?P-)|(?P>)|(?P:)|(?P~)|(?P!)', [None, (None, 'PLUS'), (None, 'MODEQUAL'), (None, 'DIVEQUAL'), (None, 'RBRACKET'), (None, 'CONDOP'), (None, 'XOR'), (None, 'LSHIFT'), (None, 'LE'), (None, 'LPAREN'), (None, 'ARROW'), (None, 'EQ'), (None, 'NE'), (None, 'MINUSMINUS'), (None, 'OR'), (None, 'TIMES'), (None, 'LBRACKET'), (None, 'GE'), (None, 'RPAREN'), (None, 'LAND'), (None, 'RSHIFT'), (None, 'MINUSEQUAL'), (None, 'PERIOD'), (None, 'ANDEQUAL'), (None, 'EQUALS'), (None, 'LT'), (None, 'COMMA'), (None, 'DIVIDE'), (None, 'AND'), (None, 'MOD'), (None, 'SEMI'), (None, 'MINUS'), (None, 'GT'), (None, 'COLON'), (None, 'NOT'), (None, 'LNOT')])]} -_lexstateignore = {'ppline': ' \t', 'pppragma': ' \t', 'INITIAL': ' \t'} -_lexstateerrorf = {'ppline': 't_ppline_error', 'pppragma': 't_pppragma_error', 'INITIAL': 't_error'} -_lexstateeoff = {} diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py deleted file mode 100644 index 6e53cddcf..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# PLY package -# Author: David Beazley (dave@dabeaz.com) - -__version__ = '3.9' -__all__ = ['lex','yacc'] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py deleted file mode 100644 index 86273eac7..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/cpp.py +++ /dev/null @@ -1,905 +0,0 @@ -# ----------------------------------------------------------------------------- -# cpp.py -# -# Author: David Beazley (http://www.dabeaz.com) -# Copyright (C) 2017 -# All rights reserved -# -# This module implements an ANSI-C style lexical preprocessor for PLY. -# ----------------------------------------------------------------------------- -import sys - -# Some Python 3 compatibility shims -if sys.version_info.major < 3: - STRING_TYPES = (str, unicode) -else: - STRING_TYPES = str - xrange = range - -# ----------------------------------------------------------------------------- -# Default preprocessor lexer definitions. These tokens are enough to get -# a basic preprocessor working. Other modules may import these if they want -# ----------------------------------------------------------------------------- - -tokens = ( - 'CPP_ID','CPP_INTEGER', 'CPP_FLOAT', 'CPP_STRING', 'CPP_CHAR', 'CPP_WS', 'CPP_COMMENT1', 'CPP_COMMENT2', 'CPP_POUND','CPP_DPOUND' -) - -literals = "+-*/%|&~^<>=!?()[]{}.,;:\\\'\"" - -# Whitespace -def t_CPP_WS(t): - r'\s+' - t.lexer.lineno += t.value.count("\n") - return t - -t_CPP_POUND = r'\#' -t_CPP_DPOUND = r'\#\#' - -# Identifier -t_CPP_ID = r'[A-Za-z_][\w_]*' - -# Integer literal -def CPP_INTEGER(t): - r'(((((0x)|(0X))[0-9a-fA-F]+)|(\d+))([uU][lL]|[lL][uU]|[uU]|[lL])?)' - return t - -t_CPP_INTEGER = CPP_INTEGER - -# Floating literal -t_CPP_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' - -# String literal -def t_CPP_STRING(t): - r'\"([^\\\n]|(\\(.|\n)))*?\"' - t.lexer.lineno += t.value.count("\n") - return t - -# Character constant 'c' or L'c' -def t_CPP_CHAR(t): - r'(L)?\'([^\\\n]|(\\(.|\n)))*?\'' - t.lexer.lineno += t.value.count("\n") - return t - -# Comment -def t_CPP_COMMENT1(t): - r'(/\*(.|\n)*?\*/)' - ncr = t.value.count("\n") - t.lexer.lineno += ncr - # replace with one space or a number of '\n' - t.type = 'CPP_WS'; t.value = '\n' * ncr if ncr else ' ' - return t - -# Line comment -def t_CPP_COMMENT2(t): - r'(//.*?(\n|$))' - # replace with '/n' - t.type = 'CPP_WS'; t.value = '\n' - return t - -def t_error(t): - t.type = t.value[0] - t.value = t.value[0] - t.lexer.skip(1) - return t - -import re -import copy -import time -import os.path - -# ----------------------------------------------------------------------------- -# trigraph() -# -# Given an input string, this function replaces all trigraph sequences. -# The following mapping is used: -# -# ??= # -# ??/ \ -# ??' ^ -# ??( [ -# ??) ] -# ??! | -# ??< { -# ??> } -# ??- ~ -# ----------------------------------------------------------------------------- - -_trigraph_pat = re.compile(r'''\?\?[=/\'\(\)\!<>\-]''') -_trigraph_rep = { - '=':'#', - '/':'\\', - "'":'^', - '(':'[', - ')':']', - '!':'|', - '<':'{', - '>':'}', - '-':'~' -} - -def trigraph(input): - return _trigraph_pat.sub(lambda g: _trigraph_rep[g.group()[-1]],input) - -# ------------------------------------------------------------------ -# Macro object -# -# This object holds information about preprocessor macros -# -# .name - Macro name (string) -# .value - Macro value (a list of tokens) -# .arglist - List of argument names -# .variadic - Boolean indicating whether or not variadic macro -# .vararg - Name of the variadic parameter -# -# When a macro is created, the macro replacement token sequence is -# pre-scanned and used to create patch lists that are later used -# during macro expansion -# ------------------------------------------------------------------ - -class Macro(object): - def __init__(self,name,value,arglist=None,variadic=False): - self.name = name - self.value = value - self.arglist = arglist - self.variadic = variadic - if variadic: - self.vararg = arglist[-1] - self.source = None - -# ------------------------------------------------------------------ -# Preprocessor object -# -# Object representing a preprocessor. Contains macro definitions, -# include directories, and other information -# ------------------------------------------------------------------ - -class Preprocessor(object): - def __init__(self,lexer=None): - if lexer is None: - lexer = lex.lexer - self.lexer = lexer - self.macros = { } - self.path = [] - self.temp_path = [] - - # Probe the lexer for selected tokens - self.lexprobe() - - tm = time.localtime() - self.define("__DATE__ \"%s\"" % time.strftime("%b %d %Y",tm)) - self.define("__TIME__ \"%s\"" % time.strftime("%H:%M:%S",tm)) - self.parser = None - - # ----------------------------------------------------------------------------- - # tokenize() - # - # Utility function. Given a string of text, tokenize into a list of tokens - # ----------------------------------------------------------------------------- - - def tokenize(self,text): - tokens = [] - self.lexer.input(text) - while True: - tok = self.lexer.token() - if not tok: break - tokens.append(tok) - return tokens - - # --------------------------------------------------------------------- - # error() - # - # Report a preprocessor error/warning of some kind - # ---------------------------------------------------------------------- - - def error(self,file,line,msg): - print("%s:%d %s" % (file,line,msg)) - - # ---------------------------------------------------------------------- - # lexprobe() - # - # This method probes the preprocessor lexer object to discover - # the token types of symbols that are important to the preprocessor. - # If this works right, the preprocessor will simply "work" - # with any suitable lexer regardless of how tokens have been named. - # ---------------------------------------------------------------------- - - def lexprobe(self): - - # Determine the token type for identifiers - self.lexer.input("identifier") - tok = self.lexer.token() - if not tok or tok.value != "identifier": - print("Couldn't determine identifier type") - else: - self.t_ID = tok.type - - # Determine the token type for integers - self.lexer.input("12345") - tok = self.lexer.token() - if not tok or int(tok.value) != 12345: - print("Couldn't determine integer type") - else: - self.t_INTEGER = tok.type - self.t_INTEGER_TYPE = type(tok.value) - - # Determine the token type for strings enclosed in double quotes - self.lexer.input("\"filename\"") - tok = self.lexer.token() - if not tok or tok.value != "\"filename\"": - print("Couldn't determine string type") - else: - self.t_STRING = tok.type - - # Determine the token type for whitespace--if any - self.lexer.input(" ") - tok = self.lexer.token() - if not tok or tok.value != " ": - self.t_SPACE = None - else: - self.t_SPACE = tok.type - - # Determine the token type for newlines - self.lexer.input("\n") - tok = self.lexer.token() - if not tok or tok.value != "\n": - self.t_NEWLINE = None - print("Couldn't determine token for newlines") - else: - self.t_NEWLINE = tok.type - - self.t_WS = (self.t_SPACE, self.t_NEWLINE) - - # Check for other characters used by the preprocessor - chars = [ '<','>','#','##','\\','(',')',',','.'] - for c in chars: - self.lexer.input(c) - tok = self.lexer.token() - if not tok or tok.value != c: - print("Unable to lex '%s' required for preprocessor" % c) - - # ---------------------------------------------------------------------- - # add_path() - # - # Adds a search path to the preprocessor. - # ---------------------------------------------------------------------- - - def add_path(self,path): - self.path.append(path) - - # ---------------------------------------------------------------------- - # group_lines() - # - # Given an input string, this function splits it into lines. Trailing whitespace - # is removed. Any line ending with \ is grouped with the next line. This - # function forms the lowest level of the preprocessor---grouping into text into - # a line-by-line format. - # ---------------------------------------------------------------------- - - def group_lines(self,input): - lex = self.lexer.clone() - lines = [x.rstrip() for x in input.splitlines()] - for i in xrange(len(lines)): - j = i+1 - while lines[i].endswith('\\') and (j < len(lines)): - lines[i] = lines[i][:-1]+lines[j] - lines[j] = "" - j += 1 - - input = "\n".join(lines) - lex.input(input) - lex.lineno = 1 - - current_line = [] - while True: - tok = lex.token() - if not tok: - break - current_line.append(tok) - if tok.type in self.t_WS and '\n' in tok.value: - yield current_line - current_line = [] - - if current_line: - yield current_line - - # ---------------------------------------------------------------------- - # tokenstrip() - # - # Remove leading/trailing whitespace tokens from a token list - # ---------------------------------------------------------------------- - - def tokenstrip(self,tokens): - i = 0 - while i < len(tokens) and tokens[i].type in self.t_WS: - i += 1 - del tokens[:i] - i = len(tokens)-1 - while i >= 0 and tokens[i].type in self.t_WS: - i -= 1 - del tokens[i+1:] - return tokens - - - # ---------------------------------------------------------------------- - # collect_args() - # - # Collects comma separated arguments from a list of tokens. The arguments - # must be enclosed in parenthesis. Returns a tuple (tokencount,args,positions) - # where tokencount is the number of tokens consumed, args is a list of arguments, - # and positions is a list of integers containing the starting index of each - # argument. Each argument is represented by a list of tokens. - # - # When collecting arguments, leading and trailing whitespace is removed - # from each argument. - # - # This function properly handles nested parenthesis and commas---these do not - # define new arguments. - # ---------------------------------------------------------------------- - - def collect_args(self,tokenlist): - args = [] - positions = [] - current_arg = [] - nesting = 1 - tokenlen = len(tokenlist) - - # Search for the opening '('. - i = 0 - while (i < tokenlen) and (tokenlist[i].type in self.t_WS): - i += 1 - - if (i < tokenlen) and (tokenlist[i].value == '('): - positions.append(i+1) - else: - self.error(self.source,tokenlist[0].lineno,"Missing '(' in macro arguments") - return 0, [], [] - - i += 1 - - while i < tokenlen: - t = tokenlist[i] - if t.value == '(': - current_arg.append(t) - nesting += 1 - elif t.value == ')': - nesting -= 1 - if nesting == 0: - if current_arg: - args.append(self.tokenstrip(current_arg)) - positions.append(i) - return i+1,args,positions - current_arg.append(t) - elif t.value == ',' and nesting == 1: - args.append(self.tokenstrip(current_arg)) - positions.append(i+1) - current_arg = [] - else: - current_arg.append(t) - i += 1 - - # Missing end argument - self.error(self.source,tokenlist[-1].lineno,"Missing ')' in macro arguments") - return 0, [],[] - - # ---------------------------------------------------------------------- - # macro_prescan() - # - # Examine the macro value (token sequence) and identify patch points - # This is used to speed up macro expansion later on---we'll know - # right away where to apply patches to the value to form the expansion - # ---------------------------------------------------------------------- - - def macro_prescan(self,macro): - macro.patch = [] # Standard macro arguments - macro.str_patch = [] # String conversion expansion - macro.var_comma_patch = [] # Variadic macro comma patch - i = 0 - while i < len(macro.value): - if macro.value[i].type == self.t_ID and macro.value[i].value in macro.arglist: - argnum = macro.arglist.index(macro.value[i].value) - # Conversion of argument to a string - if i > 0 and macro.value[i-1].value == '#': - macro.value[i] = copy.copy(macro.value[i]) - macro.value[i].type = self.t_STRING - del macro.value[i-1] - macro.str_patch.append((argnum,i-1)) - continue - # Concatenation - elif (i > 0 and macro.value[i-1].value == '##'): - macro.patch.append(('c',argnum,i-1)) - del macro.value[i-1] - continue - elif ((i+1) < len(macro.value) and macro.value[i+1].value == '##'): - macro.patch.append(('c',argnum,i)) - i += 1 - continue - # Standard expansion - else: - macro.patch.append(('e',argnum,i)) - elif macro.value[i].value == '##': - if macro.variadic and (i > 0) and (macro.value[i-1].value == ',') and \ - ((i+1) < len(macro.value)) and (macro.value[i+1].type == self.t_ID) and \ - (macro.value[i+1].value == macro.vararg): - macro.var_comma_patch.append(i-1) - i += 1 - macro.patch.sort(key=lambda x: x[2],reverse=True) - - # ---------------------------------------------------------------------- - # macro_expand_args() - # - # Given a Macro and list of arguments (each a token list), this method - # returns an expanded version of a macro. The return value is a token sequence - # representing the replacement macro tokens - # ---------------------------------------------------------------------- - - def macro_expand_args(self,macro,args): - # Make a copy of the macro token sequence - rep = [copy.copy(_x) for _x in macro.value] - - # Make string expansion patches. These do not alter the length of the replacement sequence - - str_expansion = {} - for argnum, i in macro.str_patch: - if argnum not in str_expansion: - str_expansion[argnum] = ('"%s"' % "".join([x.value for x in args[argnum]])).replace("\\","\\\\") - rep[i] = copy.copy(rep[i]) - rep[i].value = str_expansion[argnum] - - # Make the variadic macro comma patch. If the variadic macro argument is empty, we get rid - comma_patch = False - if macro.variadic and not args[-1]: - for i in macro.var_comma_patch: - rep[i] = None - comma_patch = True - - # Make all other patches. The order of these matters. It is assumed that the patch list - # has been sorted in reverse order of patch location since replacements will cause the - # size of the replacement sequence to expand from the patch point. - - expanded = { } - for ptype, argnum, i in macro.patch: - # Concatenation. Argument is left unexpanded - if ptype == 'c': - rep[i:i+1] = args[argnum] - # Normal expansion. Argument is macro expanded first - elif ptype == 'e': - if argnum not in expanded: - expanded[argnum] = self.expand_macros(args[argnum]) - rep[i:i+1] = expanded[argnum] - - # Get rid of removed comma if necessary - if comma_patch: - rep = [_i for _i in rep if _i] - - return rep - - - # ---------------------------------------------------------------------- - # expand_macros() - # - # Given a list of tokens, this function performs macro expansion. - # The expanded argument is a dictionary that contains macros already - # expanded. This is used to prevent infinite recursion. - # ---------------------------------------------------------------------- - - def expand_macros(self,tokens,expanded=None): - if expanded is None: - expanded = {} - i = 0 - while i < len(tokens): - t = tokens[i] - if t.type == self.t_ID: - if t.value in self.macros and t.value not in expanded: - # Yes, we found a macro match - expanded[t.value] = True - - m = self.macros[t.value] - if not m.arglist: - # A simple macro - ex = self.expand_macros([copy.copy(_x) for _x in m.value],expanded) - for e in ex: - e.lineno = t.lineno - tokens[i:i+1] = ex - i += len(ex) - else: - # A macro with arguments - j = i + 1 - while j < len(tokens) and tokens[j].type in self.t_WS: - j += 1 - if tokens[j].value == '(': - tokcount,args,positions = self.collect_args(tokens[j:]) - if not m.variadic and len(args) != len(m.arglist): - self.error(self.source,t.lineno,"Macro %s requires %d arguments" % (t.value,len(m.arglist))) - i = j + tokcount - elif m.variadic and len(args) < len(m.arglist)-1: - if len(m.arglist) > 2: - self.error(self.source,t.lineno,"Macro %s must have at least %d arguments" % (t.value, len(m.arglist)-1)) - else: - self.error(self.source,t.lineno,"Macro %s must have at least %d argument" % (t.value, len(m.arglist)-1)) - i = j + tokcount - else: - if m.variadic: - if len(args) == len(m.arglist)-1: - args.append([]) - else: - args[len(m.arglist)-1] = tokens[j+positions[len(m.arglist)-1]:j+tokcount-1] - del args[len(m.arglist):] - - # Get macro replacement text - rep = self.macro_expand_args(m,args) - rep = self.expand_macros(rep,expanded) - for r in rep: - r.lineno = t.lineno - tokens[i:j+tokcount] = rep - i += len(rep) - del expanded[t.value] - continue - elif t.value == '__LINE__': - t.type = self.t_INTEGER - t.value = self.t_INTEGER_TYPE(t.lineno) - - i += 1 - return tokens - - # ---------------------------------------------------------------------- - # evalexpr() - # - # Evaluate an expression token sequence for the purposes of evaluating - # integral expressions. - # ---------------------------------------------------------------------- - - def evalexpr(self,tokens): - # tokens = tokenize(line) - # Search for defined macros - i = 0 - while i < len(tokens): - if tokens[i].type == self.t_ID and tokens[i].value == 'defined': - j = i + 1 - needparen = False - result = "0L" - while j < len(tokens): - if tokens[j].type in self.t_WS: - j += 1 - continue - elif tokens[j].type == self.t_ID: - if tokens[j].value in self.macros: - result = "1L" - else: - result = "0L" - if not needparen: break - elif tokens[j].value == '(': - needparen = True - elif tokens[j].value == ')': - break - else: - self.error(self.source,tokens[i].lineno,"Malformed defined()") - j += 1 - tokens[i].type = self.t_INTEGER - tokens[i].value = self.t_INTEGER_TYPE(result) - del tokens[i+1:j+1] - i += 1 - tokens = self.expand_macros(tokens) - for i,t in enumerate(tokens): - if t.type == self.t_ID: - tokens[i] = copy.copy(t) - tokens[i].type = self.t_INTEGER - tokens[i].value = self.t_INTEGER_TYPE("0L") - elif t.type == self.t_INTEGER: - tokens[i] = copy.copy(t) - # Strip off any trailing suffixes - tokens[i].value = str(tokens[i].value) - while tokens[i].value[-1] not in "0123456789abcdefABCDEF": - tokens[i].value = tokens[i].value[:-1] - - expr = "".join([str(x.value) for x in tokens]) - expr = expr.replace("&&"," and ") - expr = expr.replace("||"," or ") - expr = expr.replace("!"," not ") - try: - result = eval(expr) - except Exception: - self.error(self.source,tokens[0].lineno,"Couldn't evaluate expression") - result = 0 - return result - - # ---------------------------------------------------------------------- - # parsegen() - # - # Parse an input string/ - # ---------------------------------------------------------------------- - def parsegen(self,input,source=None): - - # Replace trigraph sequences - t = trigraph(input) - lines = self.group_lines(t) - - if not source: - source = "" - - self.define("__FILE__ \"%s\"" % source) - - self.source = source - chunk = [] - enable = True - iftrigger = False - ifstack = [] - - for x in lines: - for i,tok in enumerate(x): - if tok.type not in self.t_WS: break - if tok.value == '#': - # Preprocessor directive - - # insert necessary whitespace instead of eaten tokens - for tok in x: - if tok.type in self.t_WS and '\n' in tok.value: - chunk.append(tok) - - dirtokens = self.tokenstrip(x[i+1:]) - if dirtokens: - name = dirtokens[0].value - args = self.tokenstrip(dirtokens[1:]) - else: - name = "" - args = [] - - if name == 'define': - if enable: - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - self.define(args) - elif name == 'include': - if enable: - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - oldfile = self.macros['__FILE__'] - for tok in self.include(args): - yield tok - self.macros['__FILE__'] = oldfile - self.source = source - elif name == 'undef': - if enable: - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - self.undef(args) - elif name == 'ifdef': - ifstack.append((enable,iftrigger)) - if enable: - if not args[0].value in self.macros: - enable = False - iftrigger = False - else: - iftrigger = True - elif name == 'ifndef': - ifstack.append((enable,iftrigger)) - if enable: - if args[0].value in self.macros: - enable = False - iftrigger = False - else: - iftrigger = True - elif name == 'if': - ifstack.append((enable,iftrigger)) - if enable: - result = self.evalexpr(args) - if not result: - enable = False - iftrigger = False - else: - iftrigger = True - elif name == 'elif': - if ifstack: - if ifstack[-1][0]: # We only pay attention if outer "if" allows this - if enable: # If already true, we flip enable False - enable = False - elif not iftrigger: # If False, but not triggered yet, we'll check expression - result = self.evalexpr(args) - if result: - enable = True - iftrigger = True - else: - self.error(self.source,dirtokens[0].lineno,"Misplaced #elif") - - elif name == 'else': - if ifstack: - if ifstack[-1][0]: - if enable: - enable = False - elif not iftrigger: - enable = True - iftrigger = True - else: - self.error(self.source,dirtokens[0].lineno,"Misplaced #else") - - elif name == 'endif': - if ifstack: - enable,iftrigger = ifstack.pop() - else: - self.error(self.source,dirtokens[0].lineno,"Misplaced #endif") - else: - # Unknown preprocessor directive - pass - - else: - # Normal text - if enable: - chunk.extend(x) - - for tok in self.expand_macros(chunk): - yield tok - chunk = [] - - # ---------------------------------------------------------------------- - # include() - # - # Implementation of file-inclusion - # ---------------------------------------------------------------------- - - def include(self,tokens): - # Try to extract the filename and then process an include file - if not tokens: - return - if tokens: - if tokens[0].value != '<' and tokens[0].type != self.t_STRING: - tokens = self.expand_macros(tokens) - - if tokens[0].value == '<': - # Include <...> - i = 1 - while i < len(tokens): - if tokens[i].value == '>': - break - i += 1 - else: - print("Malformed #include <...>") - return - filename = "".join([x.value for x in tokens[1:i]]) - path = self.path + [""] + self.temp_path - elif tokens[0].type == self.t_STRING: - filename = tokens[0].value[1:-1] - path = self.temp_path + [""] + self.path - else: - print("Malformed #include statement") - return - for p in path: - iname = os.path.join(p,filename) - try: - data = open(iname,"r").read() - dname = os.path.dirname(iname) - if dname: - self.temp_path.insert(0,dname) - for tok in self.parsegen(data,filename): - yield tok - if dname: - del self.temp_path[0] - break - except IOError: - pass - else: - print("Couldn't find '%s'" % filename) - - # ---------------------------------------------------------------------- - # define() - # - # Define a new macro - # ---------------------------------------------------------------------- - - def define(self,tokens): - if isinstance(tokens,STRING_TYPES): - tokens = self.tokenize(tokens) - - linetok = tokens - try: - name = linetok[0] - if len(linetok) > 1: - mtype = linetok[1] - else: - mtype = None - if not mtype: - m = Macro(name.value,[]) - self.macros[name.value] = m - elif mtype.type in self.t_WS: - # A normal macro - m = Macro(name.value,self.tokenstrip(linetok[2:])) - self.macros[name.value] = m - elif mtype.value == '(': - # A macro with arguments - tokcount, args, positions = self.collect_args(linetok[1:]) - variadic = False - for a in args: - if variadic: - print("No more arguments may follow a variadic argument") - break - astr = "".join([str(_i.value) for _i in a]) - if astr == "...": - variadic = True - a[0].type = self.t_ID - a[0].value = '__VA_ARGS__' - variadic = True - del a[1:] - continue - elif astr[-3:] == "..." and a[0].type == self.t_ID: - variadic = True - del a[1:] - # If, for some reason, "." is part of the identifier, strip off the name for the purposes - # of macro expansion - if a[0].value[-3:] == '...': - a[0].value = a[0].value[:-3] - continue - if len(a) > 1 or a[0].type != self.t_ID: - print("Invalid macro argument") - break - else: - mvalue = self.tokenstrip(linetok[1+tokcount:]) - i = 0 - while i < len(mvalue): - if i+1 < len(mvalue): - if mvalue[i].type in self.t_WS and mvalue[i+1].value == '##': - del mvalue[i] - continue - elif mvalue[i].value == '##' and mvalue[i+1].type in self.t_WS: - del mvalue[i+1] - i += 1 - m = Macro(name.value,mvalue,[x[0].value for x in args],variadic) - self.macro_prescan(m) - self.macros[name.value] = m - else: - print("Bad macro definition") - except LookupError: - print("Bad macro definition") - - # ---------------------------------------------------------------------- - # undef() - # - # Undefine a macro - # ---------------------------------------------------------------------- - - def undef(self,tokens): - id = tokens[0].value - try: - del self.macros[id] - except LookupError: - pass - - # ---------------------------------------------------------------------- - # parse() - # - # Parse input text. - # ---------------------------------------------------------------------- - def parse(self,input,source=None,ignore={}): - self.ignore = ignore - self.parser = self.parsegen(input,source) - - # ---------------------------------------------------------------------- - # token() - # - # Method to return individual tokens - # ---------------------------------------------------------------------- - def token(self): - try: - while True: - tok = next(self.parser) - if tok.type not in self.ignore: return tok - except StopIteration: - self.parser = None - return None - -if __name__ == '__main__': - import ply.lex as lex - lexer = lex.lex() - - # Run a preprocessor - import sys - f = open(sys.argv[1]) - input = f.read() - - p = Preprocessor(lexer) - p.parse(input,sys.argv[1]) - while True: - tok = p.token() - if not tok: break - print(p.source, tok) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py deleted file mode 100644 index f6f6952d6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ctokens.py +++ /dev/null @@ -1,133 +0,0 @@ -# ---------------------------------------------------------------------- -# ctokens.py -# -# Token specifications for symbols in ANSI C and C++. This file is -# meant to be used as a library in other tokenizers. -# ---------------------------------------------------------------------- - -# Reserved words - -tokens = [ - # Literals (identifier, integer constant, float constant, string constant, char const) - 'ID', 'TYPEID', 'INTEGER', 'FLOAT', 'STRING', 'CHARACTER', - - # Operators (+,-,*,/,%,|,&,~,^,<<,>>, ||, &&, !, <, <=, >, >=, ==, !=) - 'PLUS', 'MINUS', 'TIMES', 'DIVIDE', 'MODULO', - 'OR', 'AND', 'NOT', 'XOR', 'LSHIFT', 'RSHIFT', - 'LOR', 'LAND', 'LNOT', - 'LT', 'LE', 'GT', 'GE', 'EQ', 'NE', - - # Assignment (=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=) - 'EQUALS', 'TIMESEQUAL', 'DIVEQUAL', 'MODEQUAL', 'PLUSEQUAL', 'MINUSEQUAL', - 'LSHIFTEQUAL','RSHIFTEQUAL', 'ANDEQUAL', 'XOREQUAL', 'OREQUAL', - - # Increment/decrement (++,--) - 'INCREMENT', 'DECREMENT', - - # Structure dereference (->) - 'ARROW', - - # Ternary operator (?) - 'TERNARY', - - # Delimeters ( ) [ ] { } , . ; : - 'LPAREN', 'RPAREN', - 'LBRACKET', 'RBRACKET', - 'LBRACE', 'RBRACE', - 'COMMA', 'PERIOD', 'SEMI', 'COLON', - - # Ellipsis (...) - 'ELLIPSIS', -] - -# Operators -t_PLUS = r'\+' -t_MINUS = r'-' -t_TIMES = r'\*' -t_DIVIDE = r'/' -t_MODULO = r'%' -t_OR = r'\|' -t_AND = r'&' -t_NOT = r'~' -t_XOR = r'\^' -t_LSHIFT = r'<<' -t_RSHIFT = r'>>' -t_LOR = r'\|\|' -t_LAND = r'&&' -t_LNOT = r'!' -t_LT = r'<' -t_GT = r'>' -t_LE = r'<=' -t_GE = r'>=' -t_EQ = r'==' -t_NE = r'!=' - -# Assignment operators - -t_EQUALS = r'=' -t_TIMESEQUAL = r'\*=' -t_DIVEQUAL = r'/=' -t_MODEQUAL = r'%=' -t_PLUSEQUAL = r'\+=' -t_MINUSEQUAL = r'-=' -t_LSHIFTEQUAL = r'<<=' -t_RSHIFTEQUAL = r'>>=' -t_ANDEQUAL = r'&=' -t_OREQUAL = r'\|=' -t_XOREQUAL = r'\^=' - -# Increment/decrement -t_INCREMENT = r'\+\+' -t_DECREMENT = r'--' - -# -> -t_ARROW = r'->' - -# ? -t_TERNARY = r'\?' - -# Delimeters -t_LPAREN = r'\(' -t_RPAREN = r'\)' -t_LBRACKET = r'\[' -t_RBRACKET = r'\]' -t_LBRACE = r'\{' -t_RBRACE = r'\}' -t_COMMA = r',' -t_PERIOD = r'\.' -t_SEMI = r';' -t_COLON = r':' -t_ELLIPSIS = r'\.\.\.' - -# Identifiers -t_ID = r'[A-Za-z_][A-Za-z0-9_]*' - -# Integer literal -t_INTEGER = r'\d+([uU]|[lL]|[uU][lL]|[lL][uU])?' - -# Floating literal -t_FLOAT = r'((\d+)(\.\d+)(e(\+|-)?(\d+))? | (\d+)e(\+|-)?(\d+))([lL]|[fF])?' - -# String literal -t_STRING = r'\"([^\\\n]|(\\.))*?\"' - -# Character constant 'c' or L'c' -t_CHARACTER = r'(L)?\'([^\\\n]|(\\.))*?\'' - -# Comment (C-Style) -def t_COMMENT(t): - r'/\*(.|\n)*?\*/' - t.lexer.lineno += t.value.count('\n') - return t - -# Comment (C++-Style) -def t_CPPCOMMENT(t): - r'//.*\n' - t.lexer.lineno += 1 - return t - - - - - - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py deleted file mode 100644 index 4bdd76ca0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/lex.py +++ /dev/null @@ -1,1099 +0,0 @@ -# ----------------------------------------------------------------------------- -# ply: lex.py -# -# Copyright (C) 2001-2017 -# David M. Beazley (Dabeaz LLC) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the David Beazley or Dabeaz LLC may be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------------------------- - -__version__ = '3.10' -__tabversion__ = '3.10' - -import re -import sys -import types -import copy -import os -import inspect - -# This tuple contains known string types -try: - # Python 2.6 - StringTypes = (types.StringType, types.UnicodeType) -except AttributeError: - # Python 3.0 - StringTypes = (str, bytes) - -# This regular expression is used to match valid token names -_is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') - -# Exception thrown when invalid token encountered and no default error -# handler is defined. -class LexError(Exception): - def __init__(self, message, s): - self.args = (message,) - self.text = s - - -# Token class. This class is used to represent the tokens produced. -class LexToken(object): - def __str__(self): - return 'LexToken(%s,%r,%d,%d)' % (self.type, self.value, self.lineno, self.lexpos) - - def __repr__(self): - return str(self) - - -# This object is a stand-in for a logging object created by the -# logging module. - -class PlyLogger(object): - def __init__(self, f): - self.f = f - - def critical(self, msg, *args, **kwargs): - self.f.write((msg % args) + '\n') - - def warning(self, msg, *args, **kwargs): - self.f.write('WARNING: ' + (msg % args) + '\n') - - def error(self, msg, *args, **kwargs): - self.f.write('ERROR: ' + (msg % args) + '\n') - - info = critical - debug = critical - - -# Null logger is used when no output is generated. Does nothing. -class NullLogger(object): - def __getattribute__(self, name): - return self - - def __call__(self, *args, **kwargs): - return self - - -# ----------------------------------------------------------------------------- -# === Lexing Engine === -# -# The following Lexer class implements the lexer runtime. There are only -# a few public methods and attributes: -# -# input() - Store a new string in the lexer -# token() - Get the next token -# clone() - Clone the lexer -# -# lineno - Current line number -# lexpos - Current position in the input string -# ----------------------------------------------------------------------------- - -class Lexer: - def __init__(self): - self.lexre = None # Master regular expression. This is a list of - # tuples (re, findex) where re is a compiled - # regular expression and findex is a list - # mapping regex group numbers to rules - self.lexretext = None # Current regular expression strings - self.lexstatere = {} # Dictionary mapping lexer states to master regexs - self.lexstateretext = {} # Dictionary mapping lexer states to regex strings - self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names - self.lexstate = 'INITIAL' # Current lexer state - self.lexstatestack = [] # Stack of lexer states - self.lexstateinfo = None # State information - self.lexstateignore = {} # Dictionary of ignored characters for each state - self.lexstateerrorf = {} # Dictionary of error functions for each state - self.lexstateeoff = {} # Dictionary of eof functions for each state - self.lexreflags = 0 # Optional re compile flags - self.lexdata = None # Actual input data (as a string) - self.lexpos = 0 # Current position in input text - self.lexlen = 0 # Length of the input text - self.lexerrorf = None # Error rule (if any) - self.lexeoff = None # EOF rule (if any) - self.lextokens = None # List of valid tokens - self.lexignore = '' # Ignored characters - self.lexliterals = '' # Literal characters that can be passed through - self.lexmodule = None # Module - self.lineno = 1 # Current line number - self.lexoptimize = False # Optimized mode - - def clone(self, object=None): - c = copy.copy(self) - - # If the object parameter has been supplied, it means we are attaching the - # lexer to a new object. In this case, we have to rebind all methods in - # the lexstatere and lexstateerrorf tables. - - if object: - newtab = {} - for key, ritem in self.lexstatere.items(): - newre = [] - for cre, findex in ritem: - newfindex = [] - for f in findex: - if not f or not f[0]: - newfindex.append(f) - continue - newfindex.append((getattr(object, f[0].__name__), f[1])) - newre.append((cre, newfindex)) - newtab[key] = newre - c.lexstatere = newtab - c.lexstateerrorf = {} - for key, ef in self.lexstateerrorf.items(): - c.lexstateerrorf[key] = getattr(object, ef.__name__) - c.lexmodule = object - return c - - # ------------------------------------------------------------ - # writetab() - Write lexer information to a table file - # ------------------------------------------------------------ - def writetab(self, lextab, outputdir=''): - if isinstance(lextab, types.ModuleType): - raise IOError("Won't overwrite existing lextab module") - basetabmodule = lextab.split('.')[-1] - filename = os.path.join(outputdir, basetabmodule) + '.py' - with open(filename, 'w') as tf: - tf.write('# %s.py. This file automatically created by PLY (version %s). Don\'t edit!\n' % (basetabmodule, __version__)) - tf.write('_tabversion = %s\n' % repr(__tabversion__)) - tf.write('_lextokens = set(%s)\n' % repr(tuple(self.lextokens))) - tf.write('_lexreflags = %s\n' % repr(self.lexreflags)) - tf.write('_lexliterals = %s\n' % repr(self.lexliterals)) - tf.write('_lexstateinfo = %s\n' % repr(self.lexstateinfo)) - - # Rewrite the lexstatere table, replacing function objects with function names - tabre = {} - for statename, lre in self.lexstatere.items(): - titem = [] - for (pat, func), retext, renames in zip(lre, self.lexstateretext[statename], self.lexstaterenames[statename]): - titem.append((retext, _funcs_to_names(func, renames))) - tabre[statename] = titem - - tf.write('_lexstatere = %s\n' % repr(tabre)) - tf.write('_lexstateignore = %s\n' % repr(self.lexstateignore)) - - taberr = {} - for statename, ef in self.lexstateerrorf.items(): - taberr[statename] = ef.__name__ if ef else None - tf.write('_lexstateerrorf = %s\n' % repr(taberr)) - - tabeof = {} - for statename, ef in self.lexstateeoff.items(): - tabeof[statename] = ef.__name__ if ef else None - tf.write('_lexstateeoff = %s\n' % repr(tabeof)) - - # ------------------------------------------------------------ - # readtab() - Read lexer information from a tab file - # ------------------------------------------------------------ - def readtab(self, tabfile, fdict): - if isinstance(tabfile, types.ModuleType): - lextab = tabfile - else: - exec('import %s' % tabfile) - lextab = sys.modules[tabfile] - - if getattr(lextab, '_tabversion', '0.0') != __tabversion__: - raise ImportError('Inconsistent PLY version') - - self.lextokens = lextab._lextokens - self.lexreflags = lextab._lexreflags - self.lexliterals = lextab._lexliterals - self.lextokens_all = self.lextokens | set(self.lexliterals) - self.lexstateinfo = lextab._lexstateinfo - self.lexstateignore = lextab._lexstateignore - self.lexstatere = {} - self.lexstateretext = {} - for statename, lre in lextab._lexstatere.items(): - titem = [] - txtitem = [] - for pat, func_name in lre: - titem.append((re.compile(pat, lextab._lexreflags), _names_to_funcs(func_name, fdict))) - - self.lexstatere[statename] = titem - self.lexstateretext[statename] = txtitem - - self.lexstateerrorf = {} - for statename, ef in lextab._lexstateerrorf.items(): - self.lexstateerrorf[statename] = fdict[ef] - - self.lexstateeoff = {} - for statename, ef in lextab._lexstateeoff.items(): - self.lexstateeoff[statename] = fdict[ef] - - self.begin('INITIAL') - - # ------------------------------------------------------------ - # input() - Push a new string into the lexer - # ------------------------------------------------------------ - def input(self, s): - # Pull off the first character to see if s looks like a string - c = s[:1] - if not isinstance(c, StringTypes): - raise ValueError('Expected a string') - self.lexdata = s - self.lexpos = 0 - self.lexlen = len(s) - - # ------------------------------------------------------------ - # begin() - Changes the lexing state - # ------------------------------------------------------------ - def begin(self, state): - if state not in self.lexstatere: - raise ValueError('Undefined state') - self.lexre = self.lexstatere[state] - self.lexretext = self.lexstateretext[state] - self.lexignore = self.lexstateignore.get(state, '') - self.lexerrorf = self.lexstateerrorf.get(state, None) - self.lexeoff = self.lexstateeoff.get(state, None) - self.lexstate = state - - # ------------------------------------------------------------ - # push_state() - Changes the lexing state and saves old on stack - # ------------------------------------------------------------ - def push_state(self, state): - self.lexstatestack.append(self.lexstate) - self.begin(state) - - # ------------------------------------------------------------ - # pop_state() - Restores the previous state - # ------------------------------------------------------------ - def pop_state(self): - self.begin(self.lexstatestack.pop()) - - # ------------------------------------------------------------ - # current_state() - Returns the current lexing state - # ------------------------------------------------------------ - def current_state(self): - return self.lexstate - - # ------------------------------------------------------------ - # skip() - Skip ahead n characters - # ------------------------------------------------------------ - def skip(self, n): - self.lexpos += n - - # ------------------------------------------------------------ - # opttoken() - Return the next token from the Lexer - # - # Note: This function has been carefully implemented to be as fast - # as possible. Don't make changes unless you really know what - # you are doing - # ------------------------------------------------------------ - def token(self): - # Make local copies of frequently referenced attributes - lexpos = self.lexpos - lexlen = self.lexlen - lexignore = self.lexignore - lexdata = self.lexdata - - while lexpos < lexlen: - # This code provides some short-circuit code for whitespace, tabs, and other ignored characters - if lexdata[lexpos] in lexignore: - lexpos += 1 - continue - - # Look for a regular expression match - for lexre, lexindexfunc in self.lexre: - m = lexre.match(lexdata, lexpos) - if not m: - continue - - # Create a token for return - tok = LexToken() - tok.value = m.group() - tok.lineno = self.lineno - tok.lexpos = lexpos - - i = m.lastindex - func, tok.type = lexindexfunc[i] - - if not func: - # If no token type was set, it's an ignored token - if tok.type: - self.lexpos = m.end() - return tok - else: - lexpos = m.end() - break - - lexpos = m.end() - - # If token is processed by a function, call it - - tok.lexer = self # Set additional attributes useful in token rules - self.lexmatch = m - self.lexpos = lexpos - - newtok = func(tok) - - # Every function must return a token, if nothing, we just move to next token - if not newtok: - lexpos = self.lexpos # This is here in case user has updated lexpos. - lexignore = self.lexignore # This is here in case there was a state change - break - - # Verify type of the token. If not in the token map, raise an error - if not self.lexoptimize: - if newtok.type not in self.lextokens_all: - raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( - func.__code__.co_filename, func.__code__.co_firstlineno, - func.__name__, newtok.type), lexdata[lexpos:]) - - return newtok - else: - # No match, see if in literals - if lexdata[lexpos] in self.lexliterals: - tok = LexToken() - tok.value = lexdata[lexpos] - tok.lineno = self.lineno - tok.type = tok.value - tok.lexpos = lexpos - self.lexpos = lexpos + 1 - return tok - - # No match. Call t_error() if defined. - if self.lexerrorf: - tok = LexToken() - tok.value = self.lexdata[lexpos:] - tok.lineno = self.lineno - tok.type = 'error' - tok.lexer = self - tok.lexpos = lexpos - self.lexpos = lexpos - newtok = self.lexerrorf(tok) - if lexpos == self.lexpos: - # Error method didn't change text position at all. This is an error. - raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) - lexpos = self.lexpos - if not newtok: - continue - return newtok - - self.lexpos = lexpos - raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos], lexpos), lexdata[lexpos:]) - - if self.lexeoff: - tok = LexToken() - tok.type = 'eof' - tok.value = '' - tok.lineno = self.lineno - tok.lexpos = lexpos - tok.lexer = self - self.lexpos = lexpos - newtok = self.lexeoff(tok) - return newtok - - self.lexpos = lexpos + 1 - if self.lexdata is None: - raise RuntimeError('No input string given with input()') - return None - - # Iterator interface - def __iter__(self): - return self - - def next(self): - t = self.token() - if t is None: - raise StopIteration - return t - - __next__ = next - -# ----------------------------------------------------------------------------- -# ==== Lex Builder === -# -# The functions and classes below are used to collect lexing information -# and build a Lexer object from it. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# _get_regex(func) -# -# Returns the regular expression assigned to a function either as a doc string -# or as a .regex attribute attached by the @TOKEN decorator. -# ----------------------------------------------------------------------------- -def _get_regex(func): - return getattr(func, 'regex', func.__doc__) - -# ----------------------------------------------------------------------------- -# get_caller_module_dict() -# -# This function returns a dictionary containing all of the symbols defined within -# a caller further down the call stack. This is used to get the environment -# associated with the yacc() call if none was provided. -# ----------------------------------------------------------------------------- -def get_caller_module_dict(levels): - f = sys._getframe(levels) - ldict = f.f_globals.copy() - if f.f_globals != f.f_locals: - ldict.update(f.f_locals) - return ldict - -# ----------------------------------------------------------------------------- -# _funcs_to_names() -# -# Given a list of regular expression functions, this converts it to a list -# suitable for output to a table file -# ----------------------------------------------------------------------------- -def _funcs_to_names(funclist, namelist): - result = [] - for f, name in zip(funclist, namelist): - if f and f[0]: - result.append((name, f[1])) - else: - result.append(f) - return result - -# ----------------------------------------------------------------------------- -# _names_to_funcs() -# -# Given a list of regular expression function names, this converts it back to -# functions. -# ----------------------------------------------------------------------------- -def _names_to_funcs(namelist, fdict): - result = [] - for n in namelist: - if n and n[0]: - result.append((fdict[n[0]], n[1])) - else: - result.append(n) - return result - -# ----------------------------------------------------------------------------- -# _form_master_re() -# -# This function takes a list of all of the regex components and attempts to -# form the master regular expression. Given limitations in the Python re -# module, it may be necessary to break the master regex into separate expressions. -# ----------------------------------------------------------------------------- -def _form_master_re(relist, reflags, ldict, toknames): - if not relist: - return [] - regex = '|'.join(relist) - try: - lexre = re.compile(regex, reflags) - - # Build the index to function map for the matching engine - lexindexfunc = [None] * (max(lexre.groupindex.values()) + 1) - lexindexnames = lexindexfunc[:] - - for f, i in lexre.groupindex.items(): - handle = ldict.get(f, None) - if type(handle) in (types.FunctionType, types.MethodType): - lexindexfunc[i] = (handle, toknames[f]) - lexindexnames[i] = f - elif handle is not None: - lexindexnames[i] = f - if f.find('ignore_') > 0: - lexindexfunc[i] = (None, None) - else: - lexindexfunc[i] = (None, toknames[f]) - - return [(lexre, lexindexfunc)], [regex], [lexindexnames] - except Exception: - m = int(len(relist)/2) - if m == 0: - m = 1 - llist, lre, lnames = _form_master_re(relist[:m], reflags, ldict, toknames) - rlist, rre, rnames = _form_master_re(relist[m:], reflags, ldict, toknames) - return (llist+rlist), (lre+rre), (lnames+rnames) - -# ----------------------------------------------------------------------------- -# def _statetoken(s,names) -# -# Given a declaration name s of the form "t_" and a dictionary whose keys are -# state names, this function returns a tuple (states,tokenname) where states -# is a tuple of state names and tokenname is the name of the token. For example, -# calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') -# ----------------------------------------------------------------------------- -def _statetoken(s, names): - nonstate = 1 - parts = s.split('_') - for i, part in enumerate(parts[1:], 1): - if part not in names and part != 'ANY': - break - - if i > 1: - states = tuple(parts[1:i]) - else: - states = ('INITIAL',) - - if 'ANY' in states: - states = tuple(names) - - tokenname = '_'.join(parts[i:]) - return (states, tokenname) - - -# ----------------------------------------------------------------------------- -# LexerReflect() -# -# This class represents information needed to build a lexer as extracted from a -# user's input file. -# ----------------------------------------------------------------------------- -class LexerReflect(object): - def __init__(self, ldict, log=None, reflags=0): - self.ldict = ldict - self.error_func = None - self.tokens = [] - self.reflags = reflags - self.stateinfo = {'INITIAL': 'inclusive'} - self.modules = set() - self.error = False - self.log = PlyLogger(sys.stderr) if log is None else log - - # Get all of the basic information - def get_all(self): - self.get_tokens() - self.get_literals() - self.get_states() - self.get_rules() - - # Validate all of the information - def validate_all(self): - self.validate_tokens() - self.validate_literals() - self.validate_rules() - return self.error - - # Get the tokens map - def get_tokens(self): - tokens = self.ldict.get('tokens', None) - if not tokens: - self.log.error('No token list is defined') - self.error = True - return - - if not isinstance(tokens, (list, tuple)): - self.log.error('tokens must be a list or tuple') - self.error = True - return - - if not tokens: - self.log.error('tokens is empty') - self.error = True - return - - self.tokens = tokens - - # Validate the tokens - def validate_tokens(self): - terminals = {} - for n in self.tokens: - if not _is_identifier.match(n): - self.log.error("Bad token name '%s'", n) - self.error = True - if n in terminals: - self.log.warning("Token '%s' multiply defined", n) - terminals[n] = 1 - - # Get the literals specifier - def get_literals(self): - self.literals = self.ldict.get('literals', '') - if not self.literals: - self.literals = '' - - # Validate literals - def validate_literals(self): - try: - for c in self.literals: - if not isinstance(c, StringTypes) or len(c) > 1: - self.log.error('Invalid literal %s. Must be a single character', repr(c)) - self.error = True - - except TypeError: - self.log.error('Invalid literals specification. literals must be a sequence of characters') - self.error = True - - def get_states(self): - self.states = self.ldict.get('states', None) - # Build statemap - if self.states: - if not isinstance(self.states, (tuple, list)): - self.log.error('states must be defined as a tuple or list') - self.error = True - else: - for s in self.states: - if not isinstance(s, tuple) or len(s) != 2: - self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')", repr(s)) - self.error = True - continue - name, statetype = s - if not isinstance(name, StringTypes): - self.log.error('State name %s must be a string', repr(name)) - self.error = True - continue - if not (statetype == 'inclusive' or statetype == 'exclusive'): - self.log.error("State type for state %s must be 'inclusive' or 'exclusive'", name) - self.error = True - continue - if name in self.stateinfo: - self.log.error("State '%s' already defined", name) - self.error = True - continue - self.stateinfo[name] = statetype - - # Get all of the symbols with a t_ prefix and sort them into various - # categories (functions, strings, error functions, and ignore characters) - - def get_rules(self): - tsymbols = [f for f in self.ldict if f[:2] == 't_'] - - # Now build up a list of functions and a list of strings - self.toknames = {} # Mapping of symbols to token names - self.funcsym = {} # Symbols defined as functions - self.strsym = {} # Symbols defined as strings - self.ignore = {} # Ignore strings by state - self.errorf = {} # Error functions by state - self.eoff = {} # EOF functions by state - - for s in self.stateinfo: - self.funcsym[s] = [] - self.strsym[s] = [] - - if len(tsymbols) == 0: - self.log.error('No rules of the form t_rulename are defined') - self.error = True - return - - for f in tsymbols: - t = self.ldict[f] - states, tokname = _statetoken(f, self.stateinfo) - self.toknames[f] = tokname - - if hasattr(t, '__call__'): - if tokname == 'error': - for s in states: - self.errorf[s] = t - elif tokname == 'eof': - for s in states: - self.eoff[s] = t - elif tokname == 'ignore': - line = t.__code__.co_firstlineno - file = t.__code__.co_filename - self.log.error("%s:%d: Rule '%s' must be defined as a string", file, line, t.__name__) - self.error = True - else: - for s in states: - self.funcsym[s].append((f, t)) - elif isinstance(t, StringTypes): - if tokname == 'ignore': - for s in states: - self.ignore[s] = t - if '\\' in t: - self.log.warning("%s contains a literal backslash '\\'", f) - - elif tokname == 'error': - self.log.error("Rule '%s' must be defined as a function", f) - self.error = True - else: - for s in states: - self.strsym[s].append((f, t)) - else: - self.log.error('%s not defined as a function or string', f) - self.error = True - - # Sort the functions by line number - for f in self.funcsym.values(): - f.sort(key=lambda x: x[1].__code__.co_firstlineno) - - # Sort the strings by regular expression length - for s in self.strsym.values(): - s.sort(key=lambda x: len(x[1]), reverse=True) - - # Validate all of the t_rules collected - def validate_rules(self): - for state in self.stateinfo: - # Validate all rules defined by functions - - for fname, f in self.funcsym[state]: - line = f.__code__.co_firstlineno - file = f.__code__.co_filename - module = inspect.getmodule(f) - self.modules.add(module) - - tokname = self.toknames[fname] - if isinstance(f, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - nargs = f.__code__.co_argcount - if nargs > reqargs: - self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) - self.error = True - continue - - if nargs < reqargs: - self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) - self.error = True - continue - - if not _get_regex(f): - self.log.error("%s:%d: No regular expression defined for rule '%s'", file, line, f.__name__) - self.error = True - continue - - try: - c = re.compile('(?P<%s>%s)' % (fname, _get_regex(f)), self.reflags) - if c.match(''): - self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file, line, f.__name__) - self.error = True - except re.error as e: - self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file, line, f.__name__, e) - if '#' in _get_regex(f): - self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'", file, line, f.__name__) - self.error = True - - # Validate all rules defined by strings - for name, r in self.strsym[state]: - tokname = self.toknames[name] - if tokname == 'error': - self.log.error("Rule '%s' must be defined as a function", name) - self.error = True - continue - - if tokname not in self.tokens and tokname.find('ignore_') < 0: - self.log.error("Rule '%s' defined for an unspecified token %s", name, tokname) - self.error = True - continue - - try: - c = re.compile('(?P<%s>%s)' % (name, r), self.reflags) - if (c.match('')): - self.log.error("Regular expression for rule '%s' matches empty string", name) - self.error = True - except re.error as e: - self.log.error("Invalid regular expression for rule '%s'. %s", name, e) - if '#' in r: - self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'", name) - self.error = True - - if not self.funcsym[state] and not self.strsym[state]: - self.log.error("No rules defined for state '%s'", state) - self.error = True - - # Validate the error function - efunc = self.errorf.get(state, None) - if efunc: - f = efunc - line = f.__code__.co_firstlineno - file = f.__code__.co_filename - module = inspect.getmodule(f) - self.modules.add(module) - - if isinstance(f, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - nargs = f.__code__.co_argcount - if nargs > reqargs: - self.log.error("%s:%d: Rule '%s' has too many arguments", file, line, f.__name__) - self.error = True - - if nargs < reqargs: - self.log.error("%s:%d: Rule '%s' requires an argument", file, line, f.__name__) - self.error = True - - for module in self.modules: - self.validate_module(module) - - # ----------------------------------------------------------------------------- - # validate_module() - # - # This checks to see if there are duplicated t_rulename() functions or strings - # in the parser input file. This is done using a simple regular expression - # match on each line in the source code of the given module. - # ----------------------------------------------------------------------------- - - def validate_module(self, module): - try: - lines, linen = inspect.getsourcelines(module) - except IOError: - return - - fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') - sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') - - counthash = {} - linen += 1 - for line in lines: - m = fre.match(line) - if not m: - m = sre.match(line) - if m: - name = m.group(1) - prev = counthash.get(name) - if not prev: - counthash[name] = linen - else: - filename = inspect.getsourcefile(module) - self.log.error('%s:%d: Rule %s redefined. Previously defined on line %d', filename, linen, name, prev) - self.error = True - linen += 1 - -# ----------------------------------------------------------------------------- -# lex(module) -# -# Build all of the regular expression rules from definitions in the supplied module -# ----------------------------------------------------------------------------- -def lex(module=None, object=None, debug=False, optimize=False, lextab='lextab', - reflags=int(re.VERBOSE), nowarn=False, outputdir=None, debuglog=None, errorlog=None): - - if lextab is None: - lextab = 'lextab' - - global lexer - - ldict = None - stateinfo = {'INITIAL': 'inclusive'} - lexobj = Lexer() - lexobj.lexoptimize = optimize - global token, input - - if errorlog is None: - errorlog = PlyLogger(sys.stderr) - - if debug: - if debuglog is None: - debuglog = PlyLogger(sys.stderr) - - # Get the module dictionary used for the lexer - if object: - module = object - - # Get the module dictionary used for the parser - if module: - _items = [(k, getattr(module, k)) for k in dir(module)] - ldict = dict(_items) - # If no __file__ attribute is available, try to obtain it from the __module__ instead - if '__file__' not in ldict: - ldict['__file__'] = sys.modules[ldict['__module__']].__file__ - else: - ldict = get_caller_module_dict(2) - - # Determine if the module is package of a package or not. - # If so, fix the tabmodule setting so that tables load correctly - pkg = ldict.get('__package__') - if pkg and isinstance(lextab, str): - if '.' not in lextab: - lextab = pkg + '.' + lextab - - # Collect parser information from the dictionary - linfo = LexerReflect(ldict, log=errorlog, reflags=reflags) - linfo.get_all() - if not optimize: - if linfo.validate_all(): - raise SyntaxError("Can't build lexer") - - if optimize and lextab: - try: - lexobj.readtab(lextab, ldict) - token = lexobj.token - input = lexobj.input - lexer = lexobj - return lexobj - - except ImportError: - pass - - # Dump some basic debugging information - if debug: - debuglog.info('lex: tokens = %r', linfo.tokens) - debuglog.info('lex: literals = %r', linfo.literals) - debuglog.info('lex: states = %r', linfo.stateinfo) - - # Build a dictionary of valid token names - lexobj.lextokens = set() - for n in linfo.tokens: - lexobj.lextokens.add(n) - - # Get literals specification - if isinstance(linfo.literals, (list, tuple)): - lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) - else: - lexobj.lexliterals = linfo.literals - - lexobj.lextokens_all = lexobj.lextokens | set(lexobj.lexliterals) - - # Get the stateinfo dictionary - stateinfo = linfo.stateinfo - - regexs = {} - # Build the master regular expressions - for state in stateinfo: - regex_list = [] - - # Add rules defined by functions first - for fname, f in linfo.funcsym[state]: - line = f.__code__.co_firstlineno - file = f.__code__.co_filename - regex_list.append('(?P<%s>%s)' % (fname, _get_regex(f))) - if debug: - debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", fname, _get_regex(f), state) - - # Now add all of the simple rules - for name, r in linfo.strsym[state]: - regex_list.append('(?P<%s>%s)' % (name, r)) - if debug: - debuglog.info("lex: Adding rule %s -> '%s' (state '%s')", name, r, state) - - regexs[state] = regex_list - - # Build the master regular expressions - - if debug: - debuglog.info('lex: ==== MASTER REGEXS FOLLOW ====') - - for state in regexs: - lexre, re_text, re_names = _form_master_re(regexs[state], reflags, ldict, linfo.toknames) - lexobj.lexstatere[state] = lexre - lexobj.lexstateretext[state] = re_text - lexobj.lexstaterenames[state] = re_names - if debug: - for i, text in enumerate(re_text): - debuglog.info("lex: state '%s' : regex[%d] = '%s'", state, i, text) - - # For inclusive states, we need to add the regular expressions from the INITIAL state - for state, stype in stateinfo.items(): - if state != 'INITIAL' and stype == 'inclusive': - lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) - lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) - lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) - - lexobj.lexstateinfo = stateinfo - lexobj.lexre = lexobj.lexstatere['INITIAL'] - lexobj.lexretext = lexobj.lexstateretext['INITIAL'] - lexobj.lexreflags = reflags - - # Set up ignore variables - lexobj.lexstateignore = linfo.ignore - lexobj.lexignore = lexobj.lexstateignore.get('INITIAL', '') - - # Set up error functions - lexobj.lexstateerrorf = linfo.errorf - lexobj.lexerrorf = linfo.errorf.get('INITIAL', None) - if not lexobj.lexerrorf: - errorlog.warning('No t_error rule is defined') - - # Set up eof functions - lexobj.lexstateeoff = linfo.eoff - lexobj.lexeoff = linfo.eoff.get('INITIAL', None) - - # Check state information for ignore and error rules - for s, stype in stateinfo.items(): - if stype == 'exclusive': - if s not in linfo.errorf: - errorlog.warning("No error rule is defined for exclusive state '%s'", s) - if s not in linfo.ignore and lexobj.lexignore: - errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) - elif stype == 'inclusive': - if s not in linfo.errorf: - linfo.errorf[s] = linfo.errorf.get('INITIAL', None) - if s not in linfo.ignore: - linfo.ignore[s] = linfo.ignore.get('INITIAL', '') - - # Create global versions of the token() and input() functions - token = lexobj.token - input = lexobj.input - lexer = lexobj - - # If in optimize mode, we write the lextab - if lextab and optimize: - if outputdir is None: - # If no output directory is set, the location of the output files - # is determined according to the following rules: - # - If lextab specifies a package, files go into that package directory - # - Otherwise, files go in the same directory as the specifying module - if isinstance(lextab, types.ModuleType): - srcfile = lextab.__file__ - else: - if '.' not in lextab: - srcfile = ldict['__file__'] - else: - parts = lextab.split('.') - pkgname = '.'.join(parts[:-1]) - exec('import %s' % pkgname) - srcfile = getattr(sys.modules[pkgname], '__file__', '') - outputdir = os.path.dirname(srcfile) - try: - lexobj.writetab(lextab, outputdir) - except IOError as e: - errorlog.warning("Couldn't write lextab module %r. %s" % (lextab, e)) - - return lexobj - -# ----------------------------------------------------------------------------- -# runmain() -# -# This runs the lexer as a main program -# ----------------------------------------------------------------------------- - -def runmain(lexer=None, data=None): - if not data: - try: - filename = sys.argv[1] - f = open(filename) - data = f.read() - f.close() - except IndexError: - sys.stdout.write('Reading from standard input (type EOF to end):\n') - data = sys.stdin.read() - - if lexer: - _input = lexer.input - else: - _input = input - _input(data) - if lexer: - _token = lexer.token - else: - _token = token - - while True: - tok = _token() - if not tok: - break - sys.stdout.write('(%s,%r,%d,%d)\n' % (tok.type, tok.value, tok.lineno, tok.lexpos)) - -# ----------------------------------------------------------------------------- -# @TOKEN(regex) -# -# This decorator function can be used to set the regex expression on a function -# when its docstring might need to be set in an alternative way -# ----------------------------------------------------------------------------- - -def TOKEN(r): - def set_regex(f): - if hasattr(r, '__call__'): - f.regex = _get_regex(r) - else: - f.regex = r - return f - return set_regex - -# Alternative spelling of the TOKEN decorator -Token = TOKEN diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py deleted file mode 100644 index 20b4f2863..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/yacc.py +++ /dev/null @@ -1,3494 +0,0 @@ -# ----------------------------------------------------------------------------- -# ply: yacc.py -# -# Copyright (C) 2001-2017 -# David M. Beazley (Dabeaz LLC) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the David Beazley or Dabeaz LLC may be used to -# endorse or promote products derived from this software without -# specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# ----------------------------------------------------------------------------- -# -# This implements an LR parser that is constructed from grammar rules defined -# as Python functions. The grammer is specified by supplying the BNF inside -# Python documentation strings. The inspiration for this technique was borrowed -# from John Aycock's Spark parsing system. PLY might be viewed as cross between -# Spark and the GNU bison utility. -# -# The current implementation is only somewhat object-oriented. The -# LR parser itself is defined in terms of an object (which allows multiple -# parsers to co-exist). However, most of the variables used during table -# construction are defined in terms of global variables. Users shouldn't -# notice unless they are trying to define multiple parsers at the same -# time using threads (in which case they should have their head examined). -# -# This implementation supports both SLR and LALR(1) parsing. LALR(1) -# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), -# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, -# Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced -# by the more efficient DeRemer and Pennello algorithm. -# -# :::::::: WARNING ::::::: -# -# Construction of LR parsing tables is fairly complicated and expensive. -# To make this module run fast, a *LOT* of work has been put into -# optimization---often at the expensive of readability and what might -# consider to be good Python "coding style." Modify the code at your -# own risk! -# ---------------------------------------------------------------------------- - -import re -import types -import sys -import os.path -import inspect -import base64 -import warnings - -__version__ = '3.10' -__tabversion__ = '3.10' - -#----------------------------------------------------------------------------- -# === User configurable parameters === -# -# Change these to modify the default behavior of yacc (if you wish) -#----------------------------------------------------------------------------- - -yaccdebug = True # Debugging mode. If set, yacc generates a - # a 'parser.out' file in the current directory - -debug_file = 'parser.out' # Default name of the debugging file -tab_module = 'parsetab' # Default name of the table module -default_lr = 'LALR' # Default LR table generation method - -error_count = 3 # Number of symbols that must be shifted to leave recovery mode - -yaccdevel = False # Set to True if developing yacc. This turns off optimized - # implementations of certain functions. - -resultlimit = 40 # Size limit of results when running in debug mode. - -pickle_protocol = 0 # Protocol to use when writing pickle files - -# String type-checking compatibility -if sys.version_info[0] < 3: - string_types = basestring -else: - string_types = str - -MAXINT = sys.maxsize - -# This object is a stand-in for a logging object created by the -# logging module. PLY will use this by default to create things -# such as the parser.out file. If a user wants more detailed -# information, they can create their own logging object and pass -# it into PLY. - -class PlyLogger(object): - def __init__(self, f): - self.f = f - - def debug(self, msg, *args, **kwargs): - self.f.write((msg % args) + '\n') - - info = debug - - def warning(self, msg, *args, **kwargs): - self.f.write('WARNING: ' + (msg % args) + '\n') - - def error(self, msg, *args, **kwargs): - self.f.write('ERROR: ' + (msg % args) + '\n') - - critical = debug - -# Null logger is used when no output is generated. Does nothing. -class NullLogger(object): - def __getattribute__(self, name): - return self - - def __call__(self, *args, **kwargs): - return self - -# Exception raised for yacc-related errors -class YaccError(Exception): - pass - -# Format the result message that the parser produces when running in debug mode. -def format_result(r): - repr_str = repr(r) - if '\n' in repr_str: - repr_str = repr(repr_str) - if len(repr_str) > resultlimit: - repr_str = repr_str[:resultlimit] + ' ...' - result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str) - return result - -# Format stack entries when the parser is running in debug mode -def format_stack_entry(r): - repr_str = repr(r) - if '\n' in repr_str: - repr_str = repr(repr_str) - if len(repr_str) < 16: - return repr_str - else: - return '<%s @ 0x%x>' % (type(r).__name__, id(r)) - -# Panic mode error recovery support. This feature is being reworked--much of the -# code here is to offer a deprecation/backwards compatible transition - -_errok = None -_token = None -_restart = None -_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error(). -Instead, invoke the methods on the associated parser instance: - - def p_error(p): - ... - # Use parser.errok(), parser.token(), parser.restart() - ... - - parser = yacc.yacc() -''' - -def errok(): - warnings.warn(_warnmsg) - return _errok() - -def restart(): - warnings.warn(_warnmsg) - return _restart() - -def token(): - warnings.warn(_warnmsg) - return _token() - -# Utility function to call the p_error() function with some deprecation hacks -def call_errorfunc(errorfunc, token, parser): - global _errok, _token, _restart - _errok = parser.errok - _token = parser.token - _restart = parser.restart - r = errorfunc(token) - try: - del _errok, _token, _restart - except NameError: - pass - return r - -#----------------------------------------------------------------------------- -# === LR Parsing Engine === -# -# The following classes are used for the LR parser itself. These are not -# used during table construction and are independent of the actual LR -# table generation algorithm -#----------------------------------------------------------------------------- - -# This class is used to hold non-terminal grammar symbols during parsing. -# It normally has the following attributes set: -# .type = Grammar symbol type -# .value = Symbol value -# .lineno = Starting line number -# .endlineno = Ending line number (optional, set automatically) -# .lexpos = Starting lex position -# .endlexpos = Ending lex position (optional, set automatically) - -class YaccSymbol: - def __str__(self): - return self.type - - def __repr__(self): - return str(self) - -# This class is a wrapper around the objects actually passed to each -# grammar rule. Index lookup and assignment actually assign the -# .value attribute of the underlying YaccSymbol object. -# The lineno() method returns the line number of a given -# item (or 0 if not defined). The linespan() method returns -# a tuple of (startline,endline) representing the range of lines -# for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) -# representing the range of positional information for a symbol. - -class YaccProduction: - def __init__(self, s, stack=None): - self.slice = s - self.stack = stack - self.lexer = None - self.parser = None - - def __getitem__(self, n): - if isinstance(n, slice): - return [s.value for s in self.slice[n]] - elif n >= 0: - return self.slice[n].value - else: - return self.stack[n].value - - def __setitem__(self, n, v): - self.slice[n].value = v - - def __getslice__(self, i, j): - return [s.value for s in self.slice[i:j]] - - def __len__(self): - return len(self.slice) - - def lineno(self, n): - return getattr(self.slice[n], 'lineno', 0) - - def set_lineno(self, n, lineno): - self.slice[n].lineno = lineno - - def linespan(self, n): - startline = getattr(self.slice[n], 'lineno', 0) - endline = getattr(self.slice[n], 'endlineno', startline) - return startline, endline - - def lexpos(self, n): - return getattr(self.slice[n], 'lexpos', 0) - - def lexspan(self, n): - startpos = getattr(self.slice[n], 'lexpos', 0) - endpos = getattr(self.slice[n], 'endlexpos', startpos) - return startpos, endpos - - def error(self): - raise SyntaxError - -# ----------------------------------------------------------------------------- -# == LRParser == -# -# The LR Parsing engine. -# ----------------------------------------------------------------------------- - -class LRParser: - def __init__(self, lrtab, errorf): - self.productions = lrtab.lr_productions - self.action = lrtab.lr_action - self.goto = lrtab.lr_goto - self.errorfunc = errorf - self.set_defaulted_states() - self.errorok = True - - def errok(self): - self.errorok = True - - def restart(self): - del self.statestack[:] - del self.symstack[:] - sym = YaccSymbol() - sym.type = '$end' - self.symstack.append(sym) - self.statestack.append(0) - - # Defaulted state support. - # This method identifies parser states where there is only one possible reduction action. - # For such states, the parser can make a choose to make a rule reduction without consuming - # the next look-ahead token. This delayed invocation of the tokenizer can be useful in - # certain kinds of advanced parsing situations where the lexer and parser interact with - # each other or change states (i.e., manipulation of scope, lexer states, etc.). - # - # See: https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions - def set_defaulted_states(self): - self.defaulted_states = {} - for state, actions in self.action.items(): - rules = list(actions.values()) - if len(rules) == 1 and rules[0] < 0: - self.defaulted_states[state] = rules[0] - - def disable_defaulted_states(self): - self.defaulted_states = {} - - def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): - if debug or yaccdevel: - if isinstance(debug, int): - debug = PlyLogger(sys.stderr) - return self.parsedebug(input, lexer, debug, tracking, tokenfunc) - elif tracking: - return self.parseopt(input, lexer, debug, tracking, tokenfunc) - else: - return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc) - - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # parsedebug(). - # - # This is the debugging enabled version of parse(). All changes made to the - # parsing engine should be made here. Optimized versions of this function - # are automatically created by the ply/ygen.py script. This script cuts out - # sections enclosed in markers such as this: - # - # #--! DEBUG - # statements - # #--! DEBUG - # - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): - #--! parsedebug-start - lookahead = None # Current lookahead symbol - lookaheadstack = [] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - defaulted_states = self.defaulted_states # Local reference to defaulted states - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - #--! DEBUG - debug.info('PLY: PARSE DEBUG START') - #--! DEBUG - - # If no lexer was given, we will try to use the lex module - if not lexer: - from . import lex - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - if tokenfunc is None: - # Tokenize function - get_token = lexer.token - else: - get_token = tokenfunc - - # Set the parser() token method (sometimes used in error recovery) - self.token = get_token - - # Set up the state and symbol stacks - - statestack = [] # Stack of parsing states - self.statestack = statestack - symstack = [] # Stack of grammar symbols - self.symstack = symstack - - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = '$end' - symstack.append(sym) - state = 0 - while True: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - #--! DEBUG - debug.debug('') - debug.debug('State : %s', state) - #--! DEBUG - - if state not in defaulted_states: - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = '$end' - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - else: - t = defaulted_states[state] - #--! DEBUG - debug.debug('Defaulted state %s: Reduce using %d', state, -t) - #--! DEBUG - - #--! DEBUG - debug.debug('Stack : %s', - ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) - #--! DEBUG - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - #--! DEBUG - debug.debug('Action : Shift and goto state %s', t) - #--! DEBUG - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: - errorcount -= 1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - #--! DEBUG - if plen: - debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, - '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']', - goto[statestack[-1-plen]][pname]) - else: - debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [], - goto[statestack[-1]][pname]) - - #--! DEBUG - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - #--! TRACKING - if tracking: - t1 = targ[1] - sym.lineno = t1.lineno - sym.lexpos = t1.lexpos - t1 = targ[-1] - sym.endlineno = getattr(t1, 'endlineno', t1.lineno) - sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) - #--! TRACKING - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - self.state = state - p.callable(pslice) - del statestack[-plen:] - #--! DEBUG - debug.info('Result : %s', format_result(pslice[0])) - #--! DEBUG - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - symstack.extend(targ[1:-1]) # Put the production slice back on the stack - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - else: - - #--! TRACKING - if tracking: - sym.lineno = lexer.lineno - sym.lexpos = lexer.lexpos - #--! TRACKING - - targ = [sym] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - self.state = state - p.callable(pslice) - #--! DEBUG - debug.info('Result : %s', format_result(pslice[0])) - #--! DEBUG - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if t == 0: - n = symstack[-1] - result = getattr(n, 'value', None) - #--! DEBUG - debug.info('Done : Returning %s', format_result(result)) - debug.info('PLY: PARSE DEBUG END') - #--! DEBUG - return result - - if t is None: - - #--! DEBUG - debug.error('Error : %s', - ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) - #--! DEBUG - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = False - errtoken = lookahead - if errtoken.type == '$end': - errtoken = None # End of file! - if self.errorfunc: - if errtoken and not hasattr(errtoken, 'lexer'): - errtoken.lexer = lexer - self.state = state - tok = call_errorfunc(self.errorfunc, errtoken, self) - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken, 'lineno'): - lineno = lookahead.lineno - else: - lineno = 0 - if lineno: - sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) - else: - sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) - else: - sys.stderr.write('yacc: Parse error in input. EOF\n') - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != '$end': - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == '$end': - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - #--! TRACKING - if tracking: - sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) - sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) - #--! TRACKING - lookahead = None - continue - - # Create the error symbol for the first time and make it the new lookahead symbol - t = YaccSymbol() - t.type = 'error' - - if hasattr(lookahead, 'lineno'): - t.lineno = t.endlineno = lookahead.lineno - if hasattr(lookahead, 'lexpos'): - t.lexpos = t.endlexpos = lookahead.lexpos - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - sym = symstack.pop() - #--! TRACKING - if tracking: - lookahead.lineno = sym.lineno - lookahead.lexpos = sym.lexpos - #--! TRACKING - statestack.pop() - state = statestack[-1] - - continue - - # Call an error function here - raise RuntimeError('yacc: internal parser error!!!\n') - - #--! parsedebug-end - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # parseopt(). - # - # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY! - # This code is automatically generated by the ply/ygen.py script. Make - # changes to the parsedebug() method instead. - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): - #--! parseopt-start - lookahead = None # Current lookahead symbol - lookaheadstack = [] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - defaulted_states = self.defaulted_states # Local reference to defaulted states - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - - # If no lexer was given, we will try to use the lex module - if not lexer: - from . import lex - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - if tokenfunc is None: - # Tokenize function - get_token = lexer.token - else: - get_token = tokenfunc - - # Set the parser() token method (sometimes used in error recovery) - self.token = get_token - - # Set up the state and symbol stacks - - statestack = [] # Stack of parsing states - self.statestack = statestack - symstack = [] # Stack of grammar symbols - self.symstack = symstack - - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = '$end' - symstack.append(sym) - state = 0 - while True: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - - if state not in defaulted_states: - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = '$end' - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - else: - t = defaulted_states[state] - - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: - errorcount -= 1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - #--! TRACKING - if tracking: - t1 = targ[1] - sym.lineno = t1.lineno - sym.lexpos = t1.lexpos - t1 = targ[-1] - sym.endlineno = getattr(t1, 'endlineno', t1.lineno) - sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos) - #--! TRACKING - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - self.state = state - p.callable(pslice) - del statestack[-plen:] - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - symstack.extend(targ[1:-1]) # Put the production slice back on the stack - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - else: - - #--! TRACKING - if tracking: - sym.lineno = lexer.lineno - sym.lexpos = lexer.lexpos - #--! TRACKING - - targ = [sym] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - self.state = state - p.callable(pslice) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if t == 0: - n = symstack[-1] - result = getattr(n, 'value', None) - return result - - if t is None: - - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = False - errtoken = lookahead - if errtoken.type == '$end': - errtoken = None # End of file! - if self.errorfunc: - if errtoken and not hasattr(errtoken, 'lexer'): - errtoken.lexer = lexer - self.state = state - tok = call_errorfunc(self.errorfunc, errtoken, self) - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken, 'lineno'): - lineno = lookahead.lineno - else: - lineno = 0 - if lineno: - sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) - else: - sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) - else: - sys.stderr.write('yacc: Parse error in input. EOF\n') - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != '$end': - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == '$end': - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - #--! TRACKING - if tracking: - sym.endlineno = getattr(lookahead, 'lineno', sym.lineno) - sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos) - #--! TRACKING - lookahead = None - continue - - # Create the error symbol for the first time and make it the new lookahead symbol - t = YaccSymbol() - t.type = 'error' - - if hasattr(lookahead, 'lineno'): - t.lineno = t.endlineno = lookahead.lineno - if hasattr(lookahead, 'lexpos'): - t.lexpos = t.endlexpos = lookahead.lexpos - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - sym = symstack.pop() - #--! TRACKING - if tracking: - lookahead.lineno = sym.lineno - lookahead.lexpos = sym.lexpos - #--! TRACKING - statestack.pop() - state = statestack[-1] - - continue - - # Call an error function here - raise RuntimeError('yacc: internal parser error!!!\n') - - #--! parseopt-end - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # parseopt_notrack(). - # - # Optimized version of parseopt() with line number tracking removed. - # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated - # by the ply/ygen.py script. Make changes to the parsedebug() method instead. - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None): - #--! parseopt-notrack-start - lookahead = None # Current lookahead symbol - lookaheadstack = [] # Stack of lookahead symbols - actions = self.action # Local reference to action table (to avoid lookup on self.) - goto = self.goto # Local reference to goto table (to avoid lookup on self.) - prod = self.productions # Local reference to production list (to avoid lookup on self.) - defaulted_states = self.defaulted_states # Local reference to defaulted states - pslice = YaccProduction(None) # Production object passed to grammar rules - errorcount = 0 # Used during error recovery - - - # If no lexer was given, we will try to use the lex module - if not lexer: - from . import lex - lexer = lex.lexer - - # Set up the lexer and parser objects on pslice - pslice.lexer = lexer - pslice.parser = self - - # If input was supplied, pass to lexer - if input is not None: - lexer.input(input) - - if tokenfunc is None: - # Tokenize function - get_token = lexer.token - else: - get_token = tokenfunc - - # Set the parser() token method (sometimes used in error recovery) - self.token = get_token - - # Set up the state and symbol stacks - - statestack = [] # Stack of parsing states - self.statestack = statestack - symstack = [] # Stack of grammar symbols - self.symstack = symstack - - pslice.stack = symstack # Put in the production - errtoken = None # Err token - - # The start state is assumed to be (0,$end) - - statestack.append(0) - sym = YaccSymbol() - sym.type = '$end' - symstack.append(sym) - state = 0 - while True: - # Get the next symbol on the input. If a lookahead symbol - # is already set, we just use that. Otherwise, we'll pull - # the next token off of the lookaheadstack or from the lexer - - - if state not in defaulted_states: - if not lookahead: - if not lookaheadstack: - lookahead = get_token() # Get the next token - else: - lookahead = lookaheadstack.pop() - if not lookahead: - lookahead = YaccSymbol() - lookahead.type = '$end' - - # Check the action table - ltype = lookahead.type - t = actions[state].get(ltype) - else: - t = defaulted_states[state] - - - if t is not None: - if t > 0: - # shift a symbol on the stack - statestack.append(t) - state = t - - - symstack.append(lookahead) - lookahead = None - - # Decrease error count on successful shift - if errorcount: - errorcount -= 1 - continue - - if t < 0: - # reduce a symbol on the stack, emit a production - p = prod[-t] - pname = p.name - plen = p.len - - # Get production function - sym = YaccSymbol() - sym.type = pname # Production name - sym.value = None - - - if plen: - targ = symstack[-plen-1:] - targ[0] = sym - - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # below as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - del symstack[-plen:] - self.state = state - p.callable(pslice) - del statestack[-plen:] - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - symstack.extend(targ[1:-1]) # Put the production slice back on the stack - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - else: - - - targ = [sym] - - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # The code enclosed in this section is duplicated - # above as a performance optimization. Make sure - # changes get made in both locations. - - pslice.slice = targ - - try: - # Call the grammar rule with our special slice object - self.state = state - p.callable(pslice) - symstack.append(sym) - state = goto[statestack[-1]][pname] - statestack.append(state) - except SyntaxError: - # If an error was set. Enter error recovery state - lookaheadstack.append(lookahead) # Save the current lookahead token - statestack.pop() # Pop back one state (before the reduce) - state = statestack[-1] - sym.type = 'error' - sym.value = 'error' - lookahead = sym - errorcount = error_count - self.errorok = False - - continue - # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - - if t == 0: - n = symstack[-1] - result = getattr(n, 'value', None) - return result - - if t is None: - - - # We have some kind of parsing error here. To handle - # this, we are going to push the current token onto - # the tokenstack and replace it with an 'error' token. - # If there are any synchronization rules, they may - # catch it. - # - # In addition to pushing the error token, we call call - # the user defined p_error() function if this is the - # first syntax error. This function is only called if - # errorcount == 0. - if errorcount == 0 or self.errorok: - errorcount = error_count - self.errorok = False - errtoken = lookahead - if errtoken.type == '$end': - errtoken = None # End of file! - if self.errorfunc: - if errtoken and not hasattr(errtoken, 'lexer'): - errtoken.lexer = lexer - self.state = state - tok = call_errorfunc(self.errorfunc, errtoken, self) - if self.errorok: - # User must have done some kind of panic - # mode recovery on their own. The - # returned token is the next lookahead - lookahead = tok - errtoken = None - continue - else: - if errtoken: - if hasattr(errtoken, 'lineno'): - lineno = lookahead.lineno - else: - lineno = 0 - if lineno: - sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type)) - else: - sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type) - else: - sys.stderr.write('yacc: Parse error in input. EOF\n') - return - - else: - errorcount = error_count - - # case 1: the statestack only has 1 entry on it. If we're in this state, the - # entire parse has been rolled back and we're completely hosed. The token is - # discarded and we just keep going. - - if len(statestack) <= 1 and lookahead.type != '$end': - lookahead = None - errtoken = None - state = 0 - # Nuke the pushback stack - del lookaheadstack[:] - continue - - # case 2: the statestack has a couple of entries on it, but we're - # at the end of the file. nuke the top entry and generate an error token - - # Start nuking entries on the stack - if lookahead.type == '$end': - # Whoa. We're really hosed here. Bail out - return - - if lookahead.type != 'error': - sym = symstack[-1] - if sym.type == 'error': - # Hmmm. Error is on top of stack, we'll just nuke input - # symbol and continue - lookahead = None - continue - - # Create the error symbol for the first time and make it the new lookahead symbol - t = YaccSymbol() - t.type = 'error' - - if hasattr(lookahead, 'lineno'): - t.lineno = t.endlineno = lookahead.lineno - if hasattr(lookahead, 'lexpos'): - t.lexpos = t.endlexpos = lookahead.lexpos - t.value = lookahead - lookaheadstack.append(lookahead) - lookahead = t - else: - sym = symstack.pop() - statestack.pop() - state = statestack[-1] - - continue - - # Call an error function here - raise RuntimeError('yacc: internal parser error!!!\n') - - #--! parseopt-notrack-end - -# ----------------------------------------------------------------------------- -# === Grammar Representation === -# -# The following functions, classes, and variables are used to represent and -# manipulate the rules that make up a grammar. -# ----------------------------------------------------------------------------- - -# regex matching identifiers -_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') - -# ----------------------------------------------------------------------------- -# class Production: -# -# This class stores the raw information about a single production or grammar rule. -# A grammar rule refers to a specification such as this: -# -# expr : expr PLUS term -# -# Here are the basic attributes defined on all productions -# -# name - Name of the production. For example 'expr' -# prod - A list of symbols on the right side ['expr','PLUS','term'] -# prec - Production precedence level -# number - Production number. -# func - Function that executes on reduce -# file - File where production function is defined -# lineno - Line number where production function is defined -# -# The following attributes are defined or optional. -# -# len - Length of the production (number of symbols on right hand side) -# usyms - Set of unique symbols found in the production -# ----------------------------------------------------------------------------- - -class Production(object): - reduced = 0 - def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0): - self.name = name - self.prod = tuple(prod) - self.number = number - self.func = func - self.callable = None - self.file = file - self.line = line - self.prec = precedence - - # Internal settings used during table construction - - self.len = len(self.prod) # Length of the production - - # Create a list of unique production symbols used in the production - self.usyms = [] - for s in self.prod: - if s not in self.usyms: - self.usyms.append(s) - - # List of all LR items for the production - self.lr_items = [] - self.lr_next = None - - # Create a string representation - if self.prod: - self.str = '%s -> %s' % (self.name, ' '.join(self.prod)) - else: - self.str = '%s -> ' % self.name - - def __str__(self): - return self.str - - def __repr__(self): - return 'Production(' + str(self) + ')' - - def __len__(self): - return len(self.prod) - - def __nonzero__(self): - return 1 - - def __getitem__(self, index): - return self.prod[index] - - # Return the nth lr_item from the production (or None if at the end) - def lr_item(self, n): - if n > len(self.prod): - return None - p = LRItem(self, n) - # Precompute the list of productions immediately following. - try: - p.lr_after = Prodnames[p.prod[n+1]] - except (IndexError, KeyError): - p.lr_after = [] - try: - p.lr_before = p.prod[n-1] - except IndexError: - p.lr_before = None - return p - - # Bind the production function name to a callable - def bind(self, pdict): - if self.func: - self.callable = pdict[self.func] - -# This class serves as a minimal standin for Production objects when -# reading table data from files. It only contains information -# actually used by the LR parsing engine, plus some additional -# debugging information. -class MiniProduction(object): - def __init__(self, str, name, len, func, file, line): - self.name = name - self.len = len - self.func = func - self.callable = None - self.file = file - self.line = line - self.str = str - - def __str__(self): - return self.str - - def __repr__(self): - return 'MiniProduction(%s)' % self.str - - # Bind the production function name to a callable - def bind(self, pdict): - if self.func: - self.callable = pdict[self.func] - - -# ----------------------------------------------------------------------------- -# class LRItem -# -# This class represents a specific stage of parsing a production rule. For -# example: -# -# expr : expr . PLUS term -# -# In the above, the "." represents the current location of the parse. Here -# basic attributes: -# -# name - Name of the production. For example 'expr' -# prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] -# number - Production number. -# -# lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' -# then lr_next refers to 'expr -> expr PLUS . term' -# lr_index - LR item index (location of the ".") in the prod list. -# lookaheads - LALR lookahead symbols for this item -# len - Length of the production (number of symbols on right hand side) -# lr_after - List of all productions that immediately follow -# lr_before - Grammar symbol immediately before -# ----------------------------------------------------------------------------- - -class LRItem(object): - def __init__(self, p, n): - self.name = p.name - self.prod = list(p.prod) - self.number = p.number - self.lr_index = n - self.lookaheads = {} - self.prod.insert(n, '.') - self.prod = tuple(self.prod) - self.len = len(self.prod) - self.usyms = p.usyms - - def __str__(self): - if self.prod: - s = '%s -> %s' % (self.name, ' '.join(self.prod)) - else: - s = '%s -> ' % self.name - return s - - def __repr__(self): - return 'LRItem(' + str(self) + ')' - -# ----------------------------------------------------------------------------- -# rightmost_terminal() -# -# Return the rightmost terminal from a list of symbols. Used in add_production() -# ----------------------------------------------------------------------------- -def rightmost_terminal(symbols, terminals): - i = len(symbols) - 1 - while i >= 0: - if symbols[i] in terminals: - return symbols[i] - i -= 1 - return None - -# ----------------------------------------------------------------------------- -# === GRAMMAR CLASS === -# -# The following class represents the contents of the specified grammar along -# with various computed properties such as first sets, follow sets, LR items, etc. -# This data is used for critical parts of the table generation process later. -# ----------------------------------------------------------------------------- - -class GrammarError(YaccError): - pass - -class Grammar(object): - def __init__(self, terminals): - self.Productions = [None] # A list of all of the productions. The first - # entry is always reserved for the purpose of - # building an augmented grammar - - self.Prodnames = {} # A dictionary mapping the names of nonterminals to a list of all - # productions of that nonterminal. - - self.Prodmap = {} # A dictionary that is only used to detect duplicate - # productions. - - self.Terminals = {} # A dictionary mapping the names of terminal symbols to a - # list of the rules where they are used. - - for term in terminals: - self.Terminals[term] = [] - - self.Terminals['error'] = [] - - self.Nonterminals = {} # A dictionary mapping names of nonterminals to a list - # of rule numbers where they are used. - - self.First = {} # A dictionary of precomputed FIRST(x) symbols - - self.Follow = {} # A dictionary of precomputed FOLLOW(x) symbols - - self.Precedence = {} # Precedence rules for each terminal. Contains tuples of the - # form ('right',level) or ('nonassoc', level) or ('left',level) - - self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer. - # This is only used to provide error checking and to generate - # a warning about unused precedence rules. - - self.Start = None # Starting symbol for the grammar - - - def __len__(self): - return len(self.Productions) - - def __getitem__(self, index): - return self.Productions[index] - - # ----------------------------------------------------------------------------- - # set_precedence() - # - # Sets the precedence for a given terminal. assoc is the associativity such as - # 'left','right', or 'nonassoc'. level is a numeric level. - # - # ----------------------------------------------------------------------------- - - def set_precedence(self, term, assoc, level): - assert self.Productions == [None], 'Must call set_precedence() before add_production()' - if term in self.Precedence: - raise GrammarError('Precedence already specified for terminal %r' % term) - if assoc not in ['left', 'right', 'nonassoc']: - raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") - self.Precedence[term] = (assoc, level) - - # ----------------------------------------------------------------------------- - # add_production() - # - # Given an action function, this function assembles a production rule and - # computes its precedence level. - # - # The production rule is supplied as a list of symbols. For example, - # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and - # symbols ['expr','PLUS','term']. - # - # Precedence is determined by the precedence of the right-most non-terminal - # or the precedence of a terminal specified by %prec. - # - # A variety of error checks are performed to make sure production symbols - # are valid and that %prec is used correctly. - # ----------------------------------------------------------------------------- - - def add_production(self, prodname, syms, func=None, file='', line=0): - - if prodname in self.Terminals: - raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname)) - if prodname == 'error': - raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname)) - if not _is_identifier.match(prodname): - raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname)) - - # Look for literal tokens - for n, s in enumerate(syms): - if s[0] in "'\"": - try: - c = eval(s) - if (len(c) > 1): - raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' % - (file, line, s, prodname)) - if c not in self.Terminals: - self.Terminals[c] = [] - syms[n] = c - continue - except SyntaxError: - pass - if not _is_identifier.match(s) and s != '%prec': - raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname)) - - # Determine the precedence level - if '%prec' in syms: - if syms[-1] == '%prec': - raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line)) - if syms[-2] != '%prec': - raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' % - (file, line)) - precname = syms[-1] - prodprec = self.Precedence.get(precname) - if not prodprec: - raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname)) - else: - self.UsedPrecedence.add(precname) - del syms[-2:] # Drop %prec from the rule - else: - # If no %prec, precedence is determined by the rightmost terminal symbol - precname = rightmost_terminal(syms, self.Terminals) - prodprec = self.Precedence.get(precname, ('right', 0)) - - # See if the rule is already in the rulemap - map = '%s -> %s' % (prodname, syms) - if map in self.Prodmap: - m = self.Prodmap[map] - raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) + - 'Previous definition at %s:%d' % (m.file, m.line)) - - # From this point on, everything is valid. Create a new Production instance - pnumber = len(self.Productions) - if prodname not in self.Nonterminals: - self.Nonterminals[prodname] = [] - - # Add the production number to Terminals and Nonterminals - for t in syms: - if t in self.Terminals: - self.Terminals[t].append(pnumber) - else: - if t not in self.Nonterminals: - self.Nonterminals[t] = [] - self.Nonterminals[t].append(pnumber) - - # Create a production and add it to the list of productions - p = Production(pnumber, prodname, syms, prodprec, func, file, line) - self.Productions.append(p) - self.Prodmap[map] = p - - # Add to the global productions list - try: - self.Prodnames[prodname].append(p) - except KeyError: - self.Prodnames[prodname] = [p] - - # ----------------------------------------------------------------------------- - # set_start() - # - # Sets the starting symbol and creates the augmented grammar. Production - # rule 0 is S' -> start where start is the start symbol. - # ----------------------------------------------------------------------------- - - def set_start(self, start=None): - if not start: - start = self.Productions[1].name - if start not in self.Nonterminals: - raise GrammarError('start symbol %s undefined' % start) - self.Productions[0] = Production(0, "S'", [start]) - self.Nonterminals[start].append(0) - self.Start = start - - # ----------------------------------------------------------------------------- - # find_unreachable() - # - # Find all of the nonterminal symbols that can't be reached from the starting - # symbol. Returns a list of nonterminals that can't be reached. - # ----------------------------------------------------------------------------- - - def find_unreachable(self): - - # Mark all symbols that are reachable from a symbol s - def mark_reachable_from(s): - if s in reachable: - return - reachable.add(s) - for p in self.Prodnames.get(s, []): - for r in p.prod: - mark_reachable_from(r) - - reachable = set() - mark_reachable_from(self.Productions[0].prod[0]) - return [s for s in self.Nonterminals if s not in reachable] - - # ----------------------------------------------------------------------------- - # infinite_cycles() - # - # This function looks at the various parsing rules and tries to detect - # infinite recursion cycles (grammar rules where there is no possible way - # to derive a string of only terminals). - # ----------------------------------------------------------------------------- - - def infinite_cycles(self): - terminates = {} - - # Terminals: - for t in self.Terminals: - terminates[t] = True - - terminates['$end'] = True - - # Nonterminals: - - # Initialize to false: - for n in self.Nonterminals: - terminates[n] = False - - # Then propagate termination until no change: - while True: - some_change = False - for (n, pl) in self.Prodnames.items(): - # Nonterminal n terminates iff any of its productions terminates. - for p in pl: - # Production p terminates iff all of its rhs symbols terminate. - for s in p.prod: - if not terminates[s]: - # The symbol s does not terminate, - # so production p does not terminate. - p_terminates = False - break - else: - # didn't break from the loop, - # so every symbol s terminates - # so production p terminates. - p_terminates = True - - if p_terminates: - # symbol n terminates! - if not terminates[n]: - terminates[n] = True - some_change = True - # Don't need to consider any more productions for this n. - break - - if not some_change: - break - - infinite = [] - for (s, term) in terminates.items(): - if not term: - if s not in self.Prodnames and s not in self.Terminals and s != 'error': - # s is used-but-not-defined, and we've already warned of that, - # so it would be overkill to say that it's also non-terminating. - pass - else: - infinite.append(s) - - return infinite - - # ----------------------------------------------------------------------------- - # undefined_symbols() - # - # Find all symbols that were used the grammar, but not defined as tokens or - # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol - # and prod is the production where the symbol was used. - # ----------------------------------------------------------------------------- - def undefined_symbols(self): - result = [] - for p in self.Productions: - if not p: - continue - - for s in p.prod: - if s not in self.Prodnames and s not in self.Terminals and s != 'error': - result.append((s, p)) - return result - - # ----------------------------------------------------------------------------- - # unused_terminals() - # - # Find all terminals that were defined, but not used by the grammar. Returns - # a list of all symbols. - # ----------------------------------------------------------------------------- - def unused_terminals(self): - unused_tok = [] - for s, v in self.Terminals.items(): - if s != 'error' and not v: - unused_tok.append(s) - - return unused_tok - - # ------------------------------------------------------------------------------ - # unused_rules() - # - # Find all grammar rules that were defined, but not used (maybe not reachable) - # Returns a list of productions. - # ------------------------------------------------------------------------------ - - def unused_rules(self): - unused_prod = [] - for s, v in self.Nonterminals.items(): - if not v: - p = self.Prodnames[s][0] - unused_prod.append(p) - return unused_prod - - # ----------------------------------------------------------------------------- - # unused_precedence() - # - # Returns a list of tuples (term,precedence) corresponding to precedence - # rules that were never used by the grammar. term is the name of the terminal - # on which precedence was applied and precedence is a string such as 'left' or - # 'right' corresponding to the type of precedence. - # ----------------------------------------------------------------------------- - - def unused_precedence(self): - unused = [] - for termname in self.Precedence: - if not (termname in self.Terminals or termname in self.UsedPrecedence): - unused.append((termname, self.Precedence[termname][0])) - - return unused - - # ------------------------------------------------------------------------- - # _first() - # - # Compute the value of FIRST1(beta) where beta is a tuple of symbols. - # - # During execution of compute_first1, the result may be incomplete. - # Afterward (e.g., when called from compute_follow()), it will be complete. - # ------------------------------------------------------------------------- - def _first(self, beta): - - # We are computing First(x1,x2,x3,...,xn) - result = [] - for x in beta: - x_produces_empty = False - - # Add all the non- symbols of First[x] to the result. - for f in self.First[x]: - if f == '': - x_produces_empty = True - else: - if f not in result: - result.append(f) - - if x_produces_empty: - # We have to consider the next x in beta, - # i.e. stay in the loop. - pass - else: - # We don't have to consider any further symbols in beta. - break - else: - # There was no 'break' from the loop, - # so x_produces_empty was true for all x in beta, - # so beta produces empty as well. - result.append('') - - return result - - # ------------------------------------------------------------------------- - # compute_first() - # - # Compute the value of FIRST1(X) for all symbols - # ------------------------------------------------------------------------- - def compute_first(self): - if self.First: - return self.First - - # Terminals: - for t in self.Terminals: - self.First[t] = [t] - - self.First['$end'] = ['$end'] - - # Nonterminals: - - # Initialize to the empty set: - for n in self.Nonterminals: - self.First[n] = [] - - # Then propagate symbols until no change: - while True: - some_change = False - for n in self.Nonterminals: - for p in self.Prodnames[n]: - for f in self._first(p.prod): - if f not in self.First[n]: - self.First[n].append(f) - some_change = True - if not some_change: - break - - return self.First - - # --------------------------------------------------------------------- - # compute_follow() - # - # Computes all of the follow sets for every non-terminal symbol. The - # follow set is the set of all symbols that might follow a given - # non-terminal. See the Dragon book, 2nd Ed. p. 189. - # --------------------------------------------------------------------- - def compute_follow(self, start=None): - # If already computed, return the result - if self.Follow: - return self.Follow - - # If first sets not computed yet, do that first. - if not self.First: - self.compute_first() - - # Add '$end' to the follow list of the start symbol - for k in self.Nonterminals: - self.Follow[k] = [] - - if not start: - start = self.Productions[1].name - - self.Follow[start] = ['$end'] - - while True: - didadd = False - for p in self.Productions[1:]: - # Here is the production set - for i, B in enumerate(p.prod): - if B in self.Nonterminals: - # Okay. We got a non-terminal in a production - fst = self._first(p.prod[i+1:]) - hasempty = False - for f in fst: - if f != '' and f not in self.Follow[B]: - self.Follow[B].append(f) - didadd = True - if f == '': - hasempty = True - if hasempty or i == (len(p.prod)-1): - # Add elements of follow(a) to follow(b) - for f in self.Follow[p.name]: - if f not in self.Follow[B]: - self.Follow[B].append(f) - didadd = True - if not didadd: - break - return self.Follow - - - # ----------------------------------------------------------------------------- - # build_lritems() - # - # This function walks the list of productions and builds a complete set of the - # LR items. The LR items are stored in two ways: First, they are uniquely - # numbered and placed in the list _lritems. Second, a linked list of LR items - # is built for each production. For example: - # - # E -> E PLUS E - # - # Creates the list - # - # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] - # ----------------------------------------------------------------------------- - - def build_lritems(self): - for p in self.Productions: - lastlri = p - i = 0 - lr_items = [] - while True: - if i > len(p): - lri = None - else: - lri = LRItem(p, i) - # Precompute the list of productions immediately following - try: - lri.lr_after = self.Prodnames[lri.prod[i+1]] - except (IndexError, KeyError): - lri.lr_after = [] - try: - lri.lr_before = lri.prod[i-1] - except IndexError: - lri.lr_before = None - - lastlri.lr_next = lri - if not lri: - break - lr_items.append(lri) - lastlri = lri - i += 1 - p.lr_items = lr_items - -# ----------------------------------------------------------------------------- -# == Class LRTable == -# -# This basic class represents a basic table of LR parsing information. -# Methods for generating the tables are not defined here. They are defined -# in the derived class LRGeneratedTable. -# ----------------------------------------------------------------------------- - -class VersionError(YaccError): - pass - -class LRTable(object): - def __init__(self): - self.lr_action = None - self.lr_goto = None - self.lr_productions = None - self.lr_method = None - - def read_table(self, module): - if isinstance(module, types.ModuleType): - parsetab = module - else: - exec('import %s' % module) - parsetab = sys.modules[module] - - if parsetab._tabversion != __tabversion__: - raise VersionError('yacc table file version is out of date') - - self.lr_action = parsetab._lr_action - self.lr_goto = parsetab._lr_goto - - self.lr_productions = [] - for p in parsetab._lr_productions: - self.lr_productions.append(MiniProduction(*p)) - - self.lr_method = parsetab._lr_method - return parsetab._lr_signature - - def read_pickle(self, filename): - try: - import cPickle as pickle - except ImportError: - import pickle - - if not os.path.exists(filename): - raise ImportError - - in_f = open(filename, 'rb') - - tabversion = pickle.load(in_f) - if tabversion != __tabversion__: - raise VersionError('yacc table file version is out of date') - self.lr_method = pickle.load(in_f) - signature = pickle.load(in_f) - self.lr_action = pickle.load(in_f) - self.lr_goto = pickle.load(in_f) - productions = pickle.load(in_f) - - self.lr_productions = [] - for p in productions: - self.lr_productions.append(MiniProduction(*p)) - - in_f.close() - return signature - - # Bind all production function names to callable objects in pdict - def bind_callables(self, pdict): - for p in self.lr_productions: - p.bind(pdict) - - -# ----------------------------------------------------------------------------- -# === LR Generator === -# -# The following classes and functions are used to generate LR parsing tables on -# a grammar. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# digraph() -# traverse() -# -# The following two functions are used to compute set valued functions -# of the form: -# -# F(x) = F'(x) U U{F(y) | x R y} -# -# This is used to compute the values of Read() sets as well as FOLLOW sets -# in LALR(1) generation. -# -# Inputs: X - An input set -# R - A relation -# FP - Set-valued function -# ------------------------------------------------------------------------------ - -def digraph(X, R, FP): - N = {} - for x in X: - N[x] = 0 - stack = [] - F = {} - for x in X: - if N[x] == 0: - traverse(x, N, stack, F, X, R, FP) - return F - -def traverse(x, N, stack, F, X, R, FP): - stack.append(x) - d = len(stack) - N[x] = d - F[x] = FP(x) # F(X) <- F'(x) - - rel = R(x) # Get y's related to x - for y in rel: - if N[y] == 0: - traverse(y, N, stack, F, X, R, FP) - N[x] = min(N[x], N[y]) - for a in F.get(y, []): - if a not in F[x]: - F[x].append(a) - if N[x] == d: - N[stack[-1]] = MAXINT - F[stack[-1]] = F[x] - element = stack.pop() - while element != x: - N[stack[-1]] = MAXINT - F[stack[-1]] = F[x] - element = stack.pop() - -class LALRError(YaccError): - pass - -# ----------------------------------------------------------------------------- -# == LRGeneratedTable == -# -# This class implements the LR table generation algorithm. There are no -# public methods except for write() -# ----------------------------------------------------------------------------- - -class LRGeneratedTable(LRTable): - def __init__(self, grammar, method='LALR', log=None): - if method not in ['SLR', 'LALR']: - raise LALRError('Unsupported method %s' % method) - - self.grammar = grammar - self.lr_method = method - - # Set up the logger - if not log: - log = NullLogger() - self.log = log - - # Internal attributes - self.lr_action = {} # Action table - self.lr_goto = {} # Goto table - self.lr_productions = grammar.Productions # Copy of grammar Production array - self.lr_goto_cache = {} # Cache of computed gotos - self.lr0_cidhash = {} # Cache of closures - - self._add_count = 0 # Internal counter used to detect cycles - - # Diagonistic information filled in by the table generator - self.sr_conflict = 0 - self.rr_conflict = 0 - self.conflicts = [] # List of conflicts - - self.sr_conflicts = [] - self.rr_conflicts = [] - - # Build the tables - self.grammar.build_lritems() - self.grammar.compute_first() - self.grammar.compute_follow() - self.lr_parse_table() - - # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. - - def lr0_closure(self, I): - self._add_count += 1 - - # Add everything in I to J - J = I[:] - didadd = True - while didadd: - didadd = False - for j in J: - for x in j.lr_after: - if getattr(x, 'lr0_added', 0) == self._add_count: - continue - # Add B --> .G to J - J.append(x.lr_next) - x.lr0_added = self._add_count - didadd = True - - return J - - # Compute the LR(0) goto function goto(I,X) where I is a set - # of LR(0) items and X is a grammar symbol. This function is written - # in a way that guarantees uniqueness of the generated goto sets - # (i.e. the same goto set will never be returned as two different Python - # objects). With uniqueness, we can later do fast set comparisons using - # id(obj) instead of element-wise comparison. - - def lr0_goto(self, I, x): - # First we look for a previously cached entry - g = self.lr_goto_cache.get((id(I), x)) - if g: - return g - - # Now we generate the goto set in a way that guarantees uniqueness - # of the result - - s = self.lr_goto_cache.get(x) - if not s: - s = {} - self.lr_goto_cache[x] = s - - gs = [] - for p in I: - n = p.lr_next - if n and n.lr_before == x: - s1 = s.get(id(n)) - if not s1: - s1 = {} - s[id(n)] = s1 - gs.append(n) - s = s1 - g = s.get('$end') - if not g: - if gs: - g = self.lr0_closure(gs) - s['$end'] = g - else: - s['$end'] = gs - self.lr_goto_cache[(id(I), x)] = g - return g - - # Compute the LR(0) sets of item function - def lr0_items(self): - C = [self.lr0_closure([self.grammar.Productions[0].lr_next])] - i = 0 - for I in C: - self.lr0_cidhash[id(I)] = i - i += 1 - - # Loop over the items in C and each grammar symbols - i = 0 - while i < len(C): - I = C[i] - i += 1 - - # Collect all of the symbols that could possibly be in the goto(I,X) sets - asyms = {} - for ii in I: - for s in ii.usyms: - asyms[s] = None - - for x in asyms: - g = self.lr0_goto(I, x) - if not g or id(g) in self.lr0_cidhash: - continue - self.lr0_cidhash[id(g)] = len(C) - C.append(g) - - return C - - # ----------------------------------------------------------------------------- - # ==== LALR(1) Parsing ==== - # - # LALR(1) parsing is almost exactly the same as SLR except that instead of - # relying upon Follow() sets when performing reductions, a more selective - # lookahead set that incorporates the state of the LR(0) machine is utilized. - # Thus, we mainly just have to focus on calculating the lookahead sets. - # - # The method used here is due to DeRemer and Pennelo (1982). - # - # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) - # Lookahead Sets", ACM Transactions on Programming Languages and Systems, - # Vol. 4, No. 4, Oct. 1982, pp. 615-649 - # - # Further details can also be found in: - # - # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", - # McGraw-Hill Book Company, (1985). - # - # ----------------------------------------------------------------------------- - - # ----------------------------------------------------------------------------- - # compute_nullable_nonterminals() - # - # Creates a dictionary containing all of the non-terminals that might produce - # an empty production. - # ----------------------------------------------------------------------------- - - def compute_nullable_nonterminals(self): - nullable = set() - num_nullable = 0 - while True: - for p in self.grammar.Productions[1:]: - if p.len == 0: - nullable.add(p.name) - continue - for t in p.prod: - if t not in nullable: - break - else: - nullable.add(p.name) - if len(nullable) == num_nullable: - break - num_nullable = len(nullable) - return nullable - - # ----------------------------------------------------------------------------- - # find_nonterminal_trans(C) - # - # Given a set of LR(0) items, this functions finds all of the non-terminal - # transitions. These are transitions in which a dot appears immediately before - # a non-terminal. Returns a list of tuples of the form (state,N) where state - # is the state number and N is the nonterminal symbol. - # - # The input C is the set of LR(0) items. - # ----------------------------------------------------------------------------- - - def find_nonterminal_transitions(self, C): - trans = [] - for stateno, state in enumerate(C): - for p in state: - if p.lr_index < p.len - 1: - t = (stateno, p.prod[p.lr_index+1]) - if t[1] in self.grammar.Nonterminals: - if t not in trans: - trans.append(t) - return trans - - # ----------------------------------------------------------------------------- - # dr_relation() - # - # Computes the DR(p,A) relationships for non-terminal transitions. The input - # is a tuple (state,N) where state is a number and N is a nonterminal symbol. - # - # Returns a list of terminals. - # ----------------------------------------------------------------------------- - - def dr_relation(self, C, trans, nullable): - dr_set = {} - state, N = trans - terms = [] - - g = self.lr0_goto(C[state], N) - for p in g: - if p.lr_index < p.len - 1: - a = p.prod[p.lr_index+1] - if a in self.grammar.Terminals: - if a not in terms: - terms.append(a) - - # This extra bit is to handle the start state - if state == 0 and N == self.grammar.Productions[0].prod[0]: - terms.append('$end') - - return terms - - # ----------------------------------------------------------------------------- - # reads_relation() - # - # Computes the READS() relation (p,A) READS (t,C). - # ----------------------------------------------------------------------------- - - def reads_relation(self, C, trans, empty): - # Look for empty transitions - rel = [] - state, N = trans - - g = self.lr0_goto(C[state], N) - j = self.lr0_cidhash.get(id(g), -1) - for p in g: - if p.lr_index < p.len - 1: - a = p.prod[p.lr_index + 1] - if a in empty: - rel.append((j, a)) - - return rel - - # ----------------------------------------------------------------------------- - # compute_lookback_includes() - # - # Determines the lookback and includes relations - # - # LOOKBACK: - # - # This relation is determined by running the LR(0) state machine forward. - # For example, starting with a production "N : . A B C", we run it forward - # to obtain "N : A B C ." We then build a relationship between this final - # state and the starting state. These relationships are stored in a dictionary - # lookdict. - # - # INCLUDES: - # - # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). - # - # This relation is used to determine non-terminal transitions that occur - # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) - # if the following holds: - # - # B -> LAT, where T -> epsilon and p' -L-> p - # - # L is essentially a prefix (which may be empty), T is a suffix that must be - # able to derive an empty string. State p' must lead to state p with the string L. - # - # ----------------------------------------------------------------------------- - - def compute_lookback_includes(self, C, trans, nullable): - lookdict = {} # Dictionary of lookback relations - includedict = {} # Dictionary of include relations - - # Make a dictionary of non-terminal transitions - dtrans = {} - for t in trans: - dtrans[t] = 1 - - # Loop over all transitions and compute lookbacks and includes - for state, N in trans: - lookb = [] - includes = [] - for p in C[state]: - if p.name != N: - continue - - # Okay, we have a name match. We now follow the production all the way - # through the state machine until we get the . on the right hand side - - lr_index = p.lr_index - j = state - while lr_index < p.len - 1: - lr_index = lr_index + 1 - t = p.prod[lr_index] - - # Check to see if this symbol and state are a non-terminal transition - if (j, t) in dtrans: - # Yes. Okay, there is some chance that this is an includes relation - # the only way to know for certain is whether the rest of the - # production derives empty - - li = lr_index + 1 - while li < p.len: - if p.prod[li] in self.grammar.Terminals: - break # No forget it - if p.prod[li] not in nullable: - break - li = li + 1 - else: - # Appears to be a relation between (j,t) and (state,N) - includes.append((j, t)) - - g = self.lr0_goto(C[j], t) # Go to next set - j = self.lr0_cidhash.get(id(g), -1) # Go to next state - - # When we get here, j is the final state, now we have to locate the production - for r in C[j]: - if r.name != p.name: - continue - if r.len != p.len: - continue - i = 0 - # This look is comparing a production ". A B C" with "A B C ." - while i < r.lr_index: - if r.prod[i] != p.prod[i+1]: - break - i = i + 1 - else: - lookb.append((j, r)) - for i in includes: - if i not in includedict: - includedict[i] = [] - includedict[i].append((state, N)) - lookdict[(state, N)] = lookb - - return lookdict, includedict - - # ----------------------------------------------------------------------------- - # compute_read_sets() - # - # Given a set of LR(0) items, this function computes the read sets. - # - # Inputs: C = Set of LR(0) items - # ntrans = Set of nonterminal transitions - # nullable = Set of empty transitions - # - # Returns a set containing the read sets - # ----------------------------------------------------------------------------- - - def compute_read_sets(self, C, ntrans, nullable): - FP = lambda x: self.dr_relation(C, x, nullable) - R = lambda x: self.reads_relation(C, x, nullable) - F = digraph(ntrans, R, FP) - return F - - # ----------------------------------------------------------------------------- - # compute_follow_sets() - # - # Given a set of LR(0) items, a set of non-terminal transitions, a readset, - # and an include set, this function computes the follow sets - # - # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} - # - # Inputs: - # ntrans = Set of nonterminal transitions - # readsets = Readset (previously computed) - # inclsets = Include sets (previously computed) - # - # Returns a set containing the follow sets - # ----------------------------------------------------------------------------- - - def compute_follow_sets(self, ntrans, readsets, inclsets): - FP = lambda x: readsets[x] - R = lambda x: inclsets.get(x, []) - F = digraph(ntrans, R, FP) - return F - - # ----------------------------------------------------------------------------- - # add_lookaheads() - # - # Attaches the lookahead symbols to grammar rules. - # - # Inputs: lookbacks - Set of lookback relations - # followset - Computed follow set - # - # This function directly attaches the lookaheads to productions contained - # in the lookbacks set - # ----------------------------------------------------------------------------- - - def add_lookaheads(self, lookbacks, followset): - for trans, lb in lookbacks.items(): - # Loop over productions in lookback - for state, p in lb: - if state not in p.lookaheads: - p.lookaheads[state] = [] - f = followset.get(trans, []) - for a in f: - if a not in p.lookaheads[state]: - p.lookaheads[state].append(a) - - # ----------------------------------------------------------------------------- - # add_lalr_lookaheads() - # - # This function does all of the work of adding lookahead information for use - # with LALR parsing - # ----------------------------------------------------------------------------- - - def add_lalr_lookaheads(self, C): - # Determine all of the nullable nonterminals - nullable = self.compute_nullable_nonterminals() - - # Find all non-terminal transitions - trans = self.find_nonterminal_transitions(C) - - # Compute read sets - readsets = self.compute_read_sets(C, trans, nullable) - - # Compute lookback/includes relations - lookd, included = self.compute_lookback_includes(C, trans, nullable) - - # Compute LALR FOLLOW sets - followsets = self.compute_follow_sets(trans, readsets, included) - - # Add all of the lookaheads - self.add_lookaheads(lookd, followsets) - - # ----------------------------------------------------------------------------- - # lr_parse_table() - # - # This function constructs the parse tables for SLR or LALR - # ----------------------------------------------------------------------------- - def lr_parse_table(self): - Productions = self.grammar.Productions - Precedence = self.grammar.Precedence - goto = self.lr_goto # Goto array - action = self.lr_action # Action array - log = self.log # Logger for output - - actionp = {} # Action production array (temporary) - - log.info('Parsing method: %s', self.lr_method) - - # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items - # This determines the number of states - - C = self.lr0_items() - - if self.lr_method == 'LALR': - self.add_lalr_lookaheads(C) - - # Build the parser table, state by state - st = 0 - for I in C: - # Loop over each production in I - actlist = [] # List of actions - st_action = {} - st_actionp = {} - st_goto = {} - log.info('') - log.info('state %d', st) - log.info('') - for p in I: - log.info(' (%d) %s', p.number, p) - log.info('') - - for p in I: - if p.len == p.lr_index + 1: - if p.name == "S'": - # Start symbol. Accept! - st_action['$end'] = 0 - st_actionp['$end'] = p - else: - # We are at the end of a production. Reduce! - if self.lr_method == 'LALR': - laheads = p.lookaheads[st] - else: - laheads = self.grammar.Follow[p.name] - for a in laheads: - actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p))) - r = st_action.get(a) - if r is not None: - # Whoa. Have a shift/reduce or reduce/reduce conflict - if r > 0: - # Need to decide on shift or reduce here - # By default we favor shifting. Need to add - # some precedence rules here. - - # Shift precedence comes from the token - sprec, slevel = Precedence.get(a, ('right', 0)) - - # Reduce precedence comes from rule being reduced (p) - rprec, rlevel = Productions[p.number].prec - - if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): - # We really need to reduce here. - st_action[a] = -p.number - st_actionp[a] = p - if not slevel and not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as reduce', a) - self.sr_conflicts.append((st, a, 'reduce')) - Productions[p.number].reduced += 1 - elif (slevel == rlevel) and (rprec == 'nonassoc'): - st_action[a] = None - else: - # Hmmm. Guess we'll keep the shift - if not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as shift', a) - self.sr_conflicts.append((st, a, 'shift')) - elif r < 0: - # Reduce/reduce conflict. In this case, we favor the rule - # that was defined first in the grammar file - oldp = Productions[-r] - pp = Productions[p.number] - if oldp.line > pp.line: - st_action[a] = -p.number - st_actionp[a] = p - chosenp, rejectp = pp, oldp - Productions[p.number].reduced += 1 - Productions[oldp.number].reduced -= 1 - else: - chosenp, rejectp = oldp, pp - self.rr_conflicts.append((st, chosenp, rejectp)) - log.info(' ! reduce/reduce conflict for %s resolved using rule %d (%s)', - a, st_actionp[a].number, st_actionp[a]) - else: - raise LALRError('Unknown conflict in state %d' % st) - else: - st_action[a] = -p.number - st_actionp[a] = p - Productions[p.number].reduced += 1 - else: - i = p.lr_index - a = p.prod[i+1] # Get symbol right after the "." - if a in self.grammar.Terminals: - g = self.lr0_goto(I, a) - j = self.lr0_cidhash.get(id(g), -1) - if j >= 0: - # We are in a shift state - actlist.append((a, p, 'shift and go to state %d' % j)) - r = st_action.get(a) - if r is not None: - # Whoa have a shift/reduce or shift/shift conflict - if r > 0: - if r != j: - raise LALRError('Shift/shift conflict in state %d' % st) - elif r < 0: - # Do a precedence check. - # - if precedence of reduce rule is higher, we reduce. - # - if precedence of reduce is same and left assoc, we reduce. - # - otherwise we shift - - # Shift precedence comes from the token - sprec, slevel = Precedence.get(a, ('right', 0)) - - # Reduce precedence comes from the rule that could have been reduced - rprec, rlevel = Productions[st_actionp[a].number].prec - - if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): - # We decide to shift here... highest precedence to shift - Productions[st_actionp[a].number].reduced -= 1 - st_action[a] = j - st_actionp[a] = p - if not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as shift', a) - self.sr_conflicts.append((st, a, 'shift')) - elif (slevel == rlevel) and (rprec == 'nonassoc'): - st_action[a] = None - else: - # Hmmm. Guess we'll keep the reduce - if not slevel and not rlevel: - log.info(' ! shift/reduce conflict for %s resolved as reduce', a) - self.sr_conflicts.append((st, a, 'reduce')) - - else: - raise LALRError('Unknown conflict in state %d' % st) - else: - st_action[a] = j - st_actionp[a] = p - - # Print the actions associated with each terminal - _actprint = {} - for a, p, m in actlist: - if a in st_action: - if p is st_actionp[a]: - log.info(' %-15s %s', a, m) - _actprint[(a, m)] = 1 - log.info('') - # Print the actions that were not used. (debugging) - not_used = 0 - for a, p, m in actlist: - if a in st_action: - if p is not st_actionp[a]: - if not (a, m) in _actprint: - log.debug(' ! %-15s [ %s ]', a, m) - not_used = 1 - _actprint[(a, m)] = 1 - if not_used: - log.debug('') - - # Construct the goto table for this state - - nkeys = {} - for ii in I: - for s in ii.usyms: - if s in self.grammar.Nonterminals: - nkeys[s] = None - for n in nkeys: - g = self.lr0_goto(I, n) - j = self.lr0_cidhash.get(id(g), -1) - if j >= 0: - st_goto[n] = j - log.info(' %-30s shift and go to state %d', n, j) - - action[st] = st_action - actionp[st] = st_actionp - goto[st] = st_goto - st += 1 - - # ----------------------------------------------------------------------------- - # write() - # - # This function writes the LR parsing tables to a file - # ----------------------------------------------------------------------------- - - def write_table(self, tabmodule, outputdir='', signature=''): - if isinstance(tabmodule, types.ModuleType): - raise IOError("Won't overwrite existing tabmodule") - - basemodulename = tabmodule.split('.')[-1] - filename = os.path.join(outputdir, basemodulename) + '.py' - try: - f = open(filename, 'w') - - f.write(''' -# %s -# This file is automatically generated. Do not edit. -_tabversion = %r - -_lr_method = %r - -_lr_signature = %r - ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature)) - - # Change smaller to 0 to go back to original tables - smaller = 1 - - # Factor out names to try and make smaller - if smaller: - items = {} - - for s, nd in self.lr_action.items(): - for name, v in nd.items(): - i = items.get(name) - if not i: - i = ([], []) - items[name] = i - i[0].append(s) - i[1].append(v) - - f.write('\n_lr_action_items = {') - for k, v in items.items(): - f.write('%r:([' % k) - for i in v[0]: - f.write('%r,' % i) - f.write('],[') - for i in v[1]: - f.write('%r,' % i) - - f.write(']),') - f.write('}\n') - - f.write(''' -_lr_action = {} -for _k, _v in _lr_action_items.items(): - for _x,_y in zip(_v[0],_v[1]): - if not _x in _lr_action: _lr_action[_x] = {} - _lr_action[_x][_k] = _y -del _lr_action_items -''') - - else: - f.write('\n_lr_action = { ') - for k, v in self.lr_action.items(): - f.write('(%r,%r):%r,' % (k[0], k[1], v)) - f.write('}\n') - - if smaller: - # Factor out names to try and make smaller - items = {} - - for s, nd in self.lr_goto.items(): - for name, v in nd.items(): - i = items.get(name) - if not i: - i = ([], []) - items[name] = i - i[0].append(s) - i[1].append(v) - - f.write('\n_lr_goto_items = {') - for k, v in items.items(): - f.write('%r:([' % k) - for i in v[0]: - f.write('%r,' % i) - f.write('],[') - for i in v[1]: - f.write('%r,' % i) - - f.write(']),') - f.write('}\n') - - f.write(''' -_lr_goto = {} -for _k, _v in _lr_goto_items.items(): - for _x, _y in zip(_v[0], _v[1]): - if not _x in _lr_goto: _lr_goto[_x] = {} - _lr_goto[_x][_k] = _y -del _lr_goto_items -''') - else: - f.write('\n_lr_goto = { ') - for k, v in self.lr_goto.items(): - f.write('(%r,%r):%r,' % (k[0], k[1], v)) - f.write('}\n') - - # Write production table - f.write('_lr_productions = [\n') - for p in self.lr_productions: - if p.func: - f.write(' (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len, - p.func, os.path.basename(p.file), p.line)) - else: - f.write(' (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len)) - f.write(']\n') - f.close() - - except IOError as e: - raise - - - # ----------------------------------------------------------------------------- - # pickle_table() - # - # This function pickles the LR parsing tables to a supplied file object - # ----------------------------------------------------------------------------- - - def pickle_table(self, filename, signature=''): - try: - import cPickle as pickle - except ImportError: - import pickle - with open(filename, 'wb') as outf: - pickle.dump(__tabversion__, outf, pickle_protocol) - pickle.dump(self.lr_method, outf, pickle_protocol) - pickle.dump(signature, outf, pickle_protocol) - pickle.dump(self.lr_action, outf, pickle_protocol) - pickle.dump(self.lr_goto, outf, pickle_protocol) - - outp = [] - for p in self.lr_productions: - if p.func: - outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line)) - else: - outp.append((str(p), p.name, p.len, None, None, None)) - pickle.dump(outp, outf, pickle_protocol) - -# ----------------------------------------------------------------------------- -# === INTROSPECTION === -# -# The following functions and classes are used to implement the PLY -# introspection features followed by the yacc() function itself. -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# get_caller_module_dict() -# -# This function returns a dictionary containing all of the symbols defined within -# a caller further down the call stack. This is used to get the environment -# associated with the yacc() call if none was provided. -# ----------------------------------------------------------------------------- - -def get_caller_module_dict(levels): - f = sys._getframe(levels) - ldict = f.f_globals.copy() - if f.f_globals != f.f_locals: - ldict.update(f.f_locals) - return ldict - -# ----------------------------------------------------------------------------- -# parse_grammar() -# -# This takes a raw grammar rule string and parses it into production data -# ----------------------------------------------------------------------------- -def parse_grammar(doc, file, line): - grammar = [] - # Split the doc string into lines - pstrings = doc.splitlines() - lastp = None - dline = line - for ps in pstrings: - dline += 1 - p = ps.split() - if not p: - continue - try: - if p[0] == '|': - # This is a continuation of a previous rule - if not lastp: - raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline)) - prodname = lastp - syms = p[1:] - else: - prodname = p[0] - lastp = prodname - syms = p[2:] - assign = p[1] - if assign != ':' and assign != '::=': - raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline)) - - grammar.append((file, dline, prodname, syms)) - except SyntaxError: - raise - except Exception: - raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip())) - - return grammar - -# ----------------------------------------------------------------------------- -# ParserReflect() -# -# This class represents information extracted for building a parser including -# start symbol, error function, tokens, precedence list, action functions, -# etc. -# ----------------------------------------------------------------------------- -class ParserReflect(object): - def __init__(self, pdict, log=None): - self.pdict = pdict - self.start = None - self.error_func = None - self.tokens = None - self.modules = set() - self.grammar = [] - self.error = False - - if log is None: - self.log = PlyLogger(sys.stderr) - else: - self.log = log - - # Get all of the basic information - def get_all(self): - self.get_start() - self.get_error_func() - self.get_tokens() - self.get_precedence() - self.get_pfunctions() - - # Validate all of the information - def validate_all(self): - self.validate_start() - self.validate_error_func() - self.validate_tokens() - self.validate_precedence() - self.validate_pfunctions() - self.validate_modules() - return self.error - - # Compute a signature over the grammar - def signature(self): - parts = [] - try: - if self.start: - parts.append(self.start) - if self.prec: - parts.append(''.join([''.join(p) for p in self.prec])) - if self.tokens: - parts.append(' '.join(self.tokens)) - for f in self.pfuncs: - if f[3]: - parts.append(f[3]) - except (TypeError, ValueError): - pass - return ''.join(parts) - - # ----------------------------------------------------------------------------- - # validate_modules() - # - # This method checks to see if there are duplicated p_rulename() functions - # in the parser module file. Without this function, it is really easy for - # users to make mistakes by cutting and pasting code fragments (and it's a real - # bugger to try and figure out why the resulting parser doesn't work). Therefore, - # we just do a little regular expression pattern matching of def statements - # to try and detect duplicates. - # ----------------------------------------------------------------------------- - - def validate_modules(self): - # Match def p_funcname( - fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') - - for module in self.modules: - try: - lines, linen = inspect.getsourcelines(module) - except IOError: - continue - - counthash = {} - for linen, line in enumerate(lines): - linen += 1 - m = fre.match(line) - if m: - name = m.group(1) - prev = counthash.get(name) - if not prev: - counthash[name] = linen - else: - filename = inspect.getsourcefile(module) - self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d', - filename, linen, name, prev) - - # Get the start symbol - def get_start(self): - self.start = self.pdict.get('start') - - # Validate the start symbol - def validate_start(self): - if self.start is not None: - if not isinstance(self.start, string_types): - self.log.error("'start' must be a string") - - # Look for error handler - def get_error_func(self): - self.error_func = self.pdict.get('p_error') - - # Validate the error function - def validate_error_func(self): - if self.error_func: - if isinstance(self.error_func, types.FunctionType): - ismethod = 0 - elif isinstance(self.error_func, types.MethodType): - ismethod = 1 - else: - self.log.error("'p_error' defined, but is not a function or method") - self.error = True - return - - eline = self.error_func.__code__.co_firstlineno - efile = self.error_func.__code__.co_filename - module = inspect.getmodule(self.error_func) - self.modules.add(module) - - argcount = self.error_func.__code__.co_argcount - ismethod - if argcount != 1: - self.log.error('%s:%d: p_error() requires 1 argument', efile, eline) - self.error = True - - # Get the tokens map - def get_tokens(self): - tokens = self.pdict.get('tokens') - if not tokens: - self.log.error('No token list is defined') - self.error = True - return - - if not isinstance(tokens, (list, tuple)): - self.log.error('tokens must be a list or tuple') - self.error = True - return - - if not tokens: - self.log.error('tokens is empty') - self.error = True - return - - self.tokens = tokens - - # Validate the tokens - def validate_tokens(self): - # Validate the tokens. - if 'error' in self.tokens: - self.log.error("Illegal token name 'error'. Is a reserved word") - self.error = True - return - - terminals = set() - for n in self.tokens: - if n in terminals: - self.log.warning('Token %r multiply defined', n) - terminals.add(n) - - # Get the precedence map (if any) - def get_precedence(self): - self.prec = self.pdict.get('precedence') - - # Validate and parse the precedence map - def validate_precedence(self): - preclist = [] - if self.prec: - if not isinstance(self.prec, (list, tuple)): - self.log.error('precedence must be a list or tuple') - self.error = True - return - for level, p in enumerate(self.prec): - if not isinstance(p, (list, tuple)): - self.log.error('Bad precedence table') - self.error = True - return - - if len(p) < 2: - self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p) - self.error = True - return - assoc = p[0] - if not isinstance(assoc, string_types): - self.log.error('precedence associativity must be a string') - self.error = True - return - for term in p[1:]: - if not isinstance(term, string_types): - self.log.error('precedence items must be strings') - self.error = True - return - preclist.append((term, assoc, level+1)) - self.preclist = preclist - - # Get all p_functions from the grammar - def get_pfunctions(self): - p_functions = [] - for name, item in self.pdict.items(): - if not name.startswith('p_') or name == 'p_error': - continue - if isinstance(item, (types.FunctionType, types.MethodType)): - line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno) - module = inspect.getmodule(item) - p_functions.append((line, module, name, item.__doc__)) - - # Sort all of the actions by line number; make sure to stringify - # modules to make them sortable, since `line` may not uniquely sort all - # p functions - p_functions.sort(key=lambda p_function: ( - p_function[0], - str(p_function[1]), - p_function[2], - p_function[3])) - self.pfuncs = p_functions - - # Validate all of the p_functions - def validate_pfunctions(self): - grammar = [] - # Check for non-empty symbols - if len(self.pfuncs) == 0: - self.log.error('no rules of the form p_rulename are defined') - self.error = True - return - - for line, module, name, doc in self.pfuncs: - file = inspect.getsourcefile(module) - func = self.pdict[name] - if isinstance(func, types.MethodType): - reqargs = 2 - else: - reqargs = 1 - if func.__code__.co_argcount > reqargs: - self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__) - self.error = True - elif func.__code__.co_argcount < reqargs: - self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__) - self.error = True - elif not func.__doc__: - self.log.warning('%s:%d: No documentation string specified in function %r (ignored)', - file, line, func.__name__) - else: - try: - parsed_g = parse_grammar(doc, file, line) - for g in parsed_g: - grammar.append((name, g)) - except SyntaxError as e: - self.log.error(str(e)) - self.error = True - - # Looks like a valid grammar rule - # Mark the file in which defined. - self.modules.add(module) - - # Secondary validation step that looks for p_ definitions that are not functions - # or functions that look like they might be grammar rules. - - for n, v in self.pdict.items(): - if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)): - continue - if n.startswith('t_'): - continue - if n.startswith('p_') and n != 'p_error': - self.log.warning('%r not defined as a function', n) - if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or - (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)): - if v.__doc__: - try: - doc = v.__doc__.split(' ') - if doc[1] == ':': - self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix', - v.__code__.co_filename, v.__code__.co_firstlineno, n) - except IndexError: - pass - - self.grammar = grammar - -# ----------------------------------------------------------------------------- -# yacc(module) -# -# Build a parser -# ----------------------------------------------------------------------------- - -def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, - check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file, - outputdir=None, debuglog=None, errorlog=None, picklefile=None): - - if tabmodule is None: - tabmodule = tab_module - - # Reference to the parsing method of the last built parser - global parse - - # If pickling is enabled, table files are not created - if picklefile: - write_tables = 0 - - if errorlog is None: - errorlog = PlyLogger(sys.stderr) - - # Get the module dictionary used for the parser - if module: - _items = [(k, getattr(module, k)) for k in dir(module)] - pdict = dict(_items) - # If no __file__ attribute is available, try to obtain it from the __module__ instead - if '__file__' not in pdict: - pdict['__file__'] = sys.modules[pdict['__module__']].__file__ - else: - pdict = get_caller_module_dict(2) - - if outputdir is None: - # If no output directory is set, the location of the output files - # is determined according to the following rules: - # - If tabmodule specifies a package, files go into that package directory - # - Otherwise, files go in the same directory as the specifying module - if isinstance(tabmodule, types.ModuleType): - srcfile = tabmodule.__file__ - else: - if '.' not in tabmodule: - srcfile = pdict['__file__'] - else: - parts = tabmodule.split('.') - pkgname = '.'.join(parts[:-1]) - exec('import %s' % pkgname) - srcfile = getattr(sys.modules[pkgname], '__file__', '') - outputdir = os.path.dirname(srcfile) - - # Determine if the module is package of a package or not. - # If so, fix the tabmodule setting so that tables load correctly - pkg = pdict.get('__package__') - if pkg and isinstance(tabmodule, str): - if '.' not in tabmodule: - tabmodule = pkg + '.' + tabmodule - - - - # Set start symbol if it's specified directly using an argument - if start is not None: - pdict['start'] = start - - # Collect parser information from the dictionary - pinfo = ParserReflect(pdict, log=errorlog) - pinfo.get_all() - - if pinfo.error: - raise YaccError('Unable to build parser') - - # Check signature against table files (if any) - signature = pinfo.signature() - - # Read the tables - try: - lr = LRTable() - if picklefile: - read_signature = lr.read_pickle(picklefile) - else: - read_signature = lr.read_table(tabmodule) - if optimize or (read_signature == signature): - try: - lr.bind_callables(pinfo.pdict) - parser = LRParser(lr, pinfo.error_func) - parse = parser.parse - return parser - except Exception as e: - errorlog.warning('There was a problem loading the table file: %r', e) - except VersionError as e: - errorlog.warning(str(e)) - except ImportError: - pass - - if debuglog is None: - if debug: - try: - debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w')) - except IOError as e: - errorlog.warning("Couldn't open %r. %s" % (debugfile, e)) - debuglog = NullLogger() - else: - debuglog = NullLogger() - - debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__) - - errors = False - - # Validate the parser information - if pinfo.validate_all(): - raise YaccError('Unable to build parser') - - if not pinfo.error_func: - errorlog.warning('no p_error() function is defined') - - # Create a grammar object - grammar = Grammar(pinfo.tokens) - - # Set precedence level for terminals - for term, assoc, level in pinfo.preclist: - try: - grammar.set_precedence(term, assoc, level) - except GrammarError as e: - errorlog.warning('%s', e) - - # Add productions to the grammar - for funcname, gram in pinfo.grammar: - file, line, prodname, syms = gram - try: - grammar.add_production(prodname, syms, funcname, file, line) - except GrammarError as e: - errorlog.error('%s', e) - errors = True - - # Set the grammar start symbols - try: - if start is None: - grammar.set_start(pinfo.start) - else: - grammar.set_start(start) - except GrammarError as e: - errorlog.error(str(e)) - errors = True - - if errors: - raise YaccError('Unable to build parser') - - # Verify the grammar structure - undefined_symbols = grammar.undefined_symbols() - for sym, prod in undefined_symbols: - errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym) - errors = True - - unused_terminals = grammar.unused_terminals() - if unused_terminals: - debuglog.info('') - debuglog.info('Unused terminals:') - debuglog.info('') - for term in unused_terminals: - errorlog.warning('Token %r defined, but not used', term) - debuglog.info(' %s', term) - - # Print out all productions to the debug log - if debug: - debuglog.info('') - debuglog.info('Grammar') - debuglog.info('') - for n, p in enumerate(grammar.Productions): - debuglog.info('Rule %-5d %s', n, p) - - # Find unused non-terminals - unused_rules = grammar.unused_rules() - for prod in unused_rules: - errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name) - - if len(unused_terminals) == 1: - errorlog.warning('There is 1 unused token') - if len(unused_terminals) > 1: - errorlog.warning('There are %d unused tokens', len(unused_terminals)) - - if len(unused_rules) == 1: - errorlog.warning('There is 1 unused rule') - if len(unused_rules) > 1: - errorlog.warning('There are %d unused rules', len(unused_rules)) - - if debug: - debuglog.info('') - debuglog.info('Terminals, with rules where they appear') - debuglog.info('') - terms = list(grammar.Terminals) - terms.sort() - for term in terms: - debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]])) - - debuglog.info('') - debuglog.info('Nonterminals, with rules where they appear') - debuglog.info('') - nonterms = list(grammar.Nonterminals) - nonterms.sort() - for nonterm in nonterms: - debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]])) - debuglog.info('') - - if check_recursion: - unreachable = grammar.find_unreachable() - for u in unreachable: - errorlog.warning('Symbol %r is unreachable', u) - - infinite = grammar.infinite_cycles() - for inf in infinite: - errorlog.error('Infinite recursion detected for symbol %r', inf) - errors = True - - unused_prec = grammar.unused_precedence() - for term, assoc in unused_prec: - errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term) - errors = True - - if errors: - raise YaccError('Unable to build parser') - - # Run the LRGeneratedTable on the grammar - if debug: - errorlog.debug('Generating %s tables', method) - - lr = LRGeneratedTable(grammar, method, debuglog) - - if debug: - num_sr = len(lr.sr_conflicts) - - # Report shift/reduce and reduce/reduce conflicts - if num_sr == 1: - errorlog.warning('1 shift/reduce conflict') - elif num_sr > 1: - errorlog.warning('%d shift/reduce conflicts', num_sr) - - num_rr = len(lr.rr_conflicts) - if num_rr == 1: - errorlog.warning('1 reduce/reduce conflict') - elif num_rr > 1: - errorlog.warning('%d reduce/reduce conflicts', num_rr) - - # Write out conflicts to the output file - if debug and (lr.sr_conflicts or lr.rr_conflicts): - debuglog.warning('') - debuglog.warning('Conflicts:') - debuglog.warning('') - - for state, tok, resolution in lr.sr_conflicts: - debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s', tok, state, resolution) - - already_reported = set() - for state, rule, rejected in lr.rr_conflicts: - if (state, id(rule), id(rejected)) in already_reported: - continue - debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) - debuglog.warning('rejected rule (%s) in state %d', rejected, state) - errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule) - errorlog.warning('rejected rule (%s) in state %d', rejected, state) - already_reported.add((state, id(rule), id(rejected))) - - warned_never = [] - for state, rule, rejected in lr.rr_conflicts: - if not rejected.reduced and (rejected not in warned_never): - debuglog.warning('Rule (%s) is never reduced', rejected) - errorlog.warning('Rule (%s) is never reduced', rejected) - warned_never.append(rejected) - - # Write the table file if requested - if write_tables: - try: - lr.write_table(tabmodule, outputdir, signature) - except IOError as e: - errorlog.warning("Couldn't create %r. %s" % (tabmodule, e)) - - # Write a pickled version of the tables - if picklefile: - try: - lr.pickle_table(picklefile, signature) - except IOError as e: - errorlog.warning("Couldn't create %r. %s" % (picklefile, e)) - - # Build the parser - lr.bind_callables(pinfo.pdict) - parser = LRParser(lr, pinfo.error_func) - - parse = parser.parse - return parser diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py deleted file mode 100644 index acf5ca1a3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/ply/ygen.py +++ /dev/null @@ -1,74 +0,0 @@ -# ply: ygen.py -# -# This is a support program that auto-generates different versions of the YACC parsing -# function with different features removed for the purposes of performance. -# -# Users should edit the method LParser.parsedebug() in yacc.py. The source code -# for that method is then used to create the other methods. See the comments in -# yacc.py for further details. - -import os.path -import shutil - -def get_source_range(lines, tag): - srclines = enumerate(lines) - start_tag = '#--! %s-start' % tag - end_tag = '#--! %s-end' % tag - - for start_index, line in srclines: - if line.strip().startswith(start_tag): - break - - for end_index, line in srclines: - if line.strip().endswith(end_tag): - break - - return (start_index + 1, end_index) - -def filter_section(lines, tag): - filtered_lines = [] - include = True - tag_text = '#--! %s' % tag - for line in lines: - if line.strip().startswith(tag_text): - include = not include - elif include: - filtered_lines.append(line) - return filtered_lines - -def main(): - dirname = os.path.dirname(__file__) - shutil.copy2(os.path.join(dirname, 'yacc.py'), os.path.join(dirname, 'yacc.py.bak')) - with open(os.path.join(dirname, 'yacc.py'), 'r') as f: - lines = f.readlines() - - parse_start, parse_end = get_source_range(lines, 'parsedebug') - parseopt_start, parseopt_end = get_source_range(lines, 'parseopt') - parseopt_notrack_start, parseopt_notrack_end = get_source_range(lines, 'parseopt-notrack') - - # Get the original source - orig_lines = lines[parse_start:parse_end] - - # Filter the DEBUG sections out - parseopt_lines = filter_section(orig_lines, 'DEBUG') - - # Filter the TRACKING sections out - parseopt_notrack_lines = filter_section(parseopt_lines, 'TRACKING') - - # Replace the parser source sections with updated versions - lines[parseopt_notrack_start:parseopt_notrack_end] = parseopt_notrack_lines - lines[parseopt_start:parseopt_end] = parseopt_lines - - lines = [line.rstrip()+'\n' for line in lines] - with open(os.path.join(dirname, 'yacc.py'), 'w') as f: - f.writelines(lines) - - print('Updated yacc.py') - -if __name__ == '__main__': - main() - - - - - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py deleted file mode 100644 index b8f4c4395..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/plyparser.py +++ /dev/null @@ -1,133 +0,0 @@ -#----------------------------------------------------------------- -# plyparser.py -# -# PLYParser class and other utilities for simplifying programming -# parsers with PLY -# -# Eli Bendersky [https://eli.thegreenplace.net/] -# License: BSD -#----------------------------------------------------------------- - -import warnings - -class Coord(object): - """ Coordinates of a syntactic element. Consists of: - - File name - - Line number - - (optional) column number, for the Lexer - """ - __slots__ = ('file', 'line', 'column', '__weakref__') - def __init__(self, file, line, column=None): - self.file = file - self.line = line - self.column = column - - def __str__(self): - str = "%s:%s" % (self.file, self.line) - if self.column: str += ":%s" % self.column - return str - - -class ParseError(Exception): pass - - -class PLYParser(object): - def _create_opt_rule(self, rulename): - """ Given a rule name, creates an optional ply.yacc rule - for it. The name of the optional rule is - _opt - """ - optname = rulename + '_opt' - - def optrule(self, p): - p[0] = p[1] - - optrule.__doc__ = '%s : empty\n| %s' % (optname, rulename) - optrule.__name__ = 'p_%s' % optname - setattr(self.__class__, optrule.__name__, optrule) - - def _coord(self, lineno, column=None): - return Coord( - file=self.clex.filename, - line=lineno, - column=column) - - def _token_coord(self, p, token_idx): - """ Returns the coordinates for the YaccProduction object 'p' indexed - with 'token_idx'. The coordinate includes the 'lineno' and - 'column'. Both follow the lex semantic, starting from 1. - """ - last_cr = p.lexer.lexer.lexdata.rfind('\n', 0, p.lexpos(token_idx)) - if last_cr < 0: - last_cr = -1 - column = (p.lexpos(token_idx) - (last_cr)) - return self._coord(p.lineno(token_idx), column) - - def _parse_error(self, msg, coord): - raise ParseError("%s: %s" % (coord, msg)) - - -def parameterized(*params): - """ Decorator to create parameterized rules. - - Parameterized rule methods must be named starting with 'p_' and contain - 'xxx', and their docstrings may contain 'xxx' and 'yyy'. These will be - replaced by the given parameter tuples. For example, ``p_xxx_rule()`` with - docstring 'xxx_rule : yyy' when decorated with - ``@parameterized(('id', 'ID'))`` produces ``p_id_rule()`` with the docstring - 'id_rule : ID'. Using multiple tuples produces multiple rules. - """ - def decorate(rule_func): - rule_func._params = params - return rule_func - return decorate - - -def template(cls): - """ Class decorator to generate rules from parameterized rule templates. - - See `parameterized` for more information on parameterized rules. - """ - issued_nodoc_warning = False - for attr_name in dir(cls): - if attr_name.startswith('p_'): - method = getattr(cls, attr_name) - if hasattr(method, '_params'): - # Remove the template method - delattr(cls, attr_name) - # Create parameterized rules from this method; only run this if - # the method has a docstring. This is to address an issue when - # pycparser's users are installed in -OO mode which strips - # docstrings away. - # See: https://github.com/eliben/pycparser/pull/198/ and - # https://github.com/eliben/pycparser/issues/197 - # for discussion. - if method.__doc__ is not None: - _create_param_rules(cls, method) - elif not issued_nodoc_warning: - warnings.warn( - 'parsing methods must have __doc__ for pycparser to work properly', - RuntimeWarning, - stacklevel=2) - issued_nodoc_warning = True - return cls - - -def _create_param_rules(cls, func): - """ Create ply.yacc rules based on a parameterized rule function - - Generates new methods (one per each pair of parameters) based on the - template rule function `func`, and attaches them to `cls`. The rule - function's parameters must be accessible via its `_params` attribute. - """ - for xxx, yyy in func._params: - # Use the template method's body for each new method - def param_rule(self, p): - func(self, p) - - # Substitute in the params for the grammar rule and function name - param_rule.__doc__ = func.__doc__.replace('xxx', xxx).replace('yyy', yyy) - param_rule.__name__ = func.__name__.replace('xxx', xxx) - - # Attach the new method to the class - setattr(cls, param_rule.__name__, param_rule) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py b/dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py deleted file mode 100644 index 0622c3660..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/pycparser/yacctab.py +++ /dev/null @@ -1,366 +0,0 @@ - -# yacctab.py -# This file is automatically generated. Do not edit. -_tabversion = '3.10' - -_lr_method = 'LALR' - -_lr_signature = 'translation_unit_or_emptyleftLORleftLANDleftORleftXORleftANDleftEQNEleftGTGELTLEleftRSHIFTLSHIFTleftPLUSMINUSleftTIMESDIVIDEMODAUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN FLOAT FOR GOTO IF INLINE INT LONG REGISTER OFFSETOF RESTRICT RETURN SHORT SIGNED SIZEOF STATIC STRUCT SWITCH TYPEDEF UNION UNSIGNED VOID VOLATILE WHILE __INT128 _BOOL _COMPLEX _NORETURN _THREAD_LOCAL _STATIC_ASSERT _ATOMIC _ALIGNOF _ALIGNAS ID TYPEID INT_CONST_DEC INT_CONST_OCT INT_CONST_HEX INT_CONST_BIN INT_CONST_CHAR FLOAT_CONST HEX_FLOAT_CONST CHAR_CONST WCHAR_CONST U8CHAR_CONST U16CHAR_CONST U32CHAR_CONST STRING_LITERAL WSTRING_LITERAL U8STRING_LITERAL U16STRING_LITERAL U32STRING_LITERAL PLUS MINUS TIMES DIVIDE MOD OR AND NOT XOR LSHIFT RSHIFT LOR LAND LNOT LT LE GT GE EQ NE EQUALS TIMESEQUAL DIVEQUAL MODEQUAL PLUSEQUAL MINUSEQUAL LSHIFTEQUAL RSHIFTEQUAL ANDEQUAL XOREQUAL OREQUAL PLUSPLUS MINUSMINUS ARROW CONDOP LPAREN RPAREN LBRACKET RBRACKET LBRACE RBRACE COMMA PERIOD SEMI COLON ELLIPSIS PPHASH PPPRAGMA PPPRAGMASTRabstract_declarator_opt : empty\n| abstract_declaratorassignment_expression_opt : empty\n| assignment_expressionblock_item_list_opt : empty\n| block_item_listdeclaration_list_opt : empty\n| declaration_listdeclaration_specifiers_no_type_opt : empty\n| declaration_specifiers_no_typedesignation_opt : empty\n| designationexpression_opt : empty\n| expressionid_init_declarator_list_opt : empty\n| id_init_declarator_listidentifier_list_opt : empty\n| identifier_listinit_declarator_list_opt : empty\n| init_declarator_listinitializer_list_opt : empty\n| initializer_listparameter_type_list_opt : empty\n| parameter_type_liststruct_declarator_list_opt : empty\n| struct_declarator_listtype_qualifier_list_opt : empty\n| type_qualifier_list direct_id_declarator : ID\n direct_id_declarator : LPAREN id_declarator RPAREN\n direct_id_declarator : direct_id_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_id_declarator : direct_id_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n | direct_id_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n direct_id_declarator : direct_id_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n direct_id_declarator : direct_id_declarator LPAREN parameter_type_list RPAREN\n | direct_id_declarator LPAREN identifier_list_opt RPAREN\n direct_typeid_declarator : TYPEID\n direct_typeid_declarator : LPAREN typeid_declarator RPAREN\n direct_typeid_declarator : direct_typeid_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_typeid_declarator : direct_typeid_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n | direct_typeid_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n direct_typeid_declarator : direct_typeid_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n direct_typeid_declarator : direct_typeid_declarator LPAREN parameter_type_list RPAREN\n | direct_typeid_declarator LPAREN identifier_list_opt RPAREN\n direct_typeid_noparen_declarator : TYPEID\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET\n | direct_typeid_noparen_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET\n direct_typeid_noparen_declarator : direct_typeid_noparen_declarator LPAREN parameter_type_list RPAREN\n | direct_typeid_noparen_declarator LPAREN identifier_list_opt RPAREN\n id_declarator : direct_id_declarator\n id_declarator : pointer direct_id_declarator\n typeid_declarator : direct_typeid_declarator\n typeid_declarator : pointer direct_typeid_declarator\n typeid_noparen_declarator : direct_typeid_noparen_declarator\n typeid_noparen_declarator : pointer direct_typeid_noparen_declarator\n translation_unit_or_empty : translation_unit\n | empty\n translation_unit : external_declaration\n translation_unit : translation_unit external_declaration\n external_declaration : function_definition\n external_declaration : declaration\n external_declaration : pp_directive\n | pppragma_directive\n external_declaration : SEMI\n external_declaration : static_assert\n static_assert : _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN\n | _STATIC_ASSERT LPAREN constant_expression RPAREN\n pp_directive : PPHASH\n pppragma_directive : PPPRAGMA\n | PPPRAGMA PPPRAGMASTR\n function_definition : id_declarator declaration_list_opt compound_statement\n function_definition : declaration_specifiers id_declarator declaration_list_opt compound_statement\n statement : labeled_statement\n | expression_statement\n | compound_statement\n | selection_statement\n | iteration_statement\n | jump_statement\n | pppragma_directive\n | static_assert\n pragmacomp_or_statement : pppragma_directive statement\n | statement\n decl_body : declaration_specifiers init_declarator_list_opt\n | declaration_specifiers_no_type id_init_declarator_list_opt\n declaration : decl_body SEMI\n declaration_list : declaration\n | declaration_list declaration\n declaration_specifiers_no_type : type_qualifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : storage_class_specifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : function_specifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : atomic_specifier declaration_specifiers_no_type_opt\n declaration_specifiers_no_type : alignment_specifier declaration_specifiers_no_type_opt\n declaration_specifiers : declaration_specifiers type_qualifier\n declaration_specifiers : declaration_specifiers storage_class_specifier\n declaration_specifiers : declaration_specifiers function_specifier\n declaration_specifiers : declaration_specifiers type_specifier_no_typeid\n declaration_specifiers : type_specifier\n declaration_specifiers : declaration_specifiers_no_type type_specifier\n declaration_specifiers : declaration_specifiers alignment_specifier\n storage_class_specifier : AUTO\n | REGISTER\n | STATIC\n | EXTERN\n | TYPEDEF\n | _THREAD_LOCAL\n function_specifier : INLINE\n | _NORETURN\n type_specifier_no_typeid : VOID\n | _BOOL\n | CHAR\n | SHORT\n | INT\n | LONG\n | FLOAT\n | DOUBLE\n | _COMPLEX\n | SIGNED\n | UNSIGNED\n | __INT128\n type_specifier : typedef_name\n | enum_specifier\n | struct_or_union_specifier\n | type_specifier_no_typeid\n | atomic_specifier\n atomic_specifier : _ATOMIC LPAREN type_name RPAREN\n type_qualifier : CONST\n | RESTRICT\n | VOLATILE\n | _ATOMIC\n init_declarator_list : init_declarator\n | init_declarator_list COMMA init_declarator\n init_declarator : declarator\n | declarator EQUALS initializer\n id_init_declarator_list : id_init_declarator\n | id_init_declarator_list COMMA init_declarator\n id_init_declarator : id_declarator\n | id_declarator EQUALS initializer\n specifier_qualifier_list : specifier_qualifier_list type_specifier_no_typeid\n specifier_qualifier_list : specifier_qualifier_list type_qualifier\n specifier_qualifier_list : type_specifier\n specifier_qualifier_list : type_qualifier_list type_specifier\n specifier_qualifier_list : alignment_specifier\n specifier_qualifier_list : specifier_qualifier_list alignment_specifier\n struct_or_union_specifier : struct_or_union ID\n | struct_or_union TYPEID\n struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close\n | struct_or_union brace_open brace_close\n struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close\n | struct_or_union ID brace_open brace_close\n | struct_or_union TYPEID brace_open struct_declaration_list brace_close\n | struct_or_union TYPEID brace_open brace_close\n struct_or_union : STRUCT\n | UNION\n struct_declaration_list : struct_declaration\n | struct_declaration_list struct_declaration\n struct_declaration : specifier_qualifier_list struct_declarator_list_opt SEMI\n struct_declaration : SEMI\n struct_declaration : pppragma_directive\n struct_declarator_list : struct_declarator\n | struct_declarator_list COMMA struct_declarator\n struct_declarator : declarator\n struct_declarator : declarator COLON constant_expression\n | COLON constant_expression\n enum_specifier : ENUM ID\n | ENUM TYPEID\n enum_specifier : ENUM brace_open enumerator_list brace_close\n enum_specifier : ENUM ID brace_open enumerator_list brace_close\n | ENUM TYPEID brace_open enumerator_list brace_close\n enumerator_list : enumerator\n | enumerator_list COMMA\n | enumerator_list COMMA enumerator\n alignment_specifier : _ALIGNAS LPAREN type_name RPAREN\n | _ALIGNAS LPAREN constant_expression RPAREN\n enumerator : ID\n | ID EQUALS constant_expression\n declarator : id_declarator\n | typeid_declarator\n pointer : TIMES type_qualifier_list_opt\n | TIMES type_qualifier_list_opt pointer\n type_qualifier_list : type_qualifier\n | type_qualifier_list type_qualifier\n parameter_type_list : parameter_list\n | parameter_list COMMA ELLIPSIS\n parameter_list : parameter_declaration\n | parameter_list COMMA parameter_declaration\n parameter_declaration : declaration_specifiers id_declarator\n | declaration_specifiers typeid_noparen_declarator\n parameter_declaration : declaration_specifiers abstract_declarator_opt\n identifier_list : identifier\n | identifier_list COMMA identifier\n initializer : assignment_expression\n initializer : brace_open initializer_list_opt brace_close\n | brace_open initializer_list COMMA brace_close\n initializer_list : designation_opt initializer\n | initializer_list COMMA designation_opt initializer\n designation : designator_list EQUALS\n designator_list : designator\n | designator_list designator\n designator : LBRACKET constant_expression RBRACKET\n | PERIOD identifier\n type_name : specifier_qualifier_list abstract_declarator_opt\n abstract_declarator : pointer\n abstract_declarator : pointer direct_abstract_declarator\n abstract_declarator : direct_abstract_declarator\n direct_abstract_declarator : LPAREN abstract_declarator RPAREN direct_abstract_declarator : direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET\n direct_abstract_declarator : LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET\n direct_abstract_declarator : direct_abstract_declarator LBRACKET TIMES RBRACKET\n direct_abstract_declarator : LBRACKET TIMES RBRACKET\n direct_abstract_declarator : direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN\n direct_abstract_declarator : LPAREN parameter_type_list_opt RPAREN\n block_item : declaration\n | statement\n block_item_list : block_item\n | block_item_list block_item\n compound_statement : brace_open block_item_list_opt brace_close labeled_statement : ID COLON pragmacomp_or_statement labeled_statement : CASE constant_expression COLON pragmacomp_or_statement labeled_statement : DEFAULT COLON pragmacomp_or_statement selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement jump_statement : GOTO ID SEMI jump_statement : BREAK SEMI jump_statement : CONTINUE SEMI jump_statement : RETURN expression SEMI\n | RETURN SEMI\n expression_statement : expression_opt SEMI expression : assignment_expression\n | expression COMMA assignment_expression\n assignment_expression : LPAREN compound_statement RPAREN typedef_name : TYPEID assignment_expression : conditional_expression\n | unary_expression assignment_operator assignment_expression\n assignment_operator : EQUALS\n | XOREQUAL\n | TIMESEQUAL\n | DIVEQUAL\n | MODEQUAL\n | PLUSEQUAL\n | MINUSEQUAL\n | LSHIFTEQUAL\n | RSHIFTEQUAL\n | ANDEQUAL\n | OREQUAL\n constant_expression : conditional_expression conditional_expression : binary_expression\n | binary_expression CONDOP expression COLON conditional_expression\n binary_expression : cast_expression\n | binary_expression TIMES binary_expression\n | binary_expression DIVIDE binary_expression\n | binary_expression MOD binary_expression\n | binary_expression PLUS binary_expression\n | binary_expression MINUS binary_expression\n | binary_expression RSHIFT binary_expression\n | binary_expression LSHIFT binary_expression\n | binary_expression LT binary_expression\n | binary_expression LE binary_expression\n | binary_expression GE binary_expression\n | binary_expression GT binary_expression\n | binary_expression EQ binary_expression\n | binary_expression NE binary_expression\n | binary_expression AND binary_expression\n | binary_expression OR binary_expression\n | binary_expression XOR binary_expression\n | binary_expression LAND binary_expression\n | binary_expression LOR binary_expression\n cast_expression : unary_expression cast_expression : LPAREN type_name RPAREN cast_expression unary_expression : postfix_expression unary_expression : PLUSPLUS unary_expression\n | MINUSMINUS unary_expression\n | unary_operator cast_expression\n unary_expression : SIZEOF unary_expression\n | SIZEOF LPAREN type_name RPAREN\n | _ALIGNOF LPAREN type_name RPAREN\n unary_operator : AND\n | TIMES\n | PLUS\n | MINUS\n | NOT\n | LNOT\n postfix_expression : primary_expression postfix_expression : postfix_expression LBRACKET expression RBRACKET postfix_expression : postfix_expression LPAREN argument_expression_list RPAREN\n | postfix_expression LPAREN RPAREN\n postfix_expression : postfix_expression PERIOD ID\n | postfix_expression PERIOD TYPEID\n | postfix_expression ARROW ID\n | postfix_expression ARROW TYPEID\n postfix_expression : postfix_expression PLUSPLUS\n | postfix_expression MINUSMINUS\n postfix_expression : LPAREN type_name RPAREN brace_open initializer_list brace_close\n | LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close\n primary_expression : identifier primary_expression : constant primary_expression : unified_string_literal\n | unified_wstring_literal\n primary_expression : LPAREN expression RPAREN primary_expression : OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN\n offsetof_member_designator : identifier\n | offsetof_member_designator PERIOD identifier\n | offsetof_member_designator LBRACKET expression RBRACKET\n argument_expression_list : assignment_expression\n | argument_expression_list COMMA assignment_expression\n identifier : ID constant : INT_CONST_DEC\n | INT_CONST_OCT\n | INT_CONST_HEX\n | INT_CONST_BIN\n | INT_CONST_CHAR\n constant : FLOAT_CONST\n | HEX_FLOAT_CONST\n constant : CHAR_CONST\n | WCHAR_CONST\n | U8CHAR_CONST\n | U16CHAR_CONST\n | U32CHAR_CONST\n unified_string_literal : STRING_LITERAL\n | unified_string_literal STRING_LITERAL\n unified_wstring_literal : WSTRING_LITERAL\n | U8STRING_LITERAL\n | U16STRING_LITERAL\n | U32STRING_LITERAL\n | unified_wstring_literal WSTRING_LITERAL\n | unified_wstring_literal U8STRING_LITERAL\n | unified_wstring_literal U16STRING_LITERAL\n | unified_wstring_literal U32STRING_LITERAL\n brace_open : LBRACE\n brace_close : RBRACE\n empty : ' - -_lr_action_items = {'INT_CONST_CHAR':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,132,-335,-28,-182,-27,132,-337,-87,-72,-337,132,-286,-285,132,132,-283,-287,-288,132,-284,132,132,132,-336,-183,132,132,-28,-337,132,-28,-337,-337,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,-337,-76,-79,-82,-75,132,-77,132,132,-81,-215,-214,-80,-216,132,-78,132,132,-69,-284,132,132,-284,132,132,-244,-247,-245,-241,-242,-246,-248,132,-250,-251,-243,-249,-12,132,132,-11,132,132,132,132,-234,-233,132,-231,132,132,-217,132,-230,132,-84,-218,132,132,132,-337,-337,-198,132,132,132,-337,-284,-229,-232,132,-221,132,-83,-219,-68,132,-28,-337,132,-11,132,132,-220,132,132,132,-284,132,132,132,-337,132,-225,-224,-222,-84,132,132,132,-226,-223,132,-228,-227,]),'VOID':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[6,-337,-113,-128,6,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,6,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,6,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,6,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,6,-131,-95,-101,-97,6,-53,-126,6,-88,6,6,-93,6,-147,-335,-146,6,-167,-166,-182,-100,-126,6,-87,-90,-94,-92,-61,-72,6,-144,-142,6,6,6,-73,6,-89,6,6,6,-149,-159,-160,-156,-336,6,-183,-30,6,6,-74,6,6,6,6,-174,-175,6,-143,-140,6,-141,-145,-76,-79,-82,-75,-77,6,-81,-215,-214,-80,-216,-78,-127,6,-153,6,-151,-148,-157,-168,-69,-36,-35,6,6,6,-234,-233,6,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,6,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LBRACKET':([2,3,5,6,7,10,11,12,13,18,20,22,23,26,27,30,33,34,35,36,39,42,43,44,46,48,49,50,54,56,58,60,62,68,71,73,76,77,80,81,82,86,96,97,98,100,101,103,104,105,106,109,111,127,132,133,134,136,138,139,140,141,142,143,145,147,148,152,153,154,156,160,161,163,164,166,167,168,169,176,177,187,191,198,199,200,211,216,227,230,235,236,237,238,240,241,261,263,269,275,276,278,279,280,283,310,312,314,316,317,328,340,341,342,344,345,347,355,356,371,376,402,403,404,405,407,411,414,442,443,448,449,453,454,457,458,464,465,470,472,474,482,483,488,489,490,492,511,512,518,519,520,526,527,529,530,531,532,544,545,547,550,551,559,560,563,565,570,571,572,],[-113,-128,-124,-110,-106,-104,-107,-125,-105,-99,-109,-120,-115,-102,-126,-108,-238,-111,-337,-122,-129,-29,-121,-116,-112,117,-123,-117,-119,-114,-130,-118,-103,-96,-98,128,-131,-37,-95,-101,-97,117,-147,-335,-146,-167,-166,-28,-180,-182,-27,-100,-126,128,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-314,-142,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,265,-323,-312,282,-149,-336,-183,-181,-30,282,-38,373,-326,-334,-332,-331,-333,-174,-175,-298,-297,-143,-140,282,282,-141,-145,421,-312,-127,-153,-151,-148,-168,-36,-35,282,282,459,-45,-44,-43,-199,373,-296,-295,-294,-293,-292,-305,421,-152,-150,-170,-169,-31,-34,282,459,-39,-42,-202,373,-200,-290,-291,373,-213,-207,-211,-33,-32,-41,-40,-201,549,-307,-209,-208,-210,-212,-51,-50,-306,373,-299,-46,-49,-308,-300,-48,-47,-309,]),'WCHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,133,-335,-28,-182,-27,133,-337,-87,-72,-337,133,-286,-285,133,133,-283,-287,-288,133,-284,133,133,133,-336,-183,133,133,-28,-337,133,-28,-337,-337,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,133,-337,-76,-79,-82,-75,133,-77,133,133,-81,-215,-214,-80,-216,133,-78,133,133,-69,-284,133,133,-284,133,133,-244,-247,-245,-241,-242,-246,-248,133,-250,-251,-243,-249,-12,133,133,-11,133,133,133,133,-234,-233,133,-231,133,133,-217,133,-230,133,-84,-218,133,133,133,-337,-337,-198,133,133,133,-337,-284,-229,-232,133,-221,133,-83,-219,-68,133,-28,-337,133,-11,133,133,-220,133,133,133,-284,133,133,133,-337,133,-225,-224,-222,-84,133,133,133,-226,-223,133,-228,-227,]),'FLOAT_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,134,-335,-28,-182,-27,134,-337,-87,-72,-337,134,-286,-285,134,134,-283,-287,-288,134,-284,134,134,134,-336,-183,134,134,-28,-337,134,-28,-337,-337,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,-337,-76,-79,-82,-75,134,-77,134,134,-81,-215,-214,-80,-216,134,-78,134,134,-69,-284,134,134,-284,134,134,-244,-247,-245,-241,-242,-246,-248,134,-250,-251,-243,-249,-12,134,134,-11,134,134,134,134,-234,-233,134,-231,134,134,-217,134,-230,134,-84,-218,134,134,134,-337,-337,-198,134,134,134,-337,-284,-229,-232,134,-221,134,-83,-219,-68,134,-28,-337,134,-11,134,134,-220,134,134,134,-284,134,134,134,-337,134,-225,-224,-222,-84,134,134,134,-226,-223,134,-228,-227,]),'MINUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,224,227,229,230,231,232,233,234,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,273,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,135,-335,-28,-182,-27,135,-337,-87,-72,-337,135,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-274,-314,135,-327,135,-283,-287,-325,-304,-322,-302,-255,-315,-289,245,-328,-316,-288,-329,-320,-276,-323,135,-284,135,135,-312,135,-336,-183,135,135,-28,-337,135,-28,-337,-274,-337,135,-326,135,-280,135,-277,-334,-332,-331,-333,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,-298,-297,135,135,-279,-278,-337,-76,-79,-82,-75,135,-77,135,135,-81,-215,-214,-80,-216,135,-78,-312,135,135,-69,-284,135,135,-284,135,135,-244,-247,-245,-241,-242,-246,-248,135,-250,-251,-243,-249,-12,135,135,-11,245,245,245,-260,245,245,245,-259,245,245,-257,-256,245,245,245,245,245,-258,-296,-295,-294,-293,-292,-305,135,135,135,135,-234,-233,135,-231,135,135,-217,135,-230,135,-84,-218,135,135,135,-337,-337,-198,135,-281,-282,135,-290,-291,135,-275,-337,-284,-229,-232,135,-221,135,-83,-219,-68,135,-28,-337,135,-11,135,135,-220,135,135,135,-284,135,135,-306,135,-337,-299,135,-225,-224,-222,-84,-300,135,135,135,-226,-223,135,-228,-227,]),'RPAREN':([2,3,5,6,7,10,11,12,13,18,20,22,23,26,27,30,33,34,35,36,39,42,43,44,46,48,49,50,54,56,58,60,62,68,71,73,76,77,80,81,82,86,96,98,100,101,103,104,105,106,107,109,111,118,125,127,129,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,157,158,159,160,161,162,163,164,166,167,168,169,176,177,178,183,187,191,198,199,200,203,207,208,209,210,211,212,213,215,216,221,222,224,225,230,232,234,235,236,237,238,240,241,261,263,266,268,269,270,271,272,273,274,275,276,277,278,279,280,281,283,294,312,314,316,317,328,340,341,342,343,344,345,346,347,348,355,356,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,408,409,411,414,415,416,417,418,422,433,439,442,443,448,449,452,453,454,457,458,460,461,462,463,464,465,468,476,478,480,482,483,486,487,489,490,492,495,501,503,507,511,512,516,517,518,519,524,525,526,527,529,530,531,532,544,545,547,551,553,556,559,560,563,565,566,567,570,571,572,573,],[-113,-128,-124,-110,-106,-104,-107,-125,-105,-99,-109,-120,-115,-102,-126,-108,-238,-111,-337,-122,-129,-29,-121,-116,-112,-52,-123,-117,-119,-114,-130,-118,-103,-96,-98,-54,-131,-37,-95,-101,-97,-53,-147,-146,-167,-166,-28,-180,-182,-27,200,-100,-126,-337,216,-55,-337,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,240,-255,241,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-337,-252,312,-149,-336,-183,-181,-30,332,340,-17,341,-186,-337,-18,-184,-191,-38,355,356,-274,-239,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,407,-279,-143,411,413,-235,-278,-203,-140,-204,-1,-337,-141,-145,-2,-206,-14,-127,-153,-151,-148,-168,-36,-35,-337,-190,-204,-56,-188,-45,-189,-44,-43,476,477,478,479,480,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-310,483,-305,-205,-23,-24,489,490,-337,-13,-218,-152,-150,-170,-169,510,-31,-34,-204,-57,-337,-192,-185,-187,-39,-42,-240,-237,-281,-282,-290,-291,-236,-275,-213,-207,-211,532,535,537,539,-33,-32,544,545,-41,-40,-254,-311,547,-307,-209,-208,-210,-212,-51,-50,-306,-299,-337,568,-46,-49,-308,-300,-337,574,-48,-47,-309,577,]),'STRUCT':([0,1,3,7,10,11,13,14,16,17,19,20,21,25,26,27,29,30,38,39,40,42,45,47,48,52,53,55,58,59,61,62,63,64,65,66,67,75,85,86,87,90,91,93,94,95,97,99,105,118,119,120,121,122,123,124,129,172,174,180,181,182,184,185,186,188,189,190,191,198,200,214,223,229,231,233,239,240,241,267,278,284,285,286,289,291,298,300,301,302,303,305,308,312,313,315,318,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,446,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[24,-337,-128,-106,-104,-107,-105,-64,-60,-67,-66,-109,24,-65,-102,-337,-131,-108,-63,-129,24,-29,-62,-70,-52,-337,-337,-337,-130,24,-71,-103,-337,-9,-131,-91,-10,24,24,-53,-337,-88,24,24,-93,24,-335,24,-182,24,-87,-90,-94,-92,-61,-72,24,24,24,-73,24,-89,24,24,24,-159,-160,-156,-336,-183,-30,24,-74,24,24,24,24,-174,-175,24,24,-76,-79,-82,-75,-77,24,-81,-215,-214,-80,-216,-78,-127,24,24,-157,-69,-36,-35,24,24,24,-234,-233,24,-231,-217,-230,-81,-84,-218,-158,-31,-34,24,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LONG':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[23,-337,-113,-128,23,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,23,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,23,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,23,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,23,-131,-95,-101,-97,23,-53,-126,23,-88,23,23,-93,23,-147,-335,-146,23,-167,-166,-182,-100,-126,23,-87,-90,-94,-92,-61,-72,23,-144,-142,23,23,23,-73,23,-89,23,23,23,-149,-159,-160,-156,-336,23,-183,-30,23,23,-74,23,23,23,23,-174,-175,23,-143,-140,23,-141,-145,-76,-79,-82,-75,-77,23,-81,-215,-214,-80,-216,-78,-127,23,-153,23,-151,-148,-157,-168,-69,-36,-35,23,23,23,-234,-233,23,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,23,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PLUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,224,227,229,230,231,232,233,234,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,273,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,137,-335,-28,-182,-27,137,-337,-87,-72,-337,137,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-274,-314,137,-327,137,-283,-287,-325,-304,-322,-302,-255,-315,-289,249,-328,-316,-288,-329,-320,-276,-323,137,-284,137,137,-312,137,-336,-183,137,137,-28,-337,137,-28,-337,-274,-337,137,-326,137,-280,137,-277,-334,-332,-331,-333,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,-298,-297,137,137,-279,-278,-337,-76,-79,-82,-75,137,-77,137,137,-81,-215,-214,-80,-216,137,-78,-312,137,137,-69,-284,137,137,-284,137,137,-244,-247,-245,-241,-242,-246,-248,137,-250,-251,-243,-249,-12,137,137,-11,249,249,249,-260,249,249,249,-259,249,249,-257,-256,249,249,249,249,249,-258,-296,-295,-294,-293,-292,-305,137,137,137,137,-234,-233,137,-231,137,137,-217,137,-230,137,-84,-218,137,137,137,-337,-337,-198,137,-281,-282,137,-290,-291,137,-275,-337,-284,-229,-232,137,-221,137,-83,-219,-68,137,-28,-337,137,-11,137,137,-220,137,137,137,-284,137,137,-306,137,-337,-299,137,-225,-224,-222,-84,-300,137,137,137,-226,-223,137,-228,-227,]),'ELLIPSIS':([350,],[462,]),'U32STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,139,-335,-28,-182,-27,139,-337,-87,-72,-337,139,-286,-285,-330,139,-327,139,-283,-287,235,-328,-288,-329,139,-284,139,139,139,-336,-183,139,139,-28,-337,139,-28,-337,-337,139,139,139,-334,-332,-331,-333,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,-337,-76,-79,-82,-75,139,-77,139,139,-81,-215,-214,-80,-216,139,-78,139,139,-69,-284,139,139,-284,139,139,-244,-247,-245,-241,-242,-246,-248,139,-250,-251,-243,-249,-12,139,139,-11,139,139,139,139,-234,-233,139,-231,139,139,-217,139,-230,139,-84,-218,139,139,139,-337,-337,-198,139,139,139,-337,-284,-229,-232,139,-221,139,-83,-219,-68,139,-28,-337,139,-11,139,139,-220,139,139,139,-284,139,139,139,-337,139,-225,-224,-222,-84,139,139,139,-226,-223,139,-228,-227,]),'GT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,250,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,250,-262,-260,-264,250,-263,-259,-266,250,-257,-256,-265,250,250,250,250,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'GOTO':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,287,-336,-76,-79,-82,-75,-77,287,-81,-215,-214,-80,-216,287,-78,-69,-234,-233,-231,287,-217,-230,287,-84,-218,287,-229,-232,-221,287,-83,-219,-68,287,-220,287,287,-225,-224,-222,-84,287,287,-226,-223,287,-228,-227,]),'ENUM':([0,1,3,7,10,11,13,14,16,17,19,20,21,25,26,27,29,30,38,39,40,42,45,47,48,52,53,55,58,59,61,62,63,64,65,66,67,75,85,86,87,90,91,93,94,95,97,99,105,118,119,120,121,122,123,124,129,172,174,180,181,182,184,185,186,188,189,190,191,198,200,214,223,229,231,233,239,240,241,267,278,284,285,286,289,291,298,300,301,302,303,305,308,312,313,315,318,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,446,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[32,-337,-128,-106,-104,-107,-105,-64,-60,-67,-66,-109,32,-65,-102,-337,-131,-108,-63,-129,32,-29,-62,-70,-52,-337,-337,-337,-130,32,-71,-103,-337,-9,-131,-91,-10,32,32,-53,-337,-88,32,32,-93,32,-335,32,-182,32,-87,-90,-94,-92,-61,-72,32,32,32,-73,32,-89,32,32,32,-159,-160,-156,-336,-183,-30,32,-74,32,32,32,32,-174,-175,32,32,-76,-79,-82,-75,-77,32,-81,-215,-214,-80,-216,-78,-127,32,32,-157,-69,-36,-35,32,32,32,-234,-233,32,-231,-217,-230,-81,-84,-218,-158,-31,-34,32,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PERIOD':([97,132,133,134,136,138,139,140,141,143,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,227,230,235,236,237,238,261,263,310,371,376,402,403,404,405,407,411,470,472,474,482,483,488,520,526,527,547,550,551,563,565,572,],[-335,-317,-321,-318,-303,-324,-330,-313,-319,-301,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,264,-323,-312,-336,372,-326,-334,-332,-331,-333,-298,-297,-312,-199,372,-296,-295,-294,-293,-292,-305,-202,372,-200,-290,-291,372,-201,548,-307,-306,372,-299,-308,-300,-309,]),'GE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,254,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,254,-262,-260,-264,254,-263,-259,-266,254,-257,-256,-265,254,254,254,254,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'INT_CONST_DEC':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,140,-335,-28,-182,-27,140,-337,-87,-72,-337,140,-286,-285,140,140,-283,-287,-288,140,-284,140,140,140,-336,-183,140,140,-28,-337,140,-28,-337,-337,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,-337,-76,-79,-82,-75,140,-77,140,140,-81,-215,-214,-80,-216,140,-78,140,140,-69,-284,140,140,-284,140,140,-244,-247,-245,-241,-242,-246,-248,140,-250,-251,-243,-249,-12,140,140,-11,140,140,140,140,-234,-233,140,-231,140,140,-217,140,-230,140,-84,-218,140,140,140,-337,-337,-198,140,140,140,-337,-284,-229,-232,140,-221,140,-83,-219,-68,140,-28,-337,140,-11,140,140,-220,140,140,140,-284,140,140,140,-337,140,-225,-224,-222,-84,140,140,140,-226,-223,140,-228,-227,]),'ARROW':([132,133,134,136,138,139,140,141,143,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,230,235,236,237,238,261,263,310,402,403,404,405,407,411,482,483,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,262,-323,-312,-336,-326,-334,-332,-331,-333,-298,-297,-312,-296,-295,-294,-293,-292,-305,-290,-291,-306,-299,-300,]),'_STATIC_ASSERT':([0,14,16,17,19,25,38,45,47,59,61,97,119,123,124,180,181,191,223,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[41,-64,-60,-67,-66,-65,-63,-62,-70,41,-71,-335,-87,-61,-72,-73,41,-336,-74,-76,-79,-82,-75,-77,41,-81,-215,-214,-80,-216,41,-78,-69,-234,-233,-231,41,-217,-230,41,-84,-218,41,-229,-232,-221,41,-83,-219,-68,41,-220,41,41,-225,-224,-222,-84,41,41,-226,-223,41,-228,-227,]),'CHAR':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[46,-337,-113,-128,46,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,46,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,46,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,46,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,46,-131,-95,-101,-97,46,-53,-126,46,-88,46,46,-93,46,-147,-335,-146,46,-167,-166,-182,-100,-126,46,-87,-90,-94,-92,-61,-72,46,-144,-142,46,46,46,-73,46,-89,46,46,46,-149,-159,-160,-156,-336,46,-183,-30,46,46,-74,46,46,46,46,-174,-175,46,-143,-140,46,-141,-145,-76,-79,-82,-75,-77,46,-81,-215,-214,-80,-216,-78,-127,46,-153,46,-151,-148,-157,-168,-69,-36,-35,46,46,46,-234,-233,46,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,46,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'HEX_FLOAT_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,141,-335,-28,-182,-27,141,-337,-87,-72,-337,141,-286,-285,141,141,-283,-287,-288,141,-284,141,141,141,-336,-183,141,141,-28,-337,141,-28,-337,-337,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,-337,-76,-79,-82,-75,141,-77,141,141,-81,-215,-214,-80,-216,141,-78,141,141,-69,-284,141,141,-284,141,141,-244,-247,-245,-241,-242,-246,-248,141,-250,-251,-243,-249,-12,141,141,-11,141,141,141,141,-234,-233,141,-231,141,141,-217,141,-230,141,-84,-218,141,141,141,-337,-337,-198,141,141,141,-337,-284,-229,-232,141,-221,141,-83,-219,-68,141,-28,-337,141,-11,141,141,-220,141,141,141,-284,141,141,141,-337,141,-225,-224,-222,-84,141,141,141,-226,-223,141,-228,-227,]),'DOUBLE':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[50,-337,-113,-128,50,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,50,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,50,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,50,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,50,-131,-95,-101,-97,50,-53,-126,50,-88,50,50,-93,50,-147,-335,-146,50,-167,-166,-182,-100,-126,50,-87,-90,-94,-92,-61,-72,50,-144,-142,50,50,50,-73,50,-89,50,50,50,-149,-159,-160,-156,-336,50,-183,-30,50,50,-74,50,50,50,50,-174,-175,50,-143,-140,50,-141,-145,-76,-79,-82,-75,-77,50,-81,-215,-214,-80,-216,-78,-127,50,-153,50,-151,-148,-157,-168,-69,-36,-35,50,50,50,-234,-233,50,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,50,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'MINUSEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,358,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'INT_CONST_OCT':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,145,-335,-28,-182,-27,145,-337,-87,-72,-337,145,-286,-285,145,145,-283,-287,-288,145,-284,145,145,145,-336,-183,145,145,-28,-337,145,-28,-337,-337,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,145,-337,-76,-79,-82,-75,145,-77,145,145,-81,-215,-214,-80,-216,145,-78,145,145,-69,-284,145,145,-284,145,145,-244,-247,-245,-241,-242,-246,-248,145,-250,-251,-243,-249,-12,145,145,-11,145,145,145,145,-234,-233,145,-231,145,145,-217,145,-230,145,-84,-218,145,145,145,-337,-337,-198,145,145,145,-337,-284,-229,-232,145,-221,145,-83,-219,-68,145,-28,-337,145,-11,145,145,-220,145,145,145,-284,145,145,145,-337,145,-225,-224,-222,-84,145,145,145,-226,-223,145,-228,-227,]),'TIMESEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,367,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'OR':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,259,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,259,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,259,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'SHORT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[2,-337,-113,-128,2,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,2,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,2,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,2,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,2,-131,-95,-101,-97,2,-53,-126,2,-88,2,2,-93,2,-147,-335,-146,2,-167,-166,-182,-100,-126,2,-87,-90,-94,-92,-61,-72,2,-144,-142,2,2,2,-73,2,-89,2,2,2,-149,-159,-160,-156,-336,2,-183,-30,2,2,-74,2,2,2,2,-174,-175,2,-143,-140,2,-141,-145,-76,-79,-82,-75,-77,2,-81,-215,-214,-80,-216,-78,-127,2,-153,2,-151,-148,-157,-168,-69,-36,-35,2,2,2,-234,-233,2,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,2,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'RETURN':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,290,-336,-76,-79,-82,-75,-77,290,-81,-215,-214,-80,-216,290,-78,-69,-234,-233,-231,290,-217,-230,290,-84,-218,290,-229,-232,-221,290,-83,-219,-68,290,-220,290,290,-225,-224,-222,-84,290,290,-226,-223,290,-228,-227,]),'RSHIFTEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,368,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'_ALIGNAS':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,109,111,118,119,123,124,129,142,147,174,177,180,181,182,184,185,186,187,188,189,190,191,192,200,211,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[8,8,-113,-128,8,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,8,-120,-115,-65,-102,8,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,8,8,-119,8,-114,-130,8,-118,-71,-103,8,-131,-96,-98,8,-131,-95,-101,-97,8,-53,8,8,-88,8,8,-147,-335,-146,8,-167,-166,-100,-126,8,-87,-61,-72,8,-144,-142,8,8,-73,8,-89,8,8,8,-149,-159,-160,-156,-336,8,-30,8,-74,8,8,8,8,-174,-175,8,-143,-140,8,-141,-145,-76,-79,-82,-75,-77,8,-81,-215,-214,-80,-216,-78,-127,8,-153,8,-151,-148,-157,-168,-69,-36,-35,8,8,8,-234,-233,8,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,8,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'RESTRICT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,103,105,109,111,117,118,119,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[39,39,-113,-128,39,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,39,-120,-115,-65,-102,39,-131,-108,-238,-111,39,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,39,39,-119,39,-114,-130,39,-118,-71,-103,39,-131,-96,-98,39,-131,-95,-101,-97,39,-53,39,39,-88,39,39,-147,-335,-146,39,-167,-166,39,-182,-100,-126,39,39,-87,-61,-72,39,39,-144,-142,39,39,39,-73,39,-89,39,39,39,-149,-159,-160,-156,-336,39,-183,-30,39,39,39,39,39,-74,39,39,39,39,-174,-175,39,-143,-140,39,-141,-145,39,-76,-79,-82,-75,-77,39,-81,-215,-214,-80,-216,-78,-127,39,-153,39,-151,-148,-157,-168,-69,-36,-35,39,39,39,-234,-233,39,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,39,39,-229,-232,-221,-83,-219,-68,-33,-32,39,39,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'STATIC':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,105,109,111,117,118,119,123,124,128,129,180,181,182,187,191,198,200,205,211,219,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,536,554,555,557,558,575,576,578,579,],[10,10,-113,-128,10,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,10,-120,-115,-65,-102,10,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,10,10,-119,10,-114,-130,10,-118,-71,-103,10,-131,-96,-98,10,-131,-95,-101,-97,-53,10,10,-88,10,-147,-335,-146,-167,-166,-182,-100,-126,206,10,-87,-61,-72,220,10,-73,10,-89,-149,-336,-183,-30,338,10,353,-74,-174,-175,10,-76,-79,-82,-75,-77,10,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,10,10,10,-234,-233,10,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,515,10,-229,-232,-221,-83,-219,-68,-33,-32,542,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'SIZEOF':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,146,-335,-28,-182,-27,146,-337,-87,-72,-337,146,-286,-285,146,146,-283,-287,-288,146,-284,146,146,146,-336,-183,146,146,-28,-337,146,-28,-337,-337,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,146,-337,-76,-79,-82,-75,146,-77,146,146,-81,-215,-214,-80,-216,146,-78,146,146,-69,-284,146,146,-284,146,146,-244,-247,-245,-241,-242,-246,-248,146,-250,-251,-243,-249,-12,146,146,-11,146,146,146,146,-234,-233,146,-231,146,146,-217,146,-230,146,-84,-218,146,146,146,-337,-337,-198,146,146,146,-337,-284,-229,-232,146,-221,146,-83,-219,-68,146,-28,-337,146,-11,146,146,-220,146,146,146,-284,146,146,146,-337,146,-225,-224,-222,-84,146,146,146,-226,-223,146,-228,-227,]),'UNSIGNED':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[22,-337,-113,-128,22,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,22,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,22,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,22,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,22,-131,-95,-101,-97,22,-53,-126,22,-88,22,22,-93,22,-147,-335,-146,22,-167,-166,-182,-100,-126,22,-87,-90,-94,-92,-61,-72,22,-144,-142,22,22,22,-73,22,-89,22,22,22,-149,-159,-160,-156,-336,22,-183,-30,22,22,-74,22,22,22,22,-174,-175,22,-143,-140,22,-141,-145,-76,-79,-82,-75,-77,22,-81,-215,-214,-80,-216,-78,-127,22,-153,22,-151,-148,-157,-168,-69,-36,-35,22,22,22,-234,-233,22,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,22,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'UNION':([0,1,3,7,10,11,13,14,16,17,19,20,21,25,26,27,29,30,38,39,40,42,45,47,48,52,53,55,58,59,61,62,63,64,65,66,67,75,85,86,87,90,91,93,94,95,97,99,105,118,119,120,121,122,123,124,129,172,174,180,181,182,184,185,186,188,189,190,191,198,200,214,223,229,231,233,239,240,241,267,278,284,285,286,289,291,298,300,301,302,303,305,308,312,313,315,318,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,446,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[28,-337,-128,-106,-104,-107,-105,-64,-60,-67,-66,-109,28,-65,-102,-337,-131,-108,-63,-129,28,-29,-62,-70,-52,-337,-337,-337,-130,28,-71,-103,-337,-9,-131,-91,-10,28,28,-53,-337,-88,28,28,-93,28,-335,28,-182,28,-87,-90,-94,-92,-61,-72,28,28,28,-73,28,-89,28,28,28,-159,-160,-156,-336,-183,-30,28,-74,28,28,28,28,-174,-175,28,28,-76,-79,-82,-75,-77,28,-81,-215,-214,-80,-216,-78,-127,28,28,-157,-69,-36,-35,28,28,28,-234,-233,28,-231,-217,-230,-81,-84,-218,-158,-31,-34,28,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'COLON':([2,3,5,6,12,22,23,33,34,36,39,42,43,44,46,48,49,50,54,56,58,60,73,74,76,77,86,96,98,100,101,111,127,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,179,187,191,192,200,216,224,225,230,232,234,235,236,237,238,240,241,261,263,268,269,272,273,275,279,280,295,310,312,314,316,317,324,328,340,341,355,356,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,407,411,431,442,443,445,448,449,453,454,464,465,468,476,478,480,482,483,486,487,511,512,518,519,524,547,551,565,],[-113,-128,-124,-110,-125,-120,-115,-238,-111,-122,-129,-29,-121,-116,-112,-52,-123,-117,-119,-114,-130,-118,-54,-179,-131,-37,-53,-147,-146,-167,-166,-126,-55,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-178,-149,-336,319,-30,-38,-274,-239,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,-279,-143,-235,-278,-140,-141,-145,429,440,-127,-153,-151,-148,447,-168,-36,-35,-44,-43,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,481,-270,-258,-296,-295,-294,-293,-292,-305,502,-152,-150,319,-170,-169,-31,-34,-39,-42,-240,-237,-281,-282,-290,-291,-236,-275,-33,-32,-41,-40,-254,-306,-299,-300,]),'$end':([0,9,14,16,17,19,25,38,45,47,57,59,61,119,123,124,180,191,223,332,439,510,],[-337,0,-64,-60,-67,-66,-65,-63,-62,-70,-59,-58,-71,-87,-61,-72,-73,-336,-74,-69,-218,-68,]),'WSTRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,148,-335,-28,-182,-27,148,-337,-87,-72,-337,148,-286,-285,-330,148,-327,148,-283,-287,237,-328,-288,-329,148,-284,148,148,148,-336,-183,148,148,-28,-337,148,-28,-337,-337,148,148,148,-334,-332,-331,-333,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,148,-337,-76,-79,-82,-75,148,-77,148,148,-81,-215,-214,-80,-216,148,-78,148,148,-69,-284,148,148,-284,148,148,-244,-247,-245,-241,-242,-246,-248,148,-250,-251,-243,-249,-12,148,148,-11,148,148,148,148,-234,-233,148,-231,148,148,-217,148,-230,148,-84,-218,148,148,148,-337,-337,-198,148,148,148,-337,-284,-229,-232,148,-221,148,-83,-219,-68,148,-28,-337,148,-11,148,148,-220,148,148,148,-284,148,148,148,-337,148,-225,-224,-222,-84,148,148,148,-226,-223,148,-228,-227,]),'DIVIDE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,252,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,252,252,252,252,252,252,252,252,252,252,-257,-256,252,252,252,252,252,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'FOR':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,292,-336,-76,-79,-82,-75,-77,292,-81,-215,-214,-80,-216,292,-78,-69,-234,-233,-231,292,-217,-230,292,-84,-218,292,-229,-232,-221,292,-83,-219,-68,292,-220,292,292,-225,-224,-222,-84,292,292,-226,-223,292,-228,-227,]),'PLUSPLUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,145,146,148,149,150,151,152,153,154,156,160,161,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,227,229,230,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,482,483,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,149,-335,-28,-182,-27,149,-337,-87,-72,-337,149,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-314,149,-327,149,-283,-287,-325,-304,-322,-302,-315,-289,-328,-316,-288,-329,-320,263,-323,149,-284,149,149,-312,149,-336,-183,149,149,-28,-337,149,-28,-337,-337,149,-326,149,149,-334,-332,-331,-333,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,149,-298,-297,149,149,-337,-76,-79,-82,-75,149,-77,149,149,-81,-215,-214,-80,-216,149,-78,-312,149,149,-69,-284,149,149,-284,149,149,-244,-247,-245,-241,-242,-246,-248,149,-250,-251,-243,-249,-12,149,149,-11,-296,-295,-294,-293,-292,-305,149,149,149,149,-234,-233,149,-231,149,149,-217,149,-230,149,-84,-218,149,149,149,-337,-337,-198,149,149,-290,-291,149,-337,-284,-229,-232,149,-221,149,-83,-219,-68,149,-28,-337,149,-11,149,149,-220,149,149,149,-284,149,149,-306,149,-337,-299,149,-225,-224,-222,-84,-300,149,149,149,-226,-223,149,-228,-227,]),'EQUALS':([42,48,73,74,75,77,78,86,110,127,132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,179,191,197,200,216,224,230,232,234,235,236,237,238,261,263,268,273,310,340,341,355,356,371,376,402,403,404,405,407,411,453,454,464,465,470,474,478,480,482,483,487,511,512,518,519,520,547,551,565,],[-29,-52,-54,-179,-178,-37,131,-53,201,-55,-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-178,-336,329,-30,-38,360,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-36,-35,-44,-43,-199,475,-296,-295,-294,-293,-292,-305,-31,-34,-39,-42,-202,-200,-281,-282,-290,-291,-275,-33,-32,-41,-40,-201,-306,-299,-300,]),'ELSE':([61,124,191,284,285,286,289,291,300,303,308,332,424,425,428,435,437,438,439,496,497,500,505,506,510,536,554,555,557,558,575,576,578,579,],[-71,-72,-336,-76,-79,-82,-75,-77,-81,-80,-78,-69,-234,-233,-231,-230,-81,-84,-218,-229,-232,-221,-83,-219,-68,-220,-225,-224,-222,569,-226,-223,-228,-227,]),'ANDEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,365,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'EQ':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,256,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,256,-262,-260,-264,-268,-263,-259,-266,256,-257,-256,-265,256,-267,256,256,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'AND':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,144,145,146,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,224,227,229,230,231,232,233,234,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,273,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,150,-335,-28,-182,-27,150,-337,-87,-72,-337,150,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-274,-314,150,-327,150,-283,-287,-325,-304,-322,-302,-255,-315,-289,257,-328,-316,-288,-329,-320,-276,-323,150,-284,150,150,-312,150,-336,-183,150,150,-28,-337,150,-28,-337,-274,-337,150,-326,150,-280,150,-277,-334,-332,-331,-333,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,-298,-297,150,150,-279,-278,-337,-76,-79,-82,-75,150,-77,150,150,-81,-215,-214,-80,-216,150,-78,-312,150,150,-69,-284,150,150,-284,150,150,-244,-247,-245,-241,-242,-246,-248,150,-250,-251,-243,-249,-12,150,150,-11,-261,257,-262,-260,-264,-268,-263,-259,-266,257,-257,-256,-265,257,-267,-269,257,-258,-296,-295,-294,-293,-292,-305,150,150,150,150,-234,-233,150,-231,150,150,-217,150,-230,150,-84,-218,150,150,150,-337,-337,-198,150,-281,-282,150,-290,-291,150,-275,-337,-284,-229,-232,150,-221,150,-83,-219,-68,150,-28,-337,150,-11,150,150,-220,150,150,150,-284,150,150,-306,150,-337,-299,150,-225,-224,-222,-84,-300,150,150,150,-226,-223,150,-228,-227,]),'TYPEID':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,72,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,103,104,105,106,109,111,118,119,120,121,122,123,124,126,129,142,147,172,174,180,181,182,184,185,186,187,188,189,190,191,192,198,199,200,202,211,214,223,229,231,233,239,240,241,262,264,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,344,350,422,424,425,427,428,432,435,437,438,439,442,443,445,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[33,-337,-113,-128,77,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,33,-120,-115,-154,-65,-102,-126,-155,-131,-108,96,100,-238,-111,-337,-122,-63,-129,33,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,33,-118,-71,-103,-337,-9,-131,-91,-10,-96,77,-98,77,33,-131,-95,-101,-97,33,-53,-126,77,-88,33,33,-93,33,-147,-335,-146,33,-167,-166,-28,-180,-182,-27,-100,-126,33,-87,-90,-94,-92,-61,-72,77,33,-144,-142,33,33,-73,33,-89,33,33,33,-149,-159,-160,-156,-336,77,-183,-181,-30,77,347,33,-74,33,33,33,33,-174,-175,402,404,33,-143,-140,33,-141,-145,-76,-79,-82,-75,-77,33,-81,-215,-214,-80,-216,-78,-127,33,-153,33,-151,-148,-157,-168,-69,-36,-35,33,347,33,33,-234,-233,33,-231,-217,-230,-81,-84,-218,-152,-150,77,-158,-170,-169,-31,-34,33,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LBRACE':([21,24,28,31,32,42,48,61,75,86,88,90,92,93,96,97,98,100,101,119,124,130,131,181,182,191,200,201,227,229,284,285,286,289,291,298,300,301,302,303,305,307,308,332,340,341,369,375,377,413,424,425,428,429,432,435,437,438,439,440,453,454,472,475,477,478,479,488,496,497,500,502,505,506,510,511,512,521,522,535,536,537,539,550,554,555,557,558,569,574,575,576,577,578,579,],[-337,-154,-155,97,97,-29,-52,-71,-337,-53,-7,-88,97,-8,97,-335,97,97,97,-87,-72,97,97,97,-89,-336,-30,97,-337,97,-76,-79,-82,-75,-77,97,-81,-215,-214,-80,-216,97,-78,-69,-36,-35,-12,97,-11,97,-234,-233,-231,97,-217,-230,97,-84,-218,97,-31,-34,-337,-198,97,97,97,-337,-229,-232,-221,97,-83,-219,-68,-33,-32,97,-11,97,-220,97,97,-337,-225,-224,-222,-84,97,97,-226,-223,97,-228,-227,]),'PPHASH':([0,14,16,17,19,25,38,45,47,59,61,119,123,124,180,191,223,332,439,510,],[47,-64,-60,-67,-66,-65,-63,-62,-70,47,-71,-87,-61,-72,-73,-336,-74,-69,-218,-68,]),'INT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[56,-337,-113,-128,56,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,56,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,56,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,56,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,56,-131,-95,-101,-97,56,-53,-126,56,-88,56,56,-93,56,-147,-335,-146,56,-167,-166,-182,-100,-126,56,-87,-90,-94,-92,-61,-72,56,-144,-142,56,56,56,-73,56,-89,56,56,56,-149,-159,-160,-156,-336,56,-183,-30,56,56,-74,56,56,56,56,-174,-175,56,-143,-140,56,-141,-145,-76,-79,-82,-75,-77,56,-81,-215,-214,-80,-216,-78,-127,56,-153,56,-151,-148,-157,-168,-69,-36,-35,56,56,56,-234,-233,56,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,56,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'SIGNED':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[54,-337,-113,-128,54,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,54,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,54,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,54,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,54,-131,-95,-101,-97,54,-53,-126,54,-88,54,54,-93,54,-147,-335,-146,54,-167,-166,-182,-100,-126,54,-87,-90,-94,-92,-61,-72,54,-144,-142,54,54,54,-73,54,-89,54,54,54,-149,-159,-160,-156,-336,54,-183,-30,54,54,-74,54,54,54,54,-174,-175,54,-143,-140,54,-141,-145,-76,-79,-82,-75,-77,54,-81,-215,-214,-80,-216,-78,-127,54,-153,54,-151,-148,-157,-168,-69,-36,-35,54,54,54,-234,-233,54,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,54,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'CONTINUE':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,293,-336,-76,-79,-82,-75,-77,293,-81,-215,-214,-80,-216,293,-78,-69,-234,-233,-231,293,-217,-230,293,-84,-218,293,-229,-232,-221,293,-83,-219,-68,293,-220,293,293,-225,-224,-222,-84,293,293,-226,-223,293,-228,-227,]),'NOT':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,151,-335,-28,-182,-27,151,-337,-87,-72,-337,151,-286,-285,151,151,-283,-287,-288,151,-284,151,151,151,-336,-183,151,151,-28,-337,151,-28,-337,-337,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,151,-337,-76,-79,-82,-75,151,-77,151,151,-81,-215,-214,-80,-216,151,-78,151,151,-69,-284,151,151,-284,151,151,-244,-247,-245,-241,-242,-246,-248,151,-250,-251,-243,-249,-12,151,151,-11,151,151,151,151,-234,-233,151,-231,151,151,-217,151,-230,151,-84,-218,151,151,151,-337,-337,-198,151,151,151,-337,-284,-229,-232,151,-221,151,-83,-219,-68,151,-28,-337,151,-11,151,151,-220,151,151,151,-284,151,151,151,-337,151,-225,-224,-222,-84,151,151,151,-226,-223,151,-228,-227,]),'OREQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,366,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'MOD':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,260,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,260,260,260,260,260,260,260,260,260,260,-257,-256,260,260,260,260,260,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'RSHIFT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,242,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,242,-262,-260,242,242,242,-259,242,242,-257,-256,242,242,242,242,242,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'DEFAULT':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,295,-336,-76,-79,-82,-75,-77,295,-81,-215,-214,-80,-216,295,-78,-69,-234,-233,-231,295,-217,-230,295,-84,-218,295,-229,-232,-221,295,-83,-219,-68,295,-220,295,295,-225,-224,-222,-84,295,295,-226,-223,295,-228,-227,]),'_NORETURN':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[20,20,-113,-128,20,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,20,-120,-115,-65,-102,20,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,20,20,-119,20,-114,-130,20,-118,-71,-103,20,-131,-96,-98,20,-131,-95,-101,-97,-53,20,20,-88,20,-147,-335,-146,-167,-166,-100,-126,20,-87,-61,-72,20,-73,20,-89,-149,-336,-30,20,-74,-174,-175,20,-76,-79,-82,-75,-77,20,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,20,20,20,-234,-233,20,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,20,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'__INT128':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[43,-337,-113,-128,43,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,43,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,43,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,43,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,43,-131,-95,-101,-97,43,-53,-126,43,-88,43,43,-93,43,-147,-335,-146,43,-167,-166,-182,-100,-126,43,-87,-90,-94,-92,-61,-72,43,-144,-142,43,43,43,-73,43,-89,43,43,43,-149,-159,-160,-156,-336,43,-183,-30,43,43,-74,43,43,43,43,-174,-175,43,-143,-140,43,-141,-145,-76,-79,-82,-75,-77,43,-81,-215,-214,-80,-216,-78,-127,43,-153,43,-151,-148,-157,-168,-69,-36,-35,43,43,43,-234,-233,43,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,43,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'WHILE':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,436,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,296,-336,-76,-79,-82,-75,-77,296,-81,-215,-214,-80,-216,296,-78,-69,-234,-233,-231,296,-217,-230,504,296,-84,-218,296,-229,-232,-221,296,-83,-219,-68,296,-220,296,296,-225,-224,-222,-84,296,296,-226,-223,296,-228,-227,]),'U8CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,154,-335,-28,-182,-27,154,-337,-87,-72,-337,154,-286,-285,154,154,-283,-287,-288,154,-284,154,154,154,-336,-183,154,154,-28,-337,154,-28,-337,-337,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,154,-337,-76,-79,-82,-75,154,-77,154,154,-81,-215,-214,-80,-216,154,-78,154,154,-69,-284,154,154,-284,154,154,-244,-247,-245,-241,-242,-246,-248,154,-250,-251,-243,-249,-12,154,154,-11,154,154,154,154,-234,-233,154,-231,154,154,-217,154,-230,154,-84,-218,154,154,154,-337,-337,-198,154,154,154,-337,-284,-229,-232,154,-221,154,-83,-219,-68,154,-28,-337,154,-11,154,154,-220,154,154,154,-284,154,154,154,-337,154,-225,-224,-222,-84,154,154,154,-226,-223,154,-228,-227,]),'_ALIGNOF':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,155,-335,-28,-182,-27,155,-337,-87,-72,-337,155,-286,-285,155,155,-283,-287,-288,155,-284,155,155,155,-336,-183,155,155,-28,-337,155,-28,-337,-337,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,-337,-76,-79,-82,-75,155,-77,155,155,-81,-215,-214,-80,-216,155,-78,155,155,-69,-284,155,155,-284,155,155,-244,-247,-245,-241,-242,-246,-248,155,-250,-251,-243,-249,-12,155,155,-11,155,155,155,155,-234,-233,155,-231,155,155,-217,155,-230,155,-84,-218,155,155,155,-337,-337,-198,155,155,155,-337,-284,-229,-232,155,-221,155,-83,-219,-68,155,-28,-337,155,-11,155,155,-220,155,155,155,-284,155,155,155,-337,155,-225,-224,-222,-84,155,155,155,-226,-223,155,-228,-227,]),'EXTERN':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[13,13,-113,-128,13,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,13,-120,-115,-65,-102,13,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,13,13,-119,13,-114,-130,13,-118,-71,-103,13,-131,-96,-98,13,-131,-95,-101,-97,-53,13,13,-88,13,-147,-335,-146,-167,-166,-100,-126,13,-87,-61,-72,13,-73,13,-89,-149,-336,-30,13,-74,-174,-175,13,-76,-79,-82,-75,-77,13,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,13,13,13,-234,-233,13,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,13,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'CASE':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,297,-336,-76,-79,-82,-75,-77,297,-81,-215,-214,-80,-216,297,-78,-69,-234,-233,-231,297,-217,-230,297,-84,-218,297,-229,-232,-221,297,-83,-219,-68,297,-220,297,297,-225,-224,-222,-84,297,297,-226,-223,297,-228,-227,]),'LAND':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,255,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,255,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'REGISTER':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[62,62,-113,-128,62,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,62,-120,-115,-65,-102,62,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,62,62,-119,62,-114,-130,62,-118,-71,-103,62,-131,-96,-98,62,-131,-95,-101,-97,-53,62,62,-88,62,-147,-335,-146,-167,-166,-100,-126,62,-87,-61,-72,62,-73,62,-89,-149,-336,-30,62,-74,-174,-175,62,-76,-79,-82,-75,-77,62,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,62,62,62,-234,-233,62,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,62,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'MODEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,359,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'NE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,247,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,247,-262,-260,-264,-268,-263,-259,-266,247,-257,-256,-265,247,-267,247,247,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'SWITCH':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,299,-336,-76,-79,-82,-75,-77,299,-81,-215,-214,-80,-216,299,-78,-69,-234,-233,-231,299,-217,-230,299,-84,-218,299,-229,-232,-221,299,-83,-219,-68,299,-220,299,299,-225,-224,-222,-84,299,299,-226,-223,299,-228,-227,]),'INT_CONST_HEX':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,160,-335,-28,-182,-27,160,-337,-87,-72,-337,160,-286,-285,160,160,-283,-287,-288,160,-284,160,160,160,-336,-183,160,160,-28,-337,160,-28,-337,-337,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,-337,-76,-79,-82,-75,160,-77,160,160,-81,-215,-214,-80,-216,160,-78,160,160,-69,-284,160,160,-284,160,160,-244,-247,-245,-241,-242,-246,-248,160,-250,-251,-243,-249,-12,160,160,-11,160,160,160,160,-234,-233,160,-231,160,160,-217,160,-230,160,-84,-218,160,160,160,-337,-337,-198,160,160,160,-337,-284,-229,-232,160,-221,160,-83,-219,-68,160,-28,-337,160,-11,160,160,-220,160,160,160,-284,160,160,160,-337,160,-225,-224,-222,-84,160,160,160,-226,-223,160,-228,-227,]),'_COMPLEX':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[60,-337,-113,-128,60,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,60,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,60,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,60,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,60,-131,-95,-101,-97,60,-53,-126,60,-88,60,60,-93,60,-147,-335,-146,60,-167,-166,-182,-100,-126,60,-87,-90,-94,-92,-61,-72,60,-144,-142,60,60,60,-73,60,-89,60,60,60,-149,-159,-160,-156,-336,60,-183,-30,60,60,-74,60,60,60,60,-174,-175,60,-143,-140,60,-141,-145,-76,-79,-82,-75,-77,60,-81,-215,-214,-80,-216,-78,-127,60,-153,60,-151,-148,-157,-168,-69,-36,-35,60,60,60,-234,-233,60,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,60,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PPPRAGMASTR':([61,],[124,]),'PLUSEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,362,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'U32CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,138,-335,-28,-182,-27,138,-337,-87,-72,-337,138,-286,-285,138,138,-283,-287,-288,138,-284,138,138,138,-336,-183,138,138,-28,-337,138,-28,-337,-337,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,-337,-76,-79,-82,-75,138,-77,138,138,-81,-215,-214,-80,-216,138,-78,138,138,-69,-284,138,138,-284,138,138,-244,-247,-245,-241,-242,-246,-248,138,-250,-251,-243,-249,-12,138,138,-11,138,138,138,138,-234,-233,138,-231,138,138,-217,138,-230,138,-84,-218,138,138,138,-337,-337,-198,138,138,138,-337,-284,-229,-232,138,-221,138,-83,-219,-68,138,-28,-337,138,-11,138,138,-220,138,138,138,-284,138,138,138,-337,138,-225,-224,-222,-84,138,138,138,-226,-223,138,-228,-227,]),'CONDOP':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,258,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'U8STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,163,-335,-28,-182,-27,163,-337,-87,-72,-337,163,-286,-285,-330,163,-327,163,-283,-287,236,-328,-288,-329,163,-284,163,163,163,-336,-183,163,163,-28,-337,163,-28,-337,-337,163,163,163,-334,-332,-331,-333,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,163,-337,-76,-79,-82,-75,163,-77,163,163,-81,-215,-214,-80,-216,163,-78,163,163,-69,-284,163,163,-284,163,163,-244,-247,-245,-241,-242,-246,-248,163,-250,-251,-243,-249,-12,163,163,-11,163,163,163,163,-234,-233,163,-231,163,163,-217,163,-230,163,-84,-218,163,163,163,-337,-337,-198,163,163,163,-337,-284,-229,-232,163,-221,163,-83,-219,-68,163,-28,-337,163,-11,163,163,-220,163,163,163,-284,163,163,163,-337,163,-225,-224,-222,-84,163,163,163,-226,-223,163,-228,-227,]),'BREAK':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,304,-336,-76,-79,-82,-75,-77,304,-81,-215,-214,-80,-216,304,-78,-69,-234,-233,-231,304,-217,-230,304,-84,-218,304,-229,-232,-221,304,-83,-219,-68,304,-220,304,304,-225,-224,-222,-84,304,304,-226,-223,304,-228,-227,]),'VOLATILE':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,103,105,109,111,117,118,119,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[58,58,-113,-128,58,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,58,-120,-115,-65,-102,58,-131,-108,-238,-111,58,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,58,58,-119,58,-114,-130,58,-118,-71,-103,58,-131,-96,-98,58,-131,-95,-101,-97,58,-53,58,58,-88,58,58,-147,-335,-146,58,-167,-166,58,-182,-100,-126,58,58,-87,-61,-72,58,58,-144,-142,58,58,58,-73,58,-89,58,58,58,-149,-159,-160,-156,-336,58,-183,-30,58,58,58,58,58,-74,58,58,58,58,-174,-175,58,-143,-140,58,-141,-145,58,-76,-79,-82,-75,-77,58,-81,-215,-214,-80,-216,-78,-127,58,-153,58,-151,-148,-157,-168,-69,-36,-35,58,58,58,-234,-233,58,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,58,58,-229,-232,-221,-83,-219,-68,-33,-32,58,58,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'PPPRAGMA':([0,14,16,17,19,25,38,45,47,59,61,97,99,119,123,124,180,181,184,185,186,188,189,190,191,223,284,285,286,289,291,298,300,301,302,303,305,307,308,313,315,318,332,424,425,428,429,432,435,437,438,439,440,446,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[61,-64,-60,-67,-66,-65,-63,-62,-70,61,-71,-335,61,-87,-61,-72,-73,61,61,61,61,-159,-160,-156,-336,-74,-76,-79,-82,-75,-77,61,-81,-215,-214,-80,-216,61,-78,61,61,-157,-69,-234,-233,-231,61,-217,-230,61,-84,-218,61,-158,-229,-232,-221,61,-83,-219,-68,61,-220,61,61,-225,-224,-222,-84,61,61,-226,-223,61,-228,-227,]),'INLINE':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[30,30,-113,-128,30,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,30,-120,-115,-65,-102,30,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,30,30,-119,30,-114,-130,30,-118,-71,-103,30,-131,-96,-98,30,-131,-95,-101,-97,-53,30,30,-88,30,-147,-335,-146,-167,-166,-100,-126,30,-87,-61,-72,30,-73,30,-89,-149,-336,-30,30,-74,-174,-175,30,-76,-79,-82,-75,-77,30,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,30,30,30,-234,-233,30,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,30,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'INT_CONST_BIN':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,164,-335,-28,-182,-27,164,-337,-87,-72,-337,164,-286,-285,164,164,-283,-287,-288,164,-284,164,164,164,-336,-183,164,164,-28,-337,164,-28,-337,-337,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,164,-337,-76,-79,-82,-75,164,-77,164,164,-81,-215,-214,-80,-216,164,-78,164,164,-69,-284,164,164,-284,164,164,-244,-247,-245,-241,-242,-246,-248,164,-250,-251,-243,-249,-12,164,164,-11,164,164,164,164,-234,-233,164,-231,164,164,-217,164,-230,164,-84,-218,164,164,164,-337,-337,-198,164,164,164,-337,-284,-229,-232,164,-221,164,-83,-219,-68,164,-28,-337,164,-11,164,164,-220,164,164,164,-284,164,164,164,-337,164,-225,-224,-222,-84,164,164,164,-226,-223,164,-228,-227,]),'DO':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,307,-336,-76,-79,-82,-75,-77,307,-81,-215,-214,-80,-216,307,-78,-69,-234,-233,-231,307,-217,-230,307,-84,-218,307,-229,-232,-221,307,-83,-219,-68,307,-220,307,307,-225,-224,-222,-84,307,307,-226,-223,307,-228,-227,]),'LNOT':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,165,-335,-28,-182,-27,165,-337,-87,-72,-337,165,-286,-285,165,165,-283,-287,-288,165,-284,165,165,165,-336,-183,165,165,-28,-337,165,-28,-337,-337,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,-337,-76,-79,-82,-75,165,-77,165,165,-81,-215,-214,-80,-216,165,-78,165,165,-69,-284,165,165,-284,165,165,-244,-247,-245,-241,-242,-246,-248,165,-250,-251,-243,-249,-12,165,165,-11,165,165,165,165,-234,-233,165,-231,165,165,-217,165,-230,165,-84,-218,165,165,165,-337,-337,-198,165,165,165,-337,-284,-229,-232,165,-221,165,-83,-219,-68,165,-28,-337,165,-11,165,165,-220,165,165,165,-284,165,165,165,-337,165,-225,-224,-222,-84,165,165,165,-226,-223,165,-228,-227,]),'CONST':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,85,86,87,89,90,93,95,96,97,98,99,100,101,103,105,109,111,117,118,119,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[3,3,-113,-128,3,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,3,-120,-115,-65,-102,3,-131,-108,-238,-111,3,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,3,3,-119,3,-114,-130,3,-118,-71,-103,3,-131,-96,-98,3,-131,-95,-101,-97,3,-53,3,3,-88,3,3,-147,-335,-146,3,-167,-166,3,-182,-100,-126,3,3,-87,-61,-72,3,3,-144,-142,3,3,3,-73,3,-89,3,3,3,-149,-159,-160,-156,-336,3,-183,-30,3,3,3,3,3,-74,3,3,3,3,-174,-175,3,-143,-140,3,-141,-145,3,-76,-79,-82,-75,-77,3,-81,-215,-214,-80,-216,-78,-127,3,-153,3,-151,-148,-157,-168,-69,-36,-35,3,3,3,-234,-233,3,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,3,3,-229,-232,-221,-83,-219,-68,-33,-32,3,3,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LSHIFT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,244,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,244,-262,-260,244,244,244,-259,244,244,-257,-256,244,244,244,244,244,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'LOR':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,243,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,167,-335,-28,-182,-27,167,-337,-87,-72,-337,167,-286,-285,167,167,-283,-287,-288,167,-284,167,167,167,-336,-183,167,167,-28,-337,167,-28,-337,-337,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,167,-337,-76,-79,-82,-75,167,-77,167,167,-81,-215,-214,-80,-216,167,-78,167,167,-69,-284,167,167,-284,167,167,-244,-247,-245,-241,-242,-246,-248,167,-250,-251,-243,-249,-12,167,167,-11,167,167,167,167,-234,-233,167,-231,167,167,-217,167,-230,167,-84,-218,167,167,167,-337,-337,-198,167,167,167,-337,-284,-229,-232,167,-221,167,-83,-219,-68,167,-28,-337,167,-11,167,167,-220,167,167,167,-284,167,167,167,-337,167,-225,-224,-222,-84,167,167,167,-226,-223,167,-228,-227,]),'U16STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,139,146,148,149,150,151,153,163,165,166,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,166,-335,-28,-182,-27,166,-337,-87,-72,-337,166,-286,-285,-330,166,-327,166,-283,-287,238,-328,-288,-329,166,-284,166,166,166,-336,-183,166,166,-28,-337,166,-28,-337,-337,166,166,166,-334,-332,-331,-333,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,-337,-76,-79,-82,-75,166,-77,166,166,-81,-215,-214,-80,-216,166,-78,166,166,-69,-284,166,166,-284,166,166,-244,-247,-245,-241,-242,-246,-248,166,-250,-251,-243,-249,-12,166,166,-11,166,166,166,166,-234,-233,166,-231,166,166,-217,166,-230,166,-84,-218,166,166,166,-337,-337,-198,166,166,166,-337,-284,-229,-232,166,-221,166,-83,-219,-68,166,-28,-337,166,-11,166,166,-220,166,166,166,-284,166,166,166,-337,166,-225,-224,-222,-84,166,166,166,-226,-223,166,-228,-227,]),'RBRACE':([61,97,99,119,124,132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,181,184,185,186,188,189,190,191,195,196,197,224,225,227,228,230,232,234,235,236,237,238,261,263,268,273,284,285,286,289,291,298,300,301,302,303,305,306,308,309,313,315,318,325,326,327,332,370,374,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,424,425,428,432,435,437,438,439,446,450,451,468,469,472,473,476,478,480,482,483,487,496,497,500,505,506,510,523,524,528,536,546,547,550,551,554,555,557,558,565,575,576,578,579,],[-71,-335,191,-87,-72,-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-337,191,191,191,-159,-160,-156,-336,-171,191,-176,-274,-239,-337,-193,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-76,-79,-82,-75,-77,-6,-81,-215,-214,-80,-216,-5,-78,191,191,191,-157,191,191,-172,-69,191,-22,-21,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,-234,-233,-231,-217,-230,-81,-84,-218,-158,-173,-177,-240,-194,191,-196,-237,-281,-282,-290,-291,-275,-229,-232,-221,-83,-219,-68,-195,-254,191,-220,-197,-306,191,-299,-225,-224,-222,-84,-300,-226,-223,-228,-227,]),'_BOOL':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[34,-337,-113,-128,34,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,34,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,34,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,34,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,34,-131,-95,-101,-97,34,-53,-126,34,-88,34,34,-93,34,-147,-335,-146,34,-167,-166,-182,-100,-126,34,-87,-90,-94,-92,-61,-72,34,-144,-142,34,34,34,-73,34,-89,34,34,34,-149,-159,-160,-156,-336,34,-183,-30,34,34,-74,34,34,34,34,-174,-175,34,-143,-140,34,-141,-145,-76,-79,-82,-75,-77,34,-81,-215,-214,-80,-216,-78,-127,34,-153,34,-151,-148,-157,-168,-69,-36,-35,34,34,34,-234,-233,34,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,34,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LE':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,246,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,246,-262,-260,-264,246,-263,-259,-266,246,-257,-256,-265,246,246,246,246,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'SEMI':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,70,71,73,74,75,76,77,78,79,80,81,82,83,84,86,87,89,91,94,96,97,98,99,100,101,108,109,110,111,113,114,115,119,120,121,122,123,124,127,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,179,180,181,184,185,186,187,188,189,190,191,192,200,216,217,223,224,225,226,228,230,232,234,235,236,237,238,240,241,261,263,268,269,272,273,275,279,280,284,285,286,288,289,290,291,293,294,298,300,301,302,303,304,305,306,307,308,310,312,313,314,315,316,317,318,320,321,322,323,324,328,330,331,332,340,341,355,356,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,423,424,425,426,427,428,429,432,433,435,437,438,439,440,442,443,444,446,448,449,453,454,464,465,468,469,476,478,480,482,483,486,487,496,497,498,499,500,502,505,506,508,509,510,511,512,518,519,523,524,533,534,535,536,537,539,547,551,552,554,555,557,558,565,568,569,574,575,576,577,578,579,],[19,-337,-113,-128,-337,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,-337,-29,-121,-116,-62,-112,-70,-52,-123,-117,119,-337,-337,-119,-337,-114,-130,19,-118,-71,-103,-337,-9,-131,-91,-10,-96,-20,-98,-54,-179,-178,-131,-37,-134,-85,-95,-101,-97,-19,-132,-53,-126,-337,-337,-93,-147,-335,-146,188,-167,-166,-136,-100,-138,-126,-16,-86,-15,-87,-90,-94,-92,-61,-72,-55,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-178,-73,-337,188,188,188,-149,-159,-160,-156,-336,-337,-30,-38,-133,-74,-274,-239,-135,-193,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,-279,-143,-235,-278,-140,-141,-145,-76,-79,-82,424,-75,425,-77,428,-14,-337,-81,-215,-214,-80,435,-216,-13,-337,-78,-312,-127,188,-153,188,-151,-148,-157,-26,-25,446,-161,-163,-168,-139,-137,-69,-36,-35,-44,-43,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,-292,-305,496,-234,-233,497,-337,-231,-337,-217,-13,-230,-81,-84,-218,-337,-152,-150,-165,-158,-170,-169,-31,-34,-39,-42,-240,-194,-237,-281,-282,-290,-291,-236,-275,-229,-232,533,-337,-221,-337,-83,-219,-162,-164,-68,-33,-32,-41,-40,-195,-254,-337,553,-337,-220,-337,-337,-306,-299,566,-225,-224,-222,-84,-300,575,-337,-337,-226,-223,-337,-228,-227,]),'_THREAD_LOCAL':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[11,11,-113,-128,11,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,11,-120,-115,-65,-102,11,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,11,11,-119,11,-114,-130,11,-118,-71,-103,11,-131,-96,-98,11,-131,-95,-101,-97,-53,11,11,-88,11,-147,-335,-146,-167,-166,-100,-126,11,-87,-61,-72,11,-73,11,-89,-149,-336,-30,11,-74,-174,-175,11,-76,-79,-82,-75,-77,11,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,11,11,11,-234,-233,11,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,11,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'LT':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,248,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,248,-262,-260,-264,248,-263,-259,-266,248,-257,-256,-265,248,248,248,248,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'COMMA':([2,3,5,6,7,10,11,12,13,18,20,22,23,26,27,30,33,34,35,36,39,42,43,44,46,48,49,50,54,56,58,60,62,68,70,71,73,74,75,76,77,78,80,81,82,84,86,96,98,100,101,103,104,105,106,108,109,110,111,113,127,132,133,134,136,138,139,140,141,142,143,144,145,147,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,177,178,179,187,191,195,196,197,198,199,200,203,210,211,212,213,215,216,217,224,225,226,228,230,232,234,235,236,237,238,240,241,261,263,268,269,270,272,273,274,275,276,277,279,280,281,283,294,310,312,314,316,317,320,323,324,325,326,327,328,330,331,340,341,343,344,345,346,347,348,355,356,374,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,414,426,442,443,444,448,449,450,451,453,454,458,461,463,464,465,468,469,473,476,478,480,482,483,486,487,489,490,492,501,503,507,508,509,511,512,518,519,523,524,525,528,529,530,531,532,544,545,546,547,551,556,559,560,564,565,570,571,],[-113,-128,-124,-110,-106,-104,-107,-125,-105,-99,-109,-120,-115,-102,-126,-108,-238,-111,-337,-122,-129,-29,-121,-116,-112,-52,-123,-117,-119,-114,-130,-118,-103,-96,126,-98,-54,-179,-178,-131,-37,-134,-95,-101,-97,-132,-53,-147,-146,-167,-166,-28,-180,-182,-27,-136,-100,-138,-126,202,-55,-317,-321,-318,-303,-324,-330,-313,-319,-144,-301,-274,-314,-142,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-337,-252,-178,-149,-336,-171,327,-176,-183,-181,-30,333,-186,-337,349,350,-191,-38,-133,-274,-239,-135,-193,-326,-280,-277,-334,-332,-331,-333,-174,-175,-298,-297,-279,-143,412,-235,-278,-203,-140,-204,-1,-141,-145,-2,-206,412,-312,-127,-153,-151,-148,445,-161,-163,327,327,-172,-168,-139,-137,-36,-35,-190,-204,-56,-188,-45,-189,-44,-43,472,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,412,-270,-258,-296,-295,-294,-293,412,-292,-310,484,485,-305,-205,412,-152,-150,-165,-170,-169,-173,-177,-31,-34,-57,-192,-187,-39,-42,-240,-194,-196,-237,-281,-282,-290,-291,-236,-275,-213,-207,-211,412,412,412,-162,-164,-33,-32,-41,-40,-195,-254,-311,550,-209,-208,-210,-212,-51,-50,-197,-306,-299,412,-46,-49,412,-300,-48,-47,]),'U16CHAR_CONST':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,169,-335,-28,-182,-27,169,-337,-87,-72,-337,169,-286,-285,169,169,-283,-287,-288,169,-284,169,169,169,-336,-183,169,169,-28,-337,169,-28,-337,-337,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,-337,-76,-79,-82,-75,169,-77,169,169,-81,-215,-214,-80,-216,169,-78,169,169,-69,-284,169,169,-284,169,169,-244,-247,-245,-241,-242,-246,-248,169,-250,-251,-243,-249,-12,169,169,-11,169,169,169,169,-234,-233,169,-231,169,169,-217,169,-230,169,-84,-218,169,169,169,-337,-337,-198,169,169,169,-337,-284,-229,-232,169,-221,169,-83,-219,-68,169,-28,-337,169,-11,169,169,-220,169,169,169,-284,169,169,169,-337,169,-225,-224,-222,-84,169,169,169,-226,-223,169,-228,-227,]),'OFFSETOF':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,137,146,149,150,151,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,170,-335,-28,-182,-27,170,-337,-87,-72,-337,170,-286,-285,170,170,-283,-287,-288,170,-284,170,170,170,-336,-183,170,170,-28,-337,170,-28,-337,-337,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,-337,-76,-79,-82,-75,170,-77,170,170,-81,-215,-214,-80,-216,170,-78,170,170,-69,-284,170,170,-284,170,170,-244,-247,-245,-241,-242,-246,-248,170,-250,-251,-243,-249,-12,170,170,-11,170,170,170,170,-234,-233,170,-231,170,170,-217,170,-230,170,-84,-218,170,170,170,-337,-337,-198,170,170,170,-337,-284,-229,-232,170,-221,170,-83,-219,-68,170,-28,-337,170,-11,170,170,-220,170,170,170,-284,170,170,170,-337,170,-225,-224,-222,-84,170,170,170,-226,-223,170,-228,-227,]),'_ATOMIC':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,35,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,103,105,109,111,117,118,119,120,121,122,123,124,128,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,205,206,211,214,219,220,223,229,231,233,239,240,241,267,269,275,278,279,280,282,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,459,460,496,497,500,505,506,510,511,512,514,515,536,554,555,557,558,575,576,578,579,],[29,65,-113,-128,76,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,65,-120,-115,-65,-102,65,-131,-108,-238,-111,76,-122,-63,-129,112,-29,-121,-116,-62,-112,-70,-52,-123,-117,65,65,-119,65,-114,-130,29,-118,-71,-103,65,-9,-131,-91,-10,-96,-98,65,-131,-95,-101,-97,29,-53,65,76,-88,112,65,-93,29,-147,-335,-146,29,-167,-166,76,-182,-100,-126,76,29,-87,-90,-94,-92,-61,-72,76,29,-144,-142,65,29,76,-73,65,-89,29,29,29,-149,-159,-160,-156,-336,76,-183,-30,76,76,76,112,76,76,-74,29,29,29,29,-174,-175,29,-143,-140,29,-141,-145,76,-76,-79,-82,-75,-77,65,-81,-215,-214,-80,-216,-78,-127,29,-153,29,-151,-148,-157,-168,-69,-36,-35,29,29,29,-234,-233,65,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,76,29,-229,-232,-221,-83,-219,-68,-33,-32,76,76,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'TYPEDEF':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[7,7,-113,-128,7,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,7,-120,-115,-65,-102,7,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,7,7,-119,7,-114,-130,7,-118,-71,-103,7,-131,-96,-98,7,-131,-95,-101,-97,-53,7,7,-88,7,-147,-335,-146,-167,-166,-100,-126,7,-87,-61,-72,7,-73,7,-89,-149,-336,-30,7,-74,-174,-175,7,-76,-79,-82,-75,-77,7,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,7,7,7,-234,-233,7,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,7,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'XOR':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,251,-328,-316,-329,-320,-276,-323,-312,-336,-274,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-261,251,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,251,-267,-269,251,-258,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'AUTO':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,65,68,71,75,76,80,81,82,86,87,89,90,93,96,97,98,100,101,109,111,118,119,123,124,129,180,181,182,187,191,200,211,223,240,241,278,284,285,286,289,291,298,300,301,302,303,305,308,312,314,316,317,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[26,26,-113,-128,26,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,26,-120,-115,-65,-102,26,-131,-108,-238,-111,-122,-63,-129,-29,-121,-116,-62,-112,-70,-52,-123,-117,26,26,-119,26,-114,-130,26,-118,-71,-103,26,-131,-96,-98,26,-131,-95,-101,-97,-53,26,26,-88,26,-147,-335,-146,-167,-166,-100,-126,26,-87,-61,-72,26,-73,26,-89,-149,-336,-30,26,-74,-174,-175,26,-76,-79,-82,-75,-77,26,-81,-215,-214,-80,-216,-78,-127,-153,-151,-148,-168,-69,-36,-35,26,26,26,-234,-233,26,-231,-217,-230,-81,-84,-218,-152,-150,-170,-169,-31,-34,26,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'DIVEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,357,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'TIMES':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,22,23,25,26,27,29,30,33,34,35,36,37,38,39,40,43,44,45,46,47,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,76,80,81,82,85,87,89,91,94,96,97,98,100,101,103,104,105,106,109,111,116,117,119,120,121,122,123,124,126,128,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,156,158,160,161,162,163,164,165,166,167,168,169,171,173,174,175,176,177,180,181,187,191,192,198,201,202,204,205,206,211,218,219,220,223,224,227,229,230,231,232,233,234,235,236,237,238,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,268,269,273,275,278,279,280,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,312,314,316,317,319,328,329,332,336,338,339,342,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,442,443,445,447,448,449,459,472,475,477,478,480,481,482,483,484,487,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[35,-337,-113,-128,35,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,-120,-115,-65,-102,-126,-131,-108,-238,-111,-337,-122,35,-63,-129,35,-121,-116,-62,-112,-70,-123,-117,-337,-337,-119,-337,-114,-130,35,-118,-71,-103,-337,-9,-131,-91,-10,-96,35,-98,-131,-95,-101,-97,173,-126,35,35,-93,-147,-335,-146,-167,-166,-28,35,-182,-27,-100,-126,173,-337,-87,-90,-94,-92,-61,-72,35,-337,173,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-144,-301,-274,-314,173,-142,-327,173,-283,-287,-325,-304,-322,-302,-255,-315,-289,253,-328,-316,-288,-329,-320,-276,-323,173,-284,173,173,-312,35,-73,173,-149,-336,35,-183,173,35,336,-28,-337,35,352,-28,-337,-74,-274,-337,173,-326,173,-280,173,-277,-334,-332,-331,-333,-174,-175,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,173,-298,-297,173,173,-279,-143,-278,-140,35,-141,-145,420,-76,-79,-82,-75,173,-77,173,173,-81,-215,-214,-80,-216,173,-78,-312,-127,-153,-151,-148,173,-168,173,-69,-284,173,173,35,-284,173,173,-244,-247,-245,-241,-242,-246,-248,173,-250,-251,-243,-249,-12,173,173,-11,253,253,253,253,253,253,253,253,253,253,-257,-256,253,253,253,253,253,-258,-296,-295,-294,-293,-292,-305,173,173,173,494,-234,-233,173,-231,173,173,-217,173,-230,173,-84,-218,173,173,-152,-150,35,173,-170,-169,-337,-337,-198,173,-281,-282,173,-290,-291,173,-275,-337,-284,-229,-232,173,-221,173,-83,-219,-68,541,-28,-337,173,-11,173,173,-220,173,173,173,-284,173,173,-306,173,-337,-299,173,-225,-224,-222,-84,-300,173,173,173,-226,-223,173,-228,-227,]),'LPAREN':([0,1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,19,20,22,23,25,26,27,29,30,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,72,73,76,77,80,81,82,85,86,87,89,91,94,96,97,98,100,101,103,104,105,106,109,111,112,116,117,119,120,121,122,123,124,126,127,128,131,132,133,134,135,136,137,138,139,140,141,142,143,145,146,147,148,149,150,151,152,153,154,155,156,160,161,163,164,165,166,167,168,169,170,171,173,174,175,176,177,180,181,187,191,192,198,199,200,201,202,204,205,206,211,216,218,219,220,223,227,229,230,231,233,235,236,237,238,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,269,275,276,278,279,280,282,283,284,285,286,289,290,291,292,296,297,298,299,300,301,302,303,305,307,308,310,311,312,314,316,317,319,328,329,332,336,338,339,340,341,342,344,345,347,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,402,403,404,405,407,411,412,413,414,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,442,443,445,447,448,449,453,454,457,458,459,464,465,472,475,477,481,482,483,484,488,489,490,492,494,496,497,499,500,502,504,505,506,510,511,512,513,514,515,518,519,521,522,529,530,531,532,533,535,536,537,538,539,541,542,543,544,545,547,549,550,551,553,554,555,557,558,559,560,565,566,569,570,571,574,575,576,577,578,579,],[37,-337,-113,-128,69,-124,-110,-106,85,-104,-107,-125,-105,-64,37,-60,-67,-99,-66,-109,-120,-115,-65,-102,-126,95,-108,-238,-111,-337,-122,37,-63,-129,37,116,-29,-121,-116,-62,-112,-70,118,-123,-117,-337,-337,-119,-337,-114,-130,37,-118,-71,-103,-337,-9,95,-91,-10,-96,69,-98,69,129,-131,-37,-95,-101,-97,174,118,-126,69,37,-93,-147,-335,-146,-167,-166,-28,-180,-182,-27,-100,-126,95,174,-337,-87,-90,-94,-92,-61,-72,69,129,-337,229,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-144,-301,-314,231,-142,-327,233,-283,-287,-325,-304,-322,239,-302,-315,-289,-328,-316,-288,-329,-320,266,-323,267,174,-284,229,233,-312,278,-73,229,-149,-336,69,-183,-181,-30,229,69,229,-28,-337,342,-38,229,-28,-337,-74,-337,229,-326,229,229,-334,-332,-331,-333,-174,-175,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,229,174,174,-298,-297,229,229,-143,-140,278,278,-141,-145,-337,422,-76,-79,-82,-75,229,-77,427,430,174,229,434,-81,-215,-214,-80,-216,229,-78,-312,441,-127,-153,-151,-148,174,-168,174,-69,-284,229,229,-36,-35,342,342,460,-45,-284,229,229,-44,-43,-244,-247,-245,-241,-242,-246,-248,229,-250,-251,-243,-249,-12,174,229,-11,-296,-295,-294,-293,-292,-305,229,174,422,229,229,-234,-233,229,-231,229,229,-217,229,-230,229,-84,-218,229,229,-152,-150,69,174,-170,-169,-31,-34,342,460,-337,-39,-42,-337,-198,174,174,-290,-291,229,-337,-213,-207,-211,-284,-229,-232,229,-221,229,538,-83,-219,-68,-33,-32,229,-28,-337,-41,-40,229,-11,-209,-208,-210,-212,229,229,-220,229,229,229,-284,229,229,-51,-50,-306,229,-337,-299,229,-225,-224,-222,-84,-46,-49,-300,229,229,-48,-47,229,-226,-223,229,-228,-227,]),'MINUSMINUS':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,132,133,134,135,136,137,138,139,140,141,143,145,146,148,149,150,151,152,153,154,156,160,161,163,164,165,166,167,168,169,171,173,174,175,176,181,191,198,201,204,205,206,218,219,220,227,229,230,231,233,235,236,237,238,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,263,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,310,319,329,332,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,402,403,404,405,407,411,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,459,472,475,477,481,482,483,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,547,549,550,551,553,554,555,557,558,565,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,175,-335,-28,-182,-27,175,-337,-87,-72,-337,175,-317,-321,-318,-286,-303,-285,-324,-330,-313,-319,-301,-314,175,-327,175,-283,-287,-325,-304,-322,-302,-315,-289,-328,-316,-288,-329,-320,261,-323,175,-284,175,175,-312,175,-336,-183,175,175,-28,-337,175,-28,-337,-337,175,-326,175,175,-334,-332,-331,-333,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,175,-298,-297,175,175,-337,-76,-79,-82,-75,175,-77,175,175,-81,-215,-214,-80,-216,175,-78,-312,175,175,-69,-284,175,175,-284,175,175,-244,-247,-245,-241,-242,-246,-248,175,-250,-251,-243,-249,-12,175,175,-11,-296,-295,-294,-293,-292,-305,175,175,175,175,-234,-233,175,-231,175,175,-217,175,-230,175,-84,-218,175,175,175,-337,-337,-198,175,175,-290,-291,175,-337,-284,-229,-232,175,-221,175,-83,-219,-68,175,-28,-337,175,-11,175,175,-220,175,175,175,-284,175,175,-306,175,-337,-299,175,-225,-224,-222,-84,-300,175,175,175,-226,-223,175,-228,-227,]),'ID':([0,1,2,3,4,5,6,7,10,11,12,13,14,15,16,17,18,19,20,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,43,44,45,46,47,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,69,71,72,76,80,81,82,85,87,89,91,94,96,97,98,100,101,102,103,104,105,106,109,111,116,117,118,119,120,121,122,123,124,126,128,129,131,135,137,142,146,147,149,150,151,165,171,173,174,175,180,181,187,191,192,193,194,198,199,201,202,204,205,206,211,218,219,220,223,227,229,231,233,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,262,264,265,266,269,275,279,280,282,284,285,286,287,289,290,291,297,298,300,301,302,303,305,307,308,312,314,316,317,319,327,328,329,332,336,338,339,342,344,349,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,372,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,442,443,445,447,448,449,457,459,460,472,475,477,481,484,485,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,548,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[42,-337,-113,-128,42,-124,-110,-106,-104,-107,-125,-105,-64,42,-60,-67,-99,-66,-109,-120,-115,-154,-65,-102,-126,-155,-131,-108,98,101,-238,-111,-337,-122,42,-63,-129,42,-121,-116,-62,-112,-70,-123,-117,-337,-337,-119,-337,-114,-130,42,-118,-71,-103,-337,-9,-131,-91,-10,-96,42,-98,42,-131,-95,-101,-97,176,-126,42,42,-93,-147,-335,-146,-167,-166,197,-28,-180,-182,-27,-100,-126,176,-337,176,-87,-90,-94,-92,-61,-72,42,-337,176,176,-286,-285,-144,176,-142,176,-283,-287,-288,176,-284,176,176,-73,310,-149,-336,42,197,197,-183,-181,176,42,176,-28,-337,42,176,-28,-337,-74,-337,176,176,176,-174,-175,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,176,403,405,176,176,-143,-140,-141,-145,-337,-76,-79,-82,423,-75,176,-77,176,310,-81,-215,-214,-80,-216,310,-78,-127,-153,-151,-148,176,197,-168,176,-69,-284,176,176,42,42,176,-284,176,176,-244,-247,-245,-241,-242,-246,-248,176,-250,-251,-243,-249,-12,176,176,176,-11,176,176,176,176,-234,-233,176,-231,310,176,-217,176,-230,310,-84,-218,310,176,-152,-150,42,176,-170,-169,42,-337,176,-337,-198,176,176,176,176,-337,-284,-229,-232,176,-221,310,-83,-219,-68,176,-28,-337,176,-11,176,310,-220,310,176,310,-284,176,176,176,176,-337,176,-225,-224,-222,-84,176,310,310,-226,-223,310,-228,-227,]),'IF':([61,97,119,124,181,191,284,285,286,289,291,298,300,301,302,303,305,307,308,332,424,425,428,429,432,435,437,438,439,440,496,497,500,502,505,506,510,535,536,537,539,554,555,557,558,569,574,575,576,577,578,579,],[-71,-335,-87,-72,311,-336,-76,-79,-82,-75,-77,311,-81,-215,-214,-80,-216,311,-78,-69,-234,-233,-231,311,-217,-230,311,-84,-218,311,-229,-232,-221,311,-83,-219,-68,311,-220,311,311,-225,-224,-222,-84,311,311,-226,-223,311,-228,-227,]),'STRING_LITERAL':([3,39,58,61,76,85,97,103,105,106,116,117,119,124,128,131,135,136,137,146,149,150,151,152,165,171,173,174,175,181,191,198,201,204,205,206,218,219,220,227,229,230,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,282,284,285,286,289,290,291,297,298,300,301,302,303,305,307,308,319,329,332,333,336,338,339,352,353,354,357,358,359,360,361,362,363,364,365,366,367,368,369,373,375,377,412,413,419,421,424,425,427,428,429,430,432,434,435,437,438,439,440,441,447,452,459,472,475,477,481,484,488,494,496,497,499,500,502,505,506,510,513,514,515,521,522,533,535,536,537,538,539,541,542,543,549,550,553,554,555,557,558,566,569,574,575,576,577,578,579,],[-128,-129,-130,-71,-131,152,-335,-28,-182,-27,152,-337,-87,-72,-337,152,-286,230,-285,152,152,-283,-287,-325,-288,152,-284,152,152,152,-336,-183,152,152,-28,-337,152,-28,-337,-337,152,-326,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,-337,-76,-79,-82,-75,152,-77,152,152,-81,-215,-214,-80,-216,152,-78,152,152,-69,152,-284,152,152,-284,152,152,-244,-247,-245,-241,-242,-246,-248,152,-250,-251,-243,-249,-12,152,152,-11,152,152,152,152,-234,-233,152,-231,152,152,-217,152,-230,152,-84,-218,152,152,152,230,-337,-337,-198,152,152,152,-337,-284,-229,-232,152,-221,152,-83,-219,-68,152,-28,-337,152,-11,152,152,-220,152,152,152,-284,152,152,152,-337,152,-225,-224,-222,-84,152,152,152,-226,-223,152,-228,-227,]),'FLOAT':([0,1,2,3,4,5,6,7,10,11,12,13,14,16,17,18,19,20,21,22,23,25,26,27,29,30,33,34,36,38,39,40,42,43,44,45,46,47,48,49,50,52,53,54,55,56,58,59,60,61,62,63,64,65,66,67,68,71,75,76,80,81,82,85,86,87,89,90,91,93,94,95,96,97,98,99,100,101,105,109,111,118,119,120,121,122,123,124,129,142,147,172,174,177,180,181,182,184,185,186,187,188,189,190,191,192,198,200,211,214,223,229,231,233,239,240,241,267,269,275,278,279,280,284,285,286,289,291,298,300,301,302,303,305,308,312,313,314,315,316,317,318,328,332,340,341,342,350,422,424,425,427,428,432,435,437,438,439,442,443,446,448,449,453,454,460,496,497,500,505,506,510,511,512,536,554,555,557,558,575,576,578,579,],[44,-337,-113,-128,44,-124,-110,-106,-104,-107,-125,-105,-64,-60,-67,-99,-66,-109,44,-120,-115,-65,-102,-126,-131,-108,-238,-111,-122,-63,-129,44,-29,-121,-116,-62,-112,-70,-52,-123,-117,-337,-337,-119,-337,-114,-130,44,-118,-71,-103,-337,-9,-131,-91,-10,-96,-98,44,-131,-95,-101,-97,44,-53,-126,44,-88,44,44,-93,44,-147,-335,-146,44,-167,-166,-182,-100,-126,44,-87,-90,-94,-92,-61,-72,44,-144,-142,44,44,44,-73,44,-89,44,44,44,-149,-159,-160,-156,-336,44,-183,-30,44,44,-74,44,44,44,44,-174,-175,44,-143,-140,44,-141,-145,-76,-79,-82,-75,-77,44,-81,-215,-214,-80,-216,-78,-127,44,-153,44,-151,-148,-157,-168,-69,-36,-35,44,44,44,-234,-233,44,-231,-217,-230,-81,-84,-218,-152,-150,-158,-170,-169,-31,-34,44,-229,-232,-221,-83,-219,-68,-33,-32,-220,-225,-224,-222,-84,-226,-223,-228,-227,]),'XOREQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,361,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'LSHIFTEQUAL':([132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,160,161,163,164,166,167,168,169,176,191,224,230,232,234,235,236,237,238,261,263,268,273,310,402,403,404,405,407,411,478,480,482,483,487,547,551,565,],[-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-315,-289,-328,-316,-329,-320,-276,-323,-312,-336,363,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-278,-312,-296,-295,-294,-293,-292,-305,-281,-282,-290,-291,-275,-306,-299,-300,]),'RBRACKET':([3,39,58,76,103,105,106,117,128,132,133,134,136,138,139,140,141,143,144,145,148,152,153,154,156,158,160,161,162,163,164,166,167,168,169,176,178,191,198,204,205,218,219,224,225,230,232,234,235,236,237,238,261,263,268,272,273,282,334,335,336,337,351,352,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,400,401,402,403,404,405,406,407,411,419,420,421,455,456,459,466,467,468,471,476,478,480,482,483,486,487,491,493,494,513,514,524,540,541,547,551,561,562,564,565,],[-128,-129,-130,-131,-28,-182,-27,-337,-337,-317,-321,-318,-303,-324,-330,-313,-319,-301,-274,-314,-327,-325,-304,-322,-302,-255,-315,-289,-253,-328,-316,-329,-320,-276,-323,-312,-252,-336,-183,-337,-28,-337,-28,-274,-239,-326,-280,-277,-334,-332,-331,-333,-298,-297,-279,-235,-278,-337,453,-4,454,-3,464,465,-261,-273,-262,-260,-264,-268,-263,-259,-266,-271,-257,-256,-265,-272,-267,-269,-270,-258,-296,-295,-294,-293,482,-292,-305,-337,492,-337,511,512,-337,518,519,-240,520,-237,-281,-282,-290,-291,-236,-275,529,530,531,-337,-28,-254,559,560,-306,-299,570,571,572,-300,]),} - -_lr_action = {} -for _k, _v in _lr_action_items.items(): - for _x,_y in zip(_v[0],_v[1]): - if not _x in _lr_action: _lr_action[_x] = {} - _lr_action[_x][_k] = _y -del _lr_action_items - -_lr_goto_items = {'expression_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[284,284,284,284,284,284,284,284,284,284,284,284,284,]),'struct_or_union_specifier':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,]),'init_declarator_list':([4,89,],[70,70,]),'init_declarator_list_opt':([4,89,],[79,79,]),'iteration_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[285,285,285,285,285,285,285,285,285,285,285,285,285,]),'static_assert':([0,59,181,298,307,429,437,440,502,535,537,539,569,574,577,],[17,17,286,286,286,286,286,286,286,286,286,286,286,286,286,]),'unified_string_literal':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,333,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,452,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,]),'assignment_expression_opt':([204,218,419,421,513,],[334,351,491,493,540,]),'brace_open':([31,32,92,96,98,100,101,130,131,181,201,229,298,307,375,413,429,437,440,477,478,479,502,521,535,537,539,569,574,577,],[99,102,181,184,185,193,194,181,227,181,227,181,181,181,227,488,181,181,181,488,488,488,181,227,181,181,181,181,181,181,]),'enumerator':([102,193,194,327,],[195,195,195,450,]),'typeid_noparen_declarator':([211,],[348,]),'type_qualifier_list_opt':([35,117,128,206,220,282,459,515,],[104,204,218,339,354,419,513,543,]),'declaration_specifiers_no_type_opt':([1,27,52,53,55,63,87,],[66,94,120,121,122,94,94,]),'expression_opt':([181,298,307,427,429,437,440,499,502,533,535,537,539,553,566,569,574,577,],[288,288,288,498,288,288,288,534,288,552,288,288,288,567,573,288,288,288,]),'designation':([227,472,488,550,],[369,369,369,369,]),'parameter_list':([118,129,278,342,422,460,],[213,213,213,213,213,213,]),'alignment_specifier':([0,1,4,21,27,52,53,55,59,63,75,85,87,89,93,95,99,118,129,174,177,181,184,185,186,192,211,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[53,53,81,53,53,53,53,53,53,53,53,142,53,81,53,142,142,53,53,142,280,53,142,142,142,280,81,142,142,142,142,142,53,53,142,142,53,53,53,53,53,]),'labeled_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[289,289,289,289,289,289,289,289,289,289,289,289,289,]),'abstract_declarator':([177,211,278,342,],[281,281,418,418,]),'translation_unit':([0,],[59,]),'init_declarator':([4,89,126,202,],[84,84,217,331,]),'direct_abstract_declarator':([177,211,276,278,342,344,457,],[283,283,414,283,283,414,414,]),'designator_list':([227,472,488,550,],[376,376,376,376,]),'identifier':([85,116,118,129,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,349,353,354,364,372,373,375,412,413,419,421,427,429,430,434,437,440,441,447,460,477,481,484,485,499,502,513,521,533,535,537,538,539,542,543,548,549,553,566,569,574,577,],[143,143,215,215,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,461,143,143,143,470,143,143,143,143,143,143,143,143,143,143,143,143,143,143,215,143,143,143,527,143,143,143,143,143,143,143,143,143,143,143,563,143,143,143,143,143,143,]),'offsetof_member_designator':([485,],[526,]),'unary_expression':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[144,144,224,232,234,144,224,273,224,224,224,224,224,224,224,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,224,144,144,224,224,224,144,224,224,144,144,224,224,224,224,224,144,224,224,144,224,224,224,224,224,224,224,224,224,144,144,144,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,224,]),'abstract_declarator_opt':([177,211,],[274,343,]),'initializer':([131,201,375,521,],[226,330,473,546,]),'direct_id_declarator':([0,4,15,37,40,59,69,72,89,91,126,192,202,211,342,344,445,457,],[48,48,86,48,48,48,48,86,48,48,48,48,48,48,48,86,48,86,]),'struct_declaration_list':([99,184,185,],[186,313,315,]),'pp_directive':([0,59,],[14,14,]),'declaration_list':([21,75,],[93,93,]),'id_init_declarator':([40,91,],[108,108,]),'type_specifier':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[18,18,109,18,18,147,109,18,147,147,18,18,269,147,18,147,147,147,109,147,147,147,147,147,18,18,147,147,18,18,18,18,18,]),'compound_statement':([92,130,181,229,298,307,429,437,440,502,535,537,539,569,574,577,],[180,223,291,378,291,291,291,291,291,291,291,291,291,291,291,291,]),'pointer':([0,4,37,40,59,69,89,91,104,126,177,192,202,211,278,342,445,],[15,72,15,15,15,72,72,15,199,72,276,72,72,344,276,457,72,]),'typeid_declarator':([4,69,89,126,192,202,445,],[74,125,74,74,74,74,74,]),'id_init_declarator_list':([40,91,],[113,113,]),'declarator':([4,89,126,192,202,445,],[78,78,78,324,78,324,]),'argument_expression_list':([266,],[409,]),'struct_declarator_list_opt':([192,],[322,]),'block_item_list':([181,],[298,]),'parameter_type_list_opt':([278,342,422,],[417,417,495,]),'struct_declarator':([192,445,],[323,508,]),'type_qualifier':([0,1,4,21,27,35,52,53,55,59,63,75,85,87,89,93,95,99,103,117,118,128,129,172,174,177,181,184,185,186,192,205,206,211,219,220,229,231,233,239,267,278,282,298,313,315,342,350,422,427,459,460,514,515,],[52,52,80,52,52,105,52,52,52,52,52,52,105,52,80,52,105,105,198,105,52,105,52,198,105,279,52,105,105,105,279,198,105,80,198,105,105,105,105,105,105,52,105,52,105,105,52,52,52,52,105,52,198,105,]),'assignment_operator':([224,],[364,]),'expression':([174,181,229,231,233,258,265,290,298,307,427,429,430,434,437,440,441,499,502,533,535,537,538,539,549,553,566,569,574,577,],[270,294,270,270,270,399,406,426,294,294,294,294,501,503,294,294,507,294,294,294,294,294,556,294,564,294,294,294,294,294,]),'storage_class_specifier':([0,1,4,21,27,52,53,55,59,63,75,87,89,93,118,129,181,211,278,298,342,350,422,427,460,],[1,1,68,1,1,1,1,1,1,1,1,1,68,1,1,1,1,68,1,1,1,1,1,1,1,]),'unified_wstring_literal':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,153,]),'translation_unit_or_empty':([0,],[9,]),'initializer_list_opt':([227,],[370,]),'brace_close':([99,184,185,186,196,309,313,315,325,326,370,472,528,550,],[187,314,316,317,328,439,442,443,448,449,469,523,551,565,]),'direct_typeid_declarator':([4,69,72,89,126,192,202,445,],[73,73,127,73,73,73,73,73,]),'external_declaration':([0,59,],[16,123,]),'pragmacomp_or_statement':([307,429,440,502,535,537,539,569,574,577,],[436,500,506,536,554,555,557,576,578,579,]),'type_name':([85,95,174,229,231,233,239,267,],[157,183,271,379,380,381,382,410,]),'typedef_name':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,]),'pppragma_directive':([0,59,99,181,184,185,186,298,307,313,315,429,437,440,502,535,537,539,569,574,577,],[25,25,189,300,189,189,189,300,437,189,189,437,300,437,437,437,437,437,437,437,437,]),'statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[301,301,438,438,505,438,438,438,438,558,438,438,438,]),'cast_expression':([85,116,131,171,174,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[158,158,158,268,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,487,158,158,158,158,158,158,158,158,158,158,487,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,158,]),'atomic_specifier':([0,1,21,27,40,52,53,55,59,63,75,85,87,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[27,63,87,63,111,63,63,63,27,63,87,111,63,111,87,111,111,27,27,111,111,87,111,111,111,111,111,111,111,111,111,27,87,111,111,27,27,27,87,27,]),'struct_declarator_list':([192,],[320,]),'empty':([0,1,4,21,27,35,40,52,53,55,63,75,87,89,91,117,118,128,129,177,181,192,204,206,211,218,220,227,278,282,298,307,342,419,421,422,427,429,437,440,459,460,472,488,499,502,513,515,533,535,537,539,550,553,566,569,574,577,],[57,64,83,88,64,106,115,64,64,64,64,88,64,83,115,106,208,106,208,277,306,321,337,106,277,337,106,377,415,106,433,433,415,337,337,415,433,433,433,433,106,208,522,522,433,433,337,106,433,433,433,433,522,433,433,433,433,433,]),'parameter_declaration':([118,129,278,342,350,422,460,],[210,210,210,210,463,210,210,]),'primary_expression':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,161,]),'declaration':([0,21,59,75,93,181,298,427,],[38,90,38,90,182,302,302,499,]),'declaration_specifiers_no_type':([0,1,21,27,52,53,55,59,63,75,87,93,118,129,181,278,298,342,350,422,427,460,],[40,67,91,67,67,67,67,40,67,91,67,91,214,214,91,214,91,214,214,214,91,214,]),'jump_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[303,303,303,303,303,303,303,303,303,303,303,303,303,]),'enumerator_list':([102,193,194,],[196,325,326,]),'block_item':([181,298,],[305,432,]),'constant_expression':([85,116,297,319,329,373,447,],[159,203,431,444,451,471,509,]),'identifier_list_opt':([118,129,460,],[207,221,516,]),'constant':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,]),'type_specifier_no_typeid':([0,4,21,40,59,75,85,89,91,93,95,99,118,129,172,174,177,181,184,185,186,192,211,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[12,71,12,12,12,12,12,71,12,12,12,12,12,12,12,12,275,12,12,12,12,275,71,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,]),'struct_declaration':([99,184,185,186,313,315,],[190,190,190,318,318,318,]),'direct_typeid_noparen_declarator':([211,344,],[345,458,]),'id_declarator':([0,4,37,40,59,69,89,91,126,192,202,211,342,445,],[21,75,107,110,21,107,179,110,179,179,179,346,107,179,]),'selection_statement':([181,298,307,429,437,440,502,535,537,539,569,574,577,],[308,308,308,308,308,308,308,308,308,308,308,308,308,]),'postfix_expression':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,168,]),'initializer_list':([227,488,],[374,528,]),'unary_operator':([85,116,131,146,149,171,174,175,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,413,419,421,427,429,430,434,437,440,441,447,477,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,171,]),'struct_or_union':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,]),'block_item_list_opt':([181,],[309,]),'assignment_expression':([131,174,181,201,204,218,229,231,233,258,265,266,290,298,307,338,339,353,354,364,375,412,419,421,427,429,430,434,437,440,441,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[228,272,272,228,335,335,272,272,272,272,272,408,272,272,272,455,456,466,467,468,228,486,335,335,272,272,272,272,272,272,272,525,272,272,335,228,272,272,272,272,272,561,562,272,272,272,272,272,272,]),'designation_opt':([227,472,488,550,],[375,521,375,521,]),'parameter_type_list':([118,129,278,342,422,460,],[209,222,416,416,416,517,]),'type_qualifier_list':([35,85,95,99,117,128,174,184,185,186,206,220,229,231,233,239,267,282,313,315,459,515,],[103,172,172,172,205,219,172,172,172,172,103,103,172,172,172,172,172,103,172,172,514,103,]),'designator':([227,376,472,488,550,],[371,474,371,371,371,]),'id_init_declarator_list_opt':([40,91,],[114,114,]),'declaration_specifiers':([0,21,59,75,93,118,129,181,278,298,342,350,422,427,460,],[4,89,4,89,89,211,211,89,211,89,211,211,211,89,211,]),'identifier_list':([118,129,460,],[212,212,212,]),'declaration_list_opt':([21,75,],[92,130,]),'function_definition':([0,59,],[45,45,]),'binary_expression':([85,116,131,174,181,201,204,218,229,231,233,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,419,421,427,429,430,434,437,440,441,447,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[162,162,162,162,162,162,162,162,162,162,162,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,162,400,401,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,]),'enum_specifier':([0,21,40,59,75,85,91,93,95,99,118,129,172,174,181,184,185,186,214,229,231,233,239,267,278,298,313,315,342,350,422,427,460,],[49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,49,]),'decl_body':([0,21,59,75,93,181,298,427,],[51,51,51,51,51,51,51,51,]),'function_specifier':([0,1,4,21,27,52,53,55,59,63,75,87,89,93,118,129,181,211,278,298,342,350,422,427,460,],[55,55,82,55,55,55,55,55,55,55,55,55,82,55,55,55,55,82,55,55,55,55,55,55,55,]),'specifier_qualifier_list':([85,95,99,174,184,185,186,229,231,233,239,267,313,315,],[177,177,192,177,192,192,192,177,177,177,177,177,192,192,]),'conditional_expression':([85,116,131,174,181,201,204,218,229,231,233,258,265,266,290,297,298,307,319,329,338,339,353,354,364,373,375,412,419,421,427,429,430,434,437,440,441,447,481,484,499,502,513,521,533,535,537,538,539,542,543,549,553,566,569,574,577,],[178,178,225,225,225,225,225,225,225,225,225,225,225,225,225,178,225,225,178,178,225,225,225,225,225,178,225,225,225,225,225,225,225,225,225,225,225,178,524,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,225,]),} - -_lr_goto = {} -for _k, _v in _lr_goto_items.items(): - for _x, _y in zip(_v[0], _v[1]): - if not _x in _lr_goto: _lr_goto[_x] = {} - _lr_goto[_x][_k] = _y -del _lr_goto_items -_lr_productions = [ - ("S' -> translation_unit_or_empty","S'",1,None,None,None), - ('abstract_declarator_opt -> empty','abstract_declarator_opt',1,'p_abstract_declarator_opt','plyparser.py',43), - ('abstract_declarator_opt -> abstract_declarator','abstract_declarator_opt',1,'p_abstract_declarator_opt','plyparser.py',44), - ('assignment_expression_opt -> empty','assignment_expression_opt',1,'p_assignment_expression_opt','plyparser.py',43), - ('assignment_expression_opt -> assignment_expression','assignment_expression_opt',1,'p_assignment_expression_opt','plyparser.py',44), - ('block_item_list_opt -> empty','block_item_list_opt',1,'p_block_item_list_opt','plyparser.py',43), - ('block_item_list_opt -> block_item_list','block_item_list_opt',1,'p_block_item_list_opt','plyparser.py',44), - ('declaration_list_opt -> empty','declaration_list_opt',1,'p_declaration_list_opt','plyparser.py',43), - ('declaration_list_opt -> declaration_list','declaration_list_opt',1,'p_declaration_list_opt','plyparser.py',44), - ('declaration_specifiers_no_type_opt -> empty','declaration_specifiers_no_type_opt',1,'p_declaration_specifiers_no_type_opt','plyparser.py',43), - ('declaration_specifiers_no_type_opt -> declaration_specifiers_no_type','declaration_specifiers_no_type_opt',1,'p_declaration_specifiers_no_type_opt','plyparser.py',44), - ('designation_opt -> empty','designation_opt',1,'p_designation_opt','plyparser.py',43), - ('designation_opt -> designation','designation_opt',1,'p_designation_opt','plyparser.py',44), - ('expression_opt -> empty','expression_opt',1,'p_expression_opt','plyparser.py',43), - ('expression_opt -> expression','expression_opt',1,'p_expression_opt','plyparser.py',44), - ('id_init_declarator_list_opt -> empty','id_init_declarator_list_opt',1,'p_id_init_declarator_list_opt','plyparser.py',43), - ('id_init_declarator_list_opt -> id_init_declarator_list','id_init_declarator_list_opt',1,'p_id_init_declarator_list_opt','plyparser.py',44), - ('identifier_list_opt -> empty','identifier_list_opt',1,'p_identifier_list_opt','plyparser.py',43), - ('identifier_list_opt -> identifier_list','identifier_list_opt',1,'p_identifier_list_opt','plyparser.py',44), - ('init_declarator_list_opt -> empty','init_declarator_list_opt',1,'p_init_declarator_list_opt','plyparser.py',43), - ('init_declarator_list_opt -> init_declarator_list','init_declarator_list_opt',1,'p_init_declarator_list_opt','plyparser.py',44), - ('initializer_list_opt -> empty','initializer_list_opt',1,'p_initializer_list_opt','plyparser.py',43), - ('initializer_list_opt -> initializer_list','initializer_list_opt',1,'p_initializer_list_opt','plyparser.py',44), - ('parameter_type_list_opt -> empty','parameter_type_list_opt',1,'p_parameter_type_list_opt','plyparser.py',43), - ('parameter_type_list_opt -> parameter_type_list','parameter_type_list_opt',1,'p_parameter_type_list_opt','plyparser.py',44), - ('struct_declarator_list_opt -> empty','struct_declarator_list_opt',1,'p_struct_declarator_list_opt','plyparser.py',43), - ('struct_declarator_list_opt -> struct_declarator_list','struct_declarator_list_opt',1,'p_struct_declarator_list_opt','plyparser.py',44), - ('type_qualifier_list_opt -> empty','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','plyparser.py',43), - ('type_qualifier_list_opt -> type_qualifier_list','type_qualifier_list_opt',1,'p_type_qualifier_list_opt','plyparser.py',44), - ('direct_id_declarator -> ID','direct_id_declarator',1,'p_direct_id_declarator_1','plyparser.py',126), - ('direct_id_declarator -> LPAREN id_declarator RPAREN','direct_id_declarator',3,'p_direct_id_declarator_2','plyparser.py',126), - ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_id_declarator',5,'p_direct_id_declarator_3','plyparser.py',126), - ('direct_id_declarator -> direct_id_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_id_declarator',6,'p_direct_id_declarator_4','plyparser.py',126), - ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_id_declarator',6,'p_direct_id_declarator_4','plyparser.py',127), - ('direct_id_declarator -> direct_id_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_id_declarator',5,'p_direct_id_declarator_5','plyparser.py',126), - ('direct_id_declarator -> direct_id_declarator LPAREN parameter_type_list RPAREN','direct_id_declarator',4,'p_direct_id_declarator_6','plyparser.py',126), - ('direct_id_declarator -> direct_id_declarator LPAREN identifier_list_opt RPAREN','direct_id_declarator',4,'p_direct_id_declarator_6','plyparser.py',127), - ('direct_typeid_declarator -> TYPEID','direct_typeid_declarator',1,'p_direct_typeid_declarator_1','plyparser.py',126), - ('direct_typeid_declarator -> LPAREN typeid_declarator RPAREN','direct_typeid_declarator',3,'p_direct_typeid_declarator_2','plyparser.py',126), - ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_typeid_declarator',5,'p_direct_typeid_declarator_3','plyparser.py',126), - ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_typeid_declarator',6,'p_direct_typeid_declarator_4','plyparser.py',126), - ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_typeid_declarator',6,'p_direct_typeid_declarator_4','plyparser.py',127), - ('direct_typeid_declarator -> direct_typeid_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_typeid_declarator',5,'p_direct_typeid_declarator_5','plyparser.py',126), - ('direct_typeid_declarator -> direct_typeid_declarator LPAREN parameter_type_list RPAREN','direct_typeid_declarator',4,'p_direct_typeid_declarator_6','plyparser.py',126), - ('direct_typeid_declarator -> direct_typeid_declarator LPAREN identifier_list_opt RPAREN','direct_typeid_declarator',4,'p_direct_typeid_declarator_6','plyparser.py',127), - ('direct_typeid_noparen_declarator -> TYPEID','direct_typeid_noparen_declarator',1,'p_direct_typeid_noparen_declarator_1','plyparser.py',126), - ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_typeid_noparen_declarator',5,'p_direct_typeid_noparen_declarator_3','plyparser.py',126), - ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET STATIC type_qualifier_list_opt assignment_expression RBRACKET','direct_typeid_noparen_declarator',6,'p_direct_typeid_noparen_declarator_4','plyparser.py',126), - ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list STATIC assignment_expression RBRACKET','direct_typeid_noparen_declarator',6,'p_direct_typeid_noparen_declarator_4','plyparser.py',127), - ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LBRACKET type_qualifier_list_opt TIMES RBRACKET','direct_typeid_noparen_declarator',5,'p_direct_typeid_noparen_declarator_5','plyparser.py',126), - ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LPAREN parameter_type_list RPAREN','direct_typeid_noparen_declarator',4,'p_direct_typeid_noparen_declarator_6','plyparser.py',126), - ('direct_typeid_noparen_declarator -> direct_typeid_noparen_declarator LPAREN identifier_list_opt RPAREN','direct_typeid_noparen_declarator',4,'p_direct_typeid_noparen_declarator_6','plyparser.py',127), - ('id_declarator -> direct_id_declarator','id_declarator',1,'p_id_declarator_1','plyparser.py',126), - ('id_declarator -> pointer direct_id_declarator','id_declarator',2,'p_id_declarator_2','plyparser.py',126), - ('typeid_declarator -> direct_typeid_declarator','typeid_declarator',1,'p_typeid_declarator_1','plyparser.py',126), - ('typeid_declarator -> pointer direct_typeid_declarator','typeid_declarator',2,'p_typeid_declarator_2','plyparser.py',126), - ('typeid_noparen_declarator -> direct_typeid_noparen_declarator','typeid_noparen_declarator',1,'p_typeid_noparen_declarator_1','plyparser.py',126), - ('typeid_noparen_declarator -> pointer direct_typeid_noparen_declarator','typeid_noparen_declarator',2,'p_typeid_noparen_declarator_2','plyparser.py',126), - ('translation_unit_or_empty -> translation_unit','translation_unit_or_empty',1,'p_translation_unit_or_empty','c_parser.py',509), - ('translation_unit_or_empty -> empty','translation_unit_or_empty',1,'p_translation_unit_or_empty','c_parser.py',510), - ('translation_unit -> external_declaration','translation_unit',1,'p_translation_unit_1','c_parser.py',518), - ('translation_unit -> translation_unit external_declaration','translation_unit',2,'p_translation_unit_2','c_parser.py',524), - ('external_declaration -> function_definition','external_declaration',1,'p_external_declaration_1','c_parser.py',534), - ('external_declaration -> declaration','external_declaration',1,'p_external_declaration_2','c_parser.py',539), - ('external_declaration -> pp_directive','external_declaration',1,'p_external_declaration_3','c_parser.py',544), - ('external_declaration -> pppragma_directive','external_declaration',1,'p_external_declaration_3','c_parser.py',545), - ('external_declaration -> SEMI','external_declaration',1,'p_external_declaration_4','c_parser.py',550), - ('external_declaration -> static_assert','external_declaration',1,'p_external_declaration_5','c_parser.py',555), - ('static_assert -> _STATIC_ASSERT LPAREN constant_expression COMMA unified_string_literal RPAREN','static_assert',6,'p_static_assert_declaration','c_parser.py',560), - ('static_assert -> _STATIC_ASSERT LPAREN constant_expression RPAREN','static_assert',4,'p_static_assert_declaration','c_parser.py',561), - ('pp_directive -> PPHASH','pp_directive',1,'p_pp_directive','c_parser.py',569), - ('pppragma_directive -> PPPRAGMA','pppragma_directive',1,'p_pppragma_directive','c_parser.py',575), - ('pppragma_directive -> PPPRAGMA PPPRAGMASTR','pppragma_directive',2,'p_pppragma_directive','c_parser.py',576), - ('function_definition -> id_declarator declaration_list_opt compound_statement','function_definition',3,'p_function_definition_1','c_parser.py',586), - ('function_definition -> declaration_specifiers id_declarator declaration_list_opt compound_statement','function_definition',4,'p_function_definition_2','c_parser.py',604), - ('statement -> labeled_statement','statement',1,'p_statement','c_parser.py',619), - ('statement -> expression_statement','statement',1,'p_statement','c_parser.py',620), - ('statement -> compound_statement','statement',1,'p_statement','c_parser.py',621), - ('statement -> selection_statement','statement',1,'p_statement','c_parser.py',622), - ('statement -> iteration_statement','statement',1,'p_statement','c_parser.py',623), - ('statement -> jump_statement','statement',1,'p_statement','c_parser.py',624), - ('statement -> pppragma_directive','statement',1,'p_statement','c_parser.py',625), - ('statement -> static_assert','statement',1,'p_statement','c_parser.py',626), - ('pragmacomp_or_statement -> pppragma_directive statement','pragmacomp_or_statement',2,'p_pragmacomp_or_statement','c_parser.py',674), - ('pragmacomp_or_statement -> statement','pragmacomp_or_statement',1,'p_pragmacomp_or_statement','c_parser.py',675), - ('decl_body -> declaration_specifiers init_declarator_list_opt','decl_body',2,'p_decl_body','c_parser.py',694), - ('decl_body -> declaration_specifiers_no_type id_init_declarator_list_opt','decl_body',2,'p_decl_body','c_parser.py',695), - ('declaration -> decl_body SEMI','declaration',2,'p_declaration','c_parser.py',755), - ('declaration_list -> declaration','declaration_list',1,'p_declaration_list','c_parser.py',764), - ('declaration_list -> declaration_list declaration','declaration_list',2,'p_declaration_list','c_parser.py',765), - ('declaration_specifiers_no_type -> type_qualifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_1','c_parser.py',775), - ('declaration_specifiers_no_type -> storage_class_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_2','c_parser.py',780), - ('declaration_specifiers_no_type -> function_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_3','c_parser.py',785), - ('declaration_specifiers_no_type -> atomic_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_4','c_parser.py',792), - ('declaration_specifiers_no_type -> alignment_specifier declaration_specifiers_no_type_opt','declaration_specifiers_no_type',2,'p_declaration_specifiers_no_type_5','c_parser.py',797), - ('declaration_specifiers -> declaration_specifiers type_qualifier','declaration_specifiers',2,'p_declaration_specifiers_1','c_parser.py',802), - ('declaration_specifiers -> declaration_specifiers storage_class_specifier','declaration_specifiers',2,'p_declaration_specifiers_2','c_parser.py',807), - ('declaration_specifiers -> declaration_specifiers function_specifier','declaration_specifiers',2,'p_declaration_specifiers_3','c_parser.py',812), - ('declaration_specifiers -> declaration_specifiers type_specifier_no_typeid','declaration_specifiers',2,'p_declaration_specifiers_4','c_parser.py',817), - ('declaration_specifiers -> type_specifier','declaration_specifiers',1,'p_declaration_specifiers_5','c_parser.py',822), - ('declaration_specifiers -> declaration_specifiers_no_type type_specifier','declaration_specifiers',2,'p_declaration_specifiers_6','c_parser.py',827), - ('declaration_specifiers -> declaration_specifiers alignment_specifier','declaration_specifiers',2,'p_declaration_specifiers_7','c_parser.py',832), - ('storage_class_specifier -> AUTO','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',837), - ('storage_class_specifier -> REGISTER','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',838), - ('storage_class_specifier -> STATIC','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',839), - ('storage_class_specifier -> EXTERN','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',840), - ('storage_class_specifier -> TYPEDEF','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',841), - ('storage_class_specifier -> _THREAD_LOCAL','storage_class_specifier',1,'p_storage_class_specifier','c_parser.py',842), - ('function_specifier -> INLINE','function_specifier',1,'p_function_specifier','c_parser.py',847), - ('function_specifier -> _NORETURN','function_specifier',1,'p_function_specifier','c_parser.py',848), - ('type_specifier_no_typeid -> VOID','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',853), - ('type_specifier_no_typeid -> _BOOL','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',854), - ('type_specifier_no_typeid -> CHAR','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',855), - ('type_specifier_no_typeid -> SHORT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',856), - ('type_specifier_no_typeid -> INT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',857), - ('type_specifier_no_typeid -> LONG','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',858), - ('type_specifier_no_typeid -> FLOAT','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',859), - ('type_specifier_no_typeid -> DOUBLE','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',860), - ('type_specifier_no_typeid -> _COMPLEX','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',861), - ('type_specifier_no_typeid -> SIGNED','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',862), - ('type_specifier_no_typeid -> UNSIGNED','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',863), - ('type_specifier_no_typeid -> __INT128','type_specifier_no_typeid',1,'p_type_specifier_no_typeid','c_parser.py',864), - ('type_specifier -> typedef_name','type_specifier',1,'p_type_specifier','c_parser.py',869), - ('type_specifier -> enum_specifier','type_specifier',1,'p_type_specifier','c_parser.py',870), - ('type_specifier -> struct_or_union_specifier','type_specifier',1,'p_type_specifier','c_parser.py',871), - ('type_specifier -> type_specifier_no_typeid','type_specifier',1,'p_type_specifier','c_parser.py',872), - ('type_specifier -> atomic_specifier','type_specifier',1,'p_type_specifier','c_parser.py',873), - ('atomic_specifier -> _ATOMIC LPAREN type_name RPAREN','atomic_specifier',4,'p_atomic_specifier','c_parser.py',879), - ('type_qualifier -> CONST','type_qualifier',1,'p_type_qualifier','c_parser.py',886), - ('type_qualifier -> RESTRICT','type_qualifier',1,'p_type_qualifier','c_parser.py',887), - ('type_qualifier -> VOLATILE','type_qualifier',1,'p_type_qualifier','c_parser.py',888), - ('type_qualifier -> _ATOMIC','type_qualifier',1,'p_type_qualifier','c_parser.py',889), - ('init_declarator_list -> init_declarator','init_declarator_list',1,'p_init_declarator_list','c_parser.py',894), - ('init_declarator_list -> init_declarator_list COMMA init_declarator','init_declarator_list',3,'p_init_declarator_list','c_parser.py',895), - ('init_declarator -> declarator','init_declarator',1,'p_init_declarator','c_parser.py',903), - ('init_declarator -> declarator EQUALS initializer','init_declarator',3,'p_init_declarator','c_parser.py',904), - ('id_init_declarator_list -> id_init_declarator','id_init_declarator_list',1,'p_id_init_declarator_list','c_parser.py',909), - ('id_init_declarator_list -> id_init_declarator_list COMMA init_declarator','id_init_declarator_list',3,'p_id_init_declarator_list','c_parser.py',910), - ('id_init_declarator -> id_declarator','id_init_declarator',1,'p_id_init_declarator','c_parser.py',915), - ('id_init_declarator -> id_declarator EQUALS initializer','id_init_declarator',3,'p_id_init_declarator','c_parser.py',916), - ('specifier_qualifier_list -> specifier_qualifier_list type_specifier_no_typeid','specifier_qualifier_list',2,'p_specifier_qualifier_list_1','c_parser.py',923), - ('specifier_qualifier_list -> specifier_qualifier_list type_qualifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_2','c_parser.py',928), - ('specifier_qualifier_list -> type_specifier','specifier_qualifier_list',1,'p_specifier_qualifier_list_3','c_parser.py',933), - ('specifier_qualifier_list -> type_qualifier_list type_specifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_4','c_parser.py',938), - ('specifier_qualifier_list -> alignment_specifier','specifier_qualifier_list',1,'p_specifier_qualifier_list_5','c_parser.py',943), - ('specifier_qualifier_list -> specifier_qualifier_list alignment_specifier','specifier_qualifier_list',2,'p_specifier_qualifier_list_6','c_parser.py',948), - ('struct_or_union_specifier -> struct_or_union ID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','c_parser.py',956), - ('struct_or_union_specifier -> struct_or_union TYPEID','struct_or_union_specifier',2,'p_struct_or_union_specifier_1','c_parser.py',957), - ('struct_or_union_specifier -> struct_or_union brace_open struct_declaration_list brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_2','c_parser.py',967), - ('struct_or_union_specifier -> struct_or_union brace_open brace_close','struct_or_union_specifier',3,'p_struct_or_union_specifier_2','c_parser.py',968), - ('struct_or_union_specifier -> struct_or_union ID brace_open struct_declaration_list brace_close','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','c_parser.py',985), - ('struct_or_union_specifier -> struct_or_union ID brace_open brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_3','c_parser.py',986), - ('struct_or_union_specifier -> struct_or_union TYPEID brace_open struct_declaration_list brace_close','struct_or_union_specifier',5,'p_struct_or_union_specifier_3','c_parser.py',987), - ('struct_or_union_specifier -> struct_or_union TYPEID brace_open brace_close','struct_or_union_specifier',4,'p_struct_or_union_specifier_3','c_parser.py',988), - ('struct_or_union -> STRUCT','struct_or_union',1,'p_struct_or_union','c_parser.py',1004), - ('struct_or_union -> UNION','struct_or_union',1,'p_struct_or_union','c_parser.py',1005), - ('struct_declaration_list -> struct_declaration','struct_declaration_list',1,'p_struct_declaration_list','c_parser.py',1012), - ('struct_declaration_list -> struct_declaration_list struct_declaration','struct_declaration_list',2,'p_struct_declaration_list','c_parser.py',1013), - ('struct_declaration -> specifier_qualifier_list struct_declarator_list_opt SEMI','struct_declaration',3,'p_struct_declaration_1','c_parser.py',1021), - ('struct_declaration -> SEMI','struct_declaration',1,'p_struct_declaration_2','c_parser.py',1059), - ('struct_declaration -> pppragma_directive','struct_declaration',1,'p_struct_declaration_3','c_parser.py',1064), - ('struct_declarator_list -> struct_declarator','struct_declarator_list',1,'p_struct_declarator_list','c_parser.py',1069), - ('struct_declarator_list -> struct_declarator_list COMMA struct_declarator','struct_declarator_list',3,'p_struct_declarator_list','c_parser.py',1070), - ('struct_declarator -> declarator','struct_declarator',1,'p_struct_declarator_1','c_parser.py',1078), - ('struct_declarator -> declarator COLON constant_expression','struct_declarator',3,'p_struct_declarator_2','c_parser.py',1083), - ('struct_declarator -> COLON constant_expression','struct_declarator',2,'p_struct_declarator_2','c_parser.py',1084), - ('enum_specifier -> ENUM ID','enum_specifier',2,'p_enum_specifier_1','c_parser.py',1092), - ('enum_specifier -> ENUM TYPEID','enum_specifier',2,'p_enum_specifier_1','c_parser.py',1093), - ('enum_specifier -> ENUM brace_open enumerator_list brace_close','enum_specifier',4,'p_enum_specifier_2','c_parser.py',1098), - ('enum_specifier -> ENUM ID brace_open enumerator_list brace_close','enum_specifier',5,'p_enum_specifier_3','c_parser.py',1103), - ('enum_specifier -> ENUM TYPEID brace_open enumerator_list brace_close','enum_specifier',5,'p_enum_specifier_3','c_parser.py',1104), - ('enumerator_list -> enumerator','enumerator_list',1,'p_enumerator_list','c_parser.py',1109), - ('enumerator_list -> enumerator_list COMMA','enumerator_list',2,'p_enumerator_list','c_parser.py',1110), - ('enumerator_list -> enumerator_list COMMA enumerator','enumerator_list',3,'p_enumerator_list','c_parser.py',1111), - ('alignment_specifier -> _ALIGNAS LPAREN type_name RPAREN','alignment_specifier',4,'p_alignment_specifier','c_parser.py',1122), - ('alignment_specifier -> _ALIGNAS LPAREN constant_expression RPAREN','alignment_specifier',4,'p_alignment_specifier','c_parser.py',1123), - ('enumerator -> ID','enumerator',1,'p_enumerator','c_parser.py',1128), - ('enumerator -> ID EQUALS constant_expression','enumerator',3,'p_enumerator','c_parser.py',1129), - ('declarator -> id_declarator','declarator',1,'p_declarator','c_parser.py',1144), - ('declarator -> typeid_declarator','declarator',1,'p_declarator','c_parser.py',1145), - ('pointer -> TIMES type_qualifier_list_opt','pointer',2,'p_pointer','c_parser.py',1257), - ('pointer -> TIMES type_qualifier_list_opt pointer','pointer',3,'p_pointer','c_parser.py',1258), - ('type_qualifier_list -> type_qualifier','type_qualifier_list',1,'p_type_qualifier_list','c_parser.py',1287), - ('type_qualifier_list -> type_qualifier_list type_qualifier','type_qualifier_list',2,'p_type_qualifier_list','c_parser.py',1288), - ('parameter_type_list -> parameter_list','parameter_type_list',1,'p_parameter_type_list','c_parser.py',1293), - ('parameter_type_list -> parameter_list COMMA ELLIPSIS','parameter_type_list',3,'p_parameter_type_list','c_parser.py',1294), - ('parameter_list -> parameter_declaration','parameter_list',1,'p_parameter_list','c_parser.py',1302), - ('parameter_list -> parameter_list COMMA parameter_declaration','parameter_list',3,'p_parameter_list','c_parser.py',1303), - ('parameter_declaration -> declaration_specifiers id_declarator','parameter_declaration',2,'p_parameter_declaration_1','c_parser.py',1322), - ('parameter_declaration -> declaration_specifiers typeid_noparen_declarator','parameter_declaration',2,'p_parameter_declaration_1','c_parser.py',1323), - ('parameter_declaration -> declaration_specifiers abstract_declarator_opt','parameter_declaration',2,'p_parameter_declaration_2','c_parser.py',1334), - ('identifier_list -> identifier','identifier_list',1,'p_identifier_list','c_parser.py',1366), - ('identifier_list -> identifier_list COMMA identifier','identifier_list',3,'p_identifier_list','c_parser.py',1367), - ('initializer -> assignment_expression','initializer',1,'p_initializer_1','c_parser.py',1376), - ('initializer -> brace_open initializer_list_opt brace_close','initializer',3,'p_initializer_2','c_parser.py',1381), - ('initializer -> brace_open initializer_list COMMA brace_close','initializer',4,'p_initializer_2','c_parser.py',1382), - ('initializer_list -> designation_opt initializer','initializer_list',2,'p_initializer_list','c_parser.py',1390), - ('initializer_list -> initializer_list COMMA designation_opt initializer','initializer_list',4,'p_initializer_list','c_parser.py',1391), - ('designation -> designator_list EQUALS','designation',2,'p_designation','c_parser.py',1402), - ('designator_list -> designator','designator_list',1,'p_designator_list','c_parser.py',1410), - ('designator_list -> designator_list designator','designator_list',2,'p_designator_list','c_parser.py',1411), - ('designator -> LBRACKET constant_expression RBRACKET','designator',3,'p_designator','c_parser.py',1416), - ('designator -> PERIOD identifier','designator',2,'p_designator','c_parser.py',1417), - ('type_name -> specifier_qualifier_list abstract_declarator_opt','type_name',2,'p_type_name','c_parser.py',1422), - ('abstract_declarator -> pointer','abstract_declarator',1,'p_abstract_declarator_1','c_parser.py',1434), - ('abstract_declarator -> pointer direct_abstract_declarator','abstract_declarator',2,'p_abstract_declarator_2','c_parser.py',1442), - ('abstract_declarator -> direct_abstract_declarator','abstract_declarator',1,'p_abstract_declarator_3','c_parser.py',1447), - ('direct_abstract_declarator -> LPAREN abstract_declarator RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_1','c_parser.py',1457), - ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET assignment_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_2','c_parser.py',1461), - ('direct_abstract_declarator -> LBRACKET type_qualifier_list_opt assignment_expression_opt RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_3','c_parser.py',1472), - ('direct_abstract_declarator -> direct_abstract_declarator LBRACKET TIMES RBRACKET','direct_abstract_declarator',4,'p_direct_abstract_declarator_4','c_parser.py',1482), - ('direct_abstract_declarator -> LBRACKET TIMES RBRACKET','direct_abstract_declarator',3,'p_direct_abstract_declarator_5','c_parser.py',1493), - ('direct_abstract_declarator -> direct_abstract_declarator LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',4,'p_direct_abstract_declarator_6','c_parser.py',1502), - ('direct_abstract_declarator -> LPAREN parameter_type_list_opt RPAREN','direct_abstract_declarator',3,'p_direct_abstract_declarator_7','c_parser.py',1512), - ('block_item -> declaration','block_item',1,'p_block_item','c_parser.py',1523), - ('block_item -> statement','block_item',1,'p_block_item','c_parser.py',1524), - ('block_item_list -> block_item','block_item_list',1,'p_block_item_list','c_parser.py',1531), - ('block_item_list -> block_item_list block_item','block_item_list',2,'p_block_item_list','c_parser.py',1532), - ('compound_statement -> brace_open block_item_list_opt brace_close','compound_statement',3,'p_compound_statement_1','c_parser.py',1538), - ('labeled_statement -> ID COLON pragmacomp_or_statement','labeled_statement',3,'p_labeled_statement_1','c_parser.py',1544), - ('labeled_statement -> CASE constant_expression COLON pragmacomp_or_statement','labeled_statement',4,'p_labeled_statement_2','c_parser.py',1548), - ('labeled_statement -> DEFAULT COLON pragmacomp_or_statement','labeled_statement',3,'p_labeled_statement_3','c_parser.py',1552), - ('selection_statement -> IF LPAREN expression RPAREN pragmacomp_or_statement','selection_statement',5,'p_selection_statement_1','c_parser.py',1556), - ('selection_statement -> IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement','selection_statement',7,'p_selection_statement_2','c_parser.py',1560), - ('selection_statement -> SWITCH LPAREN expression RPAREN pragmacomp_or_statement','selection_statement',5,'p_selection_statement_3','c_parser.py',1564), - ('iteration_statement -> WHILE LPAREN expression RPAREN pragmacomp_or_statement','iteration_statement',5,'p_iteration_statement_1','c_parser.py',1569), - ('iteration_statement -> DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI','iteration_statement',7,'p_iteration_statement_2','c_parser.py',1573), - ('iteration_statement -> FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement','iteration_statement',9,'p_iteration_statement_3','c_parser.py',1577), - ('iteration_statement -> FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement','iteration_statement',8,'p_iteration_statement_4','c_parser.py',1581), - ('jump_statement -> GOTO ID SEMI','jump_statement',3,'p_jump_statement_1','c_parser.py',1586), - ('jump_statement -> BREAK SEMI','jump_statement',2,'p_jump_statement_2','c_parser.py',1590), - ('jump_statement -> CONTINUE SEMI','jump_statement',2,'p_jump_statement_3','c_parser.py',1594), - ('jump_statement -> RETURN expression SEMI','jump_statement',3,'p_jump_statement_4','c_parser.py',1598), - ('jump_statement -> RETURN SEMI','jump_statement',2,'p_jump_statement_4','c_parser.py',1599), - ('expression_statement -> expression_opt SEMI','expression_statement',2,'p_expression_statement','c_parser.py',1604), - ('expression -> assignment_expression','expression',1,'p_expression','c_parser.py',1611), - ('expression -> expression COMMA assignment_expression','expression',3,'p_expression','c_parser.py',1612), - ('assignment_expression -> LPAREN compound_statement RPAREN','assignment_expression',3,'p_parenthesized_compound_expression','c_parser.py',1624), - ('typedef_name -> TYPEID','typedef_name',1,'p_typedef_name','c_parser.py',1628), - ('assignment_expression -> conditional_expression','assignment_expression',1,'p_assignment_expression','c_parser.py',1632), - ('assignment_expression -> unary_expression assignment_operator assignment_expression','assignment_expression',3,'p_assignment_expression','c_parser.py',1633), - ('assignment_operator -> EQUALS','assignment_operator',1,'p_assignment_operator','c_parser.py',1646), - ('assignment_operator -> XOREQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1647), - ('assignment_operator -> TIMESEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1648), - ('assignment_operator -> DIVEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1649), - ('assignment_operator -> MODEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1650), - ('assignment_operator -> PLUSEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1651), - ('assignment_operator -> MINUSEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1652), - ('assignment_operator -> LSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1653), - ('assignment_operator -> RSHIFTEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1654), - ('assignment_operator -> ANDEQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1655), - ('assignment_operator -> OREQUAL','assignment_operator',1,'p_assignment_operator','c_parser.py',1656), - ('constant_expression -> conditional_expression','constant_expression',1,'p_constant_expression','c_parser.py',1661), - ('conditional_expression -> binary_expression','conditional_expression',1,'p_conditional_expression','c_parser.py',1665), - ('conditional_expression -> binary_expression CONDOP expression COLON conditional_expression','conditional_expression',5,'p_conditional_expression','c_parser.py',1666), - ('binary_expression -> cast_expression','binary_expression',1,'p_binary_expression','c_parser.py',1674), - ('binary_expression -> binary_expression TIMES binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1675), - ('binary_expression -> binary_expression DIVIDE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1676), - ('binary_expression -> binary_expression MOD binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1677), - ('binary_expression -> binary_expression PLUS binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1678), - ('binary_expression -> binary_expression MINUS binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1679), - ('binary_expression -> binary_expression RSHIFT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1680), - ('binary_expression -> binary_expression LSHIFT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1681), - ('binary_expression -> binary_expression LT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1682), - ('binary_expression -> binary_expression LE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1683), - ('binary_expression -> binary_expression GE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1684), - ('binary_expression -> binary_expression GT binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1685), - ('binary_expression -> binary_expression EQ binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1686), - ('binary_expression -> binary_expression NE binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1687), - ('binary_expression -> binary_expression AND binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1688), - ('binary_expression -> binary_expression OR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1689), - ('binary_expression -> binary_expression XOR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1690), - ('binary_expression -> binary_expression LAND binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1691), - ('binary_expression -> binary_expression LOR binary_expression','binary_expression',3,'p_binary_expression','c_parser.py',1692), - ('cast_expression -> unary_expression','cast_expression',1,'p_cast_expression_1','c_parser.py',1700), - ('cast_expression -> LPAREN type_name RPAREN cast_expression','cast_expression',4,'p_cast_expression_2','c_parser.py',1704), - ('unary_expression -> postfix_expression','unary_expression',1,'p_unary_expression_1','c_parser.py',1708), - ('unary_expression -> PLUSPLUS unary_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1712), - ('unary_expression -> MINUSMINUS unary_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1713), - ('unary_expression -> unary_operator cast_expression','unary_expression',2,'p_unary_expression_2','c_parser.py',1714), - ('unary_expression -> SIZEOF unary_expression','unary_expression',2,'p_unary_expression_3','c_parser.py',1719), - ('unary_expression -> SIZEOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','c_parser.py',1720), - ('unary_expression -> _ALIGNOF LPAREN type_name RPAREN','unary_expression',4,'p_unary_expression_3','c_parser.py',1721), - ('unary_operator -> AND','unary_operator',1,'p_unary_operator','c_parser.py',1729), - ('unary_operator -> TIMES','unary_operator',1,'p_unary_operator','c_parser.py',1730), - ('unary_operator -> PLUS','unary_operator',1,'p_unary_operator','c_parser.py',1731), - ('unary_operator -> MINUS','unary_operator',1,'p_unary_operator','c_parser.py',1732), - ('unary_operator -> NOT','unary_operator',1,'p_unary_operator','c_parser.py',1733), - ('unary_operator -> LNOT','unary_operator',1,'p_unary_operator','c_parser.py',1734), - ('postfix_expression -> primary_expression','postfix_expression',1,'p_postfix_expression_1','c_parser.py',1739), - ('postfix_expression -> postfix_expression LBRACKET expression RBRACKET','postfix_expression',4,'p_postfix_expression_2','c_parser.py',1743), - ('postfix_expression -> postfix_expression LPAREN argument_expression_list RPAREN','postfix_expression',4,'p_postfix_expression_3','c_parser.py',1747), - ('postfix_expression -> postfix_expression LPAREN RPAREN','postfix_expression',3,'p_postfix_expression_3','c_parser.py',1748), - ('postfix_expression -> postfix_expression PERIOD ID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1753), - ('postfix_expression -> postfix_expression PERIOD TYPEID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1754), - ('postfix_expression -> postfix_expression ARROW ID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1755), - ('postfix_expression -> postfix_expression ARROW TYPEID','postfix_expression',3,'p_postfix_expression_4','c_parser.py',1756), - ('postfix_expression -> postfix_expression PLUSPLUS','postfix_expression',2,'p_postfix_expression_5','c_parser.py',1762), - ('postfix_expression -> postfix_expression MINUSMINUS','postfix_expression',2,'p_postfix_expression_5','c_parser.py',1763), - ('postfix_expression -> LPAREN type_name RPAREN brace_open initializer_list brace_close','postfix_expression',6,'p_postfix_expression_6','c_parser.py',1768), - ('postfix_expression -> LPAREN type_name RPAREN brace_open initializer_list COMMA brace_close','postfix_expression',7,'p_postfix_expression_6','c_parser.py',1769), - ('primary_expression -> identifier','primary_expression',1,'p_primary_expression_1','c_parser.py',1774), - ('primary_expression -> constant','primary_expression',1,'p_primary_expression_2','c_parser.py',1778), - ('primary_expression -> unified_string_literal','primary_expression',1,'p_primary_expression_3','c_parser.py',1782), - ('primary_expression -> unified_wstring_literal','primary_expression',1,'p_primary_expression_3','c_parser.py',1783), - ('primary_expression -> LPAREN expression RPAREN','primary_expression',3,'p_primary_expression_4','c_parser.py',1788), - ('primary_expression -> OFFSETOF LPAREN type_name COMMA offsetof_member_designator RPAREN','primary_expression',6,'p_primary_expression_5','c_parser.py',1792), - ('offsetof_member_designator -> identifier','offsetof_member_designator',1,'p_offsetof_member_designator','c_parser.py',1800), - ('offsetof_member_designator -> offsetof_member_designator PERIOD identifier','offsetof_member_designator',3,'p_offsetof_member_designator','c_parser.py',1801), - ('offsetof_member_designator -> offsetof_member_designator LBRACKET expression RBRACKET','offsetof_member_designator',4,'p_offsetof_member_designator','c_parser.py',1802), - ('argument_expression_list -> assignment_expression','argument_expression_list',1,'p_argument_expression_list','c_parser.py',1814), - ('argument_expression_list -> argument_expression_list COMMA assignment_expression','argument_expression_list',3,'p_argument_expression_list','c_parser.py',1815), - ('identifier -> ID','identifier',1,'p_identifier','c_parser.py',1824), - ('constant -> INT_CONST_DEC','constant',1,'p_constant_1','c_parser.py',1828), - ('constant -> INT_CONST_OCT','constant',1,'p_constant_1','c_parser.py',1829), - ('constant -> INT_CONST_HEX','constant',1,'p_constant_1','c_parser.py',1830), - ('constant -> INT_CONST_BIN','constant',1,'p_constant_1','c_parser.py',1831), - ('constant -> INT_CONST_CHAR','constant',1,'p_constant_1','c_parser.py',1832), - ('constant -> FLOAT_CONST','constant',1,'p_constant_2','c_parser.py',1851), - ('constant -> HEX_FLOAT_CONST','constant',1,'p_constant_2','c_parser.py',1852), - ('constant -> CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1868), - ('constant -> WCHAR_CONST','constant',1,'p_constant_3','c_parser.py',1869), - ('constant -> U8CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1870), - ('constant -> U16CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1871), - ('constant -> U32CHAR_CONST','constant',1,'p_constant_3','c_parser.py',1872), - ('unified_string_literal -> STRING_LITERAL','unified_string_literal',1,'p_unified_string_literal','c_parser.py',1883), - ('unified_string_literal -> unified_string_literal STRING_LITERAL','unified_string_literal',2,'p_unified_string_literal','c_parser.py',1884), - ('unified_wstring_literal -> WSTRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1894), - ('unified_wstring_literal -> U8STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1895), - ('unified_wstring_literal -> U16STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1896), - ('unified_wstring_literal -> U32STRING_LITERAL','unified_wstring_literal',1,'p_unified_wstring_literal','c_parser.py',1897), - ('unified_wstring_literal -> unified_wstring_literal WSTRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1898), - ('unified_wstring_literal -> unified_wstring_literal U8STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1899), - ('unified_wstring_literal -> unified_wstring_literal U16STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1900), - ('unified_wstring_literal -> unified_wstring_literal U32STRING_LITERAL','unified_wstring_literal',2,'p_unified_wstring_literal','c_parser.py',1901), - ('brace_open -> LBRACE','brace_open',1,'p_brace_open','c_parser.py',1911), - ('brace_close -> RBRACE','brace_close',1,'p_brace_close','c_parser.py',1917), - ('empty -> ','empty',0,'p_empty','c_parser.py',1923), -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD index 27be1aa46..1ca5b0b1b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD +++ b/dependencies/windows_amd64/python/Lib/site-packages/pyserial-3.5.dist-info/RECORD @@ -1,5 +1,5 @@ -../../Scripts/pyserial-miniterm.exe,sha256=Hm7AFKdXFsRt4Ksru9UPZjaunm6n2W7yJzTwZq864ZM,108450 -../../Scripts/pyserial-ports.exe,sha256=qXJH7bWCjQBzU2d4BO2gHx8yomLDzuerCJv87HByi4s,108452 +../../Scripts/pyserial-miniterm.exe,sha256=lp0hG-R4CARKxLjCv8ROyiRVFGki5GIO2asf1uFibP8,108450 +../../Scripts/pyserial-ports.exe,sha256=GJdTtNy4FdSHMttlSZGQABE6W8vzf4Roho8UtgS_3I4,108452 pyserial-3.5.dist-info/DESCRIPTION.rst,sha256=rXXIUFeAsfXq2YS7DGkztGmXez-G7gAwbwdBL8t9KME,320 pyserial-3.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 pyserial-3.5.dist-info/METADATA,sha256=QqirfpTvC3uqfpTNrGXWuSVMYIR29jASDJkAB79HKUM,1650 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE deleted file mode 100644 index 946608575..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/LICENSE +++ /dev/null @@ -1,2 +0,0 @@ -Released to the public domain. Original implementation can be found at -http://en.wikiversity.org/wiki/Reed%E2%80%93Solomon_codes_for_coders \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA deleted file mode 100644 index 400b93f52..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/METADATA +++ /dev/null @@ -1,355 +0,0 @@ -Metadata-Version: 2.1 -Name: reedsolo -Version: 1.5.4 -Summary: Pure-Python Reed Solomon encoder/decoder -Home-page: https://github.com/tomerfiliba/reedsolomon -Author: Tomer Filiba -Author-email: tomerfiliba@gmail.com -Maintainer: Stephen Karl Larroque -Maintainer-email: lrq3000@gmail.com -License: Public Domain -Platform: any -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: Intended Audience :: Information Technology -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: Microsoft :: Windows -Classifier: Operating System :: MacOS :: MacOS X -Classifier: Operating System :: POSIX :: Linux -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Programming Language :: Cython -Classifier: Topic :: Communications -Classifier: Topic :: Scientific/Engineering :: Mathematics -Classifier: Topic :: System :: Archiving :: Backup -Classifier: Topic :: System :: Recovery Tools -Description-Content-Type: text/x-rst -License-File: LICENSE - -Reed Solomon -============ - -|PyPI-Status| |PyPI-Versions| |PyPI-Downloads| - -|Build-Status| |Coverage| - -A pure-python `universal errors-and-erasures Reed-Solomon Codec `_ -, based on the wonderful tutorial at `wikiversity `_, -written by "Bobmath" and "LRQ3000". - ------------------------------------- - -.. contents:: Table of contents - :backlinks: top - :local: - - -Installation ------------- - -.. code:: sh - - pip install --upgrade reedsolo - -.. note:: - - When installing from source using ``python setup.py install``, the setup.py will try to build the Cython optimized module ``creedsolo.pyx`` if Cython is installed. You can override this behavior by typing: ``python setup.py install --nocython``. - - A pre-transpiled ``creedsolo.c`` is also available, and can be compiled without Cython by typing: ``python setup.py install --native-compile``. - - The package on ``pip`` includes a pre-compiled ``creedsolo.pyd`` module for Windows 10 x64. - -Usage ------ - -Basic usage with high-level RSCodec class -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. code:: python - - # Initialization - >>> from reedsolo import RSCodec - >>> rsc = RSCodec(10) # 10 ecc symbols - - # Encoding - >>> rsc.encode([1,2,3,4]) - b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M' - >>> rsc.encode(bytearray([1,2,3,4])) - bytearray(b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M') - >>> rsc.encode(b'hello world') - b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa' - # Note that chunking is supported transparently to encode any string length. - - # Decoding (repairing) - >>> rsc.decode(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')[0] - b'hello world' - >>> rsc.decode(b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 3 errors - b'hello world' - >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 5 errors - b'hello world' - >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdXX\xf3\xa8\xaa')[0] # 6 errors - fail - Traceback (most recent call last): - ... - reedsolo.ReedSolomonError: Too many (or few) errors found by Chien Search for the errata locator polynomial! - -**Important upgrade notice for pre-1.0 users:** Note that ``RSCodec.decode()`` returns 3 variables: - - 1. the decoded (corrected) message - 2. the decoded message and error correction code (which is itself also corrected) - 3. and the list of positions of the errata (errors and erasures) - -Here is an example: - -.. code:: python - - >>> tampered_msg = b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa' - >>> decoded_msg, decoded_msgecc, errata_pos = rsc.decode(tampered_msg) - >>> print(decoded_msg) # decoded/corrected message - bytearray(b'hello world') - >>> print(decoded_msgecc) # decoded/corrected message and ecc symbols - bytearray(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa') - >>> print(errata_pos) # errata_pos is returned as a bytearray, hardly intelligible - bytearray(b'\x10\t\x02') - >>> print(list(errata_pos)) # convert to a list to get the errata positions as integer indices - [16, 9, 2] - -Since we failed to decode with 6 errors with a codec set to 10 error correction code (ecc) symbols, let's try to use a bigger codec, with 12 ecc symbols. - -.. code:: python - - >>> rsc = RSCodec(12) # using 2 more ecc symbols (to correct max 6 errors or 12 erasures) - >>> rsc.encode(b'hello world') - b'hello world?Ay\xb2\xbc\xdc\x01q\xb9\xe3\xe2=' - >>> rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')[0] # 6 errors - ok, but any more would fail - b'hello world' - >>> rsc.decode(b'helXXXXXXXXXXy\xb2XX\x01q\xb9\xe3\xe2=', erase_pos=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16])[0] # 12 erasures - OK - b'hello world' - -This shows that we can decode twice as many erasures (where we provide the location of errors ourselves) than errors (with unknown locations). This is the cost of error correction compared to erasure correction. - -To get the maximum number of errors OR erasures that can be independently corrected (ie, not simultaneously): - -.. code:: python - - >>> maxerrors, maxerasures = rsc.maxerrata(verbose=True) - This codec can correct up to 6 errors and 12 erasures independently - >>> print(maxerrors, maxerasures) - 6 12 - -To get the maximum number of errors AND erasures that can be simultaneously corrected, you need to specify the number of errors or erasures you expect: - -.. code:: python - - >>> maxerrors, maxerasures = rsc.maxerrata(erasures=6, verbose=True) # we know the number of erasures, will calculate how many errors we can afford - This codec can correct up to 3 errors and 6 erasures simultaneously - >>> print(maxerrors, maxerasures) - 3 6 - >>> maxerrors, maxerasures = rsc.maxerrata(errors=5, verbose=True) # we know the number of errors, will calculate how many erasures we can afford - This codec can correct up to 5 errors and 2 erasures simultaneously - >>> print(maxerrors, maxerasures) - 5 2 - -Note that if a chunk has more errors and erasures than the Singleton Bound as calculated by the ``maxerrata()`` method, the codec will try to raise a ``ReedSolomonError`` exception, -but may very well not detect any error either (this is a theoretical limitation of error correction codes). In other words, error correction codes are unreliable to detect if a chunk of a message -is corrupted beyond the Singleton Bound. If you want more reliability in errata detection, use a checksum or hash such as SHA or MD5 on your message, these are much more reliable and have no bounds -on the number of errata (the only potential issue is with collision but the probability is very very low). - -To check if a message is tampered given its error correction symbols, without decoding, use the ``check()`` method: - -.. code:: python - - # Checking - >> rsc.check(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') # Tampered message will return False - [False] - >> rmes, rmesecc, errata_pos = rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') - >> rsc.check(rmesecc) # Corrected or untampered message will return True - [True] - >> print('Number of detected errors and erasures: %i, their positions: %s' % (len(errata_pos), list(errata_pos))) - Number of detected errors and erasures: 6, their positions: [16, 15, 12, 11, 10, 9] - -By default, most Reed-Solomon codecs are limited to characters that can be encoded in 256 bits and with a length of maximum 256 characters. But this codec is universal, you can reduce or increase the length and maximum character value by increasing the Galois Field: - -.. code:: python - - # To use longer chunks or bigger values than 255 (may be very slow) - >> rsc = RSCodec(12, nsize=4095) # always use a power of 2 minus 1 - >> rsc = RSCodec(12, c_exp=12) # alternative way to set nsize=4095 - >> mes = 'a' * (4095-12) - >> mesecc = rsc.encode(mes) - >> mesecc[2] = 1 - >> mesecc[-1] = 1 - >> rmes, rmesecc, errata_pos = rsc.decode(mesecc) - >> rsc.check(mesecc) - [False] - >> rsc.check(rmesecc) - [True] - -Note that the ``RSCodec`` class supports transparent chunking, so you don't need to increase the Galois Field to support longer messages, but characters will still be limited to 256 bits (or -whatever field you set with ``c_exp``). - -Low-level usage via direct access to math functions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want full control, you can skip the API and directly use the library as-is. Here's how: - -First you need to init the precomputed tables: - -.. code:: python - - >> import reedsolo as rs - >> rs.init_tables(0x11d) - -Pro tip: if you get the error: ValueError: byte must be in range(0, 256), please check that your prime polynomial is correct for your field. -Pro tip2: by default, you can only encode messages of max length and max symbol value = 256. If you want to encode bigger messages, -please use the following (where c_exp is the exponent of your Galois Field, eg, 12 = max length 2^12 = 4096): - -.. code:: python - - >> prim = rs.find_prime_polys(c_exp=12, fast_primes=True, single=True) - >> rs.init_tables(c_exp=12, prim=prim) - -Let's define our RS message and ecc size: - -.. code:: python - - >> n = 255 # length of total message+ecc - >> nsym = 12 # length of ecc - >> mes = "a" * (n-nsym) # generate a sample message - -To optimize, you can precompute the generator polynomial: - -.. code:: python - - >> gen = rs.rs_generator_poly_all(n) - -Then to encode: - -.. code:: python - - >> mesecc = rs.rs_encode_msg(mes, nsym, gen=gen[nsym]) - -Let's tamper our message: - -.. code:: python - - >> mesecc[1] = 0 - -To decode: - -.. code:: python - - >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym, erase_pos=erase_pos) - -Note that both the message and the ecc are corrected (if possible of course). -Pro tip: if you know a few erasures positions, you can specify them in a list ``erase_pos`` to double the repair power. But you can also just specify an empty list. - -You can check how many errors and/or erasures were corrected, which can be useful to design adaptive bitrate algorithms: - -.. code:: python - - >> print('A total of %i errata were corrected over all chunks of this message.' % len(errata_pos)) - -If the decoding fails, it will normally automatically check and raise a ReedSolomonError exception that you can handle. -However if you want to manually check if the repaired message is correct, you can do so: - -.. code:: python - - >> rs.rs_check(rmes + recc, nsym) - -Note: if you want to use multiple reedsolomon with different parameters, you need to backup the globals and restore them before calling reedsolo functions: - -.. code:: python - - >> rs.init_tables() - >> global gf_log, gf_exp, field_charac - >> bak_gf_log, bak_gf_exp, bak_field_charac = gf_log, gf_exp, field_charac - - -Then at anytime, you can do: - -.. code:: python - - >> global gf_log, gf_exp, field_charac - >> gf_log, gf_exp, field_charac = bak_gf_log, bak_gf_exp, bak_field_charac - >> mesecc = rs.rs_encode_msg(mes, nsym) - >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym) - -The globals backup is not necessary if you use RSCodec, it will be automatically managed. - -Read the sourcecode's comments for more info about how it works, and for the various parameters you can setup if -you need to interface with other RS codecs. - -Extended description --------------------- -The code of wikiversity is here consolidated into a nice API with exceptions handling. -The algorithm can correct up to 2*e+v <= nsym, where e is the number of errors, -v the number of erasures and nsym = n-k = the number of ECC (error correction code) symbols. -This means that you can either correct exactly floor(nsym/2) errors, or nsym erasures -(errors where you know the position), and a combination of both errors and erasures. -This is called the Singleton Bound, and is the maximum/optimal theoretical number -of erasures and errors any error correction algorithm can correct (although there -are experimental approaches to go a bit further, named list decoding, not implemented -here, but feel free to do pull request!). -The code should work on pretty much any reasonable version of python (2.4-3.7), -but I'm only testing on 2.7 and 3.7. Python 3.8 should work except for Cython which is -currently incompatible with this version. - -The codec has quite reasonable performances if you either use PyPy on the pure-python -implementation (reedsolo.py) or either if you compile the Cython extension creedsolo.pyx -(which is about 2x faster than PyPy). You can expect encoding rates of several MB/s. - -This library is also thoroughly unit tested so that nearly any encoding/decoding case should be covered. - -The codec is universal, meaning that it can decode any message encoded by another RS encoder -as long as you provide the correct parameters. -Note however that if you use higher fields (ie, bigger c_exp), the algorithms will be slower, first because -we cannot then use the optimized bytearray() structure but only array.array('i', ...), and also because -Reed-Solomon's complexity is quadratic (both in encoding and decoding), so this means that the longer -your messages, the longer it will take to encode/decode (quadratically!). - -The algorithm itself can handle messages of a length up to (2^c_exp)-1 symbols per message (or chunk), including the ECC symbols, -and each symbol can have a value of up to (2^c_exp)-1 (indeed, both the message length and the maximum -value for one character is constrained by the same mathematical reason). By default, we use the field GF(2^8), -which means that you are limited to values between 0 and 255 (perfect to represent a single hexadecimal -symbol on computers, so you can encode any binary stream) and limited to messages+ecc of maximum -length 255. However, you can "chunk" longer messages to fit them into the message length limit. -The ``RSCodec`` class will automatically apply chunking, by splitting longer messages into chunks and -encode/decode them separately; it shouldn't make a difference from an API perspective (ie, from your POV). - - -To use the Cython implementation, you need to ``pip install cython`` and a C++ compiler (Microsoft Visual C++ 14.0 for Windows and Python 3.7). Then you can simply cd to the root of the folder where creedsolo.pyx is, and type ``python setup.py build_ext --inplace``. Alternatively, you can generate just the C++ code by typing `cython -3 creedsolo.pyx`. When building a distributable egg or installing the module from source, the Cython module will be automatically transpiled and compiled if both Cython and a C compiler are installed. This behavior can be modified using the ``--nocython`` and ``--native-compile`` arguments for ``setup.py``. - -Authors -------- - -This module was conceived and developed by Tomer Filiba. - -It was further extended and is currently maintained by Stephen Karl Larroque. - -For a list of all contributors, see `this page `_. - -License -------- - -This software is released to the Public Domain. - -If the Public Domain is not adequate for your purpose, you can instead consider this module under the MIT License as you prefer. - - -.. |PyPI-Status| image:: https://img.shields.io/pypi/v/reedsolo.svg - :target: https://pypi.org/project/reedsolo -.. |PyPI-Versions| image:: https://img.shields.io/pypi/pyversions/reedsolo.svg?logo=python&logoColor=white - :target: https://pypi.org/project/reedsolo -.. |PyPI-Downloads| image:: https://img.shields.io/pypi/dm/reedsolo.svg?label=pypi%20downloads&logo=python&logoColor=white - :target: https://pypi.org/project/reedsolo -.. |Build-Status| image:: https://travis-ci.org/tomerfiliba/reedsolomon.svg?branch=master - :target: https://travis-ci.org/tomerfiliba/reedsolomon -.. |Coverage| image:: https://coveralls.io/repos/tomerfiliba/reedsolomon/badge.svg?branch=master&service=github - :target: https://coveralls.io/github/tomerfiliba/reedsolomon?branch=master diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD deleted file mode 100644 index d05831fc4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -__pycache__/reedsolo.cpython-310.pyc,, -reedsolo-1.5.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -reedsolo-1.5.4.dist-info/LICENSE,sha256=C6vXtXPvw9z1uzwotYMVByb3ddPdjVTWzQYoNHNbPXM,139 -reedsolo-1.5.4.dist-info/METADATA,sha256=jfDZlhjMM7sfawn78EpGyhp4eKpwWY_ZQzdMs-WGgiw,16817 -reedsolo-1.5.4.dist-info/RECORD,, -reedsolo-1.5.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 -reedsolo-1.5.4.dist-info/top_level.txt,sha256=QRYQ78lo_mIFvTV1fxOFpLZDd9xSvrR81HlgWAx7ZqU,9 -reedsolo.py,sha256=xxSRANmvY0jwOlkEiwlMX5EpJ3kl5msQBRPQ5mtYPJM,70730 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL deleted file mode 100644 index 57e3d840d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.38.4) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt deleted file mode 100644 index 200965002..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo-1.5.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -reedsolo diff --git a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py b/dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py deleted file mode 100644 index d98acfc1f..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/reedsolo.py +++ /dev/null @@ -1,974 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (c) 2012-2015 Tomer Filiba -# Copyright (c) 2015 rotorgit -# Copyright (c) 2015-2020 Stephen Larroque - -''' -Reed Solomon -============ - -A pure-python `universal errors-and-erasures Reed-Solomon Codec `_ -, based on the wonderful tutorial at -`wikiversity `_, -written by "Bobmath" and "LRQ3000". - -The code of wikiversity is here consolidated into a nice API with exceptions handling. -The algorithm can correct up to 2*e+v <= nsym, where e is the number of errors, -v the number of erasures and nsym = n-k = the number of ECC (error correction code) symbols. -This means that you can either correct exactly floor(nsym/2) errors, or nsym erasures -(errors where you know the position), and a combination of both errors and erasures. -The code should work on pretty much any reasonable version of python (2.4-3.5), -but I'm only testing on 2.7 - 3.4. - -.. note:: - The codec is universal, meaning that it can decode any message encoded by another RS encoder - as long as you provide the correct parameters. - Note however that if you use higher fields (ie, bigger c_exp), the algorithms will be slower, first because - we cannot then use the optimized bytearray() structure but only array.array('i', ...), and also because - Reed-Solomon's complexity is quadratic (both in encoding and decoding), so this means that the longer - your messages, the longer it will take to encode/decode (quadratically!). - - The algorithm itself can handle messages up to (2^c_exp)-1 symbols, including the ECC symbols, - and each symbol can have a value of up to (2^c_exp)-1 (indeed, both the message length and the maximum - value for one character is constrained by the same mathematical reason). By default, we use the field GF(2^8), - which means that you are limited to values between 0 and 255 (perfect to represent a single hexadecimal - symbol on computers, so you can encode any binary stream) and limited to messages+ecc of maximum - length 255. However, you can "chunk" longer messages to fit them into the message length limit. - The ``RSCodec`` class will automatically apply chunking, by splitting longer messages into chunks and - encode/decode them separately; it shouldn't make a difference from an API perspective (ie, from your POV). - -:: - - # Initialization - >>> from reedsolo import RSCodec - >>> rsc = RSCodec(10) # 10 ecc symbols - - # Encoding - >>> rsc.encode([1,2,3,4]) - b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M' - >>> rsc.encode(bytearray([1,2,3,4])) - bytearray(b'\x01\x02\x03\x04,\x9d\x1c+=\xf8h\xfa\x98M') - >>> rsc.encode(b'hello world') - b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa' - # Note that chunking is supported transparently to encode any string length. - - # Decoding (repairing) - >>> rsc.decode(b'hello world\xed%T\xc4\xfd\xfd\x89\xf3\xa8\xaa')[0] - b'hello world' - >>> rsc.decode(b'heXlo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 3 errors - b'hello world' - >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdX\x89\xf3\xa8\xaa')[0] # 5 errors - b'hello world' - >>> rsc.decode(b'hXXXo worXd\xed%T\xc4\xfdXX\xf3\xa8\xaa')[0] # 6 errors - fail - Traceback (most recent call last): - ... - ReedSolomonError: Could not locate error - - >>> rsc = RSCodec(12) # using 2 more ecc symbols (to correct max 6 errors or 12 erasures) - >>> rsc.encode(b'hello world') - b'hello world?Ay\xb2\xbc\xdc\x01q\xb9\xe3\xe2=' - >>> rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=')[0] # 6 errors - ok - b'hello world' - >>> rsc.decode(b'helXXXXXXXXXXy\xb2XX\x01q\xb9\xe3\xe2=', erase_pos=[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 16])[0] # 12 erasures - OK - b'hello world' - - # Checking - >> rsc.check(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') - [False] - >> rmes, rmesecc = rsc.decode(b'hello worXXXXy\xb2XX\x01q\xb9\xe3\xe2=') - >> rsc.check(rmesecc) - [True] - - # To use longer chunks or bigger values than 255 (may be very slow) - >> rsc = RSCodec(12, nsize=4095) # always use a power of 2 minus 1 - >> rsc = RSCodec(12, c_exp=12) # alternative way to set nsize=4095 - >> mes = 'a' * (4095-12) - >> mesecc = rsc.encode(mes) - >> mesecc[2] = 1 - >> mesecc[-1] = 1 - >> rmes, rmesecc = rsc.decode(mesecc) - >> rsc.check(mesecc) - [False] - >> rsc.check(rmesecc) - [True] - - If you want full control, you can skip the API and directly use the library as-is. Here's how: - - First you need to init the precomputed tables: - >> import reedsolo as rs - >> rs.init_tables(0x11d) - Pro tip: if you get the error: ValueError: byte must be in range(0, 256), please check that your prime polynomial is correct for your field. - Pro tip2: by default, you can only encode messages of max length and max symbol value = 256. If you want to encode bigger messages, - please use the following (where c_exp is the exponent of your Galois Field, eg, 12 = max length 2^12 = 4096): - >> prim = rs.find_prime_polys(c_exp=12, fast_primes=True, single=True) - >> rs.init_tables(c_exp=12, prim=prim) - - Let's define our RS message and ecc size: - >> n = 255 # length of total message+ecc - >> nsym = 12 # length of ecc - >> mes = "a" * (n-nsym) # generate a sample message - - To optimize, you can precompute the generator polynomial: - >> gen = rs.rs_generator_poly_all(n) - - Then to encode: - >> mesecc = rs.rs_encode_msg(mes, nsym, gen=gen[nsym]) - - Let's tamper our message: - >> mesecc[1] = 0 - - To decode: - >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym, erase_pos=erase_pos) - Note that both the message and the ecc are corrected (if possible of course). - Pro tip: if you know a few erasures positions, you can specify them in a list `erase_pos` to double the repair power. But you can also just specify an empty list. - - If the decoding fails, it will normally automatically check and raise a ReedSolomonError exception that you can handle. - However if you want to manually check if the repaired message is correct, you can do so: - >> rs.rs_check(rmes + recc, nsym) - - Note: if you want to use multiple reedsolomon with different parameters, you need to backup the globals and restore them before calling reedsolo functions: - >> rs.init_tables() - >> global gf_log, gf_exp, field_charac - >> bak_gf_log, bak_gf_exp, bak_field_charac = gf_log, gf_exp, field_charac - Then at anytime, you can do: - >> global gf_log, gf_exp, field_charac - >> gf_log, gf_exp, field_charac = bak_gf_log, bak_gf_exp, bak_field_charac - >> mesecc = rs.rs_encode_msg(mes, nsym) - >> rmes, recc, errata_pos = rs.rs_correct_msg(mesecc, nsym) - The globals backup is not necessary if you use RSCodec, it will be automatically managed. - - Read the sourcecode's comments for more info about how it works, and for the various parameters you can setup if - you need to interface with other RS codecs. - -''' - -# TODO IMPORTANT: try to keep the same convention for the ordering of polynomials inside lists throughout the code and functions (because for now there are a lot of list reversing in order to make it work, you never know the order of a polynomial, ie, if the first coefficient is the major degree or the constant term...). - -import itertools -import math - - -################### INIT and stuff ################### - -try: # pragma: no cover - bytearray - _bytearray = bytearray -except NameError: # pragma: no cover - from array import array - def _bytearray(obj = 0, encoding = "latin-1"): # pragma: no cover - '''Simple bytearray replacement''' - # always use Latin-1 and not UTF8 because Latin-1 maps the first 256 characters to their bytevalue equivalents. UTF8 may mangle your data (particularly at vale 128) - if isinstance(obj, str): - obj = [ord(ch) for ch in obj.encode(encoding)] - elif isinstance(obj, int): - obj = [0] * obj - return array("B", obj) - -try: # pragma: no cover - xrange -except NameError: # pragma: no cover - # compatibility with Python 3+ - xrange = range - -class ReedSolomonError(Exception): - pass - -gf_exp = _bytearray([1] * 512) # For efficiency, gf_exp[] has size 2*GF_SIZE, so that a simple multiplication of two numbers can be resolved without calling % 255. For more infos on how to generate this extended exponentiation table, see paper: "Fast software implementation of finite field operations", Cheng Huang and Lihao Xu, Washington University in St. Louis, Tech. Rep (2003). -gf_log = _bytearray(256) -field_charac = int(2**8 - 1) - -################### GALOIS FIELD ELEMENTS MATHS ################### - -def rwh_primes1(n): - # http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188 - ''' Returns a list of primes < n ''' - sieve = [True] * int(n/2) - for i in xrange(3,int(n**0.5)+1,2): - if sieve[int(i/2)]: - sieve[int((i*i)/2)::i] = [False] * int((n-i*i-1)/(2*i)+1) - return [2] + [2*i+1 for i in xrange(1,int(n/2)) if sieve[i]] - -def find_prime_polys(generator=2, c_exp=8, fast_primes=False, single=False): - '''Compute the list of prime polynomials for the given generator and galois field characteristic exponent.''' - # fast_primes will output less results but will be significantly faster. - # single will output the first prime polynomial found, so if all you want is to just find one prime polynomial to generate the LUT for Reed-Solomon to work, then just use that. - - # A prime polynomial (necessarily irreducible) is necessary to reduce the multiplications in the Galois Field, so as to avoid overflows. - # Why do we need a "prime polynomial"? Can't we just reduce modulo 255 (for GF(2^8) for example)? Because we need the values to be unique. - # For example: if the generator (alpha) = 2 and c_exp = 8 (GF(2^8) == GF(256)), then the generated Galois Field (0, 1, α, α^1, α^2, ..., α^(p-1)) will be galois field it becomes 0, 1, 2, 4, 8, 16, etc. However, upon reaching 128, the next value will be doubled (ie, next power of 2), which will give 256. Then we must reduce, because we have overflowed above the maximum value of 255. But, if we modulo 255, this will generate 256 == 1. Then 2, 4, 8, 16, etc. giving us a repeating pattern of numbers. This is very bad, as it's then not anymore a bijection (ie, a non-zero value doesn't have a unique index). That's why we can't just modulo 255, but we need another number above 255, which is called the prime polynomial. - # Why so much hassle? Because we are using precomputed look-up tables for multiplication: instead of multiplying a*b, we precompute alpha^a, alpha^b and alpha^(a+b), so that we can just use our lookup table at alpha^(a+b) and get our result. But just like in our original field we had 0,1,2,...,p-1 distinct unique values, in our "LUT" field using alpha we must have unique distinct values (we don't care that they are different from the original field as long as they are unique and distinct). That's why we need to avoid duplicated values, and to avoid duplicated values we need to use a prime irreducible polynomial. - - # Here is implemented a bruteforce approach to find all these prime polynomials, by generating every possible prime polynomials (ie, every integers between field_charac+1 and field_charac*2), and then we build the whole Galois Field, and we reject the candidate prime polynomial if it duplicates even one value or if it generates a value above field_charac (ie, cause an overflow). - # Note that this algorithm is slow if the field is too big (above 12), because it's an exhaustive search algorithm. There are probabilistic approaches, and almost surely prime approaches, but there is no determistic polynomial time algorithm to find irreducible monic polynomials. More info can be found at: http://people.mpi-inf.mpg.de/~csaha/lectures/lec9.pdf - # Another faster algorithm may be found at Adleman, Leonard M., and Hendrik W. Lenstra. "Finding irreducible polynomials over finite fields." Proceedings of the eighteenth annual ACM symposium on Theory of computing. ACM, 1986. - - # Prepare the finite field characteristic (2^p - 1), this also represent the maximum possible value in this field - root_charac = 2 # we're in GF(2) - field_charac = int(root_charac**c_exp - 1) - field_charac_next = int(root_charac**(c_exp+1) - 1) - - prim_candidates = [] - if fast_primes: - prim_candidates = rwh_primes1(field_charac_next) # generate maybe prime polynomials and check later if they really are irreducible - prim_candidates = [x for x in prim_candidates if x > field_charac] # filter out too small primes - else: - prim_candidates = xrange(field_charac+2, field_charac_next, root_charac) # try each possible prime polynomial, but skip even numbers (because divisible by 2 so necessarily not irreducible) - - # Start of the main loop - correct_primes = [] - for prim in prim_candidates: # try potential candidates primitive irreducible polys - seen = _bytearray(field_charac+1) # memory variable to indicate if a value was already generated in the field (value at index x is set to 1) or not (set to 0 by default) - conflict = False # flag to know if there was at least one conflict - - # Second loop, build the whole Galois Field - x = 1 - for i in xrange(field_charac): - # Compute the next value in the field (ie, the next power of alpha/generator) - x = gf_mult_noLUT(x, generator, prim, field_charac+1) - - # Rejection criterion: if the value overflowed (above field_charac) or is a duplicate of a previously generated power of alpha, then we reject this polynomial (not prime) - if x > field_charac or seen[x] == 1: - conflict = True - break - # Else we flag this value as seen (to maybe detect future duplicates), and we continue onto the next power of alpha - else: - seen[x] = 1 - - # End of the second loop: if there's no conflict (no overflow nor duplicated value), this is a prime polynomial! - if not conflict: - correct_primes.append(prim) - if single: return prim - - # Return the list of all prime polynomials - return correct_primes # you can use the following to print the hexadecimal representation of each prime polynomial: print [hex(i) for i in correct_primes] - -def init_tables(prim=0x11d, generator=2, c_exp=8): - '''Precompute the logarithm and anti-log tables for faster computation later, using the provided primitive polynomial. - These tables are used for multiplication/division since addition/substraction are simple XOR operations inside GF of characteristic 2. - The basic idea is quite simple: since b**(log_b(x), log_b(y)) == x * y given any number b (the base or generator of the logarithm), then we can use any number b to precompute logarithm and anti-log (exponentiation) tables to use for multiplying two numbers x and y. - That's why when we use a different base/generator number, the log and anti-log tables are drastically different, but the resulting computations are the same given any such tables. - For more infos, see https://en.wikipedia.org/wiki/Finite_field_arithmetic#Implementation_tricks - ''' - # generator is the generator number (the "increment" that will be used to walk through the field by multiplication, this must be a prime number). This is basically the base of the logarithm/anti-log tables. Also often noted "alpha" in academic books. - # prim is the primitive/prime (binary) polynomial and must be irreducible (ie, it can't represented as the product of two smaller polynomials). It's a polynomial in the binary sense: each bit is a coefficient, but in fact it's an integer between field_charac+1 and field_charac*2, and not a list of gf values. The prime polynomial will be used to reduce the overflows back into the range of the Galois Field without duplicating values (all values should be unique). See the function find_prime_polys() and: http://research.swtch.com/field and http://www.pclviewer.com/rs2/galois.html - # note that the choice of generator or prime polynomial doesn't matter very much: any two finite fields of size p^n have identical structure, even if they give the individual elements different names (ie, the coefficients of the codeword will be different, but the final result will be the same: you can always correct as many errors/erasures with any choice for those parameters). That's why it makes sense to refer to all the finite fields, and all decoders based on Reed-Solomon, of size p^n as one concept: GF(p^n). It can however impact sensibly the speed (because some parameters will generate sparser tables). - # c_exp is the exponent for the field's characteristic GF(2^c_exp) - - # Redefine _bytearray() in case we need to support integers or messages of length > 256 - global _bytearray - if c_exp <= 8: - _bytearray = bytearray - else: - from array import array - def _bytearray(obj = 0, encoding = "latin-1"): - '''Fake bytearray replacement, supporting int values above 255''' - # always use Latin-1 and not UTF8 because Latin-1 maps the first 256 characters to their bytevalue equivalents. UTF8 may mangle your data (particularly at vale 128) - if isinstance(obj, str): # obj is a string, convert to list of ints - obj = obj.encode(encoding) - if isinstance(obj, str): # Py2 str: convert to list of ascii ints - obj = [ord(chr) for chr in obj] - elif isinstance(obj, bytes): # Py3 bytes: characters are bytes, need to convert to int for array.array('i', obj) - obj = [int(chr) for chr in obj] - else: - raise(ValueError, "Type of object not recognized!") - elif isinstance(obj, int): # compatibility with list preallocation bytearray(int) - obj = [0] * obj - # Else obj is a list of int, it's ok - return array("i", obj) - - # Init global tables - global gf_exp, gf_log, field_charac - field_charac = int(2**c_exp - 1) - gf_exp = _bytearray(field_charac * 2) # anti-log (exponential) table. The first two elements will always be [GF256int(1), generator] - gf_log = _bytearray(field_charac+1) # log table, log[0] is impossible and thus unused - - # For each possible value in the galois field 2^8, we will pre-compute the logarithm and anti-logarithm (exponential) of this value - # To do that, we generate the Galois Field F(2^p) by building a list starting with the element 0 followed by the (p-1) successive powers of the generator α : 1, α, α^1, α^2, ..., α^(p-1). - x = 1 - for i in xrange(field_charac): # we could skip index 255 which is equal to index 0 because of modulo: g^255==g^0 but either way, this does not change the later outputs (ie, the ecc symbols will be the same either way) - gf_exp[i] = x # compute anti-log for this value and store it in a table - gf_log[x] = i # compute log at the same time - x = gf_mult_noLUT(x, generator, prim, field_charac+1) - - # If you use only generator==2 or a power of 2, you can use the following which is faster than gf_mult_noLUT(): - #x <<= 1 # multiply by 2 (change 1 by another number y to multiply by a power of 2^y) - #if x & 0x100: # similar to x >= 256, but a lot faster (because 0x100 == 256) - #x ^= prim # substract the primary polynomial to the current value (instead of 255, so that we get a unique set made of coprime numbers), this is the core of the tables generation - - # Optimization: double the size of the anti-log table so that we don't need to mod 255 to stay inside the bounds (because we will mainly use this table for the multiplication of two GF numbers, no more). - for i in xrange(field_charac, field_charac * 2): - gf_exp[i] = gf_exp[i - field_charac] - - return [gf_log, gf_exp, field_charac] - -def gf_add(x, y): - return x ^ y - -def gf_sub(x, y): - return x ^ y # in binary galois field, substraction is just the same as addition (since we mod 2) - -def gf_neg(x): - return x - -def gf_inverse(x): - return gf_exp[field_charac - gf_log[x]] # gf_inverse(x) == gf_div(1, x) - -def gf_mul(x, y): - if x == 0 or y == 0: - return 0 - return gf_exp[(gf_log[x] + gf_log[y]) % field_charac] - -def gf_div(x, y): - if y == 0: - raise ZeroDivisionError() - if x == 0: - return 0 - return gf_exp[(gf_log[x] + field_charac - gf_log[y]) % field_charac] - -def gf_pow(x, power): - return gf_exp[(gf_log[x] * power) % field_charac] - -def gf_mult_noLUT_slow(x, y, prim=0): - '''Multiplication in Galois Fields without using a precomputed look-up table (and thus it's slower) by using the standard carry-less multiplication + modular reduction using an irreducible prime polynomial.''' - - ### Define bitwise carry-less operations as inner functions ### - def cl_mult(x,y): - '''Bitwise carry-less multiplication on integers''' - z = 0 - i = 0 - while (y>>i) > 0: - if y & (1<> bits: bits += 1 - return bits - - def cl_div(dividend, divisor=None): - '''Bitwise carry-less long division on integers and returns the remainder''' - # Compute the position of the most significant bit for each integers - dl1 = bit_length(dividend) - dl2 = bit_length(divisor) - # If the dividend is smaller than the divisor, just exit - if dl1 < dl2: - return dividend - # Else, align the most significant 1 of the divisor to the most significant 1 of the dividend (by shifting the divisor) - for i in xrange(dl1-dl2,-1,-1): - # Check that the dividend is divisible (useless for the first iteration but important for the next ones) - if dividend & (1 << i+dl2-1): - # If divisible, then shift the divisor to align the most significant bits and XOR (carry-less substraction) - dividend ^= divisor << i - return dividend - - ### Main GF multiplication routine ### - - # Multiply the gf numbers - result = cl_mult(x,y) - # Then do a modular reduction (ie, remainder from the division) with an irreducible primitive polynomial so that it stays inside GF bounds - if prim > 0: - result = cl_div(result, prim) - - return result - -def gf_mult_noLUT(x, y, prim=0, field_charac_full=256, carryless=True): - '''Galois Field integer multiplication using Russian Peasant Multiplication algorithm (faster than the standard multiplication + modular reduction). - If prim is 0 and carryless=False, then the function produces the result for a standard integers multiplication (no carry-less arithmetics nor modular reduction).''' - r = 0 - while y: # while y is above 0 - if y & 1: r = r ^ x if carryless else r + x # y is odd, then add the corresponding x to r (the sum of all x's corresponding to odd y's will give the final product). Note that since we're in GF(2), the addition is in fact an XOR (very important because in GF(2) the multiplication and additions are carry-less, thus it changes the result!). - y = y >> 1 # equivalent to y // 2 - x = x << 1 # equivalent to x*2 - if prim > 0 and x & field_charac_full: x = x ^ prim # GF modulo: if x >= 256 then apply modular reduction using the primitive polynomial (we just substract, but since the primitive number can be above 256 then we directly XOR). - - return r - - -################### GALOIS FIELD POLYNOMIALS MATHS ################### - -def gf_poly_scale(p, x): - return _bytearray([gf_mul(p[i], x) for i in xrange(len(p))]) - -def gf_poly_add(p, q): - r = _bytearray( max(len(p), len(q)) ) - r[len(r)-len(p):len(r)] = p - #for i in xrange(len(p)): - #r[i + len(r) - len(p)] = p[i] - for i in xrange(len(q)): - r[i + len(r) - len(q)] ^= q[i] - return r - -def gf_poly_mul(p, q): - '''Multiply two polynomials, inside Galois Field (but the procedure is generic). Optimized function by precomputation of log.''' - # Pre-allocate the result array - r = _bytearray(len(p) + len(q) - 1) - # Precompute the logarithm of p - lp = [gf_log[p[i]] for i in xrange(len(p))] - # Compute the polynomial multiplication (just like the outer product of two vectors, we multiply each coefficients of p with all coefficients of q) - for j in xrange(len(q)): - qj = q[j] # optimization: load the coefficient once - if qj != 0: # log(0) is undefined, we need to check that - lq = gf_log[qj] # Optimization: precache the logarithm of the current coefficient of q - for i in xrange(len(p)): - if p[i] != 0: # log(0) is undefined, need to check that... - r[i + j] ^= gf_exp[lp[i] + lq] # equivalent to: r[i + j] = gf_add(r[i+j], gf_mul(p[i], q[j])) - return r - -def gf_poly_mul_simple(p, q): # simple equivalent way of multiplying two polynomials without precomputation, but thus it's slower - '''Multiply two polynomials, inside Galois Field''' - # Pre-allocate the result array - r = _bytearray(len(p) + len(q) - 1) - # Compute the polynomial multiplication (just like the outer product of two vectors, we multiply each coefficients of p with all coefficients of q) - for j in xrange(len(q)): - for i in xrange(len(p)): - r[i + j] ^= gf_mul(p[i], q[j]) # equivalent to: r[i + j] = gf_add(r[i+j], gf_mul(p[i], q[j])) -- you can see it's your usual polynomial multiplication - return r - -def gf_poly_neg(poly): - '''Returns the polynomial with all coefficients negated. In GF(2^p), negation does not change the coefficient, so we return the polynomial as-is.''' - return poly - -def gf_poly_div(dividend, divisor): - '''Fast polynomial division by using Extended Synthetic Division and optimized for GF(2^p) computations (doesn't work with standard polynomials outside of this galois field).''' - # CAUTION: this function expects polynomials to follow the opposite convention at decoding: the terms must go from the biggest to lowest degree (while most other functions here expect a list from lowest to biggest degree). eg: 1 + 2x + 5x^2 = [5, 2, 1], NOT [1, 2, 5] - - msg_out = _bytearray(dividend) # Copy the dividend list and pad with 0 where the ecc bytes will be computed - #normalizer = divisor[0] # precomputing for performance - for i in xrange(len(dividend) - (len(divisor)-1)): - #msg_out[i] /= normalizer # for general polynomial division (when polynomials are non-monic), the usual way of using synthetic division is to divide the divisor g(x) with its leading coefficient (call it a). In this implementation, this means:we need to compute: coef = msg_out[i] / gen[0]. For more infos, see http://en.wikipedia.org/wiki/Synthetic_division - coef = msg_out[i] # precaching - if coef != 0: # log(0) is undefined, so we need to avoid that case explicitly (and it's also a good optimization). In fact if you remove it, it should still work because gf_mul() will take care of the condition. But it's still a good practice to put the condition here. - for j in xrange(1, len(divisor)): # in synthetic division, we always skip the first coefficient of the divisior, because it's only used to normalize the dividend coefficient - if divisor[j] != 0: # log(0) is undefined - msg_out[i + j] ^= gf_mul(divisor[j], coef) # equivalent to the more mathematically correct (but xoring directly is faster): msg_out[i + j] += -divisor[j] * coef - - # The resulting msg_out contains both the quotient and the remainder, the remainder being the size of the divisor (the remainder has necessarily the same degree as the divisor -- not length but degree == length-1 -- since it's what we couldn't divide from the dividend), so we compute the index where this separation is, and return the quotient and remainder. - separator = -(len(divisor)-1) - return msg_out[:separator], msg_out[separator:] # return quotient, remainder. - -def gf_poly_square(poly): # pragma: no cover - '''Linear time implementation of polynomial squaring. For details, see paper: "A fast software implementation for arithmetic operations in GF (2n)". De Win, E., Bosselaers, A., Vandenberghe, S., De Gersem, P., & Vandewalle, J. (1996, January). In Advances in Cryptology - Asiacrypt'96 (pp. 65-76). Springer Berlin Heidelberg.''' - length = len(poly) - out = _bytearray(2*length - 1) - for i in xrange(length-1): - p = poly[i] - k = 2*i - if p != 0: - #out[k] = gf_exp[(2*gf_log[p]) % field_charac] # not necessary to modulo (2^r)-1 since gf_exp is duplicated up to 510. - out[k] = gf_exp[2*gf_log[p]] - #else: # not necessary since the output is already initialized to an array of 0 - #out[k] = 0 - out[2*length-2] = gf_exp[2*gf_log[poly[length-1]]] - if out[0] == 0: out[0] = 2*poly[1] - 1 - return out - -def gf_poly_eval(poly, x): - '''Evaluates a polynomial in GF(2^p) given the value for x. This is based on Horner's scheme for maximum efficiency.''' - y = poly[0] - for i in xrange(1, len(poly)): - y = gf_mul(y, x) ^ poly[i] - return y - - -################### REED-SOLOMON ENCODING ################### - -def rs_generator_poly(nsym, fcr=0, generator=2): - '''Generate an irreducible generator polynomial (necessary to encode a message into Reed-Solomon)''' - g = _bytearray([1]) - for i in xrange(nsym): - g = gf_poly_mul(g, [1, gf_pow(generator, i+fcr)]) - return g - -def rs_generator_poly_all(max_nsym, fcr=0, generator=2): - '''Generate all irreducible generator polynomials up to max_nsym (usually you can use n, the length of the message+ecc). Very useful to reduce processing time if you want to encode using variable schemes and nsym rates.''' - g_all = {} - g_all[0] = g_all[1] = _bytearray([1]) - for nsym in xrange(max_nsym): - g_all[nsym] = rs_generator_poly(nsym, fcr, generator) - return g_all - -def rs_simple_encode_msg(msg_in, nsym, fcr=0, generator=2, gen=None): - '''Simple Reed-Solomon encoding (mainly an example for you to understand how it works, because it's slower than the inlined function below)''' - global field_charac - if (len(msg_in) + nsym) > field_charac: raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in)+nsym, field_charac)) - if gen is None: gen = rs_generator_poly(nsym, fcr, generator) - - # Pad the message, then divide it by the irreducible generator polynomial - _, remainder = gf_poly_div(msg_in + _bytearray(len(gen)-1), gen) - # The remainder is our RS code! Just append it to our original message to get our full codeword (this represents a polynomial of max 256 terms) - msg_out = msg_in + remainder - # Return the codeword - return msg_out - -def rs_encode_msg(msg_in, nsym, fcr=0, generator=2, gen=None): - '''Reed-Solomon main encoding function, using polynomial division (Extended Synthetic Division, the fastest algorithm available to my knowledge), better explained at http://research.swtch.com/field''' - global field_charac - if (len(msg_in) + nsym) > field_charac: raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in)+nsym, field_charac)) - if gen is None: gen = rs_generator_poly(nsym, fcr, generator) - - msg_in = _bytearray(msg_in) - msg_out = _bytearray(msg_in) + _bytearray(len(gen)-1) # init msg_out with the values inside msg_in and pad with len(gen)-1 bytes (which is the number of ecc symbols). - - # Precompute the logarithm of every items in the generator - lgen = _bytearray([gf_log[gen[j]] for j in xrange(len(gen))]) - - # Extended synthetic division main loop - # Fastest implementation with PyPy (but the Cython version in creedsolo.pyx is about 2x faster) - for i in xrange(len(msg_in)): - coef = msg_out[i] # Note that it's msg_out here, not msg_in. Thus, we reuse the updated value at each iteration (this is how Synthetic Division works: instead of storing in a temporary register the intermediate values, we directly commit them to the output). - # coef = gf_mul(msg_out[i], gf_inverse(gen[0])) # for general polynomial division (when polynomials are non-monic), the usual way of using synthetic division is to divide the divisor g(x) with its leading coefficient (call it a). In this implementation, this means:we need to compute: coef = msg_out[i] / gen[0] - if coef != 0: # log(0) is undefined, so we need to manually check for this case. There's no need to check the divisor here because we know it can't be 0 since we generated it. - lcoef = gf_log[coef] # precaching - - for j in xrange(1, len(gen)): # in synthetic division, we always skip the first coefficient of the divisior, because it's only used to normalize the dividend coefficient (which is here useless since the divisor, the generator polynomial, is always monic) - #if gen[j] != 0: # log(0) is undefined so we need to check that, but it slow things down in fact and it's useless in our case (reed-solomon encoding) since we know that all coefficients in the generator are not 0 - msg_out[i + j] ^= gf_exp[lcoef + lgen[j]] # optimization, equivalent to gf_mul(gen[j], msg_out[i]) and we just substract it to msg_out[i+j] (but since we are in GF256, it's equivalent to an addition and to an XOR). In other words, this is simply a "multiply-accumulate operation" - - # Recopy the original message bytes (overwrites the part where the quotient was computed) - msg_out[:len(msg_in)] = msg_in # equivalent to c = mprime - b, where mprime is msg_in padded with [0]*nsym - return msg_out - - -################### REED-SOLOMON DECODING ################### - -def rs_calc_syndromes(msg, nsym, fcr=0, generator=2): - '''Given the received codeword msg and the number of error correcting symbols (nsym), computes the syndromes polynomial. - Mathematically, it's essentially equivalent to a Fourrier Transform (Chien search being the inverse). - ''' - # Note the "[0] +" : we add a 0 coefficient for the lowest degree (the constant). This effectively shifts the syndrome, and will shift every computations depending on the syndromes (such as the errors locator polynomial, errors evaluator polynomial, etc. but not the errors positions). - # This is not necessary as anyway syndromes are defined such as there are only non-zero coefficients (the only 0 is the shift of the constant here) and subsequent computations will/must account for the shift by skipping the first iteration (eg, the often seen range(1, n-k+1)), but you can also avoid prepending the 0 coeff and adapt every subsequent computations to start from 0 instead of 1. - return [0] + [gf_poly_eval(msg, gf_pow(generator, i+fcr)) for i in xrange(nsym)] - -def rs_correct_errata(msg_in, synd, err_pos, fcr=0, generator=2): # err_pos is a list of the positions of the errors/erasures/errata - '''Forney algorithm, computes the values (error magnitude) to correct the input message.''' - global field_charac - msg = _bytearray(msg_in) - # calculate errata locator polynomial to correct both errors and erasures (by combining the errors positions given by the error locator polynomial found by BM with the erasures positions given by caller) - coef_pos = [len(msg) - 1 - p for p in err_pos] # need to convert the positions to coefficients degrees for the errata locator algo to work (eg: instead of [0, 1, 2] it will become [len(msg)-1, len(msg)-2, len(msg) -3]) - err_loc = rs_find_errata_locator(coef_pos, generator) - # calculate errata evaluator polynomial (often called Omega or Gamma in academic papers) - err_eval = rs_find_error_evaluator(synd[::-1], err_loc, len(err_loc)-1)[::-1] - - # Second part of Chien search to get the error location polynomial X from the error positions in err_pos (the roots of the error locator polynomial, ie, where it evaluates to 0) - X = [] # will store the position of the errors - for i in xrange(len(coef_pos)): - l = field_charac - coef_pos[i] - X.append( gf_pow(generator, -l) ) - - # Forney algorithm: compute the magnitudes - E = _bytearray(len(msg)) # will store the values that need to be corrected (substracted) to the message containing errors. This is sometimes called the error magnitude polynomial. - Xlength = len(X) - for i, Xi in enumerate(X): - - Xi_inv = gf_inverse(Xi) - - # Compute the formal derivative of the error locator polynomial (see Blahut, Algebraic codes for data transmission, pp 196-197). - # the formal derivative of the errata locator is used as the denominator of the Forney Algorithm, which simply says that the ith error value is given by error_evaluator(gf_inverse(Xi)) / error_locator_derivative(gf_inverse(Xi)). See Blahut, Algebraic codes for data transmission, pp 196-197. - err_loc_prime_tmp = [] - for j in xrange(Xlength): - if j != i: - err_loc_prime_tmp.append( gf_sub(1, gf_mul(Xi_inv, X[j])) ) - # compute the product, which is the denominator of the Forney algorithm (errata locator derivative) - err_loc_prime = 1 - for coef in err_loc_prime_tmp: - err_loc_prime = gf_mul(err_loc_prime, coef) - # equivalent to: err_loc_prime = functools.reduce(gf_mul, err_loc_prime_tmp, 1) - - # Test if we could find the errata locator, else we raise an Exception (because else since we divide y by err_loc_prime to compute the magnitude, we will get a ZeroDivisionError exception otherwise) - if err_loc_prime == 0: - raise ReedSolomonError("Decoding failed: Forney algorithm could not properly detect where the errors are located (errata locator prime is 0).") - - # Compute y (evaluation of the errata evaluator polynomial) - # This is a more faithful translation of the theoretical equation contrary to the old forney method. Here it is exactly copy/pasted from the included presentation decoding_rs.pdf: Yl = omega(Xl.inverse()) / prod(1 - Xj*Xl.inverse()) for j in len(X) (in the paper it's for j in s, but it's useless when len(X) < s because we compute neutral terms 1 for nothing, and wrong when correcting more than s erasures or erasures+errors since it prevents computing all required terms). - # Thus here this method works with erasures too because firstly we fixed the equation to be like the theoretical one (don't know why it was modified in _old_forney(), if it's an optimization, it doesn't enhance anything), and secondly because we removed the product bound on s, which prevented computing errors and erasures above the s=(n-k)//2 bound. - y = gf_poly_eval(err_eval[::-1], Xi_inv) # numerator of the Forney algorithm (errata evaluator evaluated) - y = gf_mul(gf_pow(Xi, 1-fcr), y) # adjust to fcr parameter - - # Compute the magnitude - magnitude = gf_div(y, err_loc_prime) # magnitude value of the error, calculated by the Forney algorithm (an equation in fact): dividing the errata evaluator with the errata locator derivative gives us the errata magnitude (ie, value to repair) the ith symbol - E[err_pos[i]] = magnitude # store the magnitude for this error into the magnitude polynomial - - # Apply the correction of values to get our message corrected! (note that the ecc bytes also gets corrected!) - # (this isn't the Forney algorithm, we just apply the result of decoding here) - msg = gf_poly_add(msg, E) # equivalent to Ci = Ri - Ei where Ci is the correct message, Ri the received (senseword) message, and Ei the errata magnitudes (minus is replaced by XOR since it's equivalent in GF(2^p)). So in fact here we substract from the received message the errors magnitude, which logically corrects the value to what it should be. - return msg - -def rs_find_error_locator(synd, nsym, erase_loc=None, erase_count=0): - '''Find error/errata locator and evaluator polynomials with Berlekamp-Massey algorithm''' - # The idea is that BM will iteratively estimate the error locator polynomial. - # To do this, it will compute a Discrepancy term called Delta, which will tell us if the error locator polynomial needs an update or not - # (hence why it's called discrepancy: it tells us when we are getting off board from the correct value). - - # Init the polynomials - if erase_loc: # if the erasure locator polynomial is supplied, we init with its value, so that we include erasures in the final locator polynomial - err_loc = _bytearray(erase_loc) - old_loc = _bytearray(erase_loc) - else: - err_loc = _bytearray([1]) # This is the main variable we want to fill, also called Sigma in other notations or more formally the errors/errata locator polynomial. - old_loc = _bytearray([1]) # BM is an iterative algorithm, and we need the errata locator polynomial of the previous iteration in order to update other necessary variables. - #L = 0 # update flag variable, not needed here because we use an alternative equivalent way of checking if update is needed (but using the flag could potentially be faster depending on if using length(list) is taking linear time in your language, here in Python it's constant so it's as fast. - - # Fix the syndrome shifting: when computing the syndrome, some implementations may prepend a 0 coefficient for the lowest degree term (the constant). This is a case of syndrome shifting, thus the syndrome will be bigger than the number of ecc symbols (I don't know what purpose serves this shifting). If that's the case, then we need to account for the syndrome shifting when we use the syndrome such as inside BM, by skipping those prepended coefficients. - # Another way to detect the shifting is to detect the 0 coefficients: by definition, a syndrome does not contain any 0 coefficient (except if there are no errors/erasures, in this case they are all 0). This however doesn't work with the modified Forney syndrome, which set to 0 the coefficients corresponding to erasures, leaving only the coefficients corresponding to errors. - synd_shift = 0 - if len(synd) > nsym: synd_shift = len(synd) - nsym - - for i in xrange(nsym-erase_count): # generally: nsym-erase_count == len(synd), except when you input a partial erase_loc and using the full syndrome instead of the Forney syndrome, in which case nsym-erase_count is more correct (len(synd) will fail badly with IndexError). - if erase_loc: # if an erasures locator polynomial was provided to init the errors locator polynomial, then we must skip the FIRST erase_count iterations (not the last iterations, this is very important!) - K = erase_count+i+synd_shift - else: # if erasures locator is not provided, then either there's no erasures to account or we use the Forney syndromes, so we don't need to use erase_count nor erase_loc (the erasures have been trimmed out of the Forney syndromes). - K = i+synd_shift - - # Compute the discrepancy Delta - # Here is the close-to-the-books operation to compute the discrepancy Delta: it's a simple polynomial multiplication of error locator with the syndromes, and then we get the Kth element. - #delta = gf_poly_mul(err_loc[::-1], synd)[K] # theoretically it should be gf_poly_add(synd[::-1], [1])[::-1] instead of just synd, but it seems it's not absolutely necessary to correctly decode. - # But this can be optimized: since we only need the Kth element, we don't need to compute the polynomial multiplication for any other element but the Kth. Thus to optimize, we compute the polymul only at the item we need, skipping the rest (avoiding a nested loop, thus we are linear time instead of quadratic). - # This optimization is actually described in several figures of the book "Algebraic codes for data transmission", Blahut, Richard E., 2003, Cambridge university press. - delta = synd[K] - for j in xrange(1, len(err_loc)): - delta ^= gf_mul(err_loc[-(j+1)], synd[K - j]) # delta is also called discrepancy. Here we do a partial polynomial multiplication (ie, we compute the polynomial multiplication only for the term of degree K). Should be equivalent to brownanrs.polynomial.mul_at(). - #print "delta", K, delta, list(gf_poly_mul(err_loc[::-1], synd)) # debugline - - # Shift polynomials to compute the next degree - old_loc = old_loc + _bytearray([0]) - - # Iteratively estimate the errata locator and evaluator polynomials - if delta != 0: # Update only if there's a discrepancy - if len(old_loc) > len(err_loc): # Rule B (rule A is implicitly defined because rule A just says that we skip any modification for this iteration) - #if 2*L <= K+erase_count: # equivalent to len(old_loc) > len(err_loc), as long as L is correctly computed - # Computing errata locator polynomial Sigma - new_loc = gf_poly_scale(old_loc, delta) - old_loc = gf_poly_scale(err_loc, gf_inverse(delta)) # effectively we are doing err_loc * 1/delta = err_loc // delta - err_loc = new_loc - # Update the update flag - #L = K - L # the update flag L is tricky: in Blahut's schema, it's mandatory to use `L = K - L - erase_count` (and indeed in a previous draft of this function, if you forgot to do `- erase_count` it would lead to correcting only 2*(errors+erasures) <= (n-k) instead of 2*errors+erasures <= (n-k)), but in this latest draft, this will lead to a wrong decoding in some cases where it should correctly decode! Thus you should try with and without `- erase_count` to update L on your own implementation and see which one works OK without producing wrong decoding failures. - - # Update with the discrepancy - err_loc = gf_poly_add(err_loc, gf_poly_scale(old_loc, delta)) - - # Check if the result is correct, that there's not too many errors to correct - err_loc = list(itertools.dropwhile(lambda x: x == 0, err_loc)) # drop leading 0s, else errs will not be of the correct size - errs = len(err_loc) - 1 - if (errs-erase_count) * 2 + erase_count > nsym: - raise ReedSolomonError("Too many errors to correct") - - return err_loc - -def rs_find_errata_locator(e_pos, generator=2): - '''Compute the erasures/errors/errata locator polynomial from the erasures/errors/errata positions (the positions must be relative to the x coefficient, eg: "hello worldxxxxxxxxx" is tampered to "h_ll_ worldxxxxxxxxx" with xxxxxxxxx being the ecc of length n-k=9, here the string positions are [1, 4], but the coefficients are reversed since the ecc characters are placed as the first coefficients of the polynomial, thus the coefficients of the erased characters are n-1 - [1, 4] = [18, 15] = erasures_loc to be specified as an argument.''' - # See: http://ocw.usu.edu/Electrical_and_Computer_Engineering/Error_Control_Coding/lecture7.pdf and Blahut, Richard E. "Transform techniques for error control codes." IBM Journal of Research and development 23.3 (1979): 299-315. http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.600&rep=rep1&type=pdf and also a MatLab implementation here: http://www.mathworks.com/matlabcentral/fileexchange/23567-reed-solomon-errors-and-erasures-decoder/content//RS_E_E_DEC.m - e_loc = [1] # just to init because we will multiply, so it must be 1 so that the multiplication starts correctly without nulling any term - # erasures_loc is very simple to compute: erasures_loc = prod(1 - x*alpha**i) for i in erasures_pos and where alpha is the alpha chosen to evaluate polynomials (here in this library it's gf(3)). To generate c*x where c is a constant, we simply generate a Polynomial([c, 0]) where 0 is the constant and c is positionned to be the coefficient for x^1. - for i in e_pos: - e_loc = gf_poly_mul( e_loc, gf_poly_add(_bytearray([1]), [gf_pow(generator, i), 0]) ) - return e_loc - -def rs_find_error_evaluator(synd, err_loc, nsym): - '''Compute the error (or erasures if you supply sigma=erasures locator polynomial, or errata) evaluator polynomial Omega from the syndrome and the error/erasures/errata locator Sigma. Omega is already computed at the same time as Sigma inside the Berlekamp-Massey implemented above, but in case you modify Sigma, you can recompute Omega afterwards using this method, or just ensure that Omega computed by BM is correct given Sigma.''' - # Omega(x) = [ Synd(x) * Error_loc(x) ] mod x^(n-k+1) - _, remainder = gf_poly_div( gf_poly_mul(synd, err_loc), ([1] + [0]*(nsym+1)) ) # first multiply syndromes * errata_locator, then do a polynomial division to truncate the polynomial to the required length - - # Faster way that is equivalent - #remainder = gf_poly_mul(synd, err_loc) # first multiply the syndromes with the errata locator polynomial - #remainder = remainder[len(remainder)-(nsym+1):] # then divide by a polynomial of the length we want, which is equivalent to slicing the list (which represents the polynomial) - - return remainder - -def rs_find_errors(err_loc, nmess, generator=2): - '''Find the roots (ie, where evaluation = zero) of error polynomial by bruteforce trial, this is a sort of Chien's search (but less efficient, Chien's search is a way to evaluate the polynomial such that each evaluation only takes constant time).''' - # nmess = length of whole codeword (message + ecc symbols) - errs = len(err_loc) - 1 - err_pos = [] - for i in xrange(nmess): # normally we should try all 2^8 possible values, but here we optimize to just check the interesting symbols - if gf_poly_eval(err_loc, gf_pow(generator, i)) == 0: # It's a 0? Bingo, it's a root of the error locator polynomial, in other terms this is the location of an error - err_pos.append(nmess - 1 - i) - # Sanity check: the number of errors/errata positions found should be exactly the same as the length of the errata locator polynomial - if len(err_pos) != errs: - # TODO: to decode messages+ecc with length n > 255, we may try to use a bruteforce approach: the correct positions ARE in the final array j, but the problem is because we are above the Galois Field's range, there is a wraparound so that for example if j should be [0, 1, 2, 3], we will also get [255, 256, 257, 258] (because 258 % 255 == 3, same for the other values), so we can't discriminate. The issue is that fixing any errs_nb errors among those will always give a correct output message (in the sense that the syndrome will be all 0), so we may not even be able to check if that's correct or not, so I'm not sure the bruteforce approach may even be possible. - raise ReedSolomonError("Too many (or few) errors found by Chien Search for the errata locator polynomial!") - return err_pos - -def rs_forney_syndromes(synd, pos, nmess, generator=2): - # Compute Forney syndromes, which computes a modified syndromes to compute only errors (erasures are trimmed out). Do not confuse this with Forney algorithm, which allows to correct the message based on the location of errors. - erase_pos_reversed = [nmess-1-p for p in pos] # prepare the coefficient degree positions (instead of the erasures positions) - - # Optimized method, all operations are inlined - fsynd = list(synd[1:]) # make a copy and trim the first coefficient which is always 0 by definition - for i in xrange(len(pos)): - x = gf_pow(generator, erase_pos_reversed[i]) - for j in xrange(len(fsynd) - 1): - fsynd[j] = gf_mul(fsynd[j], x) ^ fsynd[j + 1] - #fsynd.pop() # useless? it doesn't change the results of computations to leave it there - - # Theoretical way of computing the modified Forney syndromes: fsynd = (erase_loc * synd) % x^(n-k) -- although the trimming by using x^(n-k) is maybe not necessary as many books do not even mention it (and it works without trimming) - # See Shao, H. M., Truong, T. K., Deutsch, L. J., & Reed, I. S. (1986, April). A single chip VLSI Reed-Solomon decoder. In Acoustics, Speech, and Signal Processing, IEEE International Conference on ICASSP'86. (Vol. 11, pp. 2151-2154). IEEE.ISO 690 - #erase_loc = rs_find_errata_locator(erase_pos_reversed, generator=generator) # computing the erasures locator polynomial - #fsynd = gf_poly_mul(erase_loc[::-1], synd[1:]) # then multiply with the syndrome to get the untrimmed forney syndrome - #fsynd = fsynd[len(pos):] # then trim the first erase_pos coefficients which are useless. Seems to be not necessary, but this reduces the computation time later in BM (thus it's an optimization). - - return fsynd - -def rs_correct_msg(msg_in, nsym, fcr=0, generator=2, erase_pos=None, only_erasures=False): - '''Reed-Solomon main decoding function''' - global field_charac - if len(msg_in) > field_charac: - # Note that it is in fact possible to encode/decode messages that are longer than field_charac, but because this will be above the field, this will generate more error positions during Chien Search than it should, because this will generate duplicate values, which should normally be prevented thank's to the prime polynomial reduction (eg, because it can't discriminate between error at position 1 or 256, both being exactly equal under galois field 2^8). So it's really not advised to do it, but it's possible (but then you're not guaranted to be able to correct any error/erasure on symbols with a position above the length of field_charac -- if you really need a bigger message without chunking, then you should better enlarge c_exp so that you get a bigger field). - raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in), field_charac)) - - msg_out = _bytearray(msg_in) # copy of message - # erasures: set them to null bytes for easier decoding (but this is not necessary, they will be corrected anyway, but debugging will be easier with null bytes because the error locator polynomial values will only depend on the errors locations, not their values) - if erase_pos is None: - erase_pos = [] - else: - for e_pos in erase_pos: - msg_out[e_pos] = 0 - # check if there are too many erasures to correct (beyond the Singleton bound) - if len(erase_pos) > nsym: raise ReedSolomonError("Too many erasures to correct") - # prepare the syndrome polynomial using only errors (ie: errors = characters that were either replaced by null byte or changed to another character, but we don't know their positions) - synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) - # check if there's any error/erasure in the input codeword. If not (all syndromes coefficients are 0), then just return the codeword as-is. - if max(synd) == 0: - return msg_out[:-nsym], msg_out[-nsym:], [] # no errors - - # Find errors locations - if only_erasures: - err_pos = [] - else: - # compute the Forney syndromes, which hide the erasures from the original syndrome (so that BM will just have to deal with errors, not erasures) - fsynd = rs_forney_syndromes(synd, erase_pos, len(msg_out), generator) - # compute the error locator polynomial using Berlekamp-Massey - err_loc = rs_find_error_locator(fsynd, nsym, erase_count=len(erase_pos)) - # locate the message errors using Chien search (or bruteforce search) - err_pos = rs_find_errors(err_loc[::-1], len(msg_out), generator) - if err_pos is None: - raise ReedSolomonError("Could not locate error") - - # Find errors values and apply them to correct the message - # compute errata evaluator and errata magnitude polynomials, then correct errors and erasures - msg_out = rs_correct_errata(msg_out, synd, erase_pos + err_pos, fcr, generator) # note that we here use the original syndrome, not the forney syndrome (because we will correct both errors and erasures, so we need the full syndrome) - # check if the final message is fully repaired - synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) - if max(synd) > 0: - raise ReedSolomonError("Could not correct message") - # return the successfully decoded message - return msg_out[:-nsym], msg_out[-nsym:], erase_pos + err_pos # also return the corrected ecc block so that the user can check(), and the position of errors to allow for adaptive bitrate algorithm to check how the number of errors vary - -def rs_correct_msg_nofsynd(msg_in, nsym, fcr=0, generator=2, erase_pos=None, only_erasures=False): - '''Reed-Solomon main decoding function, without using the modified Forney syndromes''' - global field_charac - if len(msg_in) > field_charac: - raise ValueError("Message is too long (%i when max is %i)" % (len(msg_in), field_charac)) - - msg_out = _bytearray(msg_in) # copy of message - # erasures: set them to null bytes for easier decoding (but this is not necessary, they will be corrected anyway, but debugging will be easier with null bytes because the error locator polynomial values will only depend on the errors locations, not their values) - if erase_pos is None: - erase_pos = [] - else: - for e_pos in erase_pos: - msg_out[e_pos] = 0 - # check if there are too many erasures - if len(erase_pos) > nsym: raise ReedSolomonError("Too many erasures to correct") - # prepare the syndrome polynomial using only errors (ie: errors = characters that were either replaced by null byte or changed to another character, but we don't know their positions) - synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) - # check if there's any error/erasure in the input codeword. If not (all syndromes coefficients are 0), then just return the codeword as-is. - if max(synd) == 0: - return msg_out[:-nsym], msg_out[-nsym:], [] # no errors - - # prepare erasures locator and evaluator polynomials - erase_loc = None - #erase_eval = None - erase_count = 0 - if erase_pos: - erase_count = len(erase_pos) - erase_pos_reversed = [len(msg_out)-1-eras for eras in erase_pos] - erase_loc = rs_find_errata_locator(erase_pos_reversed, generator=generator) - #erase_eval = rs_find_error_evaluator(synd[::-1], erase_loc, len(erase_loc)-1) - - # prepare errors/errata locator polynomial - if only_erasures: - err_loc = erase_loc[::-1] - #err_eval = erase_eval[::-1] - else: - err_loc = rs_find_error_locator(synd, nsym, erase_loc=erase_loc, erase_count=erase_count) - err_loc = err_loc[::-1] - #err_eval = rs_find_error_evaluator(synd[::-1], err_loc[::-1], len(err_loc)-1)[::-1] # find error/errata evaluator polynomial (not really necessary since we already compute it at the same time as the error locator poly in BM) - - # locate the message errors - err_pos = rs_find_errors(err_loc, len(msg_out), generator) # find the roots of the errata locator polynomial (ie: the positions of the errors/errata) - if err_pos is None: - raise ReedSolomonError("Could not locate error") - - # compute errata evaluator and errata magnitude polynomials, then correct errors and erasures - msg_out = rs_correct_errata(msg_out, synd, err_pos, fcr=fcr, generator=generator) - # check if the final message is fully repaired - synd = rs_calc_syndromes(msg_out, nsym, fcr, generator) - if max(synd) > 0: - raise ReedSolomonError("Could not correct message") - # return the successfully decoded message - return msg_out[:-nsym], msg_out[-nsym:], erase_pos + err_pos # also return the corrected ecc block so that the user can check(), and the position of errors to allow for adaptive bitrate algorithm to check how the number of errors vary - -def rs_check(msg, nsym, fcr=0, generator=2): - '''Returns true if the message + ecc has no error of false otherwise (may not always catch a wrong decoding or a wrong message, particularly if there are too many errors -- above the Singleton bound --, but it usually does)''' - return ( max(rs_calc_syndromes(msg, nsym, fcr, generator)) == 0 ) - - -#=================================================================================================== -# API -#=================================================================================================== -class RSCodec(object): - ''' - A Reed Solomon encoder/decoder. After initializing the object, use ``encode`` to encode a - (byte)string to include the RS correction code, and pass such an encoded (byte)string to - ``decode`` to extract the original message (if the number of errors allows for correct decoding). - The ``nsym`` argument is the length of the correction code, and it determines the number of - error bytes (if I understand this correctly, half of ``nsym`` is correctable) - ''' - ''' - Modifications by rotorgit 2/3/2015: - Added support for US FAA ADSB UAT RS FEC, by allowing user to specify - different primitive polynomial and non-zero first consecutive root (fcr). - For UAT/ADSB use, set fcr=120 and prim=0x187 when instantiating - the class; leaving them out will default for previous values (0 and - 0x11d) - ''' - - def __init__(self, nsym=10, nsize=255, fcr=0, prim=0x11d, generator=2, c_exp=8, single_gen=True): - '''Initialize the Reed-Solomon codec. Note that different parameters change the internal values (the ecc symbols, look-up table values, etc) but not the output result (whether your message can be repaired or not, there is no influence of the parameters). - nsym : number of ecc symbols (you can repair nsym/2 errors and nsym erasures. - nsize : maximum length of each chunk. If higher than 255, will use a higher Galois Field, but the algorithm's complexity and computational cost will raise quadratically... - single_gen : if you want to use the same RSCodec for different nsym parameters (but nsize the same), then set single_gen = False. - ''' - - # Auto-setup if galois field or message length is different than default (exponent 8) - if nsize > 255 and c_exp <= 8: # nsize (chunksize) is larger than the galois field, we resize the galois field - # Get the next closest power of two - c_exp = int(math.log(2 ** (math.floor(math.log(nsize) / math.log(2)) + 1), 2)) - if c_exp != 8 and prim == 0x11d: # prim was not correctly defined, find one - prim = find_prime_polys(generator=generator, c_exp=c_exp, fast_primes=True, single=True) - if nsize == 255: # resize chunk size if not set - nsize = int(2**c_exp - 1) - - # Memorize variables - self.nsym = nsym # number of ecc symbols (ie, the repairing rate will be r=(nsym/2)/nsize, so for example if you have nsym=5 and nsize=10, you have a rate r=0.25, so you can correct up to 0.25% errors (or exactly 2 symbols out of 10), and 0.5% erasures (5 symbols out of 10). - self.nsize = nsize # maximum length of one chunk (ie, message + ecc symbols after encoding, for the message alone it's nsize-nsym) - self.fcr = fcr # first consecutive root, can be any value between 0 and (2**c_exp)-1 - self.prim = prim # prime irreducible polynomial, use find_prime_polys() to find a prime poly - self.generator = generator # generator integer, must be prime - self.c_exp = c_exp # exponent of the field's characteristic. This both defines the maximum value per symbol and the maximum length of one chunk. By default it's GF(2^8), do not change if you're not sure what it means. - - # Initialize the look-up tables for easy and quick multiplication/division - self.gf_log, self.gf_exp, self.field_charac = init_tables(prim, generator, c_exp) - # Precompute the generator polynomials - if single_gen: - self.gen = {} - self.gen[nsym] = rs_generator_poly(nsym, fcr=fcr, generator=generator) - else: - self.gen = rs_generator_poly_all(nsize, fcr=fcr, generator=generator) - - def chunk(self, data, chunksize): - '''Split a long message into chunks''' - for i in xrange(0, len(data), chunksize): - # Split the long message in a chunk - chunk = data[i:i+chunksize] - yield chunk - - def encode(self, data, nsym=None): - '''Encode a message (ie, add the ecc symbols) using Reed-Solomon, whatever the length of the message because we use chunking''' - # Restore precomputed tables (allow to use multiple RSCodec in one script) - global gf_log, gf_exp, field_charac - gf_log, gf_exp, field_charac = self.gf_log, self.gf_exp, self.field_charac - - if not nsym: - nsym = self.nsym - - if isinstance(data, str): - data = _bytearray(data) - enc = _bytearray() - for chunk in self.chunk(data, self.nsize - self.nsym): - enc.extend(rs_encode_msg(chunk, self.nsym, fcr=self.fcr, generator=self.generator, gen=self.gen[nsym])) - return enc - - def decode(self, data, nsym=None, erase_pos=None, only_erasures=False): - '''Repair a message, whatever its size is, by using chunking. May return a wrong result if number of errors > nsym. - Note that it returns a couple of vars: the repaired messages, and the repaired messages+ecc (useful for checking). - Usage: rmes, rmesecc = RSCodec.decode(data). - ''' - # erase_pos is a list of positions where you know (or greatly suspect at least) there is an erasure (ie, wrong character but you know it's at this position). Just input the list of all positions you know there are errors, and this method will automatically split the erasures positions to attach to the corresponding data chunk. - - # Restore precomputed tables (allow to use multiple RSCodec in one script) - global gf_log, gf_exp, field_charac - gf_log, gf_exp, field_charac = self.gf_log, self.gf_exp, self.field_charac - - if not nsym: - nsym = self.nsym - - if isinstance(data, str): - data = _bytearray(data) - dec = _bytearray() - dec_full = _bytearray() - errata_pos_all = _bytearray() - for chunk in self.chunk(data, self.nsize): - # Extract the erasures for this chunk - e_pos = [] - if erase_pos: - # First extract the erasures for this chunk (all erasures below the maximum chunk length) - e_pos = [x for x in erase_pos if x <= self.nsize] - # Then remove the extract erasures from the big list and also decrement all subsequent positions values by nsize (the current chunk's size) so as to prepare the correct alignment for the next iteration - erase_pos = [x - (self.nsize+1) for x in erase_pos if x > self.nsize] - # Decode/repair this chunk! - rmes, recc, errata_pos = rs_correct_msg(chunk, nsym, fcr=self.fcr, generator=self.generator, erase_pos=e_pos, only_erasures=only_erasures) - dec.extend(rmes) - dec_full.extend(rmes+recc) - errata_pos_all.extend(errata_pos) - return dec, dec_full, errata_pos_all - - def check(self, data, nsym=None): - '''Check if a message+ecc stream is not corrupted (or fully repaired). Note: may return a wrong result if number of errors > nsym.''' - if not nsym: - nsym = self.nsym - if isinstance(data, str): - data = _bytearray(data) - check = [] - for chunk in self.chunk(data, self.nsize): - check.append(rs_check(chunk, nsym, fcr=self.fcr, generator=self.generator)) - return check - - def maxerrata(self, errors=None, erasures=None, verbose=False): - '''Return the Singleton Bound for the current codec, which is the max number of errata (errors and erasures) that the codec can decode/correct. - Beyond the Singleton Bound (too many errors/erasures), the algorithm will try to raise an exception, but it may also not detect any problem with the message and return 0 errors. - Hence why you should use checksums if your goal is to detect errors (as opposed to correcting them), as checksums have no bounds on the number of errors, the only limitation being the probability of collisions. - By default, return a tuple wth the maximum number of errors (2nd output) OR erasures (2nd output) that can be corrected. - If errors or erasures (not both) is specified as argument, computes the remaining **simultaneous** correction capacity (eg, if errors specified, compute the number of erasures that can be simultaneously corrected). - Set verbose to True to get print a report.''' - nsym = self.nsym - # Compute the maximum number of errors OR erasures - maxerrors = int(nsym/2) # always floor the number, we can't correct half a symbol, it's all or nothing - maxerasures = nsym - # Compute the maximum of simultaneous errors AND erasures - if erasures is not None and erasures >= 0: - # We know the erasures count, we want to know how many errors we can correct simultaneously - if erasures > maxerasures: - raise ReedSolomonError("Specified number of errors or erasures exceeding the Singleton Bound!") - maxerrors = int((nsym-erasures)/2) - if verbose: - print('This codec can correct up to %i errors and %i erasures simultaneously' % (maxerrors, erasures)) - # Return a tuple with the maximum number of simultaneously corrected errors and erasures - return maxerrors, erasures - if errors is not None and errors >= 0: - # We know the errors count, we want to know how many erasures we can correct simultaneously - if errors > maxerrors: - raise ReedSolomonError("Specified number of errors or erasures exceeding the Singleton Bound!") - maxerasures = int(nsym-(errors*2)) - if verbose: - print('This codec can correct up to %i errors and %i erasures simultaneously' % (errors, maxerasures)) - # Return a tuple with the maximum number of simultaneously corrected errors and erasures - return errors, maxerasures - # Return a tuple with the maximum number of errors and erasures (independently corrected) - if verbose: - print('This codec can correct up to %i errors and %i erasures independently' % (maxerrors, maxerasures)) - return maxerrors, maxerasures diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/WHEEL deleted file mode 100644 index 57e3d840d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.38.4) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/INSTALLER similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/cffi-1.15.1.dist-info/INSTALLER rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/INSTALLER diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/LICENSE similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/LICENSE rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/LICENSE diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/METADATA similarity index 96% rename from dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/METADATA rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/METADATA index 05ea086d7..7b046050f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/METADATA +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: setuptools -Version: 65.6.3 +Version: 67.6.1 Summary: Easily download, build, install, upgrade, and uninstall Python packages Home-page: https://github.com/pypa/setuptools Author: Python Packaging Authority @@ -25,6 +25,7 @@ Requires-Dist: sphinx (>=3.5) ; extra == 'docs' Requires-Dist: jaraco.packaging (>=9) ; extra == 'docs' Requires-Dist: rst.linker (>=1.9) ; extra == 'docs' Requires-Dist: furo ; extra == 'docs' +Requires-Dist: sphinx-lint ; extra == 'docs' Requires-Dist: jaraco.tidelift (>=1.4) ; extra == 'docs' Requires-Dist: pygments-github-lexers (==0.0.5) ; extra == 'docs' Requires-Dist: sphinx-favicon ; extra == 'docs' @@ -37,7 +38,6 @@ Provides-Extra: ssl Provides-Extra: testing Requires-Dist: pytest (>=6) ; extra == 'testing' Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing' -Requires-Dist: pytest-flake8 ; extra == 'testing' Requires-Dist: flake8 (<5) ; extra == 'testing' Requires-Dist: pytest-enabler (>=1.3) ; extra == 'testing' Requires-Dist: pytest-perf ; extra == 'testing' @@ -68,6 +68,7 @@ Requires-Dist: filelock (>=3.4.0) ; extra == 'testing-integration' Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing' Requires-Dist: pytest-cov ; (platform_python_implementation != "PyPy") and extra == 'testing' Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy") and extra == 'testing' +Requires-Dist: pytest-flake8 ; (python_version < "3.12") and extra == 'testing' .. image:: https://img.shields.io/pypi/v/setuptools.svg :target: https://pypi.org/project/setuptools @@ -85,7 +86,7 @@ Requires-Dist: pytest-mypy (>=0.9.1) ; (platform_python_implementation != "PyPy" .. image:: https://img.shields.io/readthedocs/setuptools/latest.svg :target: https://setuptools.pypa.io -.. image:: https://img.shields.io/badge/skeleton-2022-informational +.. image:: https://img.shields.io/badge/skeleton-2023-informational :target: https://blog.jaraco.com/skeleton .. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/RECORD similarity index 60% rename from dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/RECORD rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/RECORD index f2b8a1f31..5e0f81d72 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/RECORD +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/RECORD @@ -3,13 +3,12 @@ _distutils_hack/__pycache__/__init__.cpython-310.pyc,, _distutils_hack/__pycache__/override.cpython-310.pyc,, _distutils_hack/override.py,sha256=Eu_s-NF6VIZ4Cqd0tbbA5wtWky2IZPNd8et6GLt1mzo,44 distutils-precedence.pth,sha256=JjjOniUA5XKl4N5_rtZmHrVp0baW_LoHsN0iPaX10iQ,151 -pkg_resources/__init__.py,sha256=fT5Y3P1tcSX8sJomClUU10WHeFmvqyNZM4UZHzdpAvg,108568 +pkg_resources/__init__.py,sha256=iEhCmUehMWZiwOxk1eEdFKzyeC_nL_xNJSctARdcQPs,109315 pkg_resources/__pycache__/__init__.cpython-310.pyc,, pkg_resources/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pkg_resources/_vendor/__pycache__/__init__.cpython-310.pyc,, -pkg_resources/_vendor/__pycache__/appdirs.cpython-310.pyc,, +pkg_resources/_vendor/__pycache__/typing_extensions.cpython-310.pyc,, pkg_resources/_vendor/__pycache__/zipp.cpython-310.pyc,, -pkg_resources/_vendor/appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701 pkg_resources/_vendor/importlib_resources/__init__.py,sha256=evPm12kLgYqTm-pbzm60bOuumumT8IpBNWFp0uMyrzE,506 pkg_resources/_vendor/importlib_resources/__pycache__/__init__.cpython-310.pyc,, pkg_resources/_vendor/importlib_resources/__pycache__/_adapters.cpython-310.pyc,, @@ -21,89 +20,89 @@ pkg_resources/_vendor/importlib_resources/__pycache__/abc.cpython-310.pyc,, pkg_resources/_vendor/importlib_resources/__pycache__/readers.cpython-310.pyc,, pkg_resources/_vendor/importlib_resources/__pycache__/simple.cpython-310.pyc,, pkg_resources/_vendor/importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504 -pkg_resources/_vendor/importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741 -pkg_resources/_vendor/importlib_resources/_compat.py,sha256=nFBCGMvImglrqgYkb9aPgOj68-h6xbw-ca94XOv1-zs,2706 +pkg_resources/_vendor/importlib_resources/_common.py,sha256=jSC4xfLdcMNbtbWHtpzbFkNa0W7kvf__nsYn14C_AEU,5457 +pkg_resources/_vendor/importlib_resources/_compat.py,sha256=L8HTWyAC_MIKuxWZuw0zvTq5qmUA0ttrvK941OzDKU8,2925 pkg_resources/_vendor/importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884 -pkg_resources/_vendor/importlib_resources/_legacy.py,sha256=TMLkx6aEM6U8xIREPXqGZrMbUhTiPUuPl6ESD7RdYj4,3494 -pkg_resources/_vendor/importlib_resources/abc.py,sha256=MvTJJXajbl74s36Gyeesf76egtbFnh-TMtzQMVhFWXo,3886 -pkg_resources/_vendor/importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566 -pkg_resources/_vendor/importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836 +pkg_resources/_vendor/importlib_resources/_legacy.py,sha256=0TKdZixxLWA-xwtAZw4HcpqJmj4Xprx1Zkcty0gTRZY,3481 +pkg_resources/_vendor/importlib_resources/abc.py,sha256=Icr2IJ2QtH7vvAB9vC5WRJ9KBoaDyJa7KUs8McuROzo,5140 +pkg_resources/_vendor/importlib_resources/readers.py,sha256=PZsi5qacr2Qn3KHw4qw3Gm1MzrBblPHoTdjqjH7EKWw,3581 +pkg_resources/_vendor/importlib_resources/simple.py,sha256=0__2TQBTQoqkajYmNPt1HxERcReAT6boVKJA328pr04,2576 pkg_resources/_vendor/jaraco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pkg_resources/_vendor/jaraco/__pycache__/__init__.cpython-310.pyc,, pkg_resources/_vendor/jaraco/__pycache__/context.cpython-310.pyc,, pkg_resources/_vendor/jaraco/__pycache__/functools.cpython-310.pyc,, -pkg_resources/_vendor/jaraco/context.py,sha256=7X1tpCLc5EN45iWGzGcsH0Unx62REIkvtRvglj0SiUA,5420 +pkg_resources/_vendor/jaraco/context.py,sha256=vlyDzb_PvZ9H7R9bbTr_CMRnveW5Dc56eC7eyd_GfoA,7460 pkg_resources/_vendor/jaraco/functools.py,sha256=eLwPh8FWY7rQ_cj1YxCekUkibTuerwyoJ_41H7Q7oWM,13515 pkg_resources/_vendor/jaraco/text/__init__.py,sha256=cN55bFcceW4wTHG5ruv5IuEDRarP-4hBYX8zl94_c30,15526 pkg_resources/_vendor/jaraco/text/__pycache__/__init__.cpython-310.pyc,, -pkg_resources/_vendor/more_itertools/__init__.py,sha256=ZQYu_9H6stSG7viUgT32TFqslqcZwq82kWRZooKiI8Y,83 +pkg_resources/_vendor/more_itertools/__init__.py,sha256=5PNQMpy400s5GB3jcWwzje0RCw8k0bvU9W_C49V0fd0,148 pkg_resources/_vendor/more_itertools/__pycache__/__init__.cpython-310.pyc,, pkg_resources/_vendor/more_itertools/__pycache__/more.cpython-310.pyc,, pkg_resources/_vendor/more_itertools/__pycache__/recipes.cpython-310.pyc,, -pkg_resources/_vendor/more_itertools/more.py,sha256=oave_26jctLsuF30e1SOWMgW0bEuwS-t08wkaLUwvXc,132569 -pkg_resources/_vendor/more_itertools/recipes.py,sha256=N6aCDwoIPvE-aiqpGU-nbFwqiM3X8MKRcxBM84naW88,18410 -pkg_resources/_vendor/packaging/__about__.py,sha256=ugASIO2w1oUyH8_COqQ2X_s0rDhjbhQC3yJocD03h2c,661 -pkg_resources/_vendor/packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497 -pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-310.pyc,, +pkg_resources/_vendor/more_itertools/more.py,sha256=NTUZ0P0n0gDy3qezmlR5xGtqA1LWMAockWvyHYLLuYQ,133344 +pkg_resources/_vendor/more_itertools/recipes.py,sha256=ZX4-2IfbZKlPIVaDITH2buX_fPuMDe1EVc6e2XSsCz8,22975 +pkg_resources/_vendor/packaging/__init__.py,sha256=7BlJ_DcIt1zv01UQcZLozidczzNcivKj66zIBkRL3R4,501 pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-310.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_elffile.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/_manylinux.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/_musllinux.cpython-310.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_parser.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-310.pyc,, +pkg_resources/_vendor/packaging/__pycache__/_tokenizer.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/markers.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/tags.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/utils.cpython-310.pyc,, pkg_resources/_vendor/packaging/__pycache__/version.cpython-310.pyc,, -pkg_resources/_vendor/packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488 -pkg_resources/_vendor/packaging/_musllinux.py,sha256=_KGgY_qc7vhMGpoqss25n2hiLCNKRtvz9mCrS7gkqyc,4378 +pkg_resources/_vendor/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266 +pkg_resources/_vendor/packaging/_manylinux.py,sha256=uZ821PBqQrokhUbwe7E0UodEraMHqzoSgTvfJ8MIl30,8813 +pkg_resources/_vendor/packaging/_musllinux.py,sha256=mvPk7FNjjILKRLIdMxR7IvJ1uggLgCszo-L9rjfpi0M,2524 +pkg_resources/_vendor/packaging/_parser.py,sha256=jjFjSqNf7W2-Ta6YUkywK0P4d2i0Bz_MqLOfl7O1Tkw,9399 pkg_resources/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 -pkg_resources/_vendor/packaging/markers.py,sha256=gFSKoBTb0sKDw1v_apJy15lPr0v2mEvuEkfooTtcWx4,8496 -pkg_resources/_vendor/packaging/requirements.py,sha256=uJ4cjwm3_nrfHJLCcGU9mT5aw8SXfw8v1aBUD7OFuVs,4706 -pkg_resources/_vendor/packaging/specifiers.py,sha256=LRQ0kFsHrl5qfcFNEEJrIFYsnIHQUJXY9fIsakTrrqE,30110 -pkg_resources/_vendor/packaging/tags.py,sha256=lmsnGNiJ8C4D_Pf9PbM0qgbZvD9kmB9lpZBQUZa3R_Y,15699 -pkg_resources/_vendor/packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200 -pkg_resources/_vendor/packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665 -pkg_resources/_vendor/pyparsing/__init__.py,sha256=52QH3lgPbJhba0estckoGPHRH8JvQSSCGoWiEn2m0bU,9159 -pkg_resources/_vendor/pyparsing/__pycache__/__init__.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/actions.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/common.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/core.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/exceptions.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/helpers.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/results.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/testing.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/unicode.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/__pycache__/util.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/actions.py,sha256=wU9i32e0y1ymxKE3OUwSHO-SFIrt1h_wv6Ws0GQjpNU,6426 -pkg_resources/_vendor/pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936 -pkg_resources/_vendor/pyparsing/core.py,sha256=u8GptQE_H6wMkl8OZhxeK1aAPIDXXNgwdShORBwBVS4,213310 -pkg_resources/_vendor/pyparsing/diagram/__init__.py,sha256=f_EfxahqrdkRVahmTwLJXkZ9EEDKNd-O7lBbpJYlE1g,23668 -pkg_resources/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-310.pyc,, -pkg_resources/_vendor/pyparsing/exceptions.py,sha256=3LbSafD32NYb1Tzt85GHNkhEAU1eZkTtNSk24cPMemo,9023 -pkg_resources/_vendor/pyparsing/helpers.py,sha256=QpUOjW0-psvueMwWb9bQpU2noqKCv98_wnw1VSzSdVo,39129 -pkg_resources/_vendor/pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sGA30,25341 -pkg_resources/_vendor/pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402 -pkg_resources/_vendor/pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787 -pkg_resources/_vendor/pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805 +pkg_resources/_vendor/packaging/_tokenizer.py,sha256=czGibL-4oPofx1pCSt_hrozNbHlOPrqGv6m-0d-iTdo,5148 +pkg_resources/_vendor/packaging/markers.py,sha256=HDPXE0_MPBSwsw_9upez8t8mdrqUGrgiOG_qyQy-W30,8161 +pkg_resources/_vendor/packaging/requirements.py,sha256=4nOKheaBbVEQXTGSqaOGTy1Tkg7J_sEno3u8jxC-baw,3264 +pkg_resources/_vendor/packaging/specifiers.py,sha256=-3ajZ5CkQrjNW5H8NPjvCV2RBgr-w9wcYBdb8kjPBfg,39046 +pkg_resources/_vendor/packaging/tags.py,sha256=fOKnZVfiU3oc9CPSzjJUsMk5VTfgOfpNhWobUH0sAlg,18065 +pkg_resources/_vendor/packaging/utils.py,sha256=es0cCezKspzriQ-3V88h3yJzxz028euV2sUwM61kE-o,4355 +pkg_resources/_vendor/packaging/version.py,sha256=_ULefmddLDLJ9VKRFAXhshEd0zP8OYPhcjCPfYolUbo,16295 +pkg_resources/_vendor/platformdirs/__init__.py,sha256=edi2JSKpLCapqir0AW_CjpHtinRE3hf6aDk5-VHggLk,12806 +pkg_resources/_vendor/platformdirs/__main__.py,sha256=VsC0t5m-6f0YVr96PVks93G3EDF8MSNY4KpUMvPahDA,1164 +pkg_resources/_vendor/platformdirs/__pycache__/__init__.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/__main__.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/android.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/api.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/macos.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/unix.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/version.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/__pycache__/windows.cpython-310.pyc,, +pkg_resources/_vendor/platformdirs/android.py,sha256=GKizhyS7ESRiU67u8UnBJLm46goau9937EchXWbPBlk,4068 +pkg_resources/_vendor/platformdirs/api.py,sha256=MXKHXOL3eh_-trSok-JUTjAR_zjmmKF3rjREVABjP8s,4910 +pkg_resources/_vendor/platformdirs/macos.py,sha256=-3UXQewbT0yMhMdkzRXfXGAntmLIH7Qt4a9Hlf8I5_Y,2655 +pkg_resources/_vendor/platformdirs/unix.py,sha256=P-WQjSSieE38DXjMDa1t4XHnKJQ5idEaKT0PyXwm8KQ,6911 +pkg_resources/_vendor/platformdirs/version.py,sha256=qaN-fw_htIgKUVXoAuAEVgKxQu3tZ9qE2eiKkWIS7LA,160 +pkg_resources/_vendor/platformdirs/windows.py,sha256=LOrXLgI0CjQldDo2zhOZYGYZ6g4e_cJOCB_pF9aMRWQ,6596 +pkg_resources/_vendor/typing_extensions.py,sha256=ipqWiq5AHzrwczt6c26AP05Llh6a5_GaXRpOBqbogHA,80078 pkg_resources/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425 -pkg_resources/extern/__init__.py,sha256=inFoCK9jn_yRFqkbNSOxOYyZD0aB3awch_xtbwIW_-Y,2426 +pkg_resources/extern/__init__.py,sha256=nDtjbrhEaDu388fp4O6BGSpbihZmHh7PoOz2hhFk-Qg,2442 pkg_resources/extern/__pycache__/__init__.cpython-310.pyc,, -setuptools-65.6.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -setuptools-65.6.3.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 -setuptools-65.6.3.dist-info/METADATA,sha256=IYBdBWmx-LABVaEEqlTU2DN0CkWqjPYFAqNZrkr3OXw,6138 -setuptools-65.6.3.dist-info/RECORD,, -setuptools-65.6.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -setuptools-65.6.3.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 -setuptools-65.6.3.dist-info/entry_points.txt,sha256=3siAu4kYm1ybFJHJ7ooqpX5TAW70Gitp9dcdHC-7BFM,2740 -setuptools-65.6.3.dist-info/top_level.txt,sha256=d9yL39v_W7qmKDDSH6sT4bE0j_Ls1M3P161OGgdsm4g,41 -setuptools/__init__.py,sha256=DqL4WTwyXFp0OakiBKz0HfB0nH4Fm06b3PX8sJWUg88,8429 +setuptools-67.6.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +setuptools-67.6.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050 +setuptools-67.6.1.dist-info/METADATA,sha256=-RpYFne8vsD1HBmQYcCg4OOyQfX-VdE8hOlLiccC5NA,6213 +setuptools-67.6.1.dist-info/RECORD,, +setuptools-67.6.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +setuptools-67.6.1.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92 +setuptools-67.6.1.dist-info/entry_points.txt,sha256=3siAu4kYm1ybFJHJ7ooqpX5TAW70Gitp9dcdHC-7BFM,2740 +setuptools-67.6.1.dist-info/top_level.txt,sha256=d9yL39v_W7qmKDDSH6sT4bE0j_Ls1M3P161OGgdsm4g,41 +setuptools/__init__.py,sha256=V1kPVIPkfgZs86YUoUvAAQ9ZvTSr5AD1MHpn3mrV7W0,9170 setuptools/__pycache__/__init__.cpython-310.pyc,, setuptools/__pycache__/_deprecation_warning.cpython-310.pyc,, setuptools/__pycache__/_entry_points.cpython-310.pyc,, setuptools/__pycache__/_imp.cpython-310.pyc,, setuptools/__pycache__/_importlib.cpython-310.pyc,, setuptools/__pycache__/_itertools.cpython-310.pyc,, +setuptools/__pycache__/_normalization.cpython-310.pyc,, setuptools/__pycache__/_path.cpython-310.pyc,, setuptools/__pycache__/_reqs.cpython-310.pyc,, setuptools/__pycache__/archive_util.cpython-310.pyc,, @@ -164,15 +163,15 @@ setuptools/_distutils/__pycache__/unixccompiler.cpython-310.pyc,, setuptools/_distutils/__pycache__/util.cpython-310.pyc,, setuptools/_distutils/__pycache__/version.cpython-310.pyc,, setuptools/_distutils/__pycache__/versionpredicate.cpython-310.pyc,, -setuptools/_distutils/_collections.py,sha256=MfGW9qk6SkMwIWYqdph95fvPGdhgriVCbMTFND2jQ1g,5305 +setuptools/_distutils/_collections.py,sha256=2qMJB2M_i53g0LmeYfD5V3SQ9fx3FScCXdFUS03wfiU,5300 setuptools/_distutils/_functools.py,sha256=ABZ-Lyw-igKwBFoLF3QYtFmfutwZLiAdWcpRMbcacGU,411 setuptools/_distutils/_log.py,sha256=zwFOk2ValRHMQa_kCqDXpHnwaqqZzhxGEwuR4zV-dEs,43 setuptools/_distutils/_macos_compat.py,sha256=-v_Z0M1LEH5k-VhSBBbuz_pDp3nSZ4rzU9E7iIskPDc,239 -setuptools/_distutils/_msvccompiler.py,sha256=2BJnJ2xQFQZyftYi_kgz6DT1nVB-RxqEgd_pUz3qYO4,19641 +setuptools/_distutils/_msvccompiler.py,sha256=sWNC_gUhWzQ0FkCS6bD3Tj2Fvlnk2AwLnP8OvcV_gvQ,19616 setuptools/_distutils/archive_util.py,sha256=JtMIta8JuFkCXVTHvZhmneAEdIMnpsdX84nOWKF24rk,8572 -setuptools/_distutils/bcppcompiler.py,sha256=claWCdHFRSgByxTJy3LG2LdqCGmgy9gtXGlhq-SInzQ,14752 -setuptools/_distutils/ccompiler.py,sha256=ZRMg5BKwFGfMd9hoRscEm-2JkEUVYG83ssaAFa9NZfI,47311 -setuptools/_distutils/cmd.py,sha256=pbvM1mE3KRK0pwu1JlKucLmTvsDZlTMklRNHCjxCP1U,17867 +setuptools/_distutils/bcppcompiler.py,sha256=IAFbt_mF3q3QFBhHGKHA68K1uNfU4MrkhoAJ0zA9S_k,14721 +setuptools/_distutils/ccompiler.py,sha256=rnLM-1MMQgWm-lMOHz9a7XJ0YARP1xnuCAWkQY0XsDQ,48643 +setuptools/_distutils/cmd.py,sha256=PcjcZszunlBw0FRICIr63LAAc8lUQoqia9GRLePzqc0,17861 setuptools/_distutils/command/__init__.py,sha256=fVUps4DJhvShMAod0y7xl02m46bd7r31irEhNofPrrs,430 setuptools/_distutils/command/__pycache__/__init__.cpython-310.pyc,, setuptools/_distutils/command/__pycache__/_framework_compat.cpython-310.pyc,, @@ -198,63 +197,64 @@ setuptools/_distutils/command/__pycache__/register.cpython-310.pyc,, setuptools/_distutils/command/__pycache__/sdist.cpython-310.pyc,, setuptools/_distutils/command/__pycache__/upload.cpython-310.pyc,, setuptools/_distutils/command/_framework_compat.py,sha256=HW84Z1cWmg4b6aMJvlMI9o6sGZSEH_aWMTlDKstL8lY,1614 -setuptools/_distutils/command/bdist.py,sha256=Zzg5OBMzubEd7zcvm5YrtqS3N-0pAUj9VJCCWEBFa7g,5409 -setuptools/_distutils/command/bdist_dumb.py,sha256=OldkwJUb4oNzmacrPFfYuRbXK4saYg1cVt5K8UBjn7I,4666 -setuptools/_distutils/command/bdist_rpm.py,sha256=HvYKZCogAHRtwBYVpGxtMvfw7qfFb2-4443kc0WGJ_k,22016 -setuptools/_distutils/command/build.py,sha256=u5v5dE6F44KfPTdnERYpP0XKS0sScia8WA6RicJidpA,5585 -setuptools/_distutils/command/build_clib.py,sha256=1FWSWDWik2R-Dswms5S2fGt0ApahAfHzcRpSG4QKNUA,7693 -setuptools/_distutils/command/build_ext.py,sha256=fQfpwzP77lEtfQ7aZzAZ8sGTgXxocl2LgaZmk8Vfmj4,31514 -setuptools/_distutils/command/build_py.py,sha256=j9VIdUua3_ZSLEW6BWuWNxUIHWmV7brZDTZDtIzn5D4,16544 -setuptools/_distutils/command/build_scripts.py,sha256=6YpD8tuybY2p9msIXc0zG5CCJUX4J1WsFEuodhdmWtw,5605 +setuptools/_distutils/command/bdist.py,sha256=EpbYBIrW4QTYrA6G8uUJIKZaLmj8w4S5KWnXzmr6hQo,5408 +setuptools/_distutils/command/bdist_dumb.py,sha256=FvvNgx_B7ypjf7rMxFNNBOsuF_Dj_OV8L4dmkULhQKM,4665 +setuptools/_distutils/command/bdist_rpm.py,sha256=QNQku4v38GcOcctHGNbRVoYv5mVMVcexnmCxh9fqpGw,22013 +setuptools/_distutils/command/build.py,sha256=XDgkAsMp_jLX9mj-6ESdf7GK_8RuX9kwILwXOhN1GaM,5584 +setuptools/_distutils/command/build_clib.py,sha256=stRzgT6gdXMTmsEi8PyudEO32ZDC7iP--sdUErcMuOs,7684 +setuptools/_distutils/command/build_ext.py,sha256=2poWttNAhj3Y45ZddgIVMwXjNXAdUcAOO_sc0wh6anQ,31503 +setuptools/_distutils/command/build_py.py,sha256=LK_l_5gnFv6D02YtyJRBp5kE3SWmHVEC7CbBKe2tjk8,16537 +setuptools/_distutils/command/build_scripts.py,sha256=cp6WiXTaEd8LWwxizpnFSmbCOSizPLclAHFFsqxRqqs,5604 setuptools/_distutils/command/check.py,sha256=f7QOy4LkKUXiRyyti4orzCJX9Z8sY_uOyMYUADADG6g,4872 -setuptools/_distutils/command/clean.py,sha256=UvsVh_xrDx48CZC9ZM2gk8l_-FP65soHLsrh5nfENy0,2595 -setuptools/_distutils/command/config.py,sha256=0bV9VhyxrLs5f4gdXAA8Ulq8_X1hTffaIXqgobdeFu0,13078 -setuptools/_distutils/command/install.py,sha256=kbXnkHk7rv1xvHEN7b285ouKVsoYi3cNa2RsT1fkVoM,30165 -setuptools/_distutils/command/install_data.py,sha256=Xkyi69RZzeB0LZnA54s1lAXU_15oOlkaF9SiOhBhHlA,2763 +setuptools/_distutils/command/clean.py,sha256=VCRg7BPVdLXgtevEi7t_iChJW6k6fOaO0GyqR_m_MRw,2594 +setuptools/_distutils/command/config.py,sha256=FU8kAanpAvaaecBbRZTvZ7lcoxxBXq5_nTufwOyZUXg,13077 +setuptools/_distutils/command/install.py,sha256=5h_6BldPSUPUkYDzdY1t6Jiqaw21yBZZokpkMVaBnyo,30153 +setuptools/_distutils/command/install_data.py,sha256=NgW_xUoUqcBGjGFr2VHrkYFejVqeAmwsGSu_fGQb384,2762 setuptools/_distutils/command/install_egg_info.py,sha256=Cv69kqrFORuwb1I1owe-IxyK0ZANirqGgiLyxcYSnBI,2788 -setuptools/_distutils/command/install_headers.py,sha256=H1JMCAYpuDzCUva2m1AkprHfnTiZqNSq_UHXEGaxzQo,1181 -setuptools/_distutils/command/install_lib.py,sha256=FwMicMNktfnr6kzqtD-10YbiCXng-W9DbTcyWggyD7o,8410 -setuptools/_distutils/command/install_scripts.py,sha256=c_kfAU7LKSKETupXr6kdE2K4S_pWc5jj7rqhEpO9fyY,1933 +setuptools/_distutils/command/install_headers.py,sha256=v-QcVkjaWX5yf0xaup9_KySanVlmd6LhuzEhGpmTiTU,1180 +setuptools/_distutils/command/install_lib.py,sha256=v3we1bymtqvE-j_7yCSnb4a0Jy32s3z1SLZzF91NpjY,8409 +setuptools/_distutils/command/install_scripts.py,sha256=oiYYD6IhTx9F4CQMfz5LQeGT1y5hZrndxbKBYSvzTa8,1932 setuptools/_distutils/command/py37compat.py,sha256=EoJC8gVYMIv2tA1NpVA2XDyCT1qGp4BEn7aX_5ve1gw,672 -setuptools/_distutils/command/register.py,sha256=1ZkaxvsQQeGy_VW42nJka0FeUMHu9pCdKfA7avspZqY,11818 -setuptools/_distutils/command/sdist.py,sha256=_1eV7GZYs2cYnDlab5OR4b3IgRs2WF9UdgMnSKoQoW8,19196 -setuptools/_distutils/command/upload.py,sha256=zn7ph7ft-L9zUw_UmNSTffBY90qRD8cFLixaDAXt_YQ,7492 +setuptools/_distutils/command/register.py,sha256=q8kKVA-6IPWbgHPBbc8HvWwRi9DXerjnyiMgMG1fu8A,11817 +setuptools/_distutils/command/sdist.py,sha256=JkT1SJQUgtlZyjFmyqx0lOL45tDb9I9Dn38iz9ySb-k,19232 +setuptools/_distutils/command/upload.py,sha256=jsb3Kj3XQtNqwwvtc1WUt_Jk8AEXIehjEXIj3dInv6M,7491 setuptools/_distutils/config.py,sha256=NrQjaUO9B88P-JtOfww3BMt9rSn1TirU4G7u0ut5FrM,4911 -setuptools/_distutils/core.py,sha256=td9vxB2oqIsyBCv-wnedZRWZrzYb3uIGilNtle2N2tg,9397 -setuptools/_distutils/cygwinccompiler.py,sha256=KGamMxV6dIb_IopimrVdN7Gqi5vCT1wrU0qgqrq6DFs,11942 +setuptools/_distutils/core.py,sha256=2zrS7rdu7Oe2143xsmCld8H61IbSpwnru9GDeSCQLbY,9397 +setuptools/_distutils/cygwinccompiler.py,sha256=hBv-OShb_uKvLjo_E2uqtQLEJNBBXTFglvf6mzbUN8o,11924 setuptools/_distutils/debug.py,sha256=N6MrTAqK6l9SVk6tWweR108PM8Ol7qNlfyV-nHcLhsY,139 setuptools/_distutils/dep_util.py,sha256=9pqhyGw2q2HGGGXAOpbbezj024aAr_47xDfXz5Fas7U,3414 -setuptools/_distutils/dir_util.py,sha256=J4nOKIoP32yARHEYPIRxKDr5dDDDZ_9rUWwR79Fl6LI,8072 -setuptools/_distutils/dist.py,sha256=BWq3AXCfawVM6tz4rkglJIkwDSkby_Rq6NJYyWQpbiA,50190 +setuptools/_distutils/dir_util.py,sha256=Ob0omB4OlZZXfFQtalVoIY6CgIrOkD5YZfATYv2DXZg,8072 +setuptools/_distutils/dist.py,sha256=YU6OeLdWPDWMg-GRCeykT21fOp7PxAYn1uwnoRpI-uM,50174 setuptools/_distutils/errors.py,sha256=ZtBwnhDpQA2bxIazPXNDQ25uNxM4p2omsaSRNpV3rpE,3589 setuptools/_distutils/extension.py,sha256=F0TBNjYkMmte_Yg1bhKVHXSNWWNFEPIDUgwhuHdkox8,10270 -setuptools/_distutils/fancy_getopt.py,sha256=n4QHj6LtDTdBn4bqgZ_rqGVhtFw9tvpnI6k8HbepyiY,17901 -setuptools/_distutils/file_util.py,sha256=YCXV_p8yCNywx6wGdnfmBQx7aJPCt7vdcR6AXjL20GQ,8213 +setuptools/_distutils/fancy_getopt.py,sha256=njv20bPVKKusIRbs8Md1YNWlGZQV1mW5fWPNkdYx-QI,17899 +setuptools/_distutils/file_util.py,sha256=koQCT7uz5wVTVGy-gdsFFPFQO5GfIhc06JUYbIX5V08,8212 setuptools/_distutils/filelist.py,sha256=rOKJPBvuLSjElfYuOwju95AzR3Ev5lvJoCJvI_XvZ9g,13715 setuptools/_distutils/log.py,sha256=725W7ISJzoSYNtLnEP1FwZe_IMUn1Xq6NEYwFbXg63k,1201 -setuptools/_distutils/msvc9compiler.py,sha256=4wXPx2KlT4xcoLuM_RZ7O-LK9kwEm4OXpj72Fh6cTfQ,30204 -setuptools/_distutils/msvccompiler.py,sha256=4j7mR6JWMh9Xt9V0slOqNcu-BIbeqrd5mnxzOEYkxqM,23580 +setuptools/_distutils/msvc9compiler.py,sha256=X2Xf2g-RMKzb_B4MIihiO3ogyTFjJNV1xRWpZTsbbSA,30188 +setuptools/_distutils/msvccompiler.py,sha256=Vus9UyDuNCT_PfZjwu253wL0v5PiQ9miiMZmdIro5wM,23577 setuptools/_distutils/py38compat.py,sha256=gZ-NQ5c6ufwVEkJ0BwkbrqG9TvWirVJIrVGqhgvaY-Q,217 setuptools/_distutils/py39compat.py,sha256=vkxjv22H1bhToalClz3M0UUD8Xr21klbUBTQoVQxx20,639 setuptools/_distutils/spawn.py,sha256=E6Il74CIINCRjakXUcWqSWjfC_sdp4Qtod0Bw5y_NNQ,3495 -setuptools/_distutils/sysconfig.py,sha256=i4rh3y4EyDk05eKa2wY0MmrmWZvDyz_b8Y149P4Imtg,18774 -setuptools/_distutils/text_file.py,sha256=tLjIJVBu7VMY2ZamSpQ9aBv0kbvX9_Abt26cjAAgHiQ,12096 -setuptools/_distutils/unixccompiler.py,sha256=7EpD-X7nAPYnnA8C12YdJaXezog2xtOegCjcFFMOGUc,15602 -setuptools/_distutils/util.py,sha256=Qx17Q8C68fT4FvaXeBfeY5Pzu76I6SlYneZAGCqXCtE,18097 -setuptools/_distutils/version.py,sha256=6HV4l0tHESXxMJMDwd5Fn8Y9_U8ivZIowFCNXhCSnRM,12952 +setuptools/_distutils/sysconfig.py,sha256=BbXNQAF9_tErImHCfSori3188FwSw2TUFqLBvU1BLdg,18928 +setuptools/_distutils/text_file.py,sha256=SBgU_IeHYRZMvmmqyE6I8qXAbh1Z-wd60Hf0Yv97Cls,12085 +setuptools/_distutils/unixccompiler.py,sha256=HYO3TXHm5kLGSsIdf9ytVLYCzUpdLQMt4Jd2NN7duzQ,15601 +setuptools/_distutils/util.py,sha256=bef-Z_j0XzPU2E1AHJQNvGYNovSxdiJMa3JIbanQm7g,18099 +setuptools/_distutils/version.py,sha256=9dCa7JcCWXBrfGUsv7Zzvqm-Mrf7yaK6cC5xRzx3iqg,12951 setuptools/_distutils/versionpredicate.py,sha256=mkg9LtyF3EWox-KnbBx08gKV8zu0ymIl1izIho2-f7k,5205 setuptools/_entry_points.py,sha256=FL1tONMODSygpiA_3rN_46k-HSmKqf3LgoxJdUpvox8,2282 setuptools/_imp.py,sha256=HmF91IbitRfsD5z-g4_wmcuH-RahyIONbPgiCOFgtzA,2392 setuptools/_importlib.py,sha256=1RLRzpNCPKEJRbUPVIPU1-H9dzUXulyL6N_ryxnjEwc,1311 setuptools/_itertools.py,sha256=pZAgXNz6tRPUFnHAaKJ90xAgD0gLPemcE1396Zgz73o,675 -setuptools/_path.py,sha256=9GdbEur6f_lWmokar-Y-DDyds-XmzYnXrcBy0DExwDw,749 -setuptools/_reqs.py,sha256=ApdTOmDFyK7hbHDnAH8VwhtVD5kvnOthyMNTmrUeFXs,501 +setuptools/_normalization.py,sha256=Fh5Uj_HDxyBo8AkL206XiyIjLsC5X_pnQuZOU5S8GMw,3722 +setuptools/_path.py,sha256=5xWH5ZZEJVcp_b0JjcAyTuTX2iz1H3F2Yti7fPIxueU,1056 +setuptools/_reqs.py,sha256=1UTUBFswyoz1BiCQ-ofVlHNBpFYQ1eiNjraQsARoklk,882 setuptools/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 setuptools/_vendor/__pycache__/__init__.cpython-310.pyc,, setuptools/_vendor/__pycache__/ordered_set.cpython-310.pyc,, setuptools/_vendor/__pycache__/typing_extensions.cpython-310.pyc,, setuptools/_vendor/__pycache__/zipp.cpython-310.pyc,, -setuptools/_vendor/importlib_metadata/__init__.py,sha256=xRXwTtvg4EAYuBotYeGawbjraQD4GFIvKgMClxApCDY,30130 +setuptools/_vendor/importlib_metadata/__init__.py,sha256=fQEsJb7Gs_9Vq9V0xHICB0EFxNRGyxubr4w4ZFmGcxY,26498 setuptools/_vendor/importlib_metadata/__pycache__/__init__.cpython-310.pyc,, setuptools/_vendor/importlib_metadata/__pycache__/_adapters.cpython-310.pyc,, setuptools/_vendor/importlib_metadata/__pycache__/_collections.cpython-310.pyc,, @@ -262,13 +262,15 @@ setuptools/_vendor/importlib_metadata/__pycache__/_compat.cpython-310.pyc,, setuptools/_vendor/importlib_metadata/__pycache__/_functools.cpython-310.pyc,, setuptools/_vendor/importlib_metadata/__pycache__/_itertools.cpython-310.pyc,, setuptools/_vendor/importlib_metadata/__pycache__/_meta.cpython-310.pyc,, +setuptools/_vendor/importlib_metadata/__pycache__/_py39compat.cpython-310.pyc,, setuptools/_vendor/importlib_metadata/__pycache__/_text.cpython-310.pyc,, -setuptools/_vendor/importlib_metadata/_adapters.py,sha256=B6fCi5-8mLVDFUZj3krI5nAo-mKp1dH_qIavyIyFrJs,1862 +setuptools/_vendor/importlib_metadata/_adapters.py,sha256=i8S6Ib1OQjcILA-l4gkzktMZe18TaeUNI49PLRp6OBU,2454 setuptools/_vendor/importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743 -setuptools/_vendor/importlib_metadata/_compat.py,sha256=cotBaMUB-2pIRZboQnWp9fEqm6Dwlypndn-EEn0bj5M,1828 +setuptools/_vendor/importlib_metadata/_compat.py,sha256=GtdqmFy_ykVSTkz6MdGL2g3V5kxvQKHTWxKZCk5Q59Q,1859 setuptools/_vendor/importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895 setuptools/_vendor/importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068 -setuptools/_vendor/importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO23qEygbVdF9r-6qh-hjE,1154 +setuptools/_vendor/importlib_metadata/_meta.py,sha256=v5e1ZDG7yZTH3h7TjbS5bM5p8AGzMPVOu8skDMv4h6k,1165 +setuptools/_vendor/importlib_metadata/_py39compat.py,sha256=2Tk5twb_VgLCY-1NEAQjdZp_S9OFMC-pUzP2isuaPsQ,1098 setuptools/_vendor/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166 setuptools/_vendor/importlib_resources/__init__.py,sha256=evPm12kLgYqTm-pbzm60bOuumumT8IpBNWFp0uMyrzE,506 setuptools/_vendor/importlib_resources/__pycache__/__init__.cpython-310.pyc,, @@ -281,18 +283,18 @@ setuptools/_vendor/importlib_resources/__pycache__/abc.cpython-310.pyc,, setuptools/_vendor/importlib_resources/__pycache__/readers.cpython-310.pyc,, setuptools/_vendor/importlib_resources/__pycache__/simple.cpython-310.pyc,, setuptools/_vendor/importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504 -setuptools/_vendor/importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741 -setuptools/_vendor/importlib_resources/_compat.py,sha256=nFBCGMvImglrqgYkb9aPgOj68-h6xbw-ca94XOv1-zs,2706 +setuptools/_vendor/importlib_resources/_common.py,sha256=jSC4xfLdcMNbtbWHtpzbFkNa0W7kvf__nsYn14C_AEU,5457 +setuptools/_vendor/importlib_resources/_compat.py,sha256=L8HTWyAC_MIKuxWZuw0zvTq5qmUA0ttrvK941OzDKU8,2925 setuptools/_vendor/importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884 -setuptools/_vendor/importlib_resources/_legacy.py,sha256=TMLkx6aEM6U8xIREPXqGZrMbUhTiPUuPl6ESD7RdYj4,3494 -setuptools/_vendor/importlib_resources/abc.py,sha256=MvTJJXajbl74s36Gyeesf76egtbFnh-TMtzQMVhFWXo,3886 -setuptools/_vendor/importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566 -setuptools/_vendor/importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836 +setuptools/_vendor/importlib_resources/_legacy.py,sha256=0TKdZixxLWA-xwtAZw4HcpqJmj4Xprx1Zkcty0gTRZY,3481 +setuptools/_vendor/importlib_resources/abc.py,sha256=Icr2IJ2QtH7vvAB9vC5WRJ9KBoaDyJa7KUs8McuROzo,5140 +setuptools/_vendor/importlib_resources/readers.py,sha256=PZsi5qacr2Qn3KHw4qw3Gm1MzrBblPHoTdjqjH7EKWw,3581 +setuptools/_vendor/importlib_resources/simple.py,sha256=0__2TQBTQoqkajYmNPt1HxERcReAT6boVKJA328pr04,2576 setuptools/_vendor/jaraco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 setuptools/_vendor/jaraco/__pycache__/__init__.cpython-310.pyc,, setuptools/_vendor/jaraco/__pycache__/context.cpython-310.pyc,, setuptools/_vendor/jaraco/__pycache__/functools.cpython-310.pyc,, -setuptools/_vendor/jaraco/context.py,sha256=7X1tpCLc5EN45iWGzGcsH0Unx62REIkvtRvglj0SiUA,5420 +setuptools/_vendor/jaraco/context.py,sha256=vlyDzb_PvZ9H7R9bbTr_CMRnveW5Dc56eC7eyd_GfoA,7460 setuptools/_vendor/jaraco/functools.py,sha256=ap1qoXaNABOx897366NTMEd2objrqAoSO1zuxZPjcmM,13512 setuptools/_vendor/jaraco/text/__init__.py,sha256=KfFGMerrkN_0V0rgtJVx-9dHt3tW7i_uJypjwEcLtC0,15517 setuptools/_vendor/jaraco/text/__pycache__/__init__.cpython-310.pyc,, @@ -303,50 +305,32 @@ setuptools/_vendor/more_itertools/__pycache__/recipes.cpython-310.pyc,, setuptools/_vendor/more_itertools/more.py,sha256=0rB_mibFR51sq33UlAI_bWfaNdsYNnJr1v6S0CaW7QA,117959 setuptools/_vendor/more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256 setuptools/_vendor/ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130 -setuptools/_vendor/packaging/__about__.py,sha256=ugASIO2w1oUyH8_COqQ2X_s0rDhjbhQC3yJocD03h2c,661 -setuptools/_vendor/packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497 -setuptools/_vendor/packaging/__pycache__/__about__.cpython-310.pyc,, +setuptools/_vendor/packaging/__init__.py,sha256=7BlJ_DcIt1zv01UQcZLozidczzNcivKj66zIBkRL3R4,501 setuptools/_vendor/packaging/__pycache__/__init__.cpython-310.pyc,, +setuptools/_vendor/packaging/__pycache__/_elffile.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/_manylinux.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/_musllinux.cpython-310.pyc,, +setuptools/_vendor/packaging/__pycache__/_parser.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/_structures.cpython-310.pyc,, +setuptools/_vendor/packaging/__pycache__/_tokenizer.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/markers.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/requirements.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/specifiers.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/tags.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/utils.cpython-310.pyc,, setuptools/_vendor/packaging/__pycache__/version.cpython-310.pyc,, -setuptools/_vendor/packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488 -setuptools/_vendor/packaging/_musllinux.py,sha256=_KGgY_qc7vhMGpoqss25n2hiLCNKRtvz9mCrS7gkqyc,4378 +setuptools/_vendor/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266 +setuptools/_vendor/packaging/_manylinux.py,sha256=uZ821PBqQrokhUbwe7E0UodEraMHqzoSgTvfJ8MIl30,8813 +setuptools/_vendor/packaging/_musllinux.py,sha256=mvPk7FNjjILKRLIdMxR7IvJ1uggLgCszo-L9rjfpi0M,2524 +setuptools/_vendor/packaging/_parser.py,sha256=jjFjSqNf7W2-Ta6YUkywK0P4d2i0Bz_MqLOfl7O1Tkw,9399 setuptools/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 -setuptools/_vendor/packaging/markers.py,sha256=lihRgqpZjLM-JW-vxlLPqU3kmVe79g9vypy1kxmTRuQ,8493 -setuptools/_vendor/packaging/requirements.py,sha256=Opd0FjqgdEiWkzBLyo1oLU0Dj01uIFwTAnAJQrr6j2A,4700 -setuptools/_vendor/packaging/specifiers.py,sha256=LRQ0kFsHrl5qfcFNEEJrIFYsnIHQUJXY9fIsakTrrqE,30110 -setuptools/_vendor/packaging/tags.py,sha256=lmsnGNiJ8C4D_Pf9PbM0qgbZvD9kmB9lpZBQUZa3R_Y,15699 -setuptools/_vendor/packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200 -setuptools/_vendor/packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665 -setuptools/_vendor/pyparsing/__init__.py,sha256=52QH3lgPbJhba0estckoGPHRH8JvQSSCGoWiEn2m0bU,9159 -setuptools/_vendor/pyparsing/__pycache__/__init__.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/actions.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/common.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/core.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/exceptions.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/helpers.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/results.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/testing.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/unicode.cpython-310.pyc,, -setuptools/_vendor/pyparsing/__pycache__/util.cpython-310.pyc,, -setuptools/_vendor/pyparsing/actions.py,sha256=wU9i32e0y1ymxKE3OUwSHO-SFIrt1h_wv6Ws0GQjpNU,6426 -setuptools/_vendor/pyparsing/common.py,sha256=lFL97ooIeR75CmW5hjURZqwDCTgruqltcTCZ-ulLO2Q,12936 -setuptools/_vendor/pyparsing/core.py,sha256=u8GptQE_H6wMkl8OZhxeK1aAPIDXXNgwdShORBwBVS4,213310 -setuptools/_vendor/pyparsing/diagram/__init__.py,sha256=f_EfxahqrdkRVahmTwLJXkZ9EEDKNd-O7lBbpJYlE1g,23668 -setuptools/_vendor/pyparsing/diagram/__pycache__/__init__.cpython-310.pyc,, -setuptools/_vendor/pyparsing/exceptions.py,sha256=3LbSafD32NYb1Tzt85GHNkhEAU1eZkTtNSk24cPMemo,9023 -setuptools/_vendor/pyparsing/helpers.py,sha256=QpUOjW0-psvueMwWb9bQpU2noqKCv98_wnw1VSzSdVo,39129 -setuptools/_vendor/pyparsing/results.py,sha256=HgNvWVXBdQP-Q6PtJfoCEeOJk2nwEvG-2KVKC5sGA30,25341 -setuptools/_vendor/pyparsing/testing.py,sha256=7tu4Abp4uSeJV0N_yEPRmmNUhpd18ZQP3CrX41DM814,13402 -setuptools/_vendor/pyparsing/unicode.py,sha256=fwuhMj30SQ165Cv7HJpu-rSxGbRm93kN9L4Ei7VGc1Y,10787 -setuptools/_vendor/pyparsing/util.py,sha256=kq772O5YSeXOSdP-M31EWpbH_ayj7BMHImBYo9xPD5M,6805 +setuptools/_vendor/packaging/_tokenizer.py,sha256=czGibL-4oPofx1pCSt_hrozNbHlOPrqGv6m-0d-iTdo,5148 +setuptools/_vendor/packaging/markers.py,sha256=HDPXE0_MPBSwsw_9upez8t8mdrqUGrgiOG_qyQy-W30,8161 +setuptools/_vendor/packaging/requirements.py,sha256=4nOKheaBbVEQXTGSqaOGTy1Tkg7J_sEno3u8jxC-baw,3264 +setuptools/_vendor/packaging/specifiers.py,sha256=-3ajZ5CkQrjNW5H8NPjvCV2RBgr-w9wcYBdb8kjPBfg,39046 +setuptools/_vendor/packaging/tags.py,sha256=fOKnZVfiU3oc9CPSzjJUsMk5VTfgOfpNhWobUH0sAlg,18065 +setuptools/_vendor/packaging/utils.py,sha256=es0cCezKspzriQ-3V88h3yJzxz028euV2sUwM61kE-o,4355 +setuptools/_vendor/packaging/version.py,sha256=_ULefmddLDLJ9VKRFAXhshEd0zP8OYPhcjCPfYolUbo,16295 setuptools/_vendor/tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396 setuptools/_vendor/tomli/__pycache__/__init__.cpython-310.pyc,, setuptools/_vendor/tomli/__pycache__/_parser.cpython-310.pyc,, @@ -358,7 +342,7 @@ setuptools/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQ setuptools/_vendor/typing_extensions.py,sha256=1uqi_RSlI7gos4eJB_NEV3d5wQwzTUQHd3_jrkbTo8Q,87149 setuptools/_vendor/zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425 setuptools/archive_util.py,sha256=6WShpDR_uGZOaORRfzBmJyTYtX9xtrhmXTFPqE8kL8s,7346 -setuptools/build_meta.py,sha256=gsOBREDts9wDiGUARQUQgINXnjZVeDmS74maDIqntCg,19595 +setuptools/build_meta.py,sha256=_Eqjbxj7EcfKbyKj7XzaOrJux3Syw4OhhZ_NlMdqykI,19612 setuptools/cli-32.exe,sha256=dfEuovMNnA2HLa3jRfMPVi5tk4R7alCbpTvuxtCyw0Y,65536 setuptools/cli-64.exe,sha256=KLABu5pyrnokJCv6skjXZ6GsXeyYHGcqOUT3oHI3Xpo,74752 setuptools/cli-arm64.exe,sha256=o9amxowudZ98NvNWh_a2DRY8LhoIRqTAekxABqltiMc,137216 @@ -391,21 +375,21 @@ setuptools/command/__pycache__/test.cpython-310.pyc,, setuptools/command/__pycache__/upload.cpython-310.pyc,, setuptools/command/__pycache__/upload_docs.cpython-310.pyc,, setuptools/command/alias.py,sha256=1sLQxZcNh6dDQpDmm4G7UGGTol83nY1NTPmNBbm2siI,2381 -setuptools/command/bdist_egg.py,sha256=QEIu1AkgS02j6ejonJY7kwGp6LNxfMeYZ3sxkd55ftA,16623 +setuptools/command/bdist_egg.py,sha256=sHC-ppJdCw-N3Vh4UlcMRISNAqT6V6NkyVVbmaGbFlY,16596 setuptools/command/bdist_rpm.py,sha256=PxrgoHPNaw2Pw2qNjjHDPC-Ay_IaDbCqP3d_5N-cj2A,1182 setuptools/command/build.py,sha256=cgkmzJhXFXw5lHMPgohJFyEByz8L7H9JurCnk2iRnFI,6589 setuptools/command/build_clib.py,sha256=Rq4Q5OoyF19o25XQHF1kzTO4XrA_fS1VJGO7Pw5hztk,4423 setuptools/command/build_ext.py,sha256=cYm4OvllPf6I9YE3cWlnjPqqE546Mc7nQTpdJ-yH3jg,15821 setuptools/command/build_py.py,sha256=CMoD9Gxd5vs8KfPVNFFD1cmJsCd3l0NJS5kdDTlx4Y4,14115 -setuptools/command/develop.py,sha256=5_Ss7ENd1_B_jVMY1tF5UV_y1Xu6jbVzAPG8oKeluGA,7012 -setuptools/command/dist_info.py,sha256=VdcNHtbPFGdPD_t20wxcROa4uALbyz1RnJMJEHQmrQU,4800 +setuptools/command/develop.py,sha256=e9NtSBt1FpBJkvjs6_5mo2o3peFQDUuEElsAam-Uhvc,6963 +setuptools/command/dist_info.py,sha256=xlXO2pRAOLIvcb2ZcFApwHP0KNwgzHs4UkRftDqjBGY,4074 setuptools/command/easy_install.py,sha256=sx7_Rwpa2wUvPZZTa7jLpY3shEL4Ti2d2u1yIUMahHs,85662 -setuptools/command/editable_wheel.py,sha256=yUCwBNcS75sBqcEOkW9CvRypgQ0dsMTn9646yXftAhk,31188 -setuptools/command/egg_info.py,sha256=BWo5Fw2_BT-vM3p3fgheRQP4zwym1TH38wqKPr5dmWs,26795 +setuptools/command/editable_wheel.py,sha256=QVEnc4EQFrYhXwFi8nuYr8wMnn0sNLYFg0VO94oVWNI,31903 +setuptools/command/egg_info.py,sha256=nqFdsAv-6i2rurx-fyxQKZnUt3D4SMeGwLGNsYJQg0k,28176 setuptools/command/install.py,sha256=CBdw9iITHAc0Zt1YE_8dSWY5BscuTJGrCe2jtEsnepk,5163 -setuptools/command/install_egg_info.py,sha256=pgZ64m_-kmtx3QISHN_kRtMiZC_Y8x1Nr1j38jXEbXQ,2226 +setuptools/command/install_egg_info.py,sha256=C4mdexnCBcCzWvsf8Gk1VYV-fGcmmXkLSpT-X08VWuA,2123 setuptools/command/install_lib.py,sha256=Uz42McsyHZAjrB6cw9E7Bz0xsaTbzxnM1PI9CBhiPtE,3875 -setuptools/command/install_scripts.py,sha256=APFFpt_lYUEo-viMtpXr-Hkwycwq8knTxSTNUu_TwHo,2612 +setuptools/command/install_scripts.py,sha256=QMwJMIQWNU89B3waJBb2zsQcNWtbTW00GYtMzfRF7uU,2714 setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628 setuptools/command/py36compat.py,sha256=7yLWzQj179Enx3pJ8V1cDDCzeLMFMd9XJXlK-iZTq5Y,4946 setuptools/command/register.py,sha256=kk3DxXCb5lXTvqnhfwx2g6q7iwbUmgTyXUCaBooBOUk,468 @@ -422,7 +406,7 @@ setuptools/config/__pycache__/_apply_pyprojecttoml.cpython-310.pyc,, setuptools/config/__pycache__/expand.cpython-310.pyc,, setuptools/config/__pycache__/pyprojecttoml.cpython-310.pyc,, setuptools/config/__pycache__/setupcfg.cpython-310.pyc,, -setuptools/config/_apply_pyprojecttoml.py,sha256=Ev1RwtQbPiD2za3di5T7ExY8T7TAvMIFot0efIHYzAY,13398 +setuptools/config/_apply_pyprojecttoml.py,sha256=2w0KpdHjHMBp_KoKQ0JkiCYwWXoluu2whMPBmZp5_IY,13816 setuptools/config/_validate_pyproject/__init__.py,sha256=5YXPW1sabVn5jpZ25sUjeF6ij3_4odJiwUWi4nRD2Dc,1038 setuptools/config/_validate_pyproject/__pycache__/__init__.cpython-310.pyc,, setuptools/config/_validate_pyproject/__pycache__/error_reporting.cpython-310.pyc,, @@ -433,36 +417,36 @@ setuptools/config/_validate_pyproject/__pycache__/formats.cpython-310.pyc,, setuptools/config/_validate_pyproject/error_reporting.py,sha256=vWiDs0hjlCBjZ_g4Xszsh97lIP9M4_JaLQ6MCQ26W9U,11266 setuptools/config/_validate_pyproject/extra_validations.py,sha256=wHzrgfdZUMRPBR1ke1lg5mhqRsBSbjEYOMsuFXQH9jY,1153 setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py,sha256=w749JgqKi8clBFcObdcbZVqsmF4oJ_QByhZ1SGbUFNw,1612 -setuptools/config/_validate_pyproject/fastjsonschema_validations.py,sha256=oqXSDfYecymwM2I40JGcTB-1P9vd7CtfSIW5kDxZQPM,269900 -setuptools/config/_validate_pyproject/formats.py,sha256=uMUnp4mLIjrQCTe6-LDjtqglmEFLfOW9E1ZZLqOzhMI,8736 +setuptools/config/_validate_pyproject/fastjsonschema_validations.py,sha256=SCElnuZe5jHntpTWswUf7TnXwxlVkTzM-NwlzmSsJB0,274907 +setuptools/config/_validate_pyproject/formats.py,sha256=5ocLimiqm4i8stjzHyBO89ejcVm-WKnmabDpSuIjE8U,9161 setuptools/config/expand.py,sha256=FQja-T8zG9bV_G1b7SBjWjsZNjvSbhg5vxFWhusSYoE,16319 -setuptools/config/pyprojecttoml.py,sha256=3dYGfZB_fjlwkumOQ2bhH2L4UJ3rDu0hN7HJjmd1Akc,19304 -setuptools/config/setupcfg.py,sha256=aqXdUuB5llJz9hZmQUjganZAyo34lHrRsK6wV1NzX2M,25198 +setuptools/config/pyprojecttoml.py,sha256=kEKCzDSQ9Pl2kAxnFNY9JgPQU5Fjp2s3grQYHou3gZ0,19598 +setuptools/config/setupcfg.py,sha256=HXUtUgOWSq9zWDLWjN18DC0VBDWKlbqPznE6fPUHM-E,25431 setuptools/dep_util.py,sha256=BDx1BkzNQntvAB4alypHbW5UVBzjqths000PrUL4Zqc,949 setuptools/depends.py,sha256=QYQIadr5DwLxPzkErhNt5hmRhvGhWxoXZMRXCm_jcQ0,5499 -setuptools/discovery.py,sha256=UZCeULUrV21xBTFBTTLNbta_rq2yjKa9kRwNXUIafRA,20799 -setuptools/dist.py,sha256=VXr5yqDtkSA_2FLAjWBS4CxAB_KqVX_EtMKrJaxcXtI,45252 +setuptools/discovery.py,sha256=a6bocT1n3D4MprhAZwEDkQNkmzQta4OspW2F0lY-Fzk,21087 +setuptools/dist.py,sha256=dqAIEBSzwfFarGi7MjHtluNiLBZrnmmk6B8604v90Ko,45710 setuptools/errors.py,sha256=2uToNIRA7dG995pf8ox8a4r7nJtP62-hpLhzsRirnx0,2464 setuptools/extension.py,sha256=jpsAdQvCBCkAuvmEXYI90TV4kNGO2Y13NqDr_PrvdhA,5591 -setuptools/extern/__init__.py,sha256=LYHS20uf-nl_zBPmrIzTxokYdiVMZNZBYVu6hd8c5zg,2512 +setuptools/extern/__init__.py,sha256=K1fgbtXn_E6b-MStbk4U2ULsjdZbkQNZGhd27nB26aU,2527 setuptools/extern/__pycache__/__init__.cpython-310.pyc,, setuptools/glob.py,sha256=1oZjbfjAHSXbgdhSuR6YGU8jKob9L8NtEmBYqcPTLYk,4873 setuptools/gui-32.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 setuptools/gui-64.exe,sha256=aYKMhX1IJLn4ULHgWX0sE0yREUt6B3TEHf_jOw6yNyE,75264 setuptools/gui-arm64.exe,sha256=TEFnOKDi-mq3ZszxqbCoCXTnM_lhUWjdIqBpr6fVs40,137728 setuptools/gui.exe,sha256=XBr0bHMA6Hpz2s9s9Bzjl-PwXfa9nH4ie0rFn4V2kWA,65536 -setuptools/installer.py,sha256=s6DQfsoICBJxbUqbduhOJtl1oG0S4yegRCg3EAs0i3M,3824 +setuptools/installer.py,sha256=TX94pR6wz_8EY4PdJBrDlNvPsBfhXprriknJMYHqh4w,5063 setuptools/launch.py,sha256=TyPT-Ic1T2EnYvGO26gfNRP4ysBlrhpbRjQxWsiO414,812 setuptools/logging.py,sha256=WT1k7lH5hL-mOxsdVkrBjGV468QSpwAShlQ6pP09H6g,1232 -setuptools/monkey.py,sha256=t6To7LEhTyOWRRZLwiFv7Eeg2mjHZlVmTdHD1DC94QM,4857 -setuptools/msvc.py,sha256=x6jsjA9JdUew6VAfHapIHgEjAjy-T5dxqjPCZr0Tt04,47724 +setuptools/monkey.py,sha256=isKFUEFh6zcAUTzOeS1l_4TyakYNyGNLT2T91fBbnDw,4697 +setuptools/msvc.py,sha256=C9W5dEZY19k5dH_7ZrY5HTnCWpQZsHQ-mvgN4DORNEk,47115 setuptools/namespaces.py,sha256=PMqGVPXPYQgjUTvEg9bGccRAkIODrQ6NmsDg_fwErwI,3093 -setuptools/package_index.py,sha256=NfCNavs6DWe7gZzBcLYYNCIeCYA_pzqLw-ayyKU8hRk,40329 +setuptools/package_index.py,sha256=2KY3YjJf1BvDROl-8gG3IbYSsl6ZeI6nIZZwzK8sU2M,39682 setuptools/py34compat.py,sha256=KYOd6ybRxjBW8NJmYD8t_UyyVmysppFXqHpFLdslGXU,245 setuptools/sandbox.py,sha256=mR83i-mu-ZUU_7TaMgYCeRSyzkqv8loJ_GR9xhS2DDw,14348 setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218 setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138 setuptools/unicode_utils.py,sha256=aOOFo4JGwAsiBttGYDsqFS7YqWQeZ2j6DWiCuctR_00,941 -setuptools/version.py,sha256=og_cuZQb0QI6ukKZFfZWPlr1HgJBPPn2vO2m_bI9ZTE,144 -setuptools/wheel.py,sha256=6LphzUKYfdLnIp9kIUzLGPY-F7MTJr4hiabB5almLps,8376 +setuptools/version.py,sha256=5m2OrzPFMbSv_T9_Ez6_aktytLZp5HB2N_2putFWim4,134 +setuptools/wheel.py,sha256=Ccq__F15z3Jn4xDbCRyJ9wFCf_MhrV4zufGKoc_ejtE,8608 setuptools/windows_support.py,sha256=KXrFWrteXjhIou0gGwlfBy0ttAszHP52ETq-2pc0mes,718 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/REQUESTED b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/REQUESTED similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/REQUESTED rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/REQUESTED diff --git a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/WHEEL similarity index 65% rename from dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/WHEEL index 385faab05..1f37c02f2 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/bitstring-3.1.9.dist-info/WHEEL +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/WHEEL @@ -1,5 +1,5 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) +Generator: bdist_wheel (0.40.0) Root-Is-Purelib: true Tag: py3-none-any diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/entry_points.txt similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/entry_points.txt rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/entry_points.txt diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/top_level.txt similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/top_level.txt rename to dependencies/windows_amd64/python/Lib/site-packages/setuptools-67.6.1.dist-info/top_level.txt diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/__init__.py index 6c24cc2b3..89f6f06ec 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/__init__.py @@ -77,7 +77,28 @@ def finalize_options(self): # Honor setup.cfg's options. dist.parse_config_files(ignore_option_errors=True) if dist.setup_requires: + _fetch_build_eggs(dist) + + +def _fetch_build_eggs(dist): + try: dist.fetch_build_eggs(dist.setup_requires) + except Exception as ex: + msg = """ + It is possible a package already installed in your system + contains an version that is invalid according to PEP 440. + You can try `pip install --use-pep517` as a workaround for this problem, + or rely on a new virtual environment. + + If the problem refers to a package that is not installed yet, + please contact that package's maintainers or distributors. + """ + if "InvalidVersion" in ex.__class__.__name__: + if hasattr(ex, "add_note"): + ex.add_note(msg) # PEP 678 + else: + dist.announce(f"\n{msg}\n") + raise def setup(**attrs): diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_collections.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_collections.py index 02556614a..5ad21cc7c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_collections.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_collections.py @@ -185,7 +185,7 @@ def bounds(self): return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item]) # some special values for the RangeMap - undefined_value = type(str('RangeValueUndefined'), (), {})() + undefined_value = type('RangeValueUndefined', (), {})() class Item(int): "RangeMap Item" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_msvccompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_msvccompiler.py index 8b4023c42..4f081c7e9 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_msvccompiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/_msvccompiler.py @@ -339,7 +339,6 @@ def compile( # noqa: C901 extra_postargs=None, depends=None, ): - if not self.initialized: self.initialize() compile_info = self._setup_compile( @@ -413,8 +412,7 @@ def compile( # noqa: C901 args = [self.cc] + compile_opts + pp_opts if add_cpp_opts: args.append('/EHsc') - args.append(input_opt) - args.append("/Fo" + obj) + args.extend((input_opt, "/Fo" + obj)) args.extend(extra_postargs) try: @@ -427,7 +425,6 @@ def compile( # noqa: C901 def create_static_lib( self, objects, output_libname, output_dir=None, debug=0, target_lang=None ): - if not self.initialized: self.initialize() objects, output_dir = self._fix_object_args(objects, output_dir) @@ -461,7 +458,6 @@ def link( build_temp=None, target_lang=None, ): - if not self.initialized: self.initialize() objects, output_dir = self._fix_object_args(objects, output_dir) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/bcppcompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/bcppcompiler.py index 5d6b86536..ba45ea2b9 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/bcppcompiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/bcppcompiler.py @@ -64,7 +64,6 @@ class BCPPCompiler(CCompiler): exe_extension = '.exe' def __init__(self, verbose=0, dry_run=0, force=0): - super().__init__(verbose, dry_run, force) # These executables are assumed to all be in the path. @@ -98,7 +97,6 @@ def compile( # noqa: C901 extra_postargs=None, depends=None, ): - macros, objects, extra_postargs, pp_opts, build = self._setup_compile( output_dir, macros, include_dirs, sources, depends, extra_postargs ) @@ -167,7 +165,6 @@ def compile( # noqa: C901 def create_static_lib( self, objects, output_libname, output_dir=None, debug=0, target_lang=None ): - (objects, output_dir) = self._fix_object_args(objects, output_dir) output_filename = self.library_filename(output_libname, output_dir=output_dir) @@ -200,7 +197,6 @@ def link( # noqa: C901 build_temp=None, target_lang=None, ): - # XXX this ignores 'build_temp'! should follow the lead of # msvccompiler.py @@ -219,7 +215,6 @@ def link( # noqa: C901 output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): - # Figure out linker args based on type of target. if target_desc == CCompiler.EXECUTABLE: startup_obj = 'c0w32' @@ -294,8 +289,7 @@ def link( # noqa: C901 ld_args.append(libfile) # some default libraries - ld_args.append('import32') - ld_args.append('cw32mt') + ld_args.extend(('import32', 'cw32mt')) # def file for export symbols ld_args.extend([',', def_file]) @@ -381,7 +375,6 @@ def preprocess( extra_preargs=None, extra_postargs=None, ): - (_, macros, include_dirs) = self._fix_compile_args(None, macros, include_dirs) pp_opts = gen_preprocess_options(macros, include_dirs) pp_args = ['cpp32.exe'] + pp_opts diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/ccompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/ccompiler.py index 646353111..1818fce90 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/ccompiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/ccompiler.py @@ -6,6 +6,7 @@ import sys import os import re +import warnings from .errors import ( CompileError, @@ -388,7 +389,7 @@ def _fix_compile_args(self, output_dir, macros, include_dirs): raise TypeError("'macros' (if supplied) must be a list of tuples") if include_dirs is None: - include_dirs = self.include_dirs + include_dirs = list(self.include_dirs) elif isinstance(include_dirs, (list, tuple)): include_dirs = list(include_dirs) + (self.include_dirs or []) else: @@ -824,9 +825,19 @@ def has_function( # noqa: C901 libraries=None, library_dirs=None, ): - """Return a boolean indicating whether funcname is supported on - the current platform. The optional arguments can be used to - augment the compilation environment. + """Return a boolean indicating whether funcname is provided as + a symbol on the current platform. The optional arguments can + be used to augment the compilation environment. + + The libraries argument is a list of flags to be passed to the + linker to make additional symbol definitions available for + linking. + + The includes and include_dirs arguments are deprecated. + Usually, supplying include files with function declarations + will cause function detection to fail even in cases where the + symbol is available for linking. + """ # this can't be included at module scope because it tries to # import math which might not be available at that point - maybe @@ -835,8 +846,12 @@ def has_function( # noqa: C901 if includes is None: includes = [] + else: + warnings.warn("includes is deprecated", DeprecationWarning) if include_dirs is None: include_dirs = [] + else: + warnings.warn("include_dirs is deprecated", DeprecationWarning) if libraries is None: libraries = [] if library_dirs is None: @@ -846,6 +861,23 @@ def has_function( # noqa: C901 try: for incl in includes: f.write("""#include "%s"\n""" % incl) + if not includes: + # Use "char func(void);" as the prototype to follow + # what autoconf does. This prototype does not match + # any well-known function the compiler might recognize + # as a builtin, so this ends up as a true link test. + # Without a fake prototype, the test would need to + # know the exact argument types, and the has_function + # interface does not provide that level of information. + f.write( + """\ +#ifdef __cplusplus +extern "C" +#endif +char %s(void); +""" + % funcname + ) f.write( """\ int main (int argc, char **argv) { @@ -871,7 +903,9 @@ def has_function( # noqa: C901 except (LinkError, TypeError): return False else: - os.remove(os.path.join(self.output_dir or '', "a.out")) + os.remove( + self.executable_filename("a.out", output_dir=self.output_dir or '') + ) finally: for fn in objects: os.remove(fn) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cmd.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cmd.py index 918db8532..3860c3ff1 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cmd.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cmd.py @@ -160,7 +160,7 @@ def dump_options(self, header=None, indent=""): header = "command options for '%s':" % self.get_command_name() self.announce(indent + header, level=logging.INFO) indent = indent + " " - for (option, _, _) in self.user_options: + for option, _, _ in self.user_options: option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] @@ -291,7 +291,7 @@ def set_undefined_options(self, src_cmd, *option_pairs): # Option_pairs: list of (src_option, dst_option) tuples src_cmd_obj = self.distribution.get_command_obj(src_cmd) src_cmd_obj.ensure_finalized() - for (src_option, dst_option) in option_pairs: + for src_option, dst_option in option_pairs: if getattr(self, dst_option) is None: setattr(self, dst_option, getattr(src_cmd_obj, src_option)) @@ -325,7 +325,7 @@ def get_sub_commands(self): run for the current distribution. Return a list of command names. """ commands = [] - for (cmd_name, method) in self.sub_commands: + for cmd_name, method in self.sub_commands: if method is None or method(self): commands.append(cmd_name) return commands diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist.py index bf0baab0d..6329039ce 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist.py @@ -33,7 +33,6 @@ def append(self, item): class bdist(Command): - description = "create a built (binary) distribution" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py index 071da77e1..01dd79079 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_dumb.py @@ -14,7 +14,6 @@ class bdist_dumb(Command): - description = "create a \"dumb\" built distribution" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py index 340527b08..3ed608b47 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/bdist_rpm.py @@ -21,7 +21,6 @@ class bdist_rpm(Command): - description = "create an RPM distribution" user_options = [ @@ -554,7 +553,7 @@ def _make_spec_file(self): # noqa: C901 ('postun', 'post_uninstall', None), ] - for (rpm_opt, attr, default) in script_options: + for rpm_opt, attr, default in script_options: # Insert contents of file referred to, if no file is referred to # use 'default' as contents of script val = getattr(self, attr) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build.py index c3ab410f2..cc9b367ef 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build.py @@ -16,7 +16,6 @@ def show_compilers(): class build(Command): - description = "build everything needed to install" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_clib.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_clib.py index f90c56643..b3f679b67 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_clib.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_clib.py @@ -28,7 +28,6 @@ def show_compilers(): class build_clib(Command): - description = "build C/C++ libraries used by Python extensions" user_options = [ @@ -103,7 +102,7 @@ def run(self): self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples - for (name, value) in self.define: + for name, value in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: @@ -155,14 +154,14 @@ def get_library_names(self): return None lib_names = [] - for (lib_name, build_info) in self.libraries: + for lib_name, build_info in self.libraries: lib_names.append(lib_name) return lib_names def get_source_files(self): self.check_library_list(self.libraries) filenames = [] - for (lib_name, build_info) in self.libraries: + for lib_name, build_info in self.libraries: sources = build_info.get('sources') if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError( @@ -175,7 +174,7 @@ def get_source_files(self): return filenames def build_libraries(self, libraries): - for (lib_name, build_info) in libraries: + for lib_name, build_info in libraries: sources = build_info.get('sources') if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_ext.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_ext.py index f4c0eccd4..fbeec342c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_ext.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_ext.py @@ -39,7 +39,6 @@ def show_compilers(): class build_ext(Command): - description = "build C/C++ extensions (compile/link to build directory)" # XXX thoughts on how to deal with complex command-line options like @@ -328,7 +327,7 @@ def run(self): # noqa: C901 self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples - for (name, value) in self.define: + for name, value in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: @@ -721,7 +720,7 @@ def get_export_symbols(self, ext): name = ext.name.split('.')[-1] try: # Unicode module name support as defined in PEP-489 - # https://www.python.org/dev/peps/pep-0489/#export-hook-name + # https://peps.python.org/pep-0489/#export-hook-name name.encode('ascii') except UnicodeEncodeError: suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii') diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_py.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_py.py index 9f7832445..d9df95922 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_py.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_py.py @@ -14,7 +14,6 @@ class build_py(Command): - description = "\"build\" pure Python modules (copy to build directory)" user_options = [ @@ -310,7 +309,7 @@ def get_module_outfile(self, build_dir, package, module): def get_outputs(self, include_bytecode=1): modules = self.find_all_modules() outputs = [] - for (package, module, module_file) in modules: + for package, module, module_file in modules: package = package.split('.') filename = self.get_module_outfile(self.build_lib, package, module) outputs.append(filename) @@ -352,7 +351,7 @@ def build_module(self, module, module_file, package): def build_modules(self): modules = self.find_modules() - for (package, module, module_file) in modules: + for package, module, module_file in modules: # Now "build" the module -- ie. copy the source file to # self.build_lib (the build directory for Python source). # (Actually, it gets copied to the directory for this package @@ -375,7 +374,7 @@ def build_packages(self): # Now loop over the modules we found, "building" each one (just # copy it to self.build_lib). - for (package_, module, module_file) in modules: + for package_, module, module_file in modules: assert package == package_ self.build_module(module, module_file, package) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_scripts.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_scripts.py index 87174f6bb..ce222f1e5 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_scripts.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/build_scripts.py @@ -22,7 +22,6 @@ class build_scripts(Command): - description = "\"build\" scripts (copy and fixup #! line)" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/clean.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/clean.py index d6eb3ebad..9413f7cfc 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/clean.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/clean.py @@ -11,7 +11,6 @@ class clean(Command): - description = "clean up temporary files from 'build' command" user_options = [ ('build-base=', 'b', "base build directory (default: 'build.build-base')"), diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/config.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/config.py index 8bf0e4893..494d97d16 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/config.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/config.py @@ -21,7 +21,6 @@ class config(Command): - description = "prepare to build" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install.py index 08d2f8812..a7ac4e607 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install.py @@ -180,7 +180,6 @@ def _pypy_hack(name): class install(Command): - description = "install everything from build directory" user_options = [ @@ -609,7 +608,7 @@ def _expand_attrs(self, attrs): for attr in attrs: val = getattr(self, attr) if val is not None: - if os.name == 'posix' or os.name == 'nt': + if os.name in ('posix', 'nt'): val = os.path.expanduser(val) val = subst_vars(val, self.config_vars) setattr(self, attr, val) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_data.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_data.py index d92ed87a6..7ba35eef8 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_data.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_data.py @@ -11,7 +11,6 @@ class install_data(Command): - description = "install data files" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_headers.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_headers.py index 1cdee823d..085272c1a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_headers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_headers.py @@ -8,7 +8,6 @@ # XXX force is never used class install_headers(Command): - description = "install C/C++ header files" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_lib.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_lib.py index 840d3403c..be4c24332 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_lib.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_lib.py @@ -16,7 +16,6 @@ class install_lib(Command): - description = "install all Python modules (extensions and pure Python)" # The byte-compilation options are a tad confusing. Here are the diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_scripts.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_scripts.py index ec6ec5aca..20f07aaa2 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_scripts.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/install_scripts.py @@ -12,7 +12,6 @@ class install_scripts(Command): - description = "install scripts (Python or otherwise)" user_options = [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/register.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/register.py index 55c1045ec..c19aabb91 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/register.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/register.py @@ -17,7 +17,6 @@ class register(PyPIRCCommand): - description = "register the distribution with the Python package index" user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/sdist.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/sdist.py index 5cfd4c145..ac489726c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/sdist.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/sdist.py @@ -33,7 +33,6 @@ def show_formats(): class sdist(Command): - description = "create a source distribution (tarball, zip file, etc.)" def checking_metadata(self): @@ -235,7 +234,7 @@ def add_defaults(self): """Add all the default files to self.filelist: - README or README.txt - setup.py - - test/test*.py + - tests/test*.py and test/test*.py - all pure Python modules mentioned in setup script - all files pointed by package_data (build_py) - all files defined in data_files. @@ -293,7 +292,7 @@ def _add_defaults_standards(self): self.warn("standard file '%s' not found" % fn) def _add_defaults_optional(self): - optional = ['test/test*.py', 'setup.cfg'] + optional = ['tests/test*.py', 'test/test*.py', 'setup.cfg'] for pattern in optional: files = filter(os.path.isfile, glob(pattern)) self.filelist.extend(files) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/upload.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/upload.py index 16e15d8b6..caf15f04a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/upload.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/command/upload.py @@ -27,7 +27,6 @@ class upload(PyPIRCCommand): - description = "upload binary package to PyPI" user_options = PyPIRCCommand.user_options + [ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/core.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/core.py index 34cafbcee..05d297199 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/core.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/core.py @@ -132,7 +132,7 @@ class found in 'cmdclass' is used in place of the default, which is # our Distribution (see below). klass = attrs.get('distclass') if klass: - del attrs['distclass'] + attrs.pop('distclass') else: klass = Distribution diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py index f15b8eee2..47efa377c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py @@ -43,7 +43,7 @@ # VS2013 / MSVC 12.0 1800: ['msvcr120'], # VS2015 / MSVC 14.0 - 1900: ['ucrt', 'vcruntime140'], + 1900: ['vcruntime140'], 2000: RangeMap.undefined_value, }, ) @@ -84,7 +84,6 @@ class CygwinCCompiler(UnixCCompiler): exe_extension = ".exe" def __init__(self, verbose=0, dry_run=0, force=0): - super().__init__(verbose, dry_run, force) status, details = check_config_h() @@ -118,7 +117,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): @property def gcc_version(self): - # Older numpy dependend on this existing to check for ancient + # Older numpy depended on this existing to check for ancient # gcc versions. This doesn't make much sense with clang etc so # just hardcode to something recent. # https://github.com/numpy/numpy/pull/20333 @@ -133,7 +132,7 @@ def gcc_version(self): def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): """Compiles the source by spawning GCC and windres if needed.""" - if ext == '.rc' or ext == '.res': + if ext in ('.rc', '.res'): # gcc needs '.res' and '.rc' compiled to object files !!! try: self.spawn(["windres", "-i", src, "-o", obj]) @@ -269,7 +268,6 @@ class Mingw32CCompiler(CygwinCCompiler): compiler_type = 'mingw32' def __init__(self, verbose=0, dry_run=0, force=0): - super().__init__(verbose, dry_run, force) shared_option = "-shared" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dir_util.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dir_util.py index 80f776490..23dc3392a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dir_util.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dir_util.py @@ -227,7 +227,7 @@ def remove_tree(directory, verbose=1, dry_run=0): # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if abspath in _path_created: - del _path_created[abspath] + _path_created.pop(abspath) except OSError as exc: log.warning("error removing %s: %s", directory, exc) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dist.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dist.py index d7458a052..7c0f0e5b7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dist.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/dist.py @@ -237,9 +237,9 @@ def __init__(self, attrs=None): # noqa: C901 options = attrs.get('options') if options is not None: del attrs['options'] - for (command, cmd_options) in options.items(): + for command, cmd_options in options.items(): opt_dict = self.get_option_dict(command) - for (opt, val) in cmd_options.items(): + for opt, val in cmd_options.items(): opt_dict[opt] = ("setup script", val) if 'licence' in attrs: @@ -253,7 +253,7 @@ def __init__(self, attrs=None): # noqa: C901 # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! - for (key, val) in attrs.items(): + for key, val in attrs.items(): if hasattr(self.metadata, "set_" + key): getattr(self.metadata, "set_" + key)(val) elif hasattr(self.metadata, key): @@ -414,7 +414,7 @@ def parse_config_files(self, filenames=None): # noqa: C901 # to set Distribution options. if 'global' in self.command_options: - for (opt, (src, val)) in self.command_options['global'].items(): + for opt, (src, val) in self.command_options['global'].items(): alias = self.negative_opt.get(opt) try: if alias: @@ -585,7 +585,7 @@ def _parse_command_opts(self, parser, args): # noqa: C901 cmd_class.help_options, list ): help_option_found = 0 - for (help_option, short, desc, func) in cmd_class.help_options: + for help_option, short, desc, func in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found = 1 if callable(func): @@ -603,7 +603,7 @@ def _parse_command_opts(self, parser, args): # noqa: C901 # Put the options from the command-line into their official # holding pen, the 'command_options' dictionary. opt_dict = self.get_option_dict(command) - for (name, value) in vars(opts).items(): + for name, value in vars(opts).items(): opt_dict[name] = ("command line", value) return args @@ -696,11 +696,11 @@ def handle_display_options(self, option_order): for option in self.display_options: is_display_option[option[0]] = 1 - for (opt, val) in option_order: + for opt, val in option_order: if val and is_display_option.get(opt): opt = translate_longopt(opt) value = getattr(self.metadata, "get_" + opt)() - if opt in ['keywords', 'platforms']: + if opt in ('keywords', 'platforms'): print(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): print('\n'.join(value)) @@ -887,7 +887,7 @@ def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 if DEBUG: self.announce(" setting options for '%s' command:" % command_name) - for (option, (source, value)) in option_dict.items(): + for option, (source, value) in option_dict.items(): if DEBUG: self.announce(" {} = {} (from {})".format(option, value, source)) try: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/fancy_getopt.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/fancy_getopt.py index 6abb884d3..3b887dc5a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/fancy_getopt.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/fancy_getopt.py @@ -113,7 +113,7 @@ def get_attr_name(self, long_option): def _check_alias_dict(self, aliases, what): assert isinstance(aliases, dict) - for (alias, opt) in aliases.items(): + for alias, opt in aliases.items(): if alias not in self.option_index: raise DistutilsGetoptError( ("invalid %s '%s': " "option '%s' not defined") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/file_util.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/file_util.py index 1b7cd53bd..7c6990664 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/file_util.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/file_util.py @@ -176,7 +176,6 @@ def copy_file( # noqa: C901 # XXX I suspect this is Unix-specific -- need porting help! def move_file(src, dst, verbose=1, dry_run=0): # noqa: C901 - """Move a file 'src' to 'dst'. If 'dst' is a directory, the file will be moved into it with the same name; otherwise, 'src' is just renamed to 'dst'. Return the new full name of the file. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvc9compiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvc9compiler.py index a4714a559..f9f9f2d84 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvc9compiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvc9compiler.py @@ -391,7 +391,7 @@ def initialize(self, plat_name=None): # noqa: C901 # to cross compile, you use 'x86_amd64'. # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) - if plat_name == get_platform() or plat_name == 'win32': + if plat_name in (get_platform(), 'win32'): # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] else: @@ -499,7 +499,6 @@ def compile( # noqa: C901 extra_postargs=None, depends=None, ): - if not self.initialized: self.initialize() compile_info = self._setup_compile( @@ -586,7 +585,6 @@ def compile( # noqa: C901 def create_static_lib( self, objects, output_libname, output_dir=None, debug=0, target_lang=None ): - if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) @@ -619,7 +617,6 @@ def link( # noqa: C901 build_temp=None, target_lang=None, ): - if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvccompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvccompiler.py index 59ebe99ca..c3823e257 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvccompiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/msvccompiler.py @@ -389,7 +389,6 @@ def compile( # noqa: C901 extra_postargs=None, depends=None, ): - if not self.initialized: self.initialize() compile_info = self._setup_compile( @@ -476,7 +475,6 @@ def compile( # noqa: C901 def create_static_lib( self, objects, output_libname, output_dir=None, debug=0, target_lang=None ): - if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) @@ -509,7 +507,6 @@ def link( # noqa: C901 build_temp=None, target_lang=None, ): - if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/sysconfig.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/sysconfig.py index 0ec69366f..a40a7231b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/sysconfig.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/sysconfig.py @@ -130,12 +130,20 @@ def get_python_inc(plat_specific=0, prefix=None): return getter(resolved_prefix, prefix, plat_specific) +@pass_none +def _extant(path): + """ + Replace path with None if it doesn't exist. + """ + return path if os.path.exists(path) else None + + def _get_python_inc_posix(prefix, spec_prefix, plat_specific): if IS_PYPY and sys.version_info < (3, 8): return os.path.join(prefix, 'include') return ( _get_python_inc_posix_python(plat_specific) - or _get_python_inc_from_config(plat_specific, spec_prefix) + or _extant(_get_python_inc_from_config(plat_specific, spec_prefix)) or _get_python_inc_posix_prefix(prefix) ) @@ -474,7 +482,6 @@ def parse_makefile(fn, g=None): # noqa: C901 del notdone[name] if name.startswith('PY_') and name[3:] in renamed_variables: - name = name[3:] if name not in done: done[name] = value diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/text_file.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/text_file.py index 7274d4b16..36f947e51 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/text_file.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/text_file.py @@ -180,7 +180,6 @@ def readline(self): # noqa: C901 line = None if self.strip_comments and line: - # Look for the first "#" in the line. If none, never # mind. If we find one and it's the first character, or # is not preceded by "\", then it starts a comment -- @@ -255,7 +254,7 @@ def readline(self): # noqa: C901 # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate - if (line == '' or line == '\n') and self.skip_blanks: + if line in ('', '\n') and self.skip_blanks: continue if self.join_lines: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/unixccompiler.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/unixccompiler.py index 4bf2e6a68..6ca2332ae 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/unixccompiler.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/unixccompiler.py @@ -103,7 +103,6 @@ def _linker_params(linker_cmd, compiler_cmd): class UnixCCompiler(CCompiler): - compiler_type = 'unix' # These are used by CCompiler in two places: the constructor sets diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/util.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/util.py index 8668b4369..7ef47176e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/util.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/util.py @@ -228,7 +228,7 @@ def _subst(match): import warnings warnings.warn( - "shell/Perl-style substitions are deprecated", + "shell/Perl-style substitutions are deprecated", DeprecationWarning, ) return repl diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/version.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/version.py index e29e26575..74c40d7bf 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_distutils/version.py @@ -169,7 +169,6 @@ def parse(self, vstring): self.prerelease = None def __str__(self): - if self.version[2] == 0: vstring = '.'.join(map(str, self.version[0:2])) else: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_normalization.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_normalization.py new file mode 100644 index 000000000..8ba7c802f --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_normalization.py @@ -0,0 +1,117 @@ +""" +Helpers for normalization as expected in wheel/sdist/module file names +and core metadata +""" +import re +import warnings +from inspect import cleandoc +from pathlib import Path +from typing import Union + +from setuptools.extern import packaging + +from ._deprecation_warning import SetuptoolsDeprecationWarning + +_Path = Union[str, Path] + +# https://packaging.python.org/en/latest/specifications/core-metadata/#name +_VALID_NAME = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.I) +_UNSAFE_NAME_CHARS = re.compile(r"[^A-Z0-9.]+", re.I) + + +def safe_identifier(name: str) -> str: + """Make a string safe to be used as Python identifier. + >>> safe_identifier("12abc") + '_12abc' + >>> safe_identifier("__editable__.myns.pkg-78.9.3_local") + '__editable___myns_pkg_78_9_3_local' + """ + safe = re.sub(r'\W|^(?=\d)', '_', name) + assert safe.isidentifier() + return safe + + +def safe_name(component: str) -> str: + """Escape a component used as a project name according to Core Metadata. + >>> safe_name("hello world") + 'hello-world' + >>> safe_name("hello?world") + 'hello-world' + """ + # See pkg_resources.safe_name + return _UNSAFE_NAME_CHARS.sub("-", component) + + +def safe_version(version: str) -> str: + """Convert an arbitrary string into a valid version string. + >>> safe_version("1988 12 25") + '1988.12.25' + >>> safe_version("v0.2.1") + '0.2.1' + >>> safe_version("v0.2?beta") + '0.2b0' + >>> safe_version("v0.2 beta") + '0.2b0' + >>> safe_version("ubuntu lts") + Traceback (most recent call last): + ... + setuptools.extern.packaging.version.InvalidVersion: Invalid version: 'ubuntu.lts' + """ + v = version.replace(' ', '.') + try: + return str(packaging.version.Version(v)) + except packaging.version.InvalidVersion: + attempt = _UNSAFE_NAME_CHARS.sub("-", v) + return str(packaging.version.Version(attempt)) + + +def best_effort_version(version: str) -> str: + """Convert an arbitrary string into a version-like string. + >>> best_effort_version("v0.2 beta") + '0.2b0' + + >>> import warnings + >>> warnings.simplefilter("ignore", category=SetuptoolsDeprecationWarning) + >>> best_effort_version("ubuntu lts") + 'ubuntu.lts' + """ + # See pkg_resources.safe_version + try: + return safe_version(version) + except packaging.version.InvalidVersion: + msg = f"""Invalid version: {version!r}. + !!\n\n + ################### + # Invalid version # + ################### + {version!r} is not valid according to PEP 440.\n + Please make sure specify a valid version for your package. + Also note that future releases of setuptools may halt the build process + if an invalid version is given. + \n\n!! + """ + warnings.warn(cleandoc(msg), SetuptoolsDeprecationWarning) + v = version.replace(' ', '.') + return safe_name(v) + + +def filename_component(value: str) -> str: + """Normalize each component of a filename (e.g. distribution/version part of wheel) + Note: ``value`` needs to be already normalized. + >>> filename_component("my-pkg") + 'my_pkg' + """ + return value.replace("-", "_").strip("_") + + +def safer_name(value: str) -> str: + """Like ``safe_name`` but can be used as filename component for wheel""" + # See bdist_wheel.safer_name + return filename_component(safe_name(value)) + + +def safer_best_effort_version(value: str) -> str: + """Like ``best_effort_version`` but can be used as filename component for wheel""" + # See bdist_wheel.safer_verion + # TODO: Replace with only safe_version in the future (no need for best effort) + return filename_component(best_effort_version(value)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_path.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_path.py index 3767523b7..b99d9dadc 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_path.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_path.py @@ -1,4 +1,5 @@ import os +import sys from typing import Union _Path = Union[str, os.PathLike] @@ -26,4 +27,11 @@ def same_path(p1: _Path, p2: _Path) -> bool: >>> same_path("a", "a/b") False """ - return os.path.normpath(p1) == os.path.normpath(p2) + return normpath(p1) == normpath(p2) + + +def normpath(filename: _Path) -> str: + """Normalize a file/dir name for comparison purposes.""" + # See pkg_resources.normalize_path for notes about cygwin + file = os.path.abspath(filename) if sys.platform == 'cygwin' else filename + return os.path.normcase(os.path.realpath(os.path.normpath(file))) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_reqs.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_reqs.py index ca7241746..5d5b927fd 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_reqs.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_reqs.py @@ -1,9 +1,13 @@ +from typing import Callable, Iterable, Iterator, TypeVar, Union, overload + import setuptools.extern.jaraco.text as text +from setuptools.extern.packaging.requirements import Requirement -from pkg_resources import Requirement +_T = TypeVar("_T") +_StrOrIter = Union[str, Iterable[str]] -def parse_strings(strs): +def parse_strings(strs: _StrOrIter) -> Iterator[str]: """ Yield requirement strings for each specification in `strs`. @@ -12,8 +16,18 @@ def parse_strings(strs): return text.join_continuation(map(text.drop_comment, text.yield_lines(strs))) -def parse(strs): +@overload +def parse(strs: _StrOrIter) -> Iterator[Requirement]: + ... + + +@overload +def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: + ... + + +def parse(strs, parser=Requirement): """ - Deprecated drop-in replacement for pkg_resources.parse_requirements. + Replacement for ``pkg_resources.parse_requirements`` that uses ``packaging``. """ - return map(Requirement, parse_strings(strs)) + return map(parser, parse_strings(strs)) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py index 292e0c6d4..886421437 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/__init__.py @@ -14,7 +14,7 @@ import posixpath import collections -from . import _adapters, _meta +from . import _adapters, _meta, _py39compat from ._collections import FreezableDefaultDict, Pair from ._compat import ( NullFinder, @@ -29,7 +29,7 @@ from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import List, Mapping, Optional, Union +from typing import List, Mapping, Optional __all__ = [ @@ -139,6 +139,7 @@ class DeprecatedTuple: 1 """ + # Do not remove prior to 2023-05-01 or Python 3.13 _warn = functools.partial( warnings.warn, "EntryPoint tuple interface is deprecated. Access members by name.", @@ -157,6 +158,15 @@ class EntryPoint(DeprecatedTuple): See `the packaging docs on entry points `_ for more information. + + >>> ep = EntryPoint( + ... name=None, group=None, value='package.module:attr [extra1, extra2]') + >>> ep.module + 'package.module' + >>> ep.attr + 'attr' + >>> ep.extras + ['extra1', 'extra2'] """ pattern = re.compile( @@ -180,6 +190,10 @@ class EntryPoint(DeprecatedTuple): following the attr, and following any extras. """ + name: str + value: str + group: str + dist: Optional['Distribution'] = None def __init__(self, name, value, group): @@ -208,24 +222,32 @@ def attr(self): @property def extras(self): match = self.pattern.match(self.value) - return list(re.finditer(r'\w+', match.group('extras') or '')) + return re.findall(r'\w+', match.group('extras') or '') def _for(self, dist): vars(self).update(dist=dist) return self - def __iter__(self): + def matches(self, **params): """ - Supply iter so one may construct dicts of EntryPoints by name. + EntryPoint matches the given parameters. + + >>> ep = EntryPoint(group='foo', name='bar', value='bing:bong [extra1, extra2]') + >>> ep.matches(group='foo') + True + >>> ep.matches(name='bar', value='bing:bong [extra1, extra2]') + True + >>> ep.matches(group='foo', name='other') + False + >>> ep.matches() + True + >>> ep.matches(extras=['extra1', 'extra2']) + True + >>> ep.matches(module='bing') + True + >>> ep.matches(attr='bong') + True """ - msg = ( - "Construction of dict of EntryPoints is deprecated in " - "favor of EntryPoints." - ) - warnings.warn(msg, DeprecationWarning) - return iter((self.name, self)) - - def matches(self, **params): attrs = (getattr(self, param) for param in params) return all(map(operator.eq, params.values(), attrs)) @@ -251,77 +273,7 @@ def __hash__(self): return hash(self._key()) -class DeprecatedList(list): - """ - Allow an otherwise immutable object to implement mutability - for compatibility. - - >>> recwarn = getfixture('recwarn') - >>> dl = DeprecatedList(range(3)) - >>> dl[0] = 1 - >>> dl.append(3) - >>> del dl[3] - >>> dl.reverse() - >>> dl.sort() - >>> dl.extend([4]) - >>> dl.pop(-1) - 4 - >>> dl.remove(1) - >>> dl += [5] - >>> dl + [6] - [1, 2, 5, 6] - >>> dl + (6,) - [1, 2, 5, 6] - >>> dl.insert(0, 0) - >>> dl - [0, 1, 2, 5] - >>> dl == [0, 1, 2, 5] - True - >>> dl == (0, 1, 2, 5) - True - >>> len(recwarn) - 1 - """ - - __slots__ = () - - _warn = functools.partial( - warnings.warn, - "EntryPoints list interface is deprecated. Cast to list if needed.", - DeprecationWarning, - stacklevel=pypy_partial(2), - ) - - def _wrap_deprecated_method(method_name: str): # type: ignore - def wrapped(self, *args, **kwargs): - self._warn() - return getattr(super(), method_name)(*args, **kwargs) - - return method_name, wrapped - - locals().update( - map( - _wrap_deprecated_method, - '__setitem__ __delitem__ append reverse extend pop remove ' - '__iadd__ insert sort'.split(), - ) - ) - - def __add__(self, other): - if not isinstance(other, tuple): - self._warn() - other = tuple(other) - return self.__class__(tuple(self) + other) - - def __eq__(self, other): - if not isinstance(other, tuple): - self._warn() - other = tuple(other) - - return tuple(self).__eq__(other) - - -class EntryPoints(DeprecatedList): +class EntryPoints(tuple): """ An immutable collection of selectable EntryPoint objects. """ @@ -332,14 +284,6 @@ def __getitem__(self, name): # -> EntryPoint: """ Get the EntryPoint in self matching name. """ - if isinstance(name, int): - warnings.warn( - "Accessing entry points by index is deprecated. " - "Cast to tuple if needed.", - DeprecationWarning, - stacklevel=2, - ) - return super().__getitem__(name) try: return next(iter(self.select(name=name))) except StopIteration: @@ -350,7 +294,7 @@ def select(self, **params): Select entry points from self that match the given parameters (typically group and/or name). """ - return EntryPoints(ep for ep in self if ep.matches(**params)) + return EntryPoints(ep for ep in self if _py39compat.ep_matches(ep, **params)) @property def names(self): @@ -363,10 +307,6 @@ def names(self): def groups(self): """ Return the set of all groups of all entry points. - - For coverage while SelectableGroups is present. - >>> EntryPoints().groups - set() """ return {ep.group for ep in self} @@ -382,101 +322,6 @@ def _from_text(text): ) -class Deprecated: - """ - Compatibility add-in for mapping to indicate that - mapping behavior is deprecated. - - >>> recwarn = getfixture('recwarn') - >>> class DeprecatedDict(Deprecated, dict): pass - >>> dd = DeprecatedDict(foo='bar') - >>> dd.get('baz', None) - >>> dd['foo'] - 'bar' - >>> list(dd) - ['foo'] - >>> list(dd.keys()) - ['foo'] - >>> 'foo' in dd - True - >>> list(dd.values()) - ['bar'] - >>> len(recwarn) - 1 - """ - - _warn = functools.partial( - warnings.warn, - "SelectableGroups dict interface is deprecated. Use select.", - DeprecationWarning, - stacklevel=pypy_partial(2), - ) - - def __getitem__(self, name): - self._warn() - return super().__getitem__(name) - - def get(self, name, default=None): - self._warn() - return super().get(name, default) - - def __iter__(self): - self._warn() - return super().__iter__() - - def __contains__(self, *args): - self._warn() - return super().__contains__(*args) - - def keys(self): - self._warn() - return super().keys() - - def values(self): - self._warn() - return super().values() - - -class SelectableGroups(Deprecated, dict): - """ - A backward- and forward-compatible result from - entry_points that fully implements the dict interface. - """ - - @classmethod - def load(cls, eps): - by_group = operator.attrgetter('group') - ordered = sorted(eps, key=by_group) - grouped = itertools.groupby(ordered, by_group) - return cls((group, EntryPoints(eps)) for group, eps in grouped) - - @property - def _all(self): - """ - Reconstruct a list of all entrypoints from the groups. - """ - groups = super(Deprecated, self).values() - return EntryPoints(itertools.chain.from_iterable(groups)) - - @property - def groups(self): - return self._all.groups - - @property - def names(self): - """ - for coverage: - >>> SelectableGroups().names - set() - """ - return self._all.names - - def select(self, **params): - if not params: - return self - return self._all.select(**params) - - class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" @@ -501,7 +346,7 @@ def __repr__(self): return f'' -class Distribution: +class Distribution(metaclass=abc.ABCMeta): """A Python distribution package.""" @abc.abstractmethod @@ -520,7 +365,7 @@ def locate_file(self, path): """ @classmethod - def from_name(cls, name): + def from_name(cls, name: str): """Return the Distribution for the given package name. :param name: The name of the distribution package to search for. @@ -528,13 +373,13 @@ def from_name(cls, name): package, if found. :raises PackageNotFoundError: When the named package's distribution metadata cannot be found. + :raises ValueError: When an invalid value is supplied for name. """ - for resolver in cls._discover_resolvers(): - dists = resolver(DistributionFinder.Context(name=name)) - dist = next(iter(dists), None) - if dist is not None: - return dist - else: + if not name: + raise ValueError("A distribution name is required.") + try: + return next(cls.discover(name=name)) + except StopIteration: raise PackageNotFoundError(name) @classmethod @@ -763,7 +608,7 @@ def __new__(cls, root): return super().__new__(cls) def __init__(self, root): - self.root = str(root) + self.root = root def joinpath(self, child): return pathlib.Path(self.root, child) @@ -928,13 +773,26 @@ def _normalized_name(self): normalized name from the file system path. """ stem = os.path.basename(str(self._path)) - return self._name_from_stem(stem) or super()._normalized_name + return ( + pass_none(Prepared.normalize)(self._name_from_stem(stem)) + or super()._normalized_name + ) - def _name_from_stem(self, stem): - name, ext = os.path.splitext(stem) + @staticmethod + def _name_from_stem(stem): + """ + >>> PathDistribution._name_from_stem('foo-3.0.egg-info') + 'foo' + >>> PathDistribution._name_from_stem('CherryPy-3.0.dist-info') + 'CherryPy' + >>> PathDistribution._name_from_stem('face.egg-info') + 'face' + >>> PathDistribution._name_from_stem('foo.bar') + """ + filename, ext = os.path.splitext(stem) if ext not in ('.dist-info', '.egg-info'): return - name, sep, rest = stem.partition('-') + name, sep, rest = filename.partition('-') return name @@ -974,29 +832,28 @@ def version(distribution_name): return distribution(distribution_name).version -def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: +_unique = functools.partial( + unique_everseen, + key=_py39compat.normalized_name, +) +""" +Wrapper for ``distributions`` to return unique distributions by name. +""" + + +def entry_points(**params) -> EntryPoints: """Return EntryPoint objects for all installed packages. Pass selection parameters (group or name) to filter the result to entry points matching those properties (see EntryPoints.select()). - For compatibility, returns ``SelectableGroups`` object unless - selection parameters are supplied. In the future, this function - will return ``EntryPoints`` instead of ``SelectableGroups`` - even when no selection parameters are supplied. - - For maximum future compatibility, pass selection parameters - or invoke ``.select`` with parameters on the result. - - :return: EntryPoints or SelectableGroups for all installed packages. + :return: EntryPoints for all installed packages. """ - norm_name = operator.attrgetter('_normalized_name') - unique = functools.partial(unique_everseen, key=norm_name) eps = itertools.chain.from_iterable( - dist.entry_points for dist in unique(distributions()) + dist.entry_points for dist in _unique(distributions()) ) - return SelectableGroups.load(eps).select(**params) + return EntryPoints(eps).select(**params) def files(distribution_name): diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py index aa460d3ed..e33cba5e4 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_adapters.py @@ -1,8 +1,20 @@ +import functools +import warnings import re import textwrap import email.message from ._text import FoldedCase +from ._compat import pypy_partial + + +# Do not remove prior to 2024-01-01 or Python 3.14 +_warn = functools.partial( + warnings.warn, + "Implicit None on return values is deprecated and will raise KeyErrors.", + DeprecationWarning, + stacklevel=pypy_partial(2), +) class Message(email.message.Message): @@ -39,6 +51,16 @@ def __init__(self, *args, **kwargs): def __iter__(self): return super().__iter__() + def __getitem__(self, item): + """ + Warn users that a ``KeyError`` can be expected when a + mising key is supplied. Ref python/importlib_metadata#371. + """ + res = super().__getitem__(item) + if res is None: + _warn() + return res + def _repair_headers(self): def redent(value): "Correct for RFC822 indentation" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py index ef3136f8d..84f9eea4f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_compat.py @@ -8,6 +8,7 @@ try: from typing import Protocol except ImportError: # pragma: no cover + # Python 3.7 compatibility from ..typing_extensions import Protocol # type: ignore diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py index 37ee43e6e..259b15ba1 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_meta.py @@ -30,18 +30,19 @@ def json(self) -> Dict[str, Union[str, List[str]]]: """ -class SimplePath(Protocol): +class SimplePath(Protocol[_T]): """ A minimal subset of pathlib.Path required by PathDistribution. """ - def joinpath(self) -> 'SimplePath': + def joinpath(self) -> _T: ... # pragma: no cover - def __truediv__(self) -> 'SimplePath': + def __truediv__(self, other: Union[str, _T]) -> _T: ... # pragma: no cover - def parent(self) -> 'SimplePath': + @property + def parent(self) -> _T: ... # pragma: no cover def read_text(self) -> str: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py new file mode 100644 index 000000000..cde4558fb --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_metadata/_py39compat.py @@ -0,0 +1,35 @@ +""" +Compatibility layer with Python 3.8/3.9 +""" +from typing import TYPE_CHECKING, Any, Optional + +if TYPE_CHECKING: # pragma: no cover + # Prevent circular imports on runtime. + from . import Distribution, EntryPoint +else: + Distribution = EntryPoint = Any + + +def normalized_name(dist: Distribution) -> Optional[str]: + """ + Honor name normalization for distributions that don't provide ``_normalized_name``. + """ + try: + return dist._normalized_name + except AttributeError: + from . import Prepared # -> delay to prevent circular imports. + + return Prepared.normalize(getattr(dist, "name", None) or dist.metadata['Name']) + + +def ep_matches(ep: EntryPoint, **params) -> bool: + """ + Workaround for ``EntryPoint`` objects without the ``matches`` method. + """ + try: + return ep.matches(**params) + except AttributeError: + from . import EntryPoint # -> delay to prevent circular imports. + + # Reconstruct the EntryPoint object to make sure it is compatible. + return EntryPoint(ep.name, ep.value, ep.group).matches(**params) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py index a12e2c75d..3c6de1cfb 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_common.py @@ -5,25 +5,58 @@ import contextlib import types import importlib +import inspect +import warnings +import itertools -from typing import Union, Optional +from typing import Union, Optional, cast from .abc import ResourceReader, Traversable from ._compat import wrap_spec Package = Union[types.ModuleType, str] +Anchor = Package -def files(package): - # type: (Package) -> Traversable +def package_to_anchor(func): """ - Get a Traversable resource from a package + Replace 'package' parameter as 'anchor' and warn about the change. + + Other errors should fall through. + + >>> files('a', 'b') + Traceback (most recent call last): + TypeError: files() takes from 0 to 1 positional arguments but 2 were given + """ + undefined = object() + + @functools.wraps(func) + def wrapper(anchor=undefined, package=undefined): + if package is not undefined: + if anchor is not undefined: + return func(anchor, package) + warnings.warn( + "First parameter to files is renamed to 'anchor'", + DeprecationWarning, + stacklevel=2, + ) + return func(package) + elif anchor is undefined: + return func() + return func(anchor) + + return wrapper + + +@package_to_anchor +def files(anchor: Optional[Anchor] = None) -> Traversable: + """ + Get a Traversable resource for an anchor. """ - return from_package(get_package(package)) + return from_package(resolve(anchor)) -def get_resource_reader(package): - # type: (types.ModuleType) -> Optional[ResourceReader] +def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: """ Return the package's loader if it's a ResourceReader. """ @@ -39,24 +72,39 @@ def get_resource_reader(package): return reader(spec.name) # type: ignore -def resolve(cand): - # type: (Package) -> types.ModuleType - return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand) +@functools.singledispatch +def resolve(cand: Optional[Anchor]) -> types.ModuleType: + return cast(types.ModuleType, cand) + + +@resolve.register +def _(cand: str) -> types.ModuleType: + return importlib.import_module(cand) + +@resolve.register +def _(cand: None) -> types.ModuleType: + return resolve(_infer_caller().f_globals['__name__']) -def get_package(package): - # type: (Package) -> types.ModuleType - """Take a package name or module object and return the module. - Raise an exception if the resolved module is not a package. +def _infer_caller(): """ - resolved = resolve(package) - if wrap_spec(resolved).submodule_search_locations is None: - raise TypeError(f'{package!r} is not a package') - return resolved + Walk the stack and find the frame of the first caller not in this module. + """ + + def is_this_file(frame_info): + return frame_info.filename == __file__ + + def is_wrapper(frame_info): + return frame_info.function == 'wrapper' + + not_this_file = itertools.filterfalse(is_this_file, inspect.stack()) + # also exclude 'wrapper' due to singledispatch in the call stack + callers = itertools.filterfalse(is_wrapper, not_this_file) + return next(callers).frame -def from_package(package): +def from_package(package: types.ModuleType): """ Return a Traversable object for the given package. @@ -67,7 +115,14 @@ def from_package(package): @contextlib.contextmanager -def _tempfile(reader, suffix=''): +def _tempfile( + reader, + suffix='', + # gh-93353: Keep a reference to call os.remove() in late Python + # finalization. + *, + _os_remove=os.remove, +): # Not using tempfile.NamedTemporaryFile as it leads to deeper 'try' # blocks due to the need to close the temporary file to work on Windows # properly. @@ -81,18 +136,35 @@ def _tempfile(reader, suffix=''): yield pathlib.Path(raw_path) finally: try: - os.remove(raw_path) + _os_remove(raw_path) except FileNotFoundError: pass +def _temp_file(path): + return _tempfile(path.read_bytes, suffix=path.name) + + +def _is_present_dir(path: Traversable) -> bool: + """ + Some Traversables implement ``is_dir()`` to raise an + exception (i.e. ``FileNotFoundError``) when the + directory doesn't exist. This function wraps that call + to always return a boolean and only return True + if there's a dir and it exists. + """ + with contextlib.suppress(FileNotFoundError): + return path.is_dir() + return False + + @functools.singledispatch def as_file(path): """ Given a Traversable object, return that object as a path on the local file system in a context manager. """ - return _tempfile(path.read_bytes, suffix=path.name) + return _temp_dir(path) if _is_present_dir(path) else _temp_file(path) @as_file.register(pathlib.Path) @@ -102,3 +174,34 @@ def _(path): Degenerate behavior for pathlib.Path objects. """ yield path + + +@contextlib.contextmanager +def _temp_path(dir: tempfile.TemporaryDirectory): + """ + Wrap tempfile.TemporyDirectory to return a pathlib object. + """ + with dir as result: + yield pathlib.Path(result) + + +@contextlib.contextmanager +def _temp_dir(path): + """ + Given a traversable dir, recursively replicate the whole tree + to the file system in a context manager. + """ + assert path.is_dir() + with _temp_path(tempfile.TemporaryDirectory()) as temp_dir: + yield _write_contents(temp_dir, path) + + +def _write_contents(target, source): + child = target.joinpath(source.name) + if source.is_dir(): + child.mkdir() + for item in source.iterdir(): + _write_contents(child, item) + else: + child.write_bytes(source.read_bytes()) + return child diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py index cb9fc820c..8b5b1d280 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_compat.py @@ -1,9 +1,12 @@ # flake8: noqa import abc +import os import sys import pathlib from contextlib import suppress +from typing import Union + if sys.version_info >= (3, 10): from zipfile import Path as ZipPath # type: ignore @@ -96,3 +99,10 @@ def wrap_spec(package): from . import _adapters return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) + + +if sys.version_info >= (3, 9): + StrPath = Union[str, os.PathLike[str]] +else: + # PathLike is only subscriptable at runtime in 3.9+ + StrPath = Union[str, "os.PathLike[str]"] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py index 1d5d3f1fb..b1ea8105d 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/_legacy.py @@ -27,8 +27,7 @@ def wrapper(*args, **kwargs): return wrapper -def normalize_path(path): - # type: (Any) -> str +def normalize_path(path: Any) -> str: """Normalize a path by ensuring it is a string. If the resulting string contains path separators, an exception is raised. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py index d39dc1adb..23b6aeafe 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/abc.py @@ -1,7 +1,13 @@ import abc -from typing import BinaryIO, Iterable, Text +import io +import itertools +import pathlib +from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional -from ._compat import runtime_checkable, Protocol +from ._compat import runtime_checkable, Protocol, StrPath + + +__all__ = ["ResourceReader", "Traversable", "TraversableResources"] class ResourceReader(metaclass=abc.ABCMeta): @@ -46,27 +52,34 @@ def contents(self) -> Iterable[str]: raise FileNotFoundError +class TraversalError(Exception): + pass + + @runtime_checkable class Traversable(Protocol): """ An object with a subset of pathlib.Path methods suitable for traversing directories and opening files. + + Any exceptions that occur when accessing the backing resource + may propagate unaltered. """ @abc.abstractmethod - def iterdir(self): + def iterdir(self) -> Iterator["Traversable"]: """ Yield Traversable objects in self """ - def read_bytes(self): + def read_bytes(self) -> bytes: """ Read contents of self as bytes """ with self.open('rb') as strm: return strm.read() - def read_text(self, encoding=None): + def read_text(self, encoding: Optional[str] = None) -> str: """ Read contents of self as text """ @@ -85,13 +98,32 @@ def is_file(self) -> bool: Return True if self is a file """ - @abc.abstractmethod - def joinpath(self, child): + def joinpath(self, *descendants: StrPath) -> "Traversable": """ - Return Traversable child in self + Return Traversable resolved with any descendants applied. + + Each descendant should be a path segment relative to self + and each may contain multiple levels separated by + ``posixpath.sep`` (``/``). """ + if not descendants: + return self + names = itertools.chain.from_iterable( + path.parts for path in map(pathlib.PurePosixPath, descendants) + ) + target = next(names) + matches = ( + traversable for traversable in self.iterdir() if traversable.name == target + ) + try: + match = next(matches) + except StopIteration: + raise TraversalError( + "Target not found during traversal.", target, list(names) + ) + return match.joinpath(*names) - def __truediv__(self, child): + def __truediv__(self, child: StrPath) -> "Traversable": """ Return Traversable child in self """ @@ -107,7 +139,8 @@ def open(self, mode='r', *args, **kwargs): accepted by io.TextIOWrapper. """ - @abc.abstractproperty + @property + @abc.abstractmethod def name(self) -> str: """ The base name of this object without any parent references. @@ -121,17 +154,17 @@ class TraversableResources(ResourceReader): """ @abc.abstractmethod - def files(self): + def files(self) -> "Traversable": """Return a Traversable object for the loaded package.""" - def open_resource(self, resource): + def open_resource(self, resource: StrPath) -> io.BufferedReader: return self.files().joinpath(resource).open('rb') - def resource_path(self, resource): + def resource_path(self, resource: Any) -> NoReturn: raise FileNotFoundError(resource) - def is_resource(self, path): + def is_resource(self, path: StrPath) -> bool: return self.files().joinpath(path).is_file() - def contents(self): + def contents(self) -> Iterator[str]: return (item.name for item in self.files().iterdir()) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py index f1190ca45..ab34db740 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/readers.py @@ -82,15 +82,13 @@ def is_dir(self): def is_file(self): return False - def joinpath(self, child): - # first try to find child in current paths - for file in self.iterdir(): - if file.name == child: - return file - # if it does not exist, construct it with the first path - return self._paths[0] / child - - __truediv__ = joinpath + def joinpath(self, *descendants): + try: + return super().joinpath(*descendants) + except abc.TraversalError: + # One of the paths did not resolve (a directory does not exist). + # Just return something that will not exist. + return self._paths[0].joinpath(*descendants) def open(self, *args, **kwargs): raise FileNotFoundError(f'{self} is not a file') diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py index da073cbdb..7770c922c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/importlib_resources/simple.py @@ -16,31 +16,28 @@ class SimpleReader(abc.ABC): provider. """ - @abc.abstractproperty - def package(self): - # type: () -> str + @property + @abc.abstractmethod + def package(self) -> str: """ The name of the package for which this reader loads resources. """ @abc.abstractmethod - def children(self): - # type: () -> List['SimpleReader'] + def children(self) -> List['SimpleReader']: """ Obtain an iterable of SimpleReader for available child containers (e.g. directories). """ @abc.abstractmethod - def resources(self): - # type: () -> List[str] + def resources(self) -> List[str]: """ Obtain available named resources for this virtual package. """ @abc.abstractmethod - def open_binary(self, resource): - # type: (str) -> BinaryIO + def open_binary(self, resource: str) -> BinaryIO: """ Obtain a File-like for a named resource. """ @@ -50,39 +47,12 @@ def name(self): return self.package.split('.')[-1] -class ResourceHandle(Traversable): - """ - Handle to a named resource in a ResourceReader. - """ - - def __init__(self, parent, name): - # type: (ResourceContainer, str) -> None - self.parent = parent - self.name = name # type: ignore - - def is_file(self): - return True - - def is_dir(self): - return False - - def open(self, mode='r', *args, **kwargs): - stream = self.parent.reader.open_binary(self.name) - if 'b' not in mode: - stream = io.TextIOWrapper(*args, **kwargs) - return stream - - def joinpath(self, name): - raise RuntimeError("Cannot traverse into a resource") - - class ResourceContainer(Traversable): """ Traversable container for a package's resources via its reader. """ - def __init__(self, reader): - # type: (SimpleReader) -> None + def __init__(self, reader: SimpleReader): self.reader = reader def is_dir(self): @@ -99,10 +69,30 @@ def iterdir(self): def open(self, *args, **kwargs): raise IsADirectoryError() + +class ResourceHandle(Traversable): + """ + Handle to a named resource in a ResourceReader. + """ + + def __init__(self, parent: ResourceContainer, name: str): + self.parent = parent + self.name = name # type: ignore + + def is_file(self): + return True + + def is_dir(self): + return False + + def open(self, mode='r', *args, **kwargs): + stream = self.parent.reader.open_binary(self.name) + if 'b' not in mode: + stream = io.TextIOWrapper(*args, **kwargs) + return stream + def joinpath(self, name): - return next( - traversable for traversable in self.iterdir() if traversable.name == name - ) + raise RuntimeError("Cannot traverse into a resource") class TraversableReader(TraversableResources, SimpleReader): diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/jaraco/context.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/jaraco/context.py index 87a4e3dca..b0d1ef37c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/jaraco/context.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/jaraco/context.py @@ -5,10 +5,18 @@ import tempfile import shutil import operator +import warnings @contextlib.contextmanager def pushd(dir): + """ + >>> tmp_path = getfixture('tmp_path') + >>> with pushd(tmp_path): + ... assert os.getcwd() == os.fspath(tmp_path) + >>> assert os.getcwd() != os.fspath(tmp_path) + """ + orig = os.getcwd() os.chdir(dir) try: @@ -29,6 +37,8 @@ def tarball_context(url, target_dir=None, runner=None, pushd=pushd): target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '') if runner is None: runner = functools.partial(subprocess.check_call, shell=True) + else: + warnings.warn("runner parameter is deprecated", DeprecationWarning) # In the tar command, use --strip-components=1 to strip the first path and # then # use -C to cause the files to be extracted to {target_dir}. This ensures @@ -48,6 +58,15 @@ def tarball_context(url, target_dir=None, runner=None, pushd=pushd): def infer_compression(url): """ Given a URL or filename, infer the compression code for tar. + + >>> infer_compression('http://foo/bar.tar.gz') + 'z' + >>> infer_compression('http://foo/bar.tgz') + 'z' + >>> infer_compression('file.bz') + 'j' + >>> infer_compression('file.xz') + 'J' """ # cheat and just assume it's the last two characters compression_indicator = url[-2:] @@ -61,6 +80,12 @@ def temp_dir(remover=shutil.rmtree): """ Create a temporary directory context. Pass a custom remover to override the removal behavior. + + >>> import pathlib + >>> with temp_dir() as the_dir: + ... assert os.path.isdir(the_dir) + ... _ = pathlib.Path(the_dir).joinpath('somefile').write_text('contents') + >>> assert not os.path.exists(the_dir) """ temp_dir = tempfile.mkdtemp() try: @@ -90,6 +115,12 @@ def repo_context(url, branch=None, quiet=True, dest_ctx=temp_dir): @contextlib.contextmanager def null(): + """ + A null context suitable to stand in for a meaningful context. + + >>> with null() as value: + ... assert value is None + """ yield @@ -112,6 +143,10 @@ class ExceptionTrap: ... raise ValueError("1 + 1 is not 3") >>> bool(trap) True + >>> trap.value + ValueError('1 + 1 is not 3') + >>> trap.tb + >>> with ExceptionTrap(ValueError) as trap: ... raise Exception() @@ -211,3 +246,43 @@ class suppress(contextlib.suppress, contextlib.ContextDecorator): ... {}[''] >>> key_error() """ + + +class on_interrupt(contextlib.ContextDecorator): + """ + Replace a KeyboardInterrupt with SystemExit(1) + + >>> def do_interrupt(): + ... raise KeyboardInterrupt() + >>> on_interrupt('error')(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 1 + >>> on_interrupt('error', code=255)(do_interrupt)() + Traceback (most recent call last): + ... + SystemExit: 255 + >>> on_interrupt('suppress')(do_interrupt)() + >>> with __import__('pytest').raises(KeyboardInterrupt): + ... on_interrupt('ignore')(do_interrupt)() + """ + + def __init__( + self, + action='error', + # py3.7 compat + # /, + code=1, + ): + self.action = action + self.code = code + + def __enter__(self): + return self + + def __exit__(self, exctype, excinst, exctb): + if exctype is not KeyboardInterrupt or self.action == 'ignore': + return + elif self.action == 'error': + raise SystemExit(self.code) from excinst + return self.action == 'suppress' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__about__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__about__.py deleted file mode 100644 index 3551bc2d2..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__about__.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file is dual licensed under the terms of the Apache License, Version -# 2.0, and the BSD License. See the LICENSE file in the root of this repository -# for complete details. - -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] - -__title__ = "packaging" -__summary__ = "Core utilities for Python packages" -__uri__ = "https://github.com/pypa/packaging" - -__version__ = "21.3" - -__author__ = "Donald Stufft and individual contributors" -__email__ = "donald@stufft.io" - -__license__ = "BSD-2-Clause or Apache-2.0" -__copyright__ = "2014-2019 %s" % __author__ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__init__.py index 3c50c5dcf..4112fec0a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/__init__.py @@ -2,24 +2,14 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from .__about__ import ( - __author__, - __copyright__, - __email__, - __license__, - __summary__, - __title__, - __uri__, - __version__, -) +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" -__all__ = [ - "__title__", - "__summary__", - "__uri__", - "__version__", - "__author__", - "__email__", - "__license__", - "__copyright__", -] +__version__ = "23.0" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = "2014-2019 %s" % __author__ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py new file mode 100644 index 000000000..6fb19b30b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +import enum +import os +import struct +from typing import IO, Optional, Tuple + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error: + raise ELFInvalid("unable to parse identification") + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or " + f"encoding ({self.encoding})" + ) + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> Optional[str]: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py index 4c379aa6f..2f0cc7439 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_manylinux.py @@ -1,121 +1,58 @@ import collections +import contextlib import functools import os import re -import struct import sys import warnings -from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader: - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file: IO[bytes]) -> None: - def unpack(fmt: str) -> int: - try: - data = file.read(struct.calcsize(fmt)) - result: Tuple[int, ...] = struct.unpack(fmt, data) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result[0] - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header() -> Optional[_ELFFileHeader]: +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None -def _is_linux_armhf() -> bool: +def _is_linux_armhf(executable: str) -> bool: # hard-float ABI can be detected from the ELF header of the running # process # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686() -> bool: - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) -def _have_compatible_abi(arch: str) -> bool: +def _have_compatible_abi(executable: str, arch: str) -> bool: if arch == "armv7l": - return _is_linux_armhf() + return _is_linux_armhf(executable) if arch == "i686": - return _is_linux_i686() + return _is_linux_i686(executable) return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} @@ -141,10 +78,10 @@ def _glibc_version_string_confstr() -> Optional[str]: # platform module. # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr("CS_GNU_LIBC_VERSION") + # Should be a string like "glibc 2.17". + version_string: str = getattr(os, "confstr")("CS_GNU_LIBC_VERSION") assert version_string is not None - _, version = version_string.split() + _, version = version_string.rsplit() except (AssertionError, AttributeError, OSError, ValueError): # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... return None @@ -211,8 +148,8 @@ def _parse_glibc_version(version_str: str) -> Tuple[int, int]: m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) if not m: warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", RuntimeWarning, ) return -1, -1 @@ -265,7 +202,7 @@ def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: def platform_tags(linux: str, arch: str) -> Iterator[str]: - if not _have_compatible_abi(arch): + if not _have_compatible_abi(sys.executable, arch): return # Oldest glibc to be supported regardless of architecture is (2, 17). too_old_glibc2 = _GLibCVersion(2, 16) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py index 8ac3059ba..706ba600a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_musllinux.py @@ -4,68 +4,13 @@ linked against musl, and what musl version is used. """ -import contextlib import functools -import operator -import os import re -import struct import subprocess import sys -from typing import IO, Iterator, NamedTuple, Optional, Tuple +from typing import Iterator, NamedTuple, Optional - -def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]: - return struct.unpack(fmt, f.read(struct.calcsize(fmt))) - - -def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]: - """Detect musl libc location by parsing the Python executable. - - Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca - ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html - """ - f.seek(0) - try: - ident = _read_unpacked(f, "16B") - except struct.error: - return None - if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. - return None - f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, p_fmt, p_idx = { - 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. - 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. - }[ident[4]] - except KeyError: - return None - else: - p_get = operator.itemgetter(*p_idx) - - # Find the interpreter section and return its content. - try: - _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) - except struct.error: - return None - for i in range(e_phnum + 1): - f.seek(e_phoff + e_phentsize * i) - try: - p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) - except struct.error: - return None - if p_type != 3: # Not PT_INTERP. - continue - f.seek(p_offset) - interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") - if "musl" not in interpreter: - return None - return interpreter - return None +from ._elffile import ELFFile class _MuslVersion(NamedTuple): @@ -95,13 +40,12 @@ def _get_musl_version(executable: str) -> Optional[_MuslVersion]: Version 1.2.2 Dynamic Program Loader """ - with contextlib.ExitStack() as stack: - try: - f = stack.enter_context(open(executable, "rb")) - except OSError: - return None - ld = _parse_ld_musl_from_elf(f) - if not ld: + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: return None proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) return _parse_musl_version(proc.stderr) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_parser.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_parser.py new file mode 100644 index 000000000..2bc6a8f98 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_parser.py @@ -0,0 +1,328 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains ENBF-inspired grammar representing +the implementation. +""" + +import ast +from typing import Any, List, NamedTuple, Optional, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] +# MarkerList = List[Union["MarkerList", MarkerAtom, str]] +# mypy does not support recursive type definition +# https://github.com/python/mypy/issues/731 +MarkerAtom = Any +MarkerList = List[Any] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: List[str] + specifier: str + marker: Optional[MarkerList] + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> Tuple[str, str, Optional[MarkerList]]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, span_start=url_start, after="URL and whitespace" + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + after=( + "version specifier" + if specifier + else "name and no valid version specifier" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, after: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected end or semicolon (after {after})", + span_start=span_start, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> List[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens("LEFT_BRACKET", "RIGHT_BRACKET"): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: List[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if ( + env_var == "platform_python_implementation" + or env_var == "python_implementation" + ): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of " + "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py new file mode 100644 index 000000000..b1fb207c7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/_tokenizer.py @@ -0,0 +1,188 @@ +import contextlib +import re +from dataclasses import dataclass +from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: Tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return "\n ".join([self.message, self.source, marker]) + + +DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { + "LEFT_PARENTHESIS": r"\(", + "RIGHT_PARENTHESIS": r"\)", + "LEFT_BRACKET": r"\[", + "RIGHT_BRACKET": r"\]", + "SEMICOLON": r";", + "COMMA": r",", + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": r"(===|==|~=|!=|<=|>=|<|>)", + "BOOLOP": r"\b(or|and)\b", + "IN": r"\bin\b", + "NOT": r"\bnot\b", + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extra + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": r"\@", + "URL": r"[^ \t]+", + "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", + "WS": r"[ \t]+", + "END": r"$", +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: "Dict[str, Union[str, re.Pattern[str]]]", + ) -> None: + self.source = source + self.rules: Dict[str, re.Pattern[str]] = { + name: re.compile(pattern) for name, pattern in rules.items() + } + self.next_token: Optional[Token] = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert ( + self.next_token is None + ), f"Cannot check for {name!r}, already have {self.next_token!r}" + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: Optional[int] = None, + span_end: Optional[int] = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens(self, open_token: str, close_token: str) -> Iterator[bool]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield open_position is not None + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected closing {close_token}", + span_start=open_position, + ) + + self.read() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/markers.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/markers.py index eb0541b83..68369c981 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/markers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/markers.py @@ -8,19 +8,10 @@ import sys from typing import Any, Callable, Dict, List, Optional, Tuple, Union -from setuptools.extern.pyparsing import ( # noqa: N817 - Forward, - Group, - Literal as L, - ParseException, - ParseResults, - QuotedString, - ZeroOrMore, - stringEnd, - stringStart, -) - +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable, parse_marker +from ._tokenizer import ParserSyntaxError from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name __all__ = [ "InvalidMarker", @@ -52,101 +43,24 @@ class UndefinedEnvironmentName(ValueError): """ -class Node: - def __init__(self, value: Any) -> None: - self.value = value - - def __str__(self) -> str: - return str(self.value) - - def __repr__(self) -> str: - return f"<{self.__class__.__name__}('{self}')>" - - def serialize(self) -> str: - raise NotImplementedError - - -class Variable(Node): - def serialize(self) -> str: - return str(self) - - -class Value(Node): - def serialize(self) -> str: - return f'"{self}"' - - -class Op(Node): - def serialize(self) -> str: - return str(self) - - -VARIABLE = ( - L("implementation_version") - | L("platform_python_implementation") - | L("implementation_name") - | L("python_full_version") - | L("platform_release") - | L("platform_version") - | L("platform_machine") - | L("platform_system") - | L("python_version") - | L("sys_platform") - | L("os_name") - | L("os.name") # PEP-345 - | L("sys.platform") # PEP-345 - | L("platform.version") # PEP-345 - | L("platform.machine") # PEP-345 - | L("platform.python_implementation") # PEP-345 - | L("python_implementation") # undocumented setuptools legacy - | L("extra") # PEP-508 -) -ALIASES = { - "os.name": "os_name", - "sys.platform": "sys_platform", - "platform.version": "platform_version", - "platform.machine": "platform_machine", - "platform.python_implementation": "platform_python_implementation", - "python_implementation": "platform_python_implementation", -} -VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) - -VERSION_CMP = ( - L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<") -) - -MARKER_OP = VERSION_CMP | L("not in") | L("in") -MARKER_OP.setParseAction(lambda s, l, t: Op(t[0])) - -MARKER_VALUE = QuotedString("'") | QuotedString('"') -MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0])) - -BOOLOP = L("and") | L("or") - -MARKER_VAR = VARIABLE | MARKER_VALUE - -MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR) -MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0])) - -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() - -MARKER_EXPR = Forward() -MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN) -MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR) - -MARKER = stringStart + MARKER_EXPR + stringEnd - - -def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]: - if isinstance(results, ParseResults): - return [_coerce_parse_result(i) for i in results] - else: - return results +def _normalize_extra_values(results: Any) -> Any: + """ + Normalize extra values. + """ + if isinstance(results[0], tuple): + lhs, op, rhs = results[0] + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + results[0] = lhs, op, rhs + return results def _format_marker( - marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True + marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True ) -> str: assert isinstance(marker, (list, tuple, str)) @@ -192,7 +106,7 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool: except InvalidSpecifier: pass else: - return spec.contains(lhs) + return spec.contains(lhs, prereleases=True) oper: Optional[Operator] = _operators.get(op.serialize()) if oper is None: @@ -201,25 +115,19 @@ def _eval_op(lhs: str, op: Op, rhs: str) -> bool: return oper(lhs, rhs) -class Undefined: - pass - +def _normalize(*values: str, key: str) -> Tuple[str, ...]: + # PEP 685 – Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + return tuple(canonicalize_name(v) for v in values) -_undefined = Undefined() + # other environment markers don't have such standards + return values -def _get_env(environment: Dict[str, str], name: str) -> str: - value: Union[str, Undefined] = environment.get(name, _undefined) - - if isinstance(value, Undefined): - raise UndefinedEnvironmentName( - f"{name!r} does not exist in evaluation environment." - ) - - return value - - -def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: +def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: groups: List[List[bool]] = [[]] for marker in markers: @@ -231,12 +139,15 @@ def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool: lhs, op, rhs = marker if isinstance(lhs, Variable): - lhs_value = _get_env(environment, lhs.value) + environment_key = lhs.value + lhs_value = environment[environment_key] rhs_value = rhs.value else: lhs_value = lhs.value - rhs_value = _get_env(environment, rhs.value) + environment_key = rhs.value + rhs_value = environment[environment_key] + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) groups[-1].append(_eval_op(lhs_value, op, rhs_value)) else: assert marker in ["and", "or"] @@ -274,13 +185,29 @@ def default_environment() -> Dict[str, str]: class Marker: def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. try: - self._markers = _coerce_parse_result(MARKER.parseString(marker)) - except ParseException as e: - raise InvalidMarker( - f"Invalid marker: {marker!r}, parse error at " - f"{marker[e.loc : e.loc + 8]!r}" - ) + self._markers = _normalize_extra_values(parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e def __str__(self) -> str: return _format_marker(self._markers) @@ -288,6 +215,15 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"" + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: """Evaluate a marker. @@ -298,7 +234,12 @@ def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: The environment is determined from the current Python process. """ current_environment = default_environment() + current_environment["extra"] = "" if environment is not None: current_environment.update(environment) + # The API used to allow setting extra to None. We need to handle this + # case for backwards compatibility. + if current_environment["extra"] is None: + current_environment["extra"] = "" return _evaluate_markers(self._markers, current_environment) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/requirements.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/requirements.py index 0d93231b4..a9f9b9c7c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/requirements.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/requirements.py @@ -2,26 +2,13 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -import re -import string import urllib.parse -from typing import List, Optional as TOptional, Set +from typing import Any, List, Optional, Set -from setuptools.extern.pyparsing import ( # noqa - Combine, - Literal as L, - Optional, - ParseException, - Regex, - Word, - ZeroOrMore, - originalTextFor, - stringEnd, - stringStart, -) - -from .markers import MARKER_EXPR, Marker -from .specifiers import LegacySpecifier, Specifier, SpecifierSet +from ._parser import parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet class InvalidRequirement(ValueError): @@ -30,60 +17,6 @@ class InvalidRequirement(ValueError): """ -ALPHANUM = Word(string.ascii_letters + string.digits) - -LBRACKET = L("[").suppress() -RBRACKET = L("]").suppress() -LPAREN = L("(").suppress() -RPAREN = L(")").suppress() -COMMA = L(",").suppress() -SEMICOLON = L(";").suppress() -AT = L("@").suppress() - -PUNCTUATION = Word("-_.") -IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM) -IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) - -NAME = IDENTIFIER("name") -EXTRA = IDENTIFIER - -URI = Regex(r"[^ ]+")("url") -URL = AT + URI - -EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) -EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") - -VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) -VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) - -VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY -VERSION_MANY = Combine( - VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False -)("_raw_spec") -_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY) -_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "") - -VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") -VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) - -MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") -MARKER_EXPR.setParseAction( - lambda s, l, t: Marker(s[t._original_start : t._original_end]) -) -MARKER_SEPARATOR = SEMICOLON -MARKER = MARKER_SEPARATOR + MARKER_EXPR - -VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) -URL_AND_MARKER = URL + Optional(MARKER) - -NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER) - -REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd -# setuptools.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see -# issue #104 -REQUIREMENT.parseString("x[]") - - class Requirement: """Parse a requirement. @@ -99,28 +32,29 @@ class Requirement: def __init__(self, requirement_string: str) -> None: try: - req = REQUIREMENT.parseString(requirement_string) - except ParseException as e: - raise InvalidRequirement( - f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}' - ) - - self.name: str = req.name - if req.url: - parsed_url = urllib.parse.urlparse(req.url) + parsed = parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + if parsed.url: + parsed_url = urllib.parse.urlparse(parsed.url) if parsed_url.scheme == "file": - if urllib.parse.urlunparse(parsed_url) != req.url: + if urllib.parse.urlunparse(parsed_url) != parsed.url: raise InvalidRequirement("Invalid URL given") elif not (parsed_url.scheme and parsed_url.netloc) or ( not parsed_url.scheme and not parsed_url.netloc ): - raise InvalidRequirement(f"Invalid URL: {req.url}") - self.url: TOptional[str] = req.url + raise InvalidRequirement(f"Invalid URL: {parsed.url}") + self.url: Optional[str] = parsed.url else: self.url = None - self.extras: Set[str] = set(req.extras.asList() if req.extras else []) - self.specifier: SpecifierSet = SpecifierSet(req.specifier) - self.marker: TOptional[Marker] = req.marker if req.marker else None + self.extras: Set[str] = set(parsed.extras if parsed.extras else []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Optional[Marker] = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) def __str__(self) -> str: parts: List[str] = [self.name] @@ -144,3 +78,18 @@ def __str__(self) -> str: def __repr__(self) -> str: return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + self.name == other.name + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py index 0e218a6f9..e715ecc8c 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/specifiers.py @@ -1,20 +1,22 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" import abc -import functools import itertools import re -import warnings from typing import ( Callable, - Dict, Iterable, Iterator, List, Optional, - Pattern, Set, Tuple, TypeVar, @@ -22,17 +24,28 @@ ) from .utils import canonicalize_version -from .version import LegacyVersion, Version, parse +from .version import Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] -ParsedVersion = Union[Version, LegacyVersion] -UnparsedVersion = Union[Version, LegacyVersion, str] -VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion) -CallableOperator = Callable[[ParsedVersion, str], bool] + +def _coerce_version(version: UnparsedVersion) -> Version: + if not isinstance(version, Version): + version = Version(version) + return version class InvalidSpecifier(ValueError): """ - An invalid specifier was found, users should refer to PEP 440. + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' """ @@ -40,35 +53,39 @@ class BaseSpecifier(metaclass=abc.ABCMeta): @abc.abstractmethod def __str__(self) -> str: """ - Returns the str representation of this Specifier like object. This + Returns the str representation of this Specifier-like object. This should be representative of the Specifier itself. """ @abc.abstractmethod def __hash__(self) -> int: """ - Returns a hash value for this Specifier like object. + Returns a hash value for this Specifier-like object. """ @abc.abstractmethod def __eq__(self, other: object) -> bool: """ - Returns a boolean representing whether or not the two Specifier like + Returns a boolean representing whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. """ - @abc.abstractproperty + @property + @abc.abstractmethod def prereleases(self) -> Optional[bool]: - """ - Returns whether or not pre-releases as a whole are allowed by this - specifier. + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. """ @prereleases.setter def prereleases(self, value: bool) -> None: - """ - Sets whether or not pre-releases as a whole are allowed by this - specifier. + """Setter for :attr:`prereleases`. + + :param value: The value to set. """ @abc.abstractmethod @@ -79,227 +96,28 @@ def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: @abc.abstractmethod def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: """ Takes an iterable of items and filters them so that only items which are contained within this specifier are allowed in it. """ -class _IndividualSpecifier(BaseSpecifier): - - _operators: Dict[str, str] = {} - _regex: Pattern[str] - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - match = self._regex.search(spec) - if not match: - raise InvalidSpecifier(f"Invalid specifier: '{spec}'") - - self._spec: Tuple[str, str] = ( - match.group("operator").strip(), - match.group("version").strip(), - ) - - # Store whether or not this Specifier should accept prereleases - self._prereleases = prereleases - - def __repr__(self) -> str: - pre = ( - f", prereleases={self.prereleases!r}" - if self._prereleases is not None - else "" - ) - - return f"<{self.__class__.__name__}({str(self)!r}{pre})>" - - def __str__(self) -> str: - return "{}{}".format(*self._spec) - - @property - def _canonical_spec(self) -> Tuple[str, str]: - return self._spec[0], canonicalize_version(self._spec[1]) - - def __hash__(self) -> int: - return hash(self._canonical_spec) - - def __eq__(self, other: object) -> bool: - if isinstance(other, str): - try: - other = self.__class__(str(other)) - except InvalidSpecifier: - return NotImplemented - elif not isinstance(other, self.__class__): - return NotImplemented - - return self._canonical_spec == other._canonical_spec - - def _get_operator(self, op: str) -> CallableOperator: - operator_callable: CallableOperator = getattr( - self, f"_compare_{self._operators[op]}" - ) - return operator_callable - - def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion: - if not isinstance(version, (LegacyVersion, Version)): - version = parse(version) - return version +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. - @property - def operator(self) -> str: - return self._spec[0] + .. tip:: - @property - def version(self) -> str: - return self._spec[1] - - @property - def prereleases(self) -> Optional[bool]: - return self._prereleases - - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value - - def __contains__(self, item: str) -> bool: - return self.contains(item) - - def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None - ) -> bool: - - # Determine if prereleases are to be allowed or not. - if prereleases is None: - prereleases = self.prereleases - - # Normalize item to a Version or LegacyVersion, this allows us to have - # a shortcut for ``"2.0" in Specifier(">=2") - normalized_item = self._coerce_version(item) - - # Determine if we should be supporting prereleases in this specifier - # or not, if we do not support prereleases than we can short circuit - # logic if this version is a prereleases. - if normalized_item.is_prerelease and not prereleases: - return False - - # Actually do the comparison to determine if this item is contained - # within this Specifier or not. - operator_callable: CallableOperator = self._get_operator(self.operator) - return operator_callable(normalized_item, self.version) - - def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - - yielded = False - found_prereleases = [] - - kw = {"prereleases": prereleases if prereleases is not None else True} - - # Attempt to iterate over all the values in the iterable and if any of - # them match, yield them. - for version in iterable: - parsed_version = self._coerce_version(version) - - if self.contains(parsed_version, **kw): - # If our version is a prerelease, and we were not set to allow - # prereleases, then we'll store it for later in case nothing - # else matches this specifier. - if parsed_version.is_prerelease and not ( - prereleases or self.prereleases - ): - found_prereleases.append(version) - # Either this is not a prerelease, or we should have been - # accepting prereleases from the beginning. - else: - yielded = True - yield version - - # Now that we've iterated over everything, determine if we've yielded - # any values, and if we have not and we have any prereleases stored up - # then we will go ahead and yield the prereleases. - if not yielded and found_prereleases: - for version in found_prereleases: - yield version - - -class LegacySpecifier(_IndividualSpecifier): - - _regex_str = r""" - (?P(==|!=|<=|>=|<|>)) - \s* - (?P - [^,;\s)]* # Since this is a "legacy" specifier, and the version - # string can be just about anything, we match everything - # except for whitespace, a semi-colon for marker support, - # a closing paren since versions can be enclosed in - # them, and a comma since it's a version separator. - ) - """ - - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) - - _operators = { - "==": "equal", - "!=": "not_equal", - "<=": "less_than_equal", - ">=": "greater_than_equal", - "<": "less_than", - ">": "greater_than", - } - - def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: - super().__init__(spec, prereleases) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion: - if not isinstance(version, LegacyVersion): - version = LegacyVersion(str(version)) - return version - - def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective == self._coerce_version(spec) - - def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective != self._coerce_version(spec) - - def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective <= self._coerce_version(spec) - - def _compare_greater_than_equal( - self, prospective: LegacyVersion, spec: str - ) -> bool: - return prospective >= self._coerce_version(spec) - - def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective < self._coerce_version(spec) - - def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool: - return prospective > self._coerce_version(spec) - - -def _require_version_compare( - fn: Callable[["Specifier", ParsedVersion, str], bool] -) -> Callable[["Specifier", ParsedVersion, str], bool]: - @functools.wraps(fn) - def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool: - if not isinstance(prospective, Version): - return False - return fn(self, prospective, spec) - - return wrapped - - -class Specifier(_IndividualSpecifier): + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ - _regex_str = r""" + _operator_regex_str = r""" (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" (?P (?: # The identity operators allow for an escape hatch that will @@ -309,8 +127,10 @@ class Specifier(_IndividualSpecifier): # but included entirely as an escape hatch. (?<====) # Only match for the identity operator \s* - [^\s]* # We just match everything, except for whitespace - # since we are only testing for strict identity. + [^\s;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. ) | (?: @@ -323,23 +143,23 @@ class Specifier(_IndividualSpecifier): v? (?:[0-9]+!)? # epoch [0-9]+(?:\.[0-9]+)* # release - (?: # pre release - [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) - [-_\.]? - [0-9]* - )? - (?: # post release - (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) - )? - # You cannot use a wild card and a dev or local version - # together so group them with a | and make them optional. + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local - | - \.\* # Wild card syntax of .* )? ) | @@ -354,7 +174,7 @@ class Specifier(_IndividualSpecifier): [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) (?: # pre release [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) + (alpha|beta|preview|pre|a|b|c|rc) [-_\.]? [0-9]* )? @@ -379,7 +199,7 @@ class Specifier(_IndividualSpecifier): [0-9]+(?:\.[0-9]+)* # release (?: # pre release [-_\.]? - (a|b|c|rc|alpha|beta|pre|preview) + (alpha|beta|preview|pre|a|b|c|rc) [-_\.]? [0-9]* )? @@ -391,7 +211,10 @@ class Specifier(_IndividualSpecifier): ) """ - _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE) + _regex = re.compile( + r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$", + re.VERBOSE | re.IGNORECASE, + ) _operators = { "~=": "compatible", @@ -404,8 +227,152 @@ class Specifier(_IndividualSpecifier): "===": "arbitrary", } - @_require_version_compare - def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + @property + def prereleases(self) -> bool: + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if Version(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + canonical_version = canonicalize_version( + self._spec[1], + strip_trailing_zero=(self._spec[0] != "~="), + ) + return self._spec[0], canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: str) -> bool: # Compatible releases have an equivalent combination of >= and ==. That # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to @@ -426,34 +393,33 @@ def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool: prospective, prefix ) - @_require_version_compare - def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: + def _compare_equal(self, prospective: Version, spec: str) -> bool: # We need special logic to handle prefix matching if spec.endswith(".*"): # In the case of prefix matching we want to ignore local segment. - prospective = Version(prospective.public) + normalized_prospective = canonicalize_version(prospective.public) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) # Split the spec out by dots, and pretend that there is an implicit # dot in between a release segment and a pre-release segment. - split_spec = _version_split(spec[:-2]) # Remove the trailing .* + split_spec = _version_split(normalized_spec) # Split the prospective version out by dots, and pretend that there # is an implicit dot in between a release segment and a pre-release # segment. - split_prospective = _version_split(str(prospective)) + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) # Shorten the prospective version to be the same length as the spec # so that we can determine if the specifier is a prefix of the # prospective version or not. - shortened_prospective = split_prospective[: len(split_spec)] + shortened_prospective = padded_prospective[: len(split_spec)] - # Pad out our two sides with zeros so that they both equal the same - # length. - padded_spec, padded_prospective = _pad_version( - split_spec, shortened_prospective - ) - - return padded_prospective == padded_spec + return shortened_prospective == split_spec else: # Convert our spec string into a Version spec_version = Version(spec) @@ -466,30 +432,24 @@ def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool: return prospective == spec_version - @_require_version_compare - def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool: + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: return not self._compare_equal(prospective, spec) - @_require_version_compare - def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool: + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: # NB: Local version identifiers are NOT permitted in the version # specifier, so local version labels can be universally removed from # the prospective version. return Version(prospective.public) <= Version(spec) - @_require_version_compare - def _compare_greater_than_equal( - self, prospective: ParsedVersion, spec: str - ) -> bool: + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: # NB: Local version identifiers are NOT permitted in the version # specifier, so local version labels can be universally removed from # the prospective version. return Version(prospective.public) >= Version(spec) - @_require_version_compare - def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: # Convert our spec to a Version instance, since we'll want to work with # it as a version. @@ -514,8 +474,7 @@ def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool: # version in the spec. return True - @_require_version_compare - def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool: + def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: # Convert our spec to a Version instance, since we'll want to work with # it as a version. @@ -549,34 +508,133 @@ def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bo def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: return str(prospective).lower() == str(spec).lower() - @property - def prereleases(self) -> bool: + def __contains__(self, item: Union[str, Version]) -> bool: + """Return whether or not the item is contained in this specifier. - # If there is an explicit prereleases set for this, then we'll just - # blindly use that. - if self._prereleases is not None: - return self._prereleases + :param item: The item to check for. - # Look at all of our specifiers and determine if they are inclusive - # operators, and if they are if they are including an explicit - # prerelease. - operator, version = self._spec - if operator in ["==", ">=", "<=", "~=", "==="]: - # The == specifier can include a trailing .*, if it does we - # want to remove before parsing. - if operator == "==" and version.endswith(".*"): - version = version[:-2] + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. - # Parse the version, and if it is a pre-release than this - # specifier allows pre-releases. - if parse(version).is_prerelease: - return True + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) - return False + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + False + >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") + True + >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) + True + """ - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version, this allows us to have a shortcut for + # "2.0" in Specifier(">=2") + normalized_item = _coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(Specifier().contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = _coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later in case nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") @@ -618,22 +676,39 @@ def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + def __init__( self, specifiers: str = "", prereleases: Optional[bool] = None ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ - # Split on , to break each individual specifier into it's own item, and + # Split on `,` to break each individual specifier into it's own item, and # strip each item to remove leading/trailing whitespace. split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] # Parsed each individual specifier, attempting first to make it a - # Specifier and falling back to a LegacySpecifier. - parsed: Set[_IndividualSpecifier] = set() + # Specifier. + parsed: Set[Specifier] = set() for specifier in split_specifiers: - try: - parsed.add(Specifier(specifier)) - except InvalidSpecifier: - parsed.add(LegacySpecifier(specifier)) + parsed.add(Specifier(specifier)) # Turn our parsed specifiers into a frozen set and save them for later. self._specs = frozenset(parsed) @@ -642,7 +717,40 @@ def __init__( # we accept prereleases or not. self._prereleases = prereleases + @property + def prereleases(self) -> Optional[bool]: + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ pre = ( f", prereleases={self.prereleases!r}" if self._prereleases is not None @@ -652,12 +760,31 @@ def __repr__(self) -> str: return f"" def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ return ",".join(sorted(str(s) for s in self._specs)) def __hash__(self) -> int: return hash(self._specs) def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ if isinstance(other, str): other = SpecifierSet(other) elif not isinstance(other, SpecifierSet): @@ -681,7 +808,25 @@ def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": return specifier def __eq__(self, other: object) -> bool: - if isinstance(other, (str, _IndividualSpecifier)): + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): other = SpecifierSet(str(other)) elif not isinstance(other, SpecifierSet): return NotImplemented @@ -689,43 +834,72 @@ def __eq__(self, other: object) -> bool: return self._specs == other._specs def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" return len(self._specs) - def __iter__(self) -> Iterator[_IndividualSpecifier]: - return iter(self._specs) - - @property - def prereleases(self) -> Optional[bool]: - - # If we have been given an explicit prerelease modifier, then we'll - # pass that through here. - if self._prereleases is not None: - return self._prereleases - - # If we don't have any specifiers, and we don't have a forced value, - # then we'll just return None since we don't know if this should have - # pre-releases or not. - if not self._specs: - return None - - # Otherwise we'll see if any of the given specifiers accept - # prereleases, if any of them do we'll return True, otherwise False. - return any(s.prereleases for s in self._specs) + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. - @prereleases.setter - def prereleases(self, value: bool) -> None: - self._prereleases = value + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ return self.contains(item) def contains( - self, item: UnparsedVersion, prereleases: Optional[bool] = None + self, + item: UnparsedVersion, + prereleases: Optional[bool] = None, + installed: Optional[bool] = None, ) -> bool: - - # Ensure that our item is a Version or LegacyVersion instance. - if not isinstance(item, (LegacyVersion, Version)): - item = parse(item) + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + # Ensure that our item is a Version instance. + if not isinstance(item, Version): + item = Version(item) # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the @@ -742,6 +916,9 @@ def contains( if not prereleases and item.is_prerelease: return False + if installed and item.is_prerelease: + item = Version(item.base_version) + # We simply dispatch to the underlying specs here to make sure that the # given version is contained within all of them. # Note: This use of all() here means that an empty set of specifiers @@ -749,9 +926,46 @@ def contains( return all(s.contains(item, prereleases=prereleases) for s in self._specs) def filter( - self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None - ) -> Iterable[VersionTypeVar]: - + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + [] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ # Determine if we're forcing a prerelease or not, if we're not forcing # one for this particular filter call, then we'll use whatever the # SpecifierSet thinks for whether or not we should support prereleases. @@ -764,27 +978,16 @@ def filter( if self._specs: for spec in self._specs: iterable = spec.filter(iterable, prereleases=bool(prereleases)) - return iterable + return iter(iterable) # If we do not have any specifiers, then we need to have a rough filter # which will filter out any pre-releases, unless there are no final - # releases, and which will filter out LegacyVersion in general. + # releases. else: - filtered: List[VersionTypeVar] = [] - found_prereleases: List[VersionTypeVar] = [] - - item: UnparsedVersion - parsed_version: Union[Version, LegacyVersion] + filtered: List[UnparsedVersionVar] = [] + found_prereleases: List[UnparsedVersionVar] = [] for item in iterable: - # Ensure that we some kind of Version class for this item. - if not isinstance(item, (LegacyVersion, Version)): - parsed_version = parse(item) - else: - parsed_version = item - - # Filter out any item which is parsed as a LegacyVersion - if isinstance(parsed_version, LegacyVersion): - continue + parsed_version = _coerce_version(item) # Store any item which is a pre-release for later unless we've # already found a final version or we are accepting prereleases @@ -797,6 +1000,6 @@ def filter( # If we've found no items except for pre-releases, then we'll go # ahead and use the pre-releases if not filtered and found_prereleases and prereleases is None: - return found_prereleases + return iter(found_prereleases) - return filtered + return iter(filtered) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/tags.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/tags.py index 9a3d25a71..19ccbde3e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/tags.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/tags.py @@ -4,6 +4,7 @@ import logging import platform +import subprocess import sys import sysconfig from importlib.machinery import EXTENSION_SUFFIXES @@ -36,7 +37,7 @@ } -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 +_32_BIT_INTERPRETER = sys.maxsize <= 2**32 class Tag: @@ -224,10 +225,45 @@ def cpython_tags( yield Tag(interpreter, "abi3", platform_) -def _generic_abi() -> Iterator[str]: - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) +def _generic_abi() -> List[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] def generic_tags( @@ -251,8 +287,9 @@ def generic_tags( interpreter = "".join([interp_name, interp_version]) if abis is None: abis = _generic_abi() + else: + abis = list(abis) platforms = list(platforms or platform_tags()) - abis = list(abis) if "none" not in abis: abis.append("none") for abi in abis: @@ -356,6 +393,22 @@ def mac_platforms( version_str, _, cpu_arch = platform.mac_ver() if version is None: version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) else: version = version if arch is None: @@ -446,6 +499,9 @@ def platform_tags() -> Iterator[str]: def interpreter_name() -> str: """ Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. """ name = sys.implementation.name return INTERPRETER_SHORT_NAMES.get(name) or name @@ -482,6 +538,9 @@ def sys_tags(*, warn: bool = False) -> Iterator[Tag]: yield from generic_tags() if interp_name == "pp": - yield from compatible_tags(interpreter="pp3") + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) else: - yield from compatible_tags() + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/utils.py index bab11b80c..33c613b74 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/utils.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/utils.py @@ -35,7 +35,9 @@ def canonicalize_name(name: str) -> NormalizedName: return cast(NormalizedName, value) -def canonicalize_version(version: Union[Version, str]) -> str: +def canonicalize_version( + version: Union[Version, str], *, strip_trailing_zero: bool = True +) -> str: """ This is very similar to Version.__str__, but has one subtle difference with the way it handles the release segment. @@ -56,8 +58,11 @@ def canonicalize_version(version: Union[Version, str]) -> str: parts.append(f"{parsed.epoch}!") # Release segment - # NB: This strips trailing '.0's to normalize - parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release))) + release_segment = ".".join(str(x) for x in parsed.release) + if strip_trailing_zero: + # NB: This strips trailing '.0's to normalize + release_segment = re.sub(r"(\.0)+$", "", release_segment) + parts.append(release_segment) # Pre-release if parsed.pre is not None: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/version.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/version.py index de9a09a4e..e5c738cfd 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/packaging/version.py @@ -1,16 +1,20 @@ # This file is dual licensed under the terms of the Apache License, Version # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. +""" +.. testsetup:: + + from packaging.version import parse, Version +""" import collections import itertools import re -import warnings -from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union +from typing import Callable, Optional, SupportsInt, Tuple, Union from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType -__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"] +__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] InfiniteTypes = Union[InfinityType, NegativeInfinityType] PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] @@ -29,36 +33,37 @@ CmpKey = Tuple[ int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType ] -LegacyCmpKey = Tuple[int, Tuple[str, ...]] -VersionComparisonMethod = Callable[ - [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool -] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] _Version = collections.namedtuple( "_Version", ["epoch", "release", "dev", "pre", "post", "local"] ) -def parse(version: str) -> Union["LegacyVersion", "Version"]: - """ - Parse the given version string and return either a :class:`Version` object - or a :class:`LegacyVersion` object depending on if the given version is - a valid PEP 440 version or a legacy version. +def parse(version: str) -> "Version": + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. """ - try: - return Version(version) - except InvalidVersion: - return LegacyVersion(version) + return Version(version) class InvalidVersion(ValueError): - """ - An invalid version was found, users should refer to PEP 440. + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' """ class _BaseVersion: - _key: Union[CmpKey, LegacyCmpKey] + _key: CmpKey def __hash__(self) -> int: return hash(self._key) @@ -103,126 +108,9 @@ def __ne__(self, other: object) -> bool: return self._key != other._key -class LegacyVersion(_BaseVersion): - def __init__(self, version: str) -> None: - self._version = str(version) - self._key = _legacy_cmpkey(self._version) - - warnings.warn( - "Creating a LegacyVersion has been deprecated and will be " - "removed in the next major release", - DeprecationWarning, - ) - - def __str__(self) -> str: - return self._version - - def __repr__(self) -> str: - return f"" - - @property - def public(self) -> str: - return self._version - - @property - def base_version(self) -> str: - return self._version - - @property - def epoch(self) -> int: - return -1 - - @property - def release(self) -> None: - return None - - @property - def pre(self) -> None: - return None - - @property - def post(self) -> None: - return None - - @property - def dev(self) -> None: - return None - - @property - def local(self) -> None: - return None - - @property - def is_prerelease(self) -> bool: - return False - - @property - def is_postrelease(self) -> bool: - return False - - @property - def is_devrelease(self) -> bool: - return False - - -_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE) - -_legacy_version_replacement_map = { - "pre": "c", - "preview": "c", - "-": "final-", - "rc": "c", - "dev": "@", -} - - -def _parse_version_parts(s: str) -> Iterator[str]: - for part in _legacy_version_component_re.split(s): - part = _legacy_version_replacement_map.get(part, part) - - if not part or part == ".": - continue - - if part[:1] in "0123456789": - # pad for numeric comparison - yield part.zfill(8) - else: - yield "*" + part - - # ensure that alpha/beta/candidate are before final - yield "*final" - - -def _legacy_cmpkey(version: str) -> LegacyCmpKey: - - # We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch - # greater than or equal to 0. This will effectively put the LegacyVersion, - # which uses the defacto standard originally implemented by setuptools, - # as before all PEP 440 versions. - epoch = -1 - - # This scheme is taken from pkg_resources.parse_version setuptools prior to - # it's adoption of the packaging library. - parts: List[str] = [] - for part in _parse_version_parts(version.lower()): - if part.startswith("*"): - # remove "-" before a prerelease tag - if part < "*final": - while parts and parts[-1] == "*final-": - parts.pop() - - # remove trailing zeros from each series of numeric parts - while parts and parts[-1] == "00000000": - parts.pop() - - parts.append(part) - - return epoch, tuple(parts) - - # Deliberately not anchored to the start and end of the string, to make it # easier for 3rd party code to reuse -VERSION_PATTERN = r""" +_VERSION_PATTERN = r""" v? (?: (?:(?P[0-9]+)!)? # epoch @@ -253,12 +141,55 @@ def _legacy_cmpkey(version: str) -> LegacyCmpKey: (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))? # local version """ +VERSION_PATTERN = _VERSION_PATTERN +""" +A string containing the regular expression used to match a valid version. + +The pattern is not anchored at either end, and is intended for embedding in larger +expressions (for example, matching a version number as part of a file name). The +regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE`` +flags set. + +:meta hide-value: +""" + class Version(_BaseVersion): + """This class abstracts handling of a project's versions. + + A :class:`Version` instance is comparison aware and can be compared and + sorted using the standard Python interfaces. + + >>> v1 = Version("1.0a5") + >>> v2 = Version("1.0") + >>> v1 + + >>> v2 + + >>> v1 < v2 + True + >>> v1 == v2 + False + >>> v1 > v2 + False + >>> v1 >= v2 + False + >>> v1 <= v2 + True + """ _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE) def __init__(self, version: str) -> None: + """Initialize a Version object. + + :param version: + The string representation of a version which will be parsed and normalized + before use. + :raises InvalidVersion: + If the ``version`` does not conform to PEP 440 in any way then this + exception will be raised. + """ # Validate the version and parse it into pieces match = self._regex.search(version) @@ -288,9 +219,19 @@ def __init__(self, version: str) -> None: ) def __repr__(self) -> str: + """A representation of the Version that shows all internal state. + + >>> Version('1.0.0') + + """ return f"" def __str__(self) -> str: + """A string representation of the version that can be rounded-tripped. + + >>> str(Version("1.0a5")) + '1.0a5' + """ parts = [] # Epoch @@ -320,29 +261,80 @@ def __str__(self) -> str: @property def epoch(self) -> int: + """The epoch of the version. + + >>> Version("2.0.0").epoch + 0 + >>> Version("1!2.0.0").epoch + 1 + """ _epoch: int = self._version.epoch return _epoch @property def release(self) -> Tuple[int, ...]: + """The components of the "release" segment of the version. + + >>> Version("1.2.3").release + (1, 2, 3) + >>> Version("2.0.0").release + (2, 0, 0) + >>> Version("1!2.0.0.post0").release + (2, 0, 0) + + Includes trailing zeroes but not the epoch or any pre-release / development / + post-release suffixes. + """ _release: Tuple[int, ...] = self._version.release return _release @property def pre(self) -> Optional[Tuple[str, int]]: + """The pre-release segment of the version. + + >>> print(Version("1.2.3").pre) + None + >>> Version("1.2.3a1").pre + ('a', 1) + >>> Version("1.2.3b1").pre + ('b', 1) + >>> Version("1.2.3rc1").pre + ('rc', 1) + """ _pre: Optional[Tuple[str, int]] = self._version.pre return _pre @property def post(self) -> Optional[int]: + """The post-release number of the version. + + >>> print(Version("1.2.3").post) + None + >>> Version("1.2.3.post1").post + 1 + """ return self._version.post[1] if self._version.post else None @property def dev(self) -> Optional[int]: + """The development number of the version. + + >>> print(Version("1.2.3").dev) + None + >>> Version("1.2.3.dev1").dev + 1 + """ return self._version.dev[1] if self._version.dev else None @property def local(self) -> Optional[str]: + """The local version segment of the version. + + >>> print(Version("1.2.3").local) + None + >>> Version("1.2.3+abc").local + 'abc' + """ if self._version.local: return ".".join(str(x) for x in self._version.local) else: @@ -350,10 +342,31 @@ def local(self) -> Optional[str]: @property def public(self) -> str: + """The public portion of the version. + + >>> Version("1.2.3").public + '1.2.3' + >>> Version("1.2.3+abc").public + '1.2.3' + >>> Version("1.2.3+abc.dev1").public + '1.2.3' + """ return str(self).split("+", 1)[0] @property def base_version(self) -> str: + """The "base version" of the version. + + >>> Version("1.2.3").base_version + '1.2.3' + >>> Version("1.2.3+abc").base_version + '1.2.3' + >>> Version("1!1.2.3+abc.dev1").base_version + '1!1.2.3' + + The "base version" is the public version of the project without any pre or post + release markers. + """ parts = [] # Epoch @@ -367,26 +380,72 @@ def base_version(self) -> str: @property def is_prerelease(self) -> bool: + """Whether this version is a pre-release. + + >>> Version("1.2.3").is_prerelease + False + >>> Version("1.2.3a1").is_prerelease + True + >>> Version("1.2.3b1").is_prerelease + True + >>> Version("1.2.3rc1").is_prerelease + True + >>> Version("1.2.3dev1").is_prerelease + True + """ return self.dev is not None or self.pre is not None @property def is_postrelease(self) -> bool: + """Whether this version is a post-release. + + >>> Version("1.2.3").is_postrelease + False + >>> Version("1.2.3.post1").is_postrelease + True + """ return self.post is not None @property def is_devrelease(self) -> bool: + """Whether this version is a development release. + + >>> Version("1.2.3").is_devrelease + False + >>> Version("1.2.3.dev1").is_devrelease + True + """ return self.dev is not None @property def major(self) -> int: + """The first item of :attr:`release` or ``0`` if unavailable. + + >>> Version("1.2.3").major + 1 + """ return self.release[0] if len(self.release) >= 1 else 0 @property def minor(self) -> int: + """The second item of :attr:`release` or ``0`` if unavailable. + + >>> Version("1.2.3").minor + 2 + >>> Version("1").minor + 0 + """ return self.release[1] if len(self.release) >= 2 else 0 @property def micro(self) -> int: + """The third item of :attr:`release` or ``0`` if unavailable. + + >>> Version("1.2.3").micro + 3 + >>> Version("1").micro + 0 + """ return self.release[2] if len(self.release) >= 3 else 0 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py deleted file mode 100644 index 7802ff158..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/__init__.py +++ /dev/null @@ -1,331 +0,0 @@ -# module pyparsing.py -# -# Copyright (c) 2003-2022 Paul T. McGuire -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__doc__ = """ -pyparsing module - Classes and methods to define and execute parsing grammars -============================================================================= - -The pyparsing module is an alternative approach to creating and -executing simple grammars, vs. the traditional lex/yacc approach, or the -use of regular expressions. With pyparsing, you don't need to learn -a new syntax for defining grammars or matching expressions - the parsing -module provides a library of classes that you use to construct the -grammar directly in Python. - -Here is a program to parse "Hello, World!" (or any greeting of the form -``", !"``), built up using :class:`Word`, -:class:`Literal`, and :class:`And` elements -(the :meth:`'+'` operators create :class:`And` expressions, -and the strings are auto-converted to :class:`Literal` expressions):: - - from pyparsing import Word, alphas - - # define grammar of a greeting - greet = Word(alphas) + "," + Word(alphas) + "!" - - hello = "Hello, World!" - print(hello, "->", greet.parse_string(hello)) - -The program outputs the following:: - - Hello, World! -> ['Hello', ',', 'World', '!'] - -The Python representation of the grammar is quite readable, owing to the -self-explanatory class names, and the use of :class:`'+'`, -:class:`'|'`, :class:`'^'` and :class:`'&'` operators. - -The :class:`ParseResults` object returned from -:class:`ParserElement.parseString` can be -accessed as a nested list, a dictionary, or an object with named -attributes. - -The pyparsing module handles some of the problems that are typically -vexing when writing text parsers: - - - extra or missing whitespace (the above program will also handle - "Hello,World!", "Hello , World !", etc.) - - quoted strings - - embedded comments - - -Getting Started - ------------------ -Visit the classes :class:`ParserElement` and :class:`ParseResults` to -see the base classes that most other pyparsing -classes inherit from. Use the docstrings for examples of how to: - - - construct literal match expressions from :class:`Literal` and - :class:`CaselessLiteral` classes - - construct character word-group expressions using the :class:`Word` - class - - see how to create repetitive expressions using :class:`ZeroOrMore` - and :class:`OneOrMore` classes - - use :class:`'+'`, :class:`'|'`, :class:`'^'`, - and :class:`'&'` operators to combine simple expressions into - more complex ones - - associate names with your parsed results using - :class:`ParserElement.setResultsName` - - access the parsed data, which is returned as a :class:`ParseResults` - object - - find some helpful expression short-cuts like :class:`delimitedList` - and :class:`oneOf` - - find more useful common expressions in the :class:`pyparsing_common` - namespace class -""" -from typing import NamedTuple - - -class version_info(NamedTuple): - major: int - minor: int - micro: int - releaselevel: str - serial: int - - @property - def __version__(self): - return ( - "{}.{}.{}".format(self.major, self.minor, self.micro) - + ( - "{}{}{}".format( - "r" if self.releaselevel[0] == "c" else "", - self.releaselevel[0], - self.serial, - ), - "", - )[self.releaselevel == "final"] - ) - - def __str__(self): - return "{} {} / {}".format(__name__, self.__version__, __version_time__) - - def __repr__(self): - return "{}.{}({})".format( - __name__, - type(self).__name__, - ", ".join("{}={!r}".format(*nv) for nv in zip(self._fields, self)), - ) - - -__version_info__ = version_info(3, 0, 9, "final", 0) -__version_time__ = "05 May 2022 07:02 UTC" -__version__ = __version_info__.__version__ -__versionTime__ = __version_time__ -__author__ = "Paul McGuire " - -from .util import * -from .exceptions import * -from .actions import * -from .core import __diag__, __compat__ -from .results import * -from .core import * -from .core import _builtin_exprs as core_builtin_exprs -from .helpers import * -from .helpers import _builtin_exprs as helper_builtin_exprs - -from .unicode import unicode_set, UnicodeRangeList, pyparsing_unicode as unicode -from .testing import pyparsing_test as testing -from .common import ( - pyparsing_common as common, - _builtin_exprs as common_builtin_exprs, -) - -# define backward compat synonyms -if "pyparsing_unicode" not in globals(): - pyparsing_unicode = unicode -if "pyparsing_common" not in globals(): - pyparsing_common = common -if "pyparsing_test" not in globals(): - pyparsing_test = testing - -core_builtin_exprs += common_builtin_exprs + helper_builtin_exprs - - -__all__ = [ - "__version__", - "__version_time__", - "__author__", - "__compat__", - "__diag__", - "And", - "AtLineStart", - "AtStringStart", - "CaselessKeyword", - "CaselessLiteral", - "CharsNotIn", - "Combine", - "Dict", - "Each", - "Empty", - "FollowedBy", - "Forward", - "GoToColumn", - "Group", - "IndentedBlock", - "Keyword", - "LineEnd", - "LineStart", - "Literal", - "Located", - "PrecededBy", - "MatchFirst", - "NoMatch", - "NotAny", - "OneOrMore", - "OnlyOnce", - "OpAssoc", - "Opt", - "Optional", - "Or", - "ParseBaseException", - "ParseElementEnhance", - "ParseException", - "ParseExpression", - "ParseFatalException", - "ParseResults", - "ParseSyntaxException", - "ParserElement", - "PositionToken", - "QuotedString", - "RecursiveGrammarException", - "Regex", - "SkipTo", - "StringEnd", - "StringStart", - "Suppress", - "Token", - "TokenConverter", - "White", - "Word", - "WordEnd", - "WordStart", - "ZeroOrMore", - "Char", - "alphanums", - "alphas", - "alphas8bit", - "any_close_tag", - "any_open_tag", - "c_style_comment", - "col", - "common_html_entity", - "counted_array", - "cpp_style_comment", - "dbl_quoted_string", - "dbl_slash_comment", - "delimited_list", - "dict_of", - "empty", - "hexnums", - "html_comment", - "identchars", - "identbodychars", - "java_style_comment", - "line", - "line_end", - "line_start", - "lineno", - "make_html_tags", - "make_xml_tags", - "match_only_at_col", - "match_previous_expr", - "match_previous_literal", - "nested_expr", - "null_debug_action", - "nums", - "one_of", - "printables", - "punc8bit", - "python_style_comment", - "quoted_string", - "remove_quotes", - "replace_with", - "replace_html_entity", - "rest_of_line", - "sgl_quoted_string", - "srange", - "string_end", - "string_start", - "trace_parse_action", - "unicode_string", - "with_attribute", - "indentedBlock", - "original_text_for", - "ungroup", - "infix_notation", - "locatedExpr", - "with_class", - "CloseMatch", - "token_map", - "pyparsing_common", - "pyparsing_unicode", - "unicode_set", - "condition_as_parse_action", - "pyparsing_test", - # pre-PEP8 compatibility names - "__versionTime__", - "anyCloseTag", - "anyOpenTag", - "cStyleComment", - "commonHTMLEntity", - "countedArray", - "cppStyleComment", - "dblQuotedString", - "dblSlashComment", - "delimitedList", - "dictOf", - "htmlComment", - "javaStyleComment", - "lineEnd", - "lineStart", - "makeHTMLTags", - "makeXMLTags", - "matchOnlyAtCol", - "matchPreviousExpr", - "matchPreviousLiteral", - "nestedExpr", - "nullDebugAction", - "oneOf", - "opAssoc", - "pythonStyleComment", - "quotedString", - "removeQuotes", - "replaceHTMLEntity", - "replaceWith", - "restOfLine", - "sglQuotedString", - "stringEnd", - "stringStart", - "traceParseAction", - "unicodeString", - "withAttribute", - "indentedBlock", - "originalTextFor", - "infixNotation", - "locatedExpr", - "withClass", - "tokenMap", - "conditionAsParseAction", - "autoname_elements", -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py deleted file mode 100644 index f72c66e74..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/actions.py +++ /dev/null @@ -1,207 +0,0 @@ -# actions.py - -from .exceptions import ParseException -from .util import col - - -class OnlyOnce: - """ - Wrapper for parse actions, to ensure they are only called once. - """ - - def __init__(self, method_call): - from .core import _trim_arity - - self.callable = _trim_arity(method_call) - self.called = False - - def __call__(self, s, l, t): - if not self.called: - results = self.callable(s, l, t) - self.called = True - return results - raise ParseException(s, l, "OnlyOnce obj called multiple times w/out reset") - - def reset(self): - """ - Allow the associated parse action to be called once more. - """ - - self.called = False - - -def match_only_at_col(n): - """ - Helper method for defining parse actions that require matching at - a specific column in the input text. - """ - - def verify_col(strg, locn, toks): - if col(locn, strg) != n: - raise ParseException(strg, locn, "matched token not at column {}".format(n)) - - return verify_col - - -def replace_with(repl_str): - """ - Helper method for common parse actions that simply return - a literal value. Especially useful when used with - :class:`transform_string` (). - - Example:: - - num = Word(nums).set_parse_action(lambda toks: int(toks[0])) - na = one_of("N/A NA").set_parse_action(replace_with(math.nan)) - term = na | num - - term[1, ...].parse_string("324 234 N/A 234") # -> [324, 234, nan, 234] - """ - return lambda s, l, t: [repl_str] - - -def remove_quotes(s, l, t): - """ - Helper parse action for removing quotation marks from parsed - quoted strings. - - Example:: - - # by default, quotation marks are included in parsed results - quoted_string.parse_string("'Now is the Winter of our Discontent'") # -> ["'Now is the Winter of our Discontent'"] - - # use remove_quotes to strip quotation marks from parsed results - quoted_string.set_parse_action(remove_quotes) - quoted_string.parse_string("'Now is the Winter of our Discontent'") # -> ["Now is the Winter of our Discontent"] - """ - return t[0][1:-1] - - -def with_attribute(*args, **attr_dict): - """ - Helper to create a validating parse action to be used with start - tags created with :class:`make_xml_tags` or - :class:`make_html_tags`. Use ``with_attribute`` to qualify - a starting tag with a required attribute value, to avoid false - matches on common tags such as ```` or ``
      ``. - - Call ``with_attribute`` with a series of attribute names and - values. Specify the list of filter attributes names and values as: - - - keyword arguments, as in ``(align="right")``, or - - as an explicit dict with ``**`` operator, when an attribute - name is also a Python reserved word, as in ``**{"class":"Customer", "align":"right"}`` - - a list of name-value tuples, as in ``(("ns1:class", "Customer"), ("ns2:align", "right"))`` - - For attribute names with a namespace prefix, you must use the second - form. Attribute names are matched insensitive to upper/lower case. - - If just testing for ``class`` (with or without a namespace), use - :class:`with_class`. - - To verify that the attribute exists, but without specifying a value, - pass ``with_attribute.ANY_VALUE`` as the value. - - Example:: - - html = ''' -
      - Some text -
      1 4 0 1 0
      -
      1,3 2,3 1,1
      -
      this has no type
      -
      - - ''' - div,div_end = make_html_tags("div") - - # only match div tag having a type attribute with value "grid" - div_grid = div().set_parse_action(with_attribute(type="grid")) - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.search_string(html): - print(grid_header.body) - - # construct a match with any div tag having a type attribute, regardless of the value - div_any_type = div().set_parse_action(with_attribute(type=with_attribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.search_string(html): - print(div_header.body) - - prints:: - - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - if args: - attrs = args[:] - else: - attrs = attr_dict.items() - attrs = [(k, v) for k, v in attrs] - - def pa(s, l, tokens): - for attrName, attrValue in attrs: - if attrName not in tokens: - raise ParseException(s, l, "no matching attribute " + attrName) - if attrValue != with_attribute.ANY_VALUE and tokens[attrName] != attrValue: - raise ParseException( - s, - l, - "attribute {!r} has value {!r}, must be {!r}".format( - attrName, tokens[attrName], attrValue - ), - ) - - return pa - - -with_attribute.ANY_VALUE = object() - - -def with_class(classname, namespace=""): - """ - Simplified version of :class:`with_attribute` when - matching on a div class - made difficult because ``class`` is - a reserved word in Python. - - Example:: - - html = ''' -
      - Some text -
      1 4 0 1 0
      -
      1,3 2,3 1,1
      -
      this <div> has no class
      -
      - - ''' - div,div_end = make_html_tags("div") - div_grid = div().set_parse_action(with_class("grid")) - - grid_expr = div_grid + SkipTo(div | div_end)("body") - for grid_header in grid_expr.search_string(html): - print(grid_header.body) - - div_any_type = div().set_parse_action(with_class(withAttribute.ANY_VALUE)) - div_expr = div_any_type + SkipTo(div | div_end)("body") - for div_header in div_expr.search_string(html): - print(div_header.body) - - prints:: - - 1 4 0 1 0 - - 1 4 0 1 0 - 1,3 2,3 1,1 - """ - classattr = "{}:class".format(namespace) if namespace else "class" - return with_attribute(**{classattr: classname}) - - -# pre-PEP8 compatibility symbols -replaceWith = replace_with -removeQuotes = remove_quotes -withAttribute = with_attribute -withClass = with_class -matchOnlyAtCol = match_only_at_col diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/common.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/common.py deleted file mode 100644 index 1859fb79c..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/common.py +++ /dev/null @@ -1,424 +0,0 @@ -# common.py -from .core import * -from .helpers import delimited_list, any_open_tag, any_close_tag -from datetime import datetime - - -# some other useful expressions - using lower-case class name since we are really using this as a namespace -class pyparsing_common: - """Here are some common low-level expressions that may be useful in - jump-starting parser development: - - - numeric forms (:class:`integers`, :class:`reals`, - :class:`scientific notation`) - - common :class:`programming identifiers` - - network addresses (:class:`MAC`, - :class:`IPv4`, :class:`IPv6`) - - ISO8601 :class:`dates` and - :class:`datetime` - - :class:`UUID` - - :class:`comma-separated list` - - :class:`url` - - Parse actions: - - - :class:`convertToInteger` - - :class:`convertToFloat` - - :class:`convertToDate` - - :class:`convertToDatetime` - - :class:`stripHTMLTags` - - :class:`upcaseTokens` - - :class:`downcaseTokens` - - Example:: - - pyparsing_common.number.runTests(''' - # any int or real number, returned as the appropriate type - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.fnumber.runTests(''' - # any int or real number, returned as float - 100 - -100 - +100 - 3.14159 - 6.02e23 - 1e-12 - ''') - - pyparsing_common.hex_integer.runTests(''' - # hex numbers - 100 - FF - ''') - - pyparsing_common.fraction.runTests(''' - # fractions - 1/2 - -3/4 - ''') - - pyparsing_common.mixed_integer.runTests(''' - # mixed fractions - 1 - 1/2 - -3/4 - 1-3/4 - ''') - - import uuid - pyparsing_common.uuid.setParseAction(tokenMap(uuid.UUID)) - pyparsing_common.uuid.runTests(''' - # uuid - 12345678-1234-5678-1234-567812345678 - ''') - - prints:: - - # any int or real number, returned as the appropriate type - 100 - [100] - - -100 - [-100] - - +100 - [100] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # any int or real number, returned as float - 100 - [100.0] - - -100 - [-100.0] - - +100 - [100.0] - - 3.14159 - [3.14159] - - 6.02e23 - [6.02e+23] - - 1e-12 - [1e-12] - - # hex numbers - 100 - [256] - - FF - [255] - - # fractions - 1/2 - [0.5] - - -3/4 - [-0.75] - - # mixed fractions - 1 - [1] - - 1/2 - [0.5] - - -3/4 - [-0.75] - - 1-3/4 - [1.75] - - # uuid - 12345678-1234-5678-1234-567812345678 - [UUID('12345678-1234-5678-1234-567812345678')] - """ - - convert_to_integer = token_map(int) - """ - Parse action for converting parsed integers to Python int - """ - - convert_to_float = token_map(float) - """ - Parse action for converting parsed numbers to Python float - """ - - integer = Word(nums).set_name("integer").set_parse_action(convert_to_integer) - """expression that parses an unsigned integer, returns an int""" - - hex_integer = ( - Word(hexnums).set_name("hex integer").set_parse_action(token_map(int, 16)) - ) - """expression that parses a hexadecimal integer, returns an int""" - - signed_integer = ( - Regex(r"[+-]?\d+") - .set_name("signed integer") - .set_parse_action(convert_to_integer) - ) - """expression that parses an integer with optional leading sign, returns an int""" - - fraction = ( - signed_integer().set_parse_action(convert_to_float) - + "/" - + signed_integer().set_parse_action(convert_to_float) - ).set_name("fraction") - """fractional expression of an integer divided by an integer, returns a float""" - fraction.add_parse_action(lambda tt: tt[0] / tt[-1]) - - mixed_integer = ( - fraction | signed_integer + Opt(Opt("-").suppress() + fraction) - ).set_name("fraction or mixed integer-fraction") - """mixed integer of the form 'integer - fraction', with optional leading integer, returns float""" - mixed_integer.add_parse_action(sum) - - real = ( - Regex(r"[+-]?(?:\d+\.\d*|\.\d+)") - .set_name("real number") - .set_parse_action(convert_to_float) - ) - """expression that parses a floating point number and returns a float""" - - sci_real = ( - Regex(r"[+-]?(?:\d+(?:[eE][+-]?\d+)|(?:\d+\.\d*|\.\d+)(?:[eE][+-]?\d+)?)") - .set_name("real number with scientific notation") - .set_parse_action(convert_to_float) - ) - """expression that parses a floating point number with optional - scientific notation and returns a float""" - - # streamlining this expression makes the docs nicer-looking - number = (sci_real | real | signed_integer).setName("number").streamline() - """any numeric expression, returns the corresponding Python type""" - - fnumber = ( - Regex(r"[+-]?\d+\.?\d*([eE][+-]?\d+)?") - .set_name("fnumber") - .set_parse_action(convert_to_float) - ) - """any int or real number, returned as float""" - - identifier = Word(identchars, identbodychars).set_name("identifier") - """typical code identifier (leading alpha or '_', followed by 0 or more alphas, nums, or '_')""" - - ipv4_address = Regex( - r"(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1?[0-9]{1,2})){3}" - ).set_name("IPv4 address") - "IPv4 address (``0.0.0.0 - 255.255.255.255``)" - - _ipv6_part = Regex(r"[0-9a-fA-F]{1,4}").set_name("hex_integer") - _full_ipv6_address = (_ipv6_part + (":" + _ipv6_part) * 7).set_name( - "full IPv6 address" - ) - _short_ipv6_address = ( - Opt(_ipv6_part + (":" + _ipv6_part) * (0, 6)) - + "::" - + Opt(_ipv6_part + (":" + _ipv6_part) * (0, 6)) - ).set_name("short IPv6 address") - _short_ipv6_address.add_condition( - lambda t: sum(1 for tt in t if pyparsing_common._ipv6_part.matches(tt)) < 8 - ) - _mixed_ipv6_address = ("::ffff:" + ipv4_address).set_name("mixed IPv6 address") - ipv6_address = Combine( - (_full_ipv6_address | _mixed_ipv6_address | _short_ipv6_address).set_name( - "IPv6 address" - ) - ).set_name("IPv6 address") - "IPv6 address (long, short, or mixed form)" - - mac_address = Regex( - r"[0-9a-fA-F]{2}([:.-])[0-9a-fA-F]{2}(?:\1[0-9a-fA-F]{2}){4}" - ).set_name("MAC address") - "MAC address xx:xx:xx:xx:xx (may also have '-' or '.' delimiters)" - - @staticmethod - def convert_to_date(fmt: str = "%Y-%m-%d"): - """ - Helper to create a parse action for converting parsed date string to Python datetime.date - - Params - - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%d"``) - - Example:: - - date_expr = pyparsing_common.iso8601_date.copy() - date_expr.setParseAction(pyparsing_common.convertToDate()) - print(date_expr.parseString("1999-12-31")) - - prints:: - - [datetime.date(1999, 12, 31)] - """ - - def cvt_fn(ss, ll, tt): - try: - return datetime.strptime(tt[0], fmt).date() - except ValueError as ve: - raise ParseException(ss, ll, str(ve)) - - return cvt_fn - - @staticmethod - def convert_to_datetime(fmt: str = "%Y-%m-%dT%H:%M:%S.%f"): - """Helper to create a parse action for converting parsed - datetime string to Python datetime.datetime - - Params - - - fmt - format to be passed to datetime.strptime (default= ``"%Y-%m-%dT%H:%M:%S.%f"``) - - Example:: - - dt_expr = pyparsing_common.iso8601_datetime.copy() - dt_expr.setParseAction(pyparsing_common.convertToDatetime()) - print(dt_expr.parseString("1999-12-31T23:59:59.999")) - - prints:: - - [datetime.datetime(1999, 12, 31, 23, 59, 59, 999000)] - """ - - def cvt_fn(s, l, t): - try: - return datetime.strptime(t[0], fmt) - except ValueError as ve: - raise ParseException(s, l, str(ve)) - - return cvt_fn - - iso8601_date = Regex( - r"(?P\d{4})(?:-(?P\d\d)(?:-(?P\d\d))?)?" - ).set_name("ISO8601 date") - "ISO8601 date (``yyyy-mm-dd``)" - - iso8601_datetime = Regex( - r"(?P\d{4})-(?P\d\d)-(?P\d\d)[T ](?P\d\d):(?P\d\d)(:(?P\d\d(\.\d*)?)?)?(?PZ|[+-]\d\d:?\d\d)?" - ).set_name("ISO8601 datetime") - "ISO8601 datetime (``yyyy-mm-ddThh:mm:ss.s(Z|+-00:00)``) - trailing seconds, milliseconds, and timezone optional; accepts separating ``'T'`` or ``' '``" - - uuid = Regex(r"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}").set_name("UUID") - "UUID (``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``)" - - _html_stripper = any_open_tag.suppress() | any_close_tag.suppress() - - @staticmethod - def strip_html_tags(s: str, l: int, tokens: ParseResults): - """Parse action to remove HTML tags from web page HTML source - - Example:: - - # strip HTML links from normal text - text = 'More info at the
      pyparsing wiki page' - td, td_end = makeHTMLTags("TD") - table_text = td + SkipTo(td_end).setParseAction(pyparsing_common.stripHTMLTags)("body") + td_end - print(table_text.parseString(text).body) - - Prints:: - - More info at the pyparsing wiki page - """ - return pyparsing_common._html_stripper.transform_string(tokens[0]) - - _commasepitem = ( - Combine( - OneOrMore( - ~Literal(",") - + ~LineEnd() - + Word(printables, exclude_chars=",") - + Opt(White(" \t") + ~FollowedBy(LineEnd() | ",")) - ) - ) - .streamline() - .set_name("commaItem") - ) - comma_separated_list = delimited_list( - Opt(quoted_string.copy() | _commasepitem, default="") - ).set_name("comma separated list") - """Predefined expression of 1 or more printable words or quoted strings, separated by commas.""" - - upcase_tokens = staticmethod(token_map(lambda t: t.upper())) - """Parse action to convert tokens to upper case.""" - - downcase_tokens = staticmethod(token_map(lambda t: t.lower())) - """Parse action to convert tokens to lower case.""" - - # fmt: off - url = Regex( - # https://mathiasbynens.be/demo/url-regex - # https://gist.github.com/dperini/729294 - r"^" + - # protocol identifier (optional) - # short syntax // still required - r"(?:(?:(?Phttps?|ftp):)?\/\/)" + - # user:pass BasicAuth (optional) - r"(?:(?P\S+(?::\S*)?)@)?" + - r"(?P" + - # IP address exclusion - # private & local networks - r"(?!(?:10|127)(?:\.\d{1,3}){3})" + - r"(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})" + - r"(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})" + - # IP address dotted notation octets - # excludes loopback network 0.0.0.0 - # excludes reserved space >= 224.0.0.0 - # excludes network & broadcast addresses - # (first & last IP address of each class) - r"(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])" + - r"(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}" + - r"(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))" + - r"|" + - # host & domain names, may end with dot - # can be replaced by a shortest alternative - # (?![-_])(?:[-\w\u00a1-\uffff]{0,63}[^-_]\.)+ - r"(?:" + - r"(?:" + - r"[a-z0-9\u00a1-\uffff]" + - r"[a-z0-9\u00a1-\uffff_-]{0,62}" + - r")?" + - r"[a-z0-9\u00a1-\uffff]\." + - r")+" + - # TLD identifier name, may end with dot - r"(?:[a-z\u00a1-\uffff]{2,}\.?)" + - r")" + - # port number (optional) - r"(:(?P\d{2,5}))?" + - # resource path (optional) - r"(?P\/[^?# ]*)?" + - # query string (optional) - r"(\?(?P[^#]*))?" + - # fragment (optional) - r"(#(?P\S*))?" + - r"$" - ).set_name("url") - # fmt: on - - # pre-PEP8 compatibility names - convertToInteger = convert_to_integer - convertToFloat = convert_to_float - convertToDate = convert_to_date - convertToDatetime = convert_to_datetime - stripHTMLTags = strip_html_tags - upcaseTokens = upcase_tokens - downcaseTokens = downcase_tokens - - -_builtin_exprs = [ - v for v in vars(pyparsing_common).values() if isinstance(v, ParserElement) -] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/core.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/core.py deleted file mode 100644 index 9acba3f3e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/core.py +++ /dev/null @@ -1,5814 +0,0 @@ -# -# core.py -# -import os -import typing -from typing import ( - NamedTuple, - Union, - Callable, - Any, - Generator, - Tuple, - List, - TextIO, - Set, - Sequence, -) -from abc import ABC, abstractmethod -from enum import Enum -import string -import copy -import warnings -import re -import sys -from collections.abc import Iterable -import traceback -import types -from operator import itemgetter -from functools import wraps -from threading import RLock -from pathlib import Path - -from .util import ( - _FifoCache, - _UnboundedCache, - __config_flags, - _collapse_string_to_ranges, - _escape_regex_range_chars, - _bslash, - _flatten, - LRUMemo as _LRUMemo, - UnboundedMemo as _UnboundedMemo, -) -from .exceptions import * -from .actions import * -from .results import ParseResults, _ParseResultsWithOffset -from .unicode import pyparsing_unicode - -_MAX_INT = sys.maxsize -str_type: Tuple[type, ...] = (str, bytes) - -# -# Copyright (c) 2003-2022 Paul T. McGuire -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - - -if sys.version_info >= (3, 8): - from functools import cached_property -else: - - class cached_property: - def __init__(self, func): - self._func = func - - def __get__(self, instance, owner=None): - ret = instance.__dict__[self._func.__name__] = self._func(instance) - return ret - - -class __compat__(__config_flags): - """ - A cross-version compatibility configuration for pyparsing features that will be - released in a future version. By setting values in this configuration to True, - those features can be enabled in prior versions for compatibility development - and testing. - - - ``collect_all_And_tokens`` - flag to enable fix for Issue #63 that fixes erroneous grouping - of results names when an :class:`And` expression is nested within an :class:`Or` or :class:`MatchFirst`; - maintained for compatibility, but setting to ``False`` no longer restores pre-2.3.1 - behavior - """ - - _type_desc = "compatibility" - - collect_all_And_tokens = True - - _all_names = [__ for __ in locals() if not __.startswith("_")] - _fixed_names = """ - collect_all_And_tokens - """.split() - - -class __diag__(__config_flags): - _type_desc = "diagnostic" - - warn_multiple_tokens_in_named_alternation = False - warn_ungrouped_named_tokens_in_collection = False - warn_name_set_on_empty_Forward = False - warn_on_parse_using_empty_Forward = False - warn_on_assignment_to_Forward = False - warn_on_multiple_string_args_to_oneof = False - warn_on_match_first_with_lshift_operator = False - enable_debug_on_named_expressions = False - - _all_names = [__ for __ in locals() if not __.startswith("_")] - _warning_names = [name for name in _all_names if name.startswith("warn")] - _debug_names = [name for name in _all_names if name.startswith("enable_debug")] - - @classmethod - def enable_all_warnings(cls) -> None: - for name in cls._warning_names: - cls.enable(name) - - -class Diagnostics(Enum): - """ - Diagnostic configuration (all default to disabled) - - ``warn_multiple_tokens_in_named_alternation`` - flag to enable warnings when a results - name is defined on a :class:`MatchFirst` or :class:`Or` expression with one or more :class:`And` subexpressions - - ``warn_ungrouped_named_tokens_in_collection`` - flag to enable warnings when a results - name is defined on a containing expression with ungrouped subexpressions that also - have results names - - ``warn_name_set_on_empty_Forward`` - flag to enable warnings when a :class:`Forward` is defined - with a results name, but has no contents defined - - ``warn_on_parse_using_empty_Forward`` - flag to enable warnings when a :class:`Forward` is - defined in a grammar but has never had an expression attached to it - - ``warn_on_assignment_to_Forward`` - flag to enable warnings when a :class:`Forward` is defined - but is overwritten by assigning using ``'='`` instead of ``'<<='`` or ``'<<'`` - - ``warn_on_multiple_string_args_to_oneof`` - flag to enable warnings when :class:`one_of` is - incorrectly called with multiple str arguments - - ``enable_debug_on_named_expressions`` - flag to auto-enable debug on all subsequent - calls to :class:`ParserElement.set_name` - - Diagnostics are enabled/disabled by calling :class:`enable_diag` and :class:`disable_diag`. - All warnings can be enabled by calling :class:`enable_all_warnings`. - """ - - warn_multiple_tokens_in_named_alternation = 0 - warn_ungrouped_named_tokens_in_collection = 1 - warn_name_set_on_empty_Forward = 2 - warn_on_parse_using_empty_Forward = 3 - warn_on_assignment_to_Forward = 4 - warn_on_multiple_string_args_to_oneof = 5 - warn_on_match_first_with_lshift_operator = 6 - enable_debug_on_named_expressions = 7 - - -def enable_diag(diag_enum: Diagnostics) -> None: - """ - Enable a global pyparsing diagnostic flag (see :class:`Diagnostics`). - """ - __diag__.enable(diag_enum.name) - - -def disable_diag(diag_enum: Diagnostics) -> None: - """ - Disable a global pyparsing diagnostic flag (see :class:`Diagnostics`). - """ - __diag__.disable(diag_enum.name) - - -def enable_all_warnings() -> None: - """ - Enable all global pyparsing diagnostic warnings (see :class:`Diagnostics`). - """ - __diag__.enable_all_warnings() - - -# hide abstract class -del __config_flags - - -def _should_enable_warnings( - cmd_line_warn_options: typing.Iterable[str], warn_env_var: typing.Optional[str] -) -> bool: - enable = bool(warn_env_var) - for warn_opt in cmd_line_warn_options: - w_action, w_message, w_category, w_module, w_line = (warn_opt + "::::").split( - ":" - )[:5] - if not w_action.lower().startswith("i") and ( - not (w_message or w_category or w_module) or w_module == "pyparsing" - ): - enable = True - elif w_action.lower().startswith("i") and w_module in ("pyparsing", ""): - enable = False - return enable - - -if _should_enable_warnings( - sys.warnoptions, os.environ.get("PYPARSINGENABLEALLWARNINGS") -): - enable_all_warnings() - - -# build list of single arg builtins, that can be used as parse actions -_single_arg_builtins = { - sum, - len, - sorted, - reversed, - list, - tuple, - set, - any, - all, - min, - max, -} - -_generatorType = types.GeneratorType -ParseAction = Union[ - Callable[[], Any], - Callable[[ParseResults], Any], - Callable[[int, ParseResults], Any], - Callable[[str, int, ParseResults], Any], -] -ParseCondition = Union[ - Callable[[], bool], - Callable[[ParseResults], bool], - Callable[[int, ParseResults], bool], - Callable[[str, int, ParseResults], bool], -] -ParseFailAction = Callable[[str, int, "ParserElement", Exception], None] -DebugStartAction = Callable[[str, int, "ParserElement", bool], None] -DebugSuccessAction = Callable[ - [str, int, int, "ParserElement", ParseResults, bool], None -] -DebugExceptionAction = Callable[[str, int, "ParserElement", Exception, bool], None] - - -alphas = string.ascii_uppercase + string.ascii_lowercase -identchars = pyparsing_unicode.Latin1.identchars -identbodychars = pyparsing_unicode.Latin1.identbodychars -nums = "0123456789" -hexnums = nums + "ABCDEFabcdef" -alphanums = alphas + nums -printables = "".join([c for c in string.printable if c not in string.whitespace]) - -_trim_arity_call_line: traceback.StackSummary = None - - -def _trim_arity(func, max_limit=3): - """decorator to trim function calls to match the arity of the target""" - global _trim_arity_call_line - - if func in _single_arg_builtins: - return lambda s, l, t: func(t) - - limit = 0 - found_arity = False - - def extract_tb(tb, limit=0): - frames = traceback.extract_tb(tb, limit=limit) - frame_summary = frames[-1] - return [frame_summary[:2]] - - # synthesize what would be returned by traceback.extract_stack at the call to - # user's parse action 'func', so that we don't incur call penalty at parse time - - # fmt: off - LINE_DIFF = 7 - # IF ANY CODE CHANGES, EVEN JUST COMMENTS OR BLANK LINES, BETWEEN THE NEXT LINE AND - # THE CALL TO FUNC INSIDE WRAPPER, LINE_DIFF MUST BE MODIFIED!!!! - _trim_arity_call_line = (_trim_arity_call_line or traceback.extract_stack(limit=2)[-1]) - pa_call_line_synth = (_trim_arity_call_line[0], _trim_arity_call_line[1] + LINE_DIFF) - - def wrapper(*args): - nonlocal found_arity, limit - while 1: - try: - ret = func(*args[limit:]) - found_arity = True - return ret - except TypeError as te: - # re-raise TypeErrors if they did not come from our arity testing - if found_arity: - raise - else: - tb = te.__traceback__ - trim_arity_type_error = ( - extract_tb(tb, limit=2)[-1][:2] == pa_call_line_synth - ) - del tb - - if trim_arity_type_error: - if limit < max_limit: - limit += 1 - continue - - raise - # fmt: on - - # copy func name to wrapper for sensible debug output - # (can't use functools.wraps, since that messes with function signature) - func_name = getattr(func, "__name__", getattr(func, "__class__").__name__) - wrapper.__name__ = func_name - wrapper.__doc__ = func.__doc__ - - return wrapper - - -def condition_as_parse_action( - fn: ParseCondition, message: str = None, fatal: bool = False -) -> ParseAction: - """ - Function to convert a simple predicate function that returns ``True`` or ``False`` - into a parse action. Can be used in places when a parse action is required - and :class:`ParserElement.add_condition` cannot be used (such as when adding a condition - to an operator level in :class:`infix_notation`). - - Optional keyword arguments: - - - ``message`` - define a custom message to be used in the raised exception - - ``fatal`` - if True, will raise :class:`ParseFatalException` to stop parsing immediately; - otherwise will raise :class:`ParseException` - - """ - msg = message if message is not None else "failed user-defined condition" - exc_type = ParseFatalException if fatal else ParseException - fn = _trim_arity(fn) - - @wraps(fn) - def pa(s, l, t): - if not bool(fn(s, l, t)): - raise exc_type(s, l, msg) - - return pa - - -def _default_start_debug_action( - instring: str, loc: int, expr: "ParserElement", cache_hit: bool = False -): - cache_hit_str = "*" if cache_hit else "" - print( - ( - "{}Match {} at loc {}({},{})\n {}\n {}^".format( - cache_hit_str, - expr, - loc, - lineno(loc, instring), - col(loc, instring), - line(loc, instring), - " " * (col(loc, instring) - 1), - ) - ) - ) - - -def _default_success_debug_action( - instring: str, - startloc: int, - endloc: int, - expr: "ParserElement", - toks: ParseResults, - cache_hit: bool = False, -): - cache_hit_str = "*" if cache_hit else "" - print("{}Matched {} -> {}".format(cache_hit_str, expr, toks.as_list())) - - -def _default_exception_debug_action( - instring: str, - loc: int, - expr: "ParserElement", - exc: Exception, - cache_hit: bool = False, -): - cache_hit_str = "*" if cache_hit else "" - print( - "{}Match {} failed, {} raised: {}".format( - cache_hit_str, expr, type(exc).__name__, exc - ) - ) - - -def null_debug_action(*args): - """'Do-nothing' debug action, to suppress debugging output during parsing.""" - - -class ParserElement(ABC): - """Abstract base level parser element class.""" - - DEFAULT_WHITE_CHARS: str = " \n\t\r" - verbose_stacktrace: bool = False - _literalStringClass: typing.Optional[type] = None - - @staticmethod - def set_default_whitespace_chars(chars: str) -> None: - r""" - Overrides the default whitespace chars - - Example:: - - # default whitespace chars are space, and newline - Word(alphas)[1, ...].parse_string("abc def\nghi jkl") # -> ['abc', 'def', 'ghi', 'jkl'] - - # change to just treat newline as significant - ParserElement.set_default_whitespace_chars(" \t") - Word(alphas)[1, ...].parse_string("abc def\nghi jkl") # -> ['abc', 'def'] - """ - ParserElement.DEFAULT_WHITE_CHARS = chars - - # update whitespace all parse expressions defined in this module - for expr in _builtin_exprs: - if expr.copyDefaultWhiteChars: - expr.whiteChars = set(chars) - - @staticmethod - def inline_literals_using(cls: type) -> None: - """ - Set class to be used for inclusion of string literals into a parser. - - Example:: - - # default literal class used is Literal - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - date_str.parse_string("1999/12/31") # -> ['1999', '/', '12', '/', '31'] - - - # change to Suppress - ParserElement.inline_literals_using(Suppress) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - date_str.parse_string("1999/12/31") # -> ['1999', '12', '31'] - """ - ParserElement._literalStringClass = cls - - class DebugActions(NamedTuple): - debug_try: typing.Optional[DebugStartAction] - debug_match: typing.Optional[DebugSuccessAction] - debug_fail: typing.Optional[DebugExceptionAction] - - def __init__(self, savelist: bool = False): - self.parseAction: List[ParseAction] = list() - self.failAction: typing.Optional[ParseFailAction] = None - self.customName = None - self._defaultName = None - self.resultsName = None - self.saveAsList = savelist - self.skipWhitespace = True - self.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS) - self.copyDefaultWhiteChars = True - # used when checking for left-recursion - self.mayReturnEmpty = False - self.keepTabs = False - self.ignoreExprs: List["ParserElement"] = list() - self.debug = False - self.streamlined = False - # optimize exception handling for subclasses that don't advance parse index - self.mayIndexError = True - self.errmsg = "" - # mark results names as modal (report only last) or cumulative (list all) - self.modalResults = True - # custom debug actions - self.debugActions = self.DebugActions(None, None, None) - # avoid redundant calls to preParse - self.callPreparse = True - self.callDuringTry = False - self.suppress_warnings_: List[Diagnostics] = [] - - def suppress_warning(self, warning_type: Diagnostics) -> "ParserElement": - """ - Suppress warnings emitted for a particular diagnostic on this expression. - - Example:: - - base = pp.Forward() - base.suppress_warning(Diagnostics.warn_on_parse_using_empty_Forward) - - # statement would normally raise a warning, but is now suppressed - print(base.parseString("x")) - - """ - self.suppress_warnings_.append(warning_type) - return self - - def copy(self) -> "ParserElement": - """ - Make a copy of this :class:`ParserElement`. Useful for defining - different parse actions for the same parsing pattern, using copies of - the original parse element. - - Example:: - - integer = Word(nums).set_parse_action(lambda toks: int(toks[0])) - integerK = integer.copy().add_parse_action(lambda toks: toks[0] * 1024) + Suppress("K") - integerM = integer.copy().add_parse_action(lambda toks: toks[0] * 1024 * 1024) + Suppress("M") - - print((integerK | integerM | integer)[1, ...].parse_string("5K 100 640K 256M")) - - prints:: - - [5120, 100, 655360, 268435456] - - Equivalent form of ``expr.copy()`` is just ``expr()``:: - - integerM = integer().add_parse_action(lambda toks: toks[0] * 1024 * 1024) + Suppress("M") - """ - cpy = copy.copy(self) - cpy.parseAction = self.parseAction[:] - cpy.ignoreExprs = self.ignoreExprs[:] - if self.copyDefaultWhiteChars: - cpy.whiteChars = set(ParserElement.DEFAULT_WHITE_CHARS) - return cpy - - def set_results_name( - self, name: str, list_all_matches: bool = False, *, listAllMatches: bool = False - ) -> "ParserElement": - """ - Define name for referencing matching tokens as a nested attribute - of the returned parse results. - - Normally, results names are assigned as you would assign keys in a dict: - any existing value is overwritten by later values. If it is necessary to - keep all values captured for a particular results name, call ``set_results_name`` - with ``list_all_matches`` = True. - - NOTE: ``set_results_name`` returns a *copy* of the original :class:`ParserElement` object; - this is so that the client can define a basic element, such as an - integer, and reference it in multiple places with different names. - - You can also set results names using the abbreviated syntax, - ``expr("name")`` in place of ``expr.set_results_name("name")`` - - see :class:`__call__`. If ``list_all_matches`` is required, use - ``expr("name*")``. - - Example:: - - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - - # equivalent form: - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - """ - listAllMatches = listAllMatches or list_all_matches - return self._setResultsName(name, listAllMatches) - - def _setResultsName(self, name, listAllMatches=False): - if name is None: - return self - newself = self.copy() - if name.endswith("*"): - name = name[:-1] - listAllMatches = True - newself.resultsName = name - newself.modalResults = not listAllMatches - return newself - - def set_break(self, break_flag: bool = True) -> "ParserElement": - """ - Method to invoke the Python pdb debugger when this element is - about to be parsed. Set ``break_flag`` to ``True`` to enable, ``False`` to - disable. - """ - if break_flag: - _parseMethod = self._parse - - def breaker(instring, loc, doActions=True, callPreParse=True): - import pdb - - # this call to pdb.set_trace() is intentional, not a checkin error - pdb.set_trace() - return _parseMethod(instring, loc, doActions, callPreParse) - - breaker._originalParseMethod = _parseMethod - self._parse = breaker - else: - if hasattr(self._parse, "_originalParseMethod"): - self._parse = self._parse._originalParseMethod - return self - - def set_parse_action(self, *fns: ParseAction, **kwargs) -> "ParserElement": - """ - Define one or more actions to perform when successfully matching parse element definition. - - Parse actions can be called to perform data conversions, do extra validation, - update external data structures, or enhance or replace the parsed tokens. - Each parse action ``fn`` is a callable method with 0-3 arguments, called as - ``fn(s, loc, toks)`` , ``fn(loc, toks)`` , ``fn(toks)`` , or just ``fn()`` , where: - - - s = the original string being parsed (see note below) - - loc = the location of the matching substring - - toks = a list of the matched tokens, packaged as a :class:`ParseResults` object - - The parsed tokens are passed to the parse action as ParseResults. They can be - modified in place using list-style append, extend, and pop operations to update - the parsed list elements; and with dictionary-style item set and del operations - to add, update, or remove any named results. If the tokens are modified in place, - it is not necessary to return them with a return statement. - - Parse actions can also completely replace the given tokens, with another ``ParseResults`` - object, or with some entirely different object (common for parse actions that perform data - conversions). A convenient way to build a new parse result is to define the values - using a dict, and then create the return value using :class:`ParseResults.from_dict`. - - If None is passed as the ``fn`` parse action, all previously added parse actions for this - expression are cleared. - - Optional keyword arguments: - - - call_during_try = (default= ``False``) indicate if parse action should be run during - lookaheads and alternate testing. For parse actions that have side effects, it is - important to only call the parse action once it is determined that it is being - called as part of a successful parse. For parse actions that perform additional - validation, then call_during_try should be passed as True, so that the validation - code is included in the preliminary "try" parses. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`parse_string` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - - Example:: - - # parse dates in the form YYYY/MM/DD - - # use parse action to convert toks from str to int at parse time - def convert_to_int(toks): - return int(toks[0]) - - # use a parse action to verify that the date is a valid date - def is_valid_date(instring, loc, toks): - from datetime import date - year, month, day = toks[::2] - try: - date(year, month, day) - except ValueError: - raise ParseException(instring, loc, "invalid date given") - - integer = Word(nums) - date_str = integer + '/' + integer + '/' + integer - - # add parse actions - integer.set_parse_action(convert_to_int) - date_str.set_parse_action(is_valid_date) - - # note that integer fields are now ints, not strings - date_str.run_tests(''' - # successful parse - note that integer fields were converted to ints - 1999/12/31 - - # fail - invalid date - 1999/13/31 - ''') - """ - if list(fns) == [None]: - self.parseAction = [] - else: - if not all(callable(fn) for fn in fns): - raise TypeError("parse actions must be callable") - self.parseAction = [_trim_arity(fn) for fn in fns] - self.callDuringTry = kwargs.get( - "call_during_try", kwargs.get("callDuringTry", False) - ) - return self - - def add_parse_action(self, *fns: ParseAction, **kwargs) -> "ParserElement": - """ - Add one or more parse actions to expression's list of parse actions. See :class:`set_parse_action`. - - See examples in :class:`copy`. - """ - self.parseAction += [_trim_arity(fn) for fn in fns] - self.callDuringTry = self.callDuringTry or kwargs.get( - "call_during_try", kwargs.get("callDuringTry", False) - ) - return self - - def add_condition(self, *fns: ParseCondition, **kwargs) -> "ParserElement": - """Add a boolean predicate function to expression's list of parse actions. See - :class:`set_parse_action` for function call signatures. Unlike ``set_parse_action``, - functions passed to ``add_condition`` need to return boolean success/fail of the condition. - - Optional keyword arguments: - - - message = define a custom message to be used in the raised exception - - fatal = if True, will raise ParseFatalException to stop parsing immediately; otherwise will raise - ParseException - - call_during_try = boolean to indicate if this method should be called during internal tryParse calls, - default=False - - Example:: - - integer = Word(nums).set_parse_action(lambda toks: int(toks[0])) - year_int = integer.copy() - year_int.add_condition(lambda toks: toks[0] >= 2000, message="Only support years 2000 and later") - date_str = year_int + '/' + integer + '/' + integer - - result = date_str.parse_string("1999/12/31") # -> Exception: Only support years 2000 and later (at char 0), - (line:1, col:1) - """ - for fn in fns: - self.parseAction.append( - condition_as_parse_action( - fn, message=kwargs.get("message"), fatal=kwargs.get("fatal", False) - ) - ) - - self.callDuringTry = self.callDuringTry or kwargs.get( - "call_during_try", kwargs.get("callDuringTry", False) - ) - return self - - def set_fail_action(self, fn: ParseFailAction) -> "ParserElement": - """ - Define action to perform if parsing fails at this expression. - Fail acton fn is a callable function that takes the arguments - ``fn(s, loc, expr, err)`` where: - - - s = string being parsed - - loc = location where expression match was attempted and failed - - expr = the parse expression that failed - - err = the exception thrown - - The function returns no value. It may throw :class:`ParseFatalException` - if it is desired to stop parsing immediately.""" - self.failAction = fn - return self - - def _skipIgnorables(self, instring, loc): - exprsFound = True - while exprsFound: - exprsFound = False - for e in self.ignoreExprs: - try: - while 1: - loc, dummy = e._parse(instring, loc) - exprsFound = True - except ParseException: - pass - return loc - - def preParse(self, instring, loc): - if self.ignoreExprs: - loc = self._skipIgnorables(instring, loc) - - if self.skipWhitespace: - instrlen = len(instring) - white_chars = self.whiteChars - while loc < instrlen and instring[loc] in white_chars: - loc += 1 - - return loc - - def parseImpl(self, instring, loc, doActions=True): - return loc, [] - - def postParse(self, instring, loc, tokenlist): - return tokenlist - - # @profile - def _parseNoCache( - self, instring, loc, doActions=True, callPreParse=True - ) -> Tuple[int, ParseResults]: - TRY, MATCH, FAIL = 0, 1, 2 - debugging = self.debug # and doActions) - len_instring = len(instring) - - if debugging or self.failAction: - # print("Match {} at loc {}({}, {})".format(self, loc, lineno(loc, instring), col(loc, instring))) - try: - if callPreParse and self.callPreparse: - pre_loc = self.preParse(instring, loc) - else: - pre_loc = loc - tokens_start = pre_loc - if self.debugActions.debug_try: - self.debugActions.debug_try(instring, tokens_start, self, False) - if self.mayIndexError or pre_loc >= len_instring: - try: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - except IndexError: - raise ParseException(instring, len_instring, self.errmsg, self) - else: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - except Exception as err: - # print("Exception raised:", err) - if self.debugActions.debug_fail: - self.debugActions.debug_fail( - instring, tokens_start, self, err, False - ) - if self.failAction: - self.failAction(instring, tokens_start, self, err) - raise - else: - if callPreParse and self.callPreparse: - pre_loc = self.preParse(instring, loc) - else: - pre_loc = loc - tokens_start = pre_loc - if self.mayIndexError or pre_loc >= len_instring: - try: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - except IndexError: - raise ParseException(instring, len_instring, self.errmsg, self) - else: - loc, tokens = self.parseImpl(instring, pre_loc, doActions) - - tokens = self.postParse(instring, loc, tokens) - - ret_tokens = ParseResults( - tokens, self.resultsName, asList=self.saveAsList, modal=self.modalResults - ) - if self.parseAction and (doActions or self.callDuringTry): - if debugging: - try: - for fn in self.parseAction: - try: - tokens = fn(instring, tokens_start, ret_tokens) - except IndexError as parse_action_exc: - exc = ParseException("exception raised in parse action") - raise exc from parse_action_exc - - if tokens is not None and tokens is not ret_tokens: - ret_tokens = ParseResults( - tokens, - self.resultsName, - asList=self.saveAsList - and isinstance(tokens, (ParseResults, list)), - modal=self.modalResults, - ) - except Exception as err: - # print "Exception raised in user parse action:", err - if self.debugActions.debug_fail: - self.debugActions.debug_fail( - instring, tokens_start, self, err, False - ) - raise - else: - for fn in self.parseAction: - try: - tokens = fn(instring, tokens_start, ret_tokens) - except IndexError as parse_action_exc: - exc = ParseException("exception raised in parse action") - raise exc from parse_action_exc - - if tokens is not None and tokens is not ret_tokens: - ret_tokens = ParseResults( - tokens, - self.resultsName, - asList=self.saveAsList - and isinstance(tokens, (ParseResults, list)), - modal=self.modalResults, - ) - if debugging: - # print("Matched", self, "->", ret_tokens.as_list()) - if self.debugActions.debug_match: - self.debugActions.debug_match( - instring, tokens_start, loc, self, ret_tokens, False - ) - - return loc, ret_tokens - - def try_parse(self, instring: str, loc: int, raise_fatal: bool = False) -> int: - try: - return self._parse(instring, loc, doActions=False)[0] - except ParseFatalException: - if raise_fatal: - raise - raise ParseException(instring, loc, self.errmsg, self) - - def can_parse_next(self, instring: str, loc: int) -> bool: - try: - self.try_parse(instring, loc) - except (ParseException, IndexError): - return False - else: - return True - - # cache for left-recursion in Forward references - recursion_lock = RLock() - recursion_memos: typing.Dict[ - Tuple[int, "Forward", bool], Tuple[int, Union[ParseResults, Exception]] - ] = {} - - # argument cache for optimizing repeated calls when backtracking through recursive expressions - packrat_cache = ( - {} - ) # this is set later by enabled_packrat(); this is here so that reset_cache() doesn't fail - packrat_cache_lock = RLock() - packrat_cache_stats = [0, 0] - - # this method gets repeatedly called during backtracking with the same arguments - - # we can cache these arguments and save ourselves the trouble of re-parsing the contained expression - def _parseCache( - self, instring, loc, doActions=True, callPreParse=True - ) -> Tuple[int, ParseResults]: - HIT, MISS = 0, 1 - TRY, MATCH, FAIL = 0, 1, 2 - lookup = (self, instring, loc, callPreParse, doActions) - with ParserElement.packrat_cache_lock: - cache = ParserElement.packrat_cache - value = cache.get(lookup) - if value is cache.not_in_cache: - ParserElement.packrat_cache_stats[MISS] += 1 - try: - value = self._parseNoCache(instring, loc, doActions, callPreParse) - except ParseBaseException as pe: - # cache a copy of the exception, without the traceback - cache.set(lookup, pe.__class__(*pe.args)) - raise - else: - cache.set(lookup, (value[0], value[1].copy(), loc)) - return value - else: - ParserElement.packrat_cache_stats[HIT] += 1 - if self.debug and self.debugActions.debug_try: - try: - self.debugActions.debug_try(instring, loc, self, cache_hit=True) - except TypeError: - pass - if isinstance(value, Exception): - if self.debug and self.debugActions.debug_fail: - try: - self.debugActions.debug_fail( - instring, loc, self, value, cache_hit=True - ) - except TypeError: - pass - raise value - - loc_, result, endloc = value[0], value[1].copy(), value[2] - if self.debug and self.debugActions.debug_match: - try: - self.debugActions.debug_match( - instring, loc_, endloc, self, result, cache_hit=True - ) - except TypeError: - pass - - return loc_, result - - _parse = _parseNoCache - - @staticmethod - def reset_cache() -> None: - ParserElement.packrat_cache.clear() - ParserElement.packrat_cache_stats[:] = [0] * len( - ParserElement.packrat_cache_stats - ) - ParserElement.recursion_memos.clear() - - _packratEnabled = False - _left_recursion_enabled = False - - @staticmethod - def disable_memoization() -> None: - """ - Disables active Packrat or Left Recursion parsing and their memoization - - This method also works if neither Packrat nor Left Recursion are enabled. - This makes it safe to call before activating Packrat nor Left Recursion - to clear any previous settings. - """ - ParserElement.reset_cache() - ParserElement._left_recursion_enabled = False - ParserElement._packratEnabled = False - ParserElement._parse = ParserElement._parseNoCache - - @staticmethod - def enable_left_recursion( - cache_size_limit: typing.Optional[int] = None, *, force=False - ) -> None: - """ - Enables "bounded recursion" parsing, which allows for both direct and indirect - left-recursion. During parsing, left-recursive :class:`Forward` elements are - repeatedly matched with a fixed recursion depth that is gradually increased - until finding the longest match. - - Example:: - - import pyparsing as pp - pp.ParserElement.enable_left_recursion() - - E = pp.Forward("E") - num = pp.Word(pp.nums) - # match `num`, or `num '+' num`, or `num '+' num '+' num`, ... - E <<= E + '+' - num | num - - print(E.parse_string("1+2+3")) - - Recursion search naturally memoizes matches of ``Forward`` elements and may - thus skip reevaluation of parse actions during backtracking. This may break - programs with parse actions which rely on strict ordering of side-effects. - - Parameters: - - - cache_size_limit - (default=``None``) - memoize at most this many - ``Forward`` elements during matching; if ``None`` (the default), - memoize all ``Forward`` elements. - - Bounded Recursion parsing works similar but not identical to Packrat parsing, - thus the two cannot be used together. Use ``force=True`` to disable any - previous, conflicting settings. - """ - if force: - ParserElement.disable_memoization() - elif ParserElement._packratEnabled: - raise RuntimeError("Packrat and Bounded Recursion are not compatible") - if cache_size_limit is None: - ParserElement.recursion_memos = _UnboundedMemo() - elif cache_size_limit > 0: - ParserElement.recursion_memos = _LRUMemo(capacity=cache_size_limit) - else: - raise NotImplementedError("Memo size of %s" % cache_size_limit) - ParserElement._left_recursion_enabled = True - - @staticmethod - def enable_packrat(cache_size_limit: int = 128, *, force: bool = False) -> None: - """ - Enables "packrat" parsing, which adds memoizing to the parsing logic. - Repeated parse attempts at the same string location (which happens - often in many complex grammars) can immediately return a cached value, - instead of re-executing parsing/validating code. Memoizing is done of - both valid results and parsing exceptions. - - Parameters: - - - cache_size_limit - (default= ``128``) - if an integer value is provided - will limit the size of the packrat cache; if None is passed, then - the cache size will be unbounded; if 0 is passed, the cache will - be effectively disabled. - - This speedup may break existing programs that use parse actions that - have side-effects. For this reason, packrat parsing is disabled when - you first import pyparsing. To activate the packrat feature, your - program must call the class method :class:`ParserElement.enable_packrat`. - For best results, call ``enable_packrat()`` immediately after - importing pyparsing. - - Example:: - - import pyparsing - pyparsing.ParserElement.enable_packrat() - - Packrat parsing works similar but not identical to Bounded Recursion parsing, - thus the two cannot be used together. Use ``force=True`` to disable any - previous, conflicting settings. - """ - if force: - ParserElement.disable_memoization() - elif ParserElement._left_recursion_enabled: - raise RuntimeError("Packrat and Bounded Recursion are not compatible") - if not ParserElement._packratEnabled: - ParserElement._packratEnabled = True - if cache_size_limit is None: - ParserElement.packrat_cache = _UnboundedCache() - else: - ParserElement.packrat_cache = _FifoCache(cache_size_limit) - ParserElement._parse = ParserElement._parseCache - - def parse_string( - self, instring: str, parse_all: bool = False, *, parseAll: bool = False - ) -> ParseResults: - """ - Parse a string with respect to the parser definition. This function is intended as the primary interface to the - client code. - - :param instring: The input string to be parsed. - :param parse_all: If set, the entire input string must match the grammar. - :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. - :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. - :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or - an object with attributes if the given parser includes results names. - - If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This - is also equivalent to ending the grammar with :class:`StringEnd`(). - - To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are - converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string - contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string - being parsed, one can ensure a consistent view of the input string by doing one of the following: - - - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the - parse action's ``s`` argument, or - - explicitly expand the tabs in your input string before calling ``parse_string``. - - Examples: - - By default, partial matches are OK. - - >>> res = Word('a').parse_string('aaaaabaaa') - >>> print(res) - ['aaaaa'] - - The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children - directly to see more examples. - - It raises an exception if parse_all flag is set and instring does not match the whole grammar. - - >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) - Traceback (most recent call last): - ... - pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) - """ - parseAll = parse_all or parseAll - - ParserElement.reset_cache() - if not self.streamlined: - self.streamline() - for e in self.ignoreExprs: - e.streamline() - if not self.keepTabs: - instring = instring.expandtabs() - try: - loc, tokens = self._parse(instring, 0) - if parseAll: - loc = self.preParse(instring, loc) - se = Empty() + StringEnd() - se._parse(instring, loc) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clearing out pyparsing internal stack trace - raise exc.with_traceback(None) - else: - return tokens - - def scan_string( - self, - instring: str, - max_matches: int = _MAX_INT, - overlap: bool = False, - *, - debug: bool = False, - maxMatches: int = _MAX_INT, - ) -> Generator[Tuple[ParseResults, int, int], None, None]: - """ - Scan the input string for expression matches. Each match will return the - matching tokens, start location, and end location. May be called with optional - ``max_matches`` argument, to clip scanning after 'n' matches are found. If - ``overlap`` is specified, then overlapping matches will be reported. - - Note that the start and end locations are reported relative to the string - being parsed. See :class:`parse_string` for more information on parsing - strings with embedded tabs. - - Example:: - - source = "sldjf123lsdjjkf345sldkjf879lkjsfd987" - print(source) - for tokens, start, end in Word(alphas).scan_string(source): - print(' '*start + '^'*(end-start)) - print(' '*start + tokens[0]) - - prints:: - - sldjf123lsdjjkf345sldkjf879lkjsfd987 - ^^^^^ - sldjf - ^^^^^^^ - lsdjjkf - ^^^^^^ - sldkjf - ^^^^^^ - lkjsfd - """ - maxMatches = min(maxMatches, max_matches) - if not self.streamlined: - self.streamline() - for e in self.ignoreExprs: - e.streamline() - - if not self.keepTabs: - instring = str(instring).expandtabs() - instrlen = len(instring) - loc = 0 - preparseFn = self.preParse - parseFn = self._parse - ParserElement.resetCache() - matches = 0 - try: - while loc <= instrlen and matches < maxMatches: - try: - preloc = preparseFn(instring, loc) - nextLoc, tokens = parseFn(instring, preloc, callPreParse=False) - except ParseException: - loc = preloc + 1 - else: - if nextLoc > loc: - matches += 1 - if debug: - print( - { - "tokens": tokens.asList(), - "start": preloc, - "end": nextLoc, - } - ) - yield tokens, preloc, nextLoc - if overlap: - nextloc = preparseFn(instring, loc) - if nextloc > loc: - loc = nextLoc - else: - loc += 1 - else: - loc = nextLoc - else: - loc = preloc + 1 - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def transform_string(self, instring: str, *, debug: bool = False) -> str: - """ - Extension to :class:`scan_string`, to modify matching text with modified tokens that may - be returned from a parse action. To use ``transform_string``, define a grammar and - attach a parse action to it that modifies the returned token list. - Invoking ``transform_string()`` on a target string will then scan for matches, - and replace the matched text patterns according to the logic in the parse - action. ``transform_string()`` returns the resulting transformed string. - - Example:: - - wd = Word(alphas) - wd.set_parse_action(lambda toks: toks[0].title()) - - print(wd.transform_string("now is the winter of our discontent made glorious summer by this sun of york.")) - - prints:: - - Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York. - """ - out: List[str] = [] - lastE = 0 - # force preservation of s, to minimize unwanted transformation of string, and to - # keep string locs straight between transform_string and scan_string - self.keepTabs = True - try: - for t, s, e in self.scan_string(instring, debug=debug): - out.append(instring[lastE:s]) - if t: - if isinstance(t, ParseResults): - out += t.as_list() - elif isinstance(t, Iterable) and not isinstance(t, str_type): - out.extend(t) - else: - out.append(t) - lastE = e - out.append(instring[lastE:]) - out = [o for o in out if o] - return "".join([str(s) for s in _flatten(out)]) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def search_string( - self, - instring: str, - max_matches: int = _MAX_INT, - *, - debug: bool = False, - maxMatches: int = _MAX_INT, - ) -> ParseResults: - """ - Another extension to :class:`scan_string`, simplifying the access to the tokens found - to match the given parse expression. May be called with optional - ``max_matches`` argument, to clip searching after 'n' matches are found. - - Example:: - - # a capitalized word starts with an uppercase letter, followed by zero or more lowercase letters - cap_word = Word(alphas.upper(), alphas.lower()) - - print(cap_word.search_string("More than Iron, more than Lead, more than Gold I need Electricity")) - - # the sum() builtin can be used to merge results into a single ParseResults object - print(sum(cap_word.search_string("More than Iron, more than Lead, more than Gold I need Electricity"))) - - prints:: - - [['More'], ['Iron'], ['Lead'], ['Gold'], ['I'], ['Electricity']] - ['More', 'Iron', 'Lead', 'Gold', 'I', 'Electricity'] - """ - maxMatches = min(maxMatches, max_matches) - try: - return ParseResults( - [t for t, s, e in self.scan_string(instring, maxMatches, debug=debug)] - ) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def split( - self, - instring: str, - maxsplit: int = _MAX_INT, - include_separators: bool = False, - *, - includeSeparators=False, - ) -> Generator[str, None, None]: - """ - Generator method to split a string using the given expression as a separator. - May be called with optional ``maxsplit`` argument, to limit the number of splits; - and the optional ``include_separators`` argument (default= ``False``), if the separating - matching text should be included in the split results. - - Example:: - - punc = one_of(list(".,;:/-!?")) - print(list(punc.split("This, this?, this sentence, is badly punctuated!"))) - - prints:: - - ['This', ' this', '', ' this sentence', ' is badly punctuated', ''] - """ - includeSeparators = includeSeparators or include_separators - last = 0 - for t, s, e in self.scan_string(instring, max_matches=maxsplit): - yield instring[last:s] - if includeSeparators: - yield t[0] - last = e - yield instring[last:] - - def __add__(self, other) -> "ParserElement": - """ - Implementation of ``+`` operator - returns :class:`And`. Adding strings to a :class:`ParserElement` - converts them to :class:`Literal`s by default. - - Example:: - - greet = Word(alphas) + "," + Word(alphas) + "!" - hello = "Hello, World!" - print(hello, "->", greet.parse_string(hello)) - - prints:: - - Hello, World! -> ['Hello', ',', 'World', '!'] - - ``...`` may be used as a parse expression as a short form of :class:`SkipTo`. - - Literal('start') + ... + Literal('end') - - is equivalent to: - - Literal('start') + SkipTo('end')("_skipped*") + Literal('end') - - Note that the skipped text is returned with '_skipped' as a results name, - and to support having multiple skips in the same parser, the value returned is - a list of all skipped text. - """ - if other is Ellipsis: - return _PendingSkip(self) - - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return And([self, other]) - - def __radd__(self, other) -> "ParserElement": - """ - Implementation of ``+`` operator when left operand is not a :class:`ParserElement` - """ - if other is Ellipsis: - return SkipTo(self)("_skipped*") + self - - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other + self - - def __sub__(self, other) -> "ParserElement": - """ - Implementation of ``-`` operator, returns :class:`And` with error stop - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return self + And._ErrorStop() + other - - def __rsub__(self, other) -> "ParserElement": - """ - Implementation of ``-`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other - self - - def __mul__(self, other) -> "ParserElement": - """ - Implementation of ``*`` operator, allows use of ``expr * 3`` in place of - ``expr + expr + expr``. Expressions may also be multiplied by a 2-integer - tuple, similar to ``{min, max}`` multipliers in regular expressions. Tuples - may also include ``None`` as in: - - ``expr*(n, None)`` or ``expr*(n, )`` is equivalent - to ``expr*n + ZeroOrMore(expr)`` - (read as "at least n instances of ``expr``") - - ``expr*(None, n)`` is equivalent to ``expr*(0, n)`` - (read as "0 to n instances of ``expr``") - - ``expr*(None, None)`` is equivalent to ``ZeroOrMore(expr)`` - - ``expr*(1, None)`` is equivalent to ``OneOrMore(expr)`` - - Note that ``expr*(None, n)`` does not raise an exception if - more than n exprs exist in the input stream; that is, - ``expr*(None, n)`` does not enforce a maximum number of expr - occurrences. If this behavior is desired, then write - ``expr*(None, n) + ~expr`` - """ - if other is Ellipsis: - other = (0, None) - elif isinstance(other, tuple) and other[:1] == (Ellipsis,): - other = ((0,) + other[1:] + (None,))[:2] - - if isinstance(other, int): - minElements, optElements = other, 0 - elif isinstance(other, tuple): - other = tuple(o if o is not Ellipsis else None for o in other) - other = (other + (None, None))[:2] - if other[0] is None: - other = (0, other[1]) - if isinstance(other[0], int) and other[1] is None: - if other[0] == 0: - return ZeroOrMore(self) - if other[0] == 1: - return OneOrMore(self) - else: - return self * other[0] + ZeroOrMore(self) - elif isinstance(other[0], int) and isinstance(other[1], int): - minElements, optElements = other - optElements -= minElements - else: - raise TypeError( - "cannot multiply ParserElement and ({}) objects".format( - ",".join(type(item).__name__ for item in other) - ) - ) - else: - raise TypeError( - "cannot multiply ParserElement and {} objects".format( - type(other).__name__ - ) - ) - - if minElements < 0: - raise ValueError("cannot multiply ParserElement by negative value") - if optElements < 0: - raise ValueError( - "second tuple value must be greater or equal to first tuple value" - ) - if minElements == optElements == 0: - return And([]) - - if optElements: - - def makeOptionalList(n): - if n > 1: - return Opt(self + makeOptionalList(n - 1)) - else: - return Opt(self) - - if minElements: - if minElements == 1: - ret = self + makeOptionalList(optElements) - else: - ret = And([self] * minElements) + makeOptionalList(optElements) - else: - ret = makeOptionalList(optElements) - else: - if minElements == 1: - ret = self - else: - ret = And([self] * minElements) - return ret - - def __rmul__(self, other) -> "ParserElement": - return self.__mul__(other) - - def __or__(self, other) -> "ParserElement": - """ - Implementation of ``|`` operator - returns :class:`MatchFirst` - """ - if other is Ellipsis: - return _PendingSkip(self, must_skip=True) - - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return MatchFirst([self, other]) - - def __ror__(self, other) -> "ParserElement": - """ - Implementation of ``|`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other | self - - def __xor__(self, other) -> "ParserElement": - """ - Implementation of ``^`` operator - returns :class:`Or` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return Or([self, other]) - - def __rxor__(self, other) -> "ParserElement": - """ - Implementation of ``^`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other ^ self - - def __and__(self, other) -> "ParserElement": - """ - Implementation of ``&`` operator - returns :class:`Each` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return Each([self, other]) - - def __rand__(self, other) -> "ParserElement": - """ - Implementation of ``&`` operator when left operand is not a :class:`ParserElement` - """ - if isinstance(other, str_type): - other = self._literalStringClass(other) - if not isinstance(other, ParserElement): - raise TypeError( - "Cannot combine element of type {} with ParserElement".format( - type(other).__name__ - ) - ) - return other & self - - def __invert__(self) -> "ParserElement": - """ - Implementation of ``~`` operator - returns :class:`NotAny` - """ - return NotAny(self) - - # disable __iter__ to override legacy use of sequential access to __getitem__ to - # iterate over a sequence - __iter__ = None - - def __getitem__(self, key): - """ - use ``[]`` indexing notation as a short form for expression repetition: - - - ``expr[n]`` is equivalent to ``expr*n`` - - ``expr[m, n]`` is equivalent to ``expr*(m, n)`` - - ``expr[n, ...]`` or ``expr[n,]`` is equivalent - to ``expr*n + ZeroOrMore(expr)`` - (read as "at least n instances of ``expr``") - - ``expr[..., n]`` is equivalent to ``expr*(0, n)`` - (read as "0 to n instances of ``expr``") - - ``expr[...]`` and ``expr[0, ...]`` are equivalent to ``ZeroOrMore(expr)`` - - ``expr[1, ...]`` is equivalent to ``OneOrMore(expr)`` - - ``None`` may be used in place of ``...``. - - Note that ``expr[..., n]`` and ``expr[m, n]``do not raise an exception - if more than ``n`` ``expr``s exist in the input stream. If this behavior is - desired, then write ``expr[..., n] + ~expr``. - """ - - # convert single arg keys to tuples - try: - if isinstance(key, str_type): - key = (key,) - iter(key) - except TypeError: - key = (key, key) - - if len(key) > 2: - raise TypeError( - "only 1 or 2 index arguments supported ({}{})".format( - key[:5], "... [{}]".format(len(key)) if len(key) > 5 else "" - ) - ) - - # clip to 2 elements - ret = self * tuple(key[:2]) - return ret - - def __call__(self, name: str = None) -> "ParserElement": - """ - Shortcut for :class:`set_results_name`, with ``list_all_matches=False``. - - If ``name`` is given with a trailing ``'*'`` character, then ``list_all_matches`` will be - passed as ``True``. - - If ``name` is omitted, same as calling :class:`copy`. - - Example:: - - # these are equivalent - userdata = Word(alphas).set_results_name("name") + Word(nums + "-").set_results_name("socsecno") - userdata = Word(alphas)("name") + Word(nums + "-")("socsecno") - """ - if name is not None: - return self._setResultsName(name) - else: - return self.copy() - - def suppress(self) -> "ParserElement": - """ - Suppresses the output of this :class:`ParserElement`; useful to keep punctuation from - cluttering up returned output. - """ - return Suppress(self) - - def ignore_whitespace(self, recursive: bool = True) -> "ParserElement": - """ - Enables the skipping of whitespace before matching the characters in the - :class:`ParserElement`'s defined pattern. - - :param recursive: If ``True`` (the default), also enable whitespace skipping in child elements (if any) - """ - self.skipWhitespace = True - return self - - def leave_whitespace(self, recursive: bool = True) -> "ParserElement": - """ - Disables the skipping of whitespace before matching the characters in the - :class:`ParserElement`'s defined pattern. This is normally only used internally by - the pyparsing module, but may be needed in some whitespace-sensitive grammars. - - :param recursive: If true (the default), also disable whitespace skipping in child elements (if any) - """ - self.skipWhitespace = False - return self - - def set_whitespace_chars( - self, chars: Union[Set[str], str], copy_defaults: bool = False - ) -> "ParserElement": - """ - Overrides the default whitespace chars - """ - self.skipWhitespace = True - self.whiteChars = set(chars) - self.copyDefaultWhiteChars = copy_defaults - return self - - def parse_with_tabs(self) -> "ParserElement": - """ - Overrides default behavior to expand ```` s to spaces before parsing the input string. - Must be called before ``parse_string`` when the input grammar contains elements that - match ```` characters. - """ - self.keepTabs = True - return self - - def ignore(self, other: "ParserElement") -> "ParserElement": - """ - Define expression to be ignored (e.g., comments) while doing pattern - matching; may be called repeatedly, to define multiple comment or other - ignorable patterns. - - Example:: - - patt = Word(alphas)[1, ...] - patt.parse_string('ablaj /* comment */ lskjd') - # -> ['ablaj'] - - patt.ignore(c_style_comment) - patt.parse_string('ablaj /* comment */ lskjd') - # -> ['ablaj', 'lskjd'] - """ - import typing - - if isinstance(other, str_type): - other = Suppress(other) - - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - self.ignoreExprs.append(other) - else: - self.ignoreExprs.append(Suppress(other.copy())) - return self - - def set_debug_actions( - self, - start_action: DebugStartAction, - success_action: DebugSuccessAction, - exception_action: DebugExceptionAction, - ) -> "ParserElement": - """ - Customize display of debugging messages while doing pattern matching: - - - ``start_action`` - method to be called when an expression is about to be parsed; - should have the signature ``fn(input_string: str, location: int, expression: ParserElement, cache_hit: bool)`` - - - ``success_action`` - method to be called when an expression has successfully parsed; - should have the signature ``fn(input_string: str, start_location: int, end_location: int, expression: ParserELement, parsed_tokens: ParseResults, cache_hit: bool)`` - - - ``exception_action`` - method to be called when expression fails to parse; - should have the signature ``fn(input_string: str, location: int, expression: ParserElement, exception: Exception, cache_hit: bool)`` - """ - self.debugActions = self.DebugActions( - start_action or _default_start_debug_action, - success_action or _default_success_debug_action, - exception_action or _default_exception_debug_action, - ) - self.debug = True - return self - - def set_debug(self, flag: bool = True) -> "ParserElement": - """ - Enable display of debugging messages while doing pattern matching. - Set ``flag`` to ``True`` to enable, ``False`` to disable. - - Example:: - - wd = Word(alphas).set_name("alphaword") - integer = Word(nums).set_name("numword") - term = wd | integer - - # turn on debugging for wd - wd.set_debug() - - term[1, ...].parse_string("abc 123 xyz 890") - - prints:: - - Match alphaword at loc 0(1,1) - Matched alphaword -> ['abc'] - Match alphaword at loc 3(1,4) - Exception raised:Expected alphaword (at char 4), (line:1, col:5) - Match alphaword at loc 7(1,8) - Matched alphaword -> ['xyz'] - Match alphaword at loc 11(1,12) - Exception raised:Expected alphaword (at char 12), (line:1, col:13) - Match alphaword at loc 15(1,16) - Exception raised:Expected alphaword (at char 15), (line:1, col:16) - - The output shown is that produced by the default debug actions - custom debug actions can be - specified using :class:`set_debug_actions`. Prior to attempting - to match the ``wd`` expression, the debugging message ``"Match at loc (,)"`` - is shown. Then if the parse succeeds, a ``"Matched"`` message is shown, or an ``"Exception raised"`` - message is shown. Also note the use of :class:`set_name` to assign a human-readable name to the expression, - which makes debugging and exception messages easier to understand - for instance, the default - name created for the :class:`Word` expression without calling ``set_name`` is ``"W:(A-Za-z)"``. - """ - if flag: - self.set_debug_actions( - _default_start_debug_action, - _default_success_debug_action, - _default_exception_debug_action, - ) - else: - self.debug = False - return self - - @property - def default_name(self) -> str: - if self._defaultName is None: - self._defaultName = self._generateDefaultName() - return self._defaultName - - @abstractmethod - def _generateDefaultName(self): - """ - Child classes must define this method, which defines how the ``default_name`` is set. - """ - - def set_name(self, name: str) -> "ParserElement": - """ - Define name for this expression, makes debugging and exception messages clearer. - Example:: - Word(nums).parse_string("ABC") # -> Exception: Expected W:(0-9) (at char 0), (line:1, col:1) - Word(nums).set_name("integer").parse_string("ABC") # -> Exception: Expected integer (at char 0), (line:1, col:1) - """ - self.customName = name - self.errmsg = "Expected " + self.name - if __diag__.enable_debug_on_named_expressions: - self.set_debug() - return self - - @property - def name(self) -> str: - # This will use a user-defined name if available, but otherwise defaults back to the auto-generated name - return self.customName if self.customName is not None else self.default_name - - def __str__(self) -> str: - return self.name - - def __repr__(self) -> str: - return str(self) - - def streamline(self) -> "ParserElement": - self.streamlined = True - self._defaultName = None - return self - - def recurse(self) -> Sequence["ParserElement"]: - return [] - - def _checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.recurse(): - e._checkRecursion(subRecCheckList) - - def validate(self, validateTrace=None) -> None: - """ - Check defined expressions for valid structure, check for infinite recursive definitions. - """ - self._checkRecursion([]) - - def parse_file( - self, - file_or_filename: Union[str, Path, TextIO], - encoding: str = "utf-8", - parse_all: bool = False, - *, - parseAll: bool = False, - ) -> ParseResults: - """ - Execute the parse expression on the given file or filename. - If a filename is specified (instead of a file object), - the entire file is opened, read, and closed before parsing. - """ - parseAll = parseAll or parse_all - try: - file_contents = file_or_filename.read() - except AttributeError: - with open(file_or_filename, "r", encoding=encoding) as f: - file_contents = f.read() - try: - return self.parse_string(file_contents, parseAll) - except ParseBaseException as exc: - if ParserElement.verbose_stacktrace: - raise - else: - # catch and re-raise exception from here, clears out pyparsing internal stack trace - raise exc.with_traceback(None) - - def __eq__(self, other): - if self is other: - return True - elif isinstance(other, str_type): - return self.matches(other, parse_all=True) - elif isinstance(other, ParserElement): - return vars(self) == vars(other) - return False - - def __hash__(self): - return id(self) - - def matches( - self, test_string: str, parse_all: bool = True, *, parseAll: bool = True - ) -> bool: - """ - Method for quick testing of a parser against a test string. Good for simple - inline microtests of sub expressions while building up larger parser. - - Parameters: - - ``test_string`` - to test against this expression for a match - - ``parse_all`` - (default= ``True``) - flag to pass to :class:`parse_string` when running tests - - Example:: - - expr = Word(nums) - assert expr.matches("100") - """ - parseAll = parseAll and parse_all - try: - self.parse_string(str(test_string), parse_all=parseAll) - return True - except ParseBaseException: - return False - - def run_tests( - self, - tests: Union[str, List[str]], - parse_all: bool = True, - comment: typing.Optional[Union["ParserElement", str]] = "#", - full_dump: bool = True, - print_results: bool = True, - failure_tests: bool = False, - post_parse: Callable[[str, ParseResults], str] = None, - file: typing.Optional[TextIO] = None, - with_line_numbers: bool = False, - *, - parseAll: bool = True, - fullDump: bool = True, - printResults: bool = True, - failureTests: bool = False, - postParse: Callable[[str, ParseResults], str] = None, - ) -> Tuple[bool, List[Tuple[str, Union[ParseResults, Exception]]]]: - """ - Execute the parse expression on a series of test strings, showing each - test, the parsed results or where the parse failed. Quick and easy way to - run a parse expression against a list of sample strings. - - Parameters: - - ``tests`` - a list of separate test strings, or a multiline string of test strings - - ``parse_all`` - (default= ``True``) - flag to pass to :class:`parse_string` when running tests - - ``comment`` - (default= ``'#'``) - expression for indicating embedded comments in the test - string; pass None to disable comment filtering - - ``full_dump`` - (default= ``True``) - dump results as list followed by results names in nested outline; - if False, only dump nested list - - ``print_results`` - (default= ``True``) prints test output to stdout - - ``failure_tests`` - (default= ``False``) indicates if these tests are expected to fail parsing - - ``post_parse`` - (default= ``None``) optional callback for successful parse results; called as - `fn(test_string, parse_results)` and returns a string to be added to the test output - - ``file`` - (default= ``None``) optional file-like object to which test output will be written; - if None, will default to ``sys.stdout`` - - ``with_line_numbers`` - default= ``False``) show test strings with line and column numbers - - Returns: a (success, results) tuple, where success indicates that all tests succeeded - (or failed if ``failure_tests`` is True), and the results contain a list of lines of each - test's output - - Example:: - - number_expr = pyparsing_common.number.copy() - - result = number_expr.run_tests(''' - # unsigned integer - 100 - # negative integer - -100 - # float with scientific notation - 6.02e23 - # integer with scientific notation - 1e-12 - ''') - print("Success" if result[0] else "Failed!") - - result = number_expr.run_tests(''' - # stray character - 100Z - # missing leading digit before '.' - -.100 - # too many '.' - 3.14.159 - ''', failure_tests=True) - print("Success" if result[0] else "Failed!") - - prints:: - - # unsigned integer - 100 - [100] - - # negative integer - -100 - [-100] - - # float with scientific notation - 6.02e23 - [6.02e+23] - - # integer with scientific notation - 1e-12 - [1e-12] - - Success - - # stray character - 100Z - ^ - FAIL: Expected end of text (at char 3), (line:1, col:4) - - # missing leading digit before '.' - -.100 - ^ - FAIL: Expected {real number with scientific notation | real number | signed integer} (at char 0), (line:1, col:1) - - # too many '.' - 3.14.159 - ^ - FAIL: Expected end of text (at char 4), (line:1, col:5) - - Success - - Each test string must be on a single line. If you want to test a string that spans multiple - lines, create a test like this:: - - expr.run_tests(r"this is a test\\n of strings that spans \\n 3 lines") - - (Note that this is a raw string literal, you must include the leading ``'r'``.) - """ - from .testing import pyparsing_test - - parseAll = parseAll and parse_all - fullDump = fullDump and full_dump - printResults = printResults and print_results - failureTests = failureTests or failure_tests - postParse = postParse or post_parse - if isinstance(tests, str_type): - line_strip = type(tests).strip - tests = [line_strip(test_line) for test_line in tests.rstrip().splitlines()] - if isinstance(comment, str_type): - comment = Literal(comment) - if file is None: - file = sys.stdout - print_ = file.write - - result: Union[ParseResults, Exception] - allResults = [] - comments = [] - success = True - NL = Literal(r"\n").add_parse_action(replace_with("\n")).ignore(quoted_string) - BOM = "\ufeff" - for t in tests: - if comment is not None and comment.matches(t, False) or comments and not t: - comments.append( - pyparsing_test.with_line_numbers(t) if with_line_numbers else t - ) - continue - if not t: - continue - out = [ - "\n" + "\n".join(comments) if comments else "", - pyparsing_test.with_line_numbers(t) if with_line_numbers else t, - ] - comments = [] - try: - # convert newline marks to actual newlines, and strip leading BOM if present - t = NL.transform_string(t.lstrip(BOM)) - result = self.parse_string(t, parse_all=parseAll) - except ParseBaseException as pe: - fatal = "(FATAL)" if isinstance(pe, ParseFatalException) else "" - out.append(pe.explain()) - out.append("FAIL: " + str(pe)) - if ParserElement.verbose_stacktrace: - out.extend(traceback.format_tb(pe.__traceback__)) - success = success and failureTests - result = pe - except Exception as exc: - out.append("FAIL-EXCEPTION: {}: {}".format(type(exc).__name__, exc)) - if ParserElement.verbose_stacktrace: - out.extend(traceback.format_tb(exc.__traceback__)) - success = success and failureTests - result = exc - else: - success = success and not failureTests - if postParse is not None: - try: - pp_value = postParse(t, result) - if pp_value is not None: - if isinstance(pp_value, ParseResults): - out.append(pp_value.dump()) - else: - out.append(str(pp_value)) - else: - out.append(result.dump()) - except Exception as e: - out.append(result.dump(full=fullDump)) - out.append( - "{} failed: {}: {}".format( - postParse.__name__, type(e).__name__, e - ) - ) - else: - out.append(result.dump(full=fullDump)) - out.append("") - - if printResults: - print_("\n".join(out)) - - allResults.append((t, result)) - - return success, allResults - - def create_diagram( - self, - output_html: Union[TextIO, Path, str], - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, - **kwargs, - ) -> None: - """ - Create a railroad diagram for the parser. - - Parameters: - - output_html (str or file-like object) - output target for generated - diagram HTML - - vertical (int) - threshold for formatting multiple alternatives vertically - instead of horizontally (default=3) - - show_results_names - bool flag whether diagram should show annotations for - defined results names - - show_groups - bool flag whether groups should be highlighted with an unlabeled surrounding box - Additional diagram-formatting keyword arguments can also be included; - see railroad.Diagram class. - """ - - try: - from .diagram import to_railroad, railroad_to_html - except ImportError as ie: - raise Exception( - "must ``pip install pyparsing[diagrams]`` to generate parser railroad diagrams" - ) from ie - - self.streamline() - - railroad = to_railroad( - self, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - diagram_kwargs=kwargs, - ) - if isinstance(output_html, (str, Path)): - with open(output_html, "w", encoding="utf-8") as diag_file: - diag_file.write(railroad_to_html(railroad)) - else: - # we were passed a file-like object, just write to it - output_html.write(railroad_to_html(railroad)) - - setDefaultWhitespaceChars = set_default_whitespace_chars - inlineLiteralsUsing = inline_literals_using - setResultsName = set_results_name - setBreak = set_break - setParseAction = set_parse_action - addParseAction = add_parse_action - addCondition = add_condition - setFailAction = set_fail_action - tryParse = try_parse - canParseNext = can_parse_next - resetCache = reset_cache - enableLeftRecursion = enable_left_recursion - enablePackrat = enable_packrat - parseString = parse_string - scanString = scan_string - searchString = search_string - transformString = transform_string - setWhitespaceChars = set_whitespace_chars - parseWithTabs = parse_with_tabs - setDebugActions = set_debug_actions - setDebug = set_debug - defaultName = default_name - setName = set_name - parseFile = parse_file - runTests = run_tests - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class _PendingSkip(ParserElement): - # internal placeholder class to hold a place were '...' is added to a parser element, - # once another ParserElement is added, this placeholder will be replaced with a SkipTo - def __init__(self, expr: ParserElement, must_skip: bool = False): - super().__init__() - self.anchor = expr - self.must_skip = must_skip - - def _generateDefaultName(self): - return str(self.anchor + Empty()).replace("Empty", "...") - - def __add__(self, other) -> "ParserElement": - skipper = SkipTo(other).set_name("...")("_skipped*") - if self.must_skip: - - def must_skip(t): - if not t._skipped or t._skipped.as_list() == [""]: - del t[0] - t.pop("_skipped", None) - - def show_skip(t): - if t._skipped.as_list()[-1:] == [""]: - t.pop("_skipped") - t["_skipped"] = "missing <" + repr(self.anchor) + ">" - - return ( - self.anchor + skipper().add_parse_action(must_skip) - | skipper().add_parse_action(show_skip) - ) + other - - return self.anchor + skipper + other - - def __repr__(self): - return self.defaultName - - def parseImpl(self, *args): - raise Exception( - "use of `...` expression without following SkipTo target expression" - ) - - -class Token(ParserElement): - """Abstract :class:`ParserElement` subclass, for defining atomic - matching patterns. - """ - - def __init__(self): - super().__init__(savelist=False) - - def _generateDefaultName(self): - return type(self).__name__ - - -class Empty(Token): - """ - An empty token, will always match. - """ - - def __init__(self): - super().__init__() - self.mayReturnEmpty = True - self.mayIndexError = False - - -class NoMatch(Token): - """ - A token that will never match. - """ - - def __init__(self): - super().__init__() - self.mayReturnEmpty = True - self.mayIndexError = False - self.errmsg = "Unmatchable token" - - def parseImpl(self, instring, loc, doActions=True): - raise ParseException(instring, loc, self.errmsg, self) - - -class Literal(Token): - """ - Token to exactly match a specified string. - - Example:: - - Literal('blah').parse_string('blah') # -> ['blah'] - Literal('blah').parse_string('blahfooblah') # -> ['blah'] - Literal('blah').parse_string('bla') # -> Exception: Expected "blah" - - For case-insensitive matching, use :class:`CaselessLiteral`. - - For keyword matching (force word break before and after the matched string), - use :class:`Keyword` or :class:`CaselessKeyword`. - """ - - def __init__(self, match_string: str = "", *, matchString: str = ""): - super().__init__() - match_string = matchString or match_string - self.match = match_string - self.matchLen = len(match_string) - try: - self.firstMatchChar = match_string[0] - except IndexError: - raise ValueError("null string passed to Literal; use Empty() instead") - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = False - self.mayIndexError = False - - # Performance tuning: modify __class__ to select - # a parseImpl optimized for single-character check - if self.matchLen == 1 and type(self) is Literal: - self.__class__ = _SingleCharLiteral - - def _generateDefaultName(self): - return repr(self.match) - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] == self.firstMatchChar and instring.startswith( - self.match, loc - ): - return loc + self.matchLen, self.match - raise ParseException(instring, loc, self.errmsg, self) - - -class _SingleCharLiteral(Literal): - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] == self.firstMatchChar: - return loc + 1, self.match - raise ParseException(instring, loc, self.errmsg, self) - - -ParserElement._literalStringClass = Literal - - -class Keyword(Token): - """ - Token to exactly match a specified string as a keyword, that is, - it must be immediately followed by a non-keyword character. Compare - with :class:`Literal`: - - - ``Literal("if")`` will match the leading ``'if'`` in - ``'ifAndOnlyIf'``. - - ``Keyword("if")`` will not; it will only match the leading - ``'if'`` in ``'if x=1'``, or ``'if(y==2)'`` - - Accepts two optional constructor arguments in addition to the - keyword string: - - - ``identChars`` is a string of characters that would be valid - identifier characters, defaulting to all alphanumerics + "_" and - "$" - - ``caseless`` allows case-insensitive matching, default is ``False``. - - Example:: - - Keyword("start").parse_string("start") # -> ['start'] - Keyword("start").parse_string("starting") # -> Exception - - For case-insensitive matching, use :class:`CaselessKeyword`. - """ - - DEFAULT_KEYWORD_CHARS = alphanums + "_$" - - def __init__( - self, - match_string: str = "", - ident_chars: typing.Optional[str] = None, - caseless: bool = False, - *, - matchString: str = "", - identChars: typing.Optional[str] = None, - ): - super().__init__() - identChars = identChars or ident_chars - if identChars is None: - identChars = Keyword.DEFAULT_KEYWORD_CHARS - match_string = matchString or match_string - self.match = match_string - self.matchLen = len(match_string) - try: - self.firstMatchChar = match_string[0] - except IndexError: - raise ValueError("null string passed to Keyword; use Empty() instead") - self.errmsg = "Expected {} {}".format(type(self).__name__, self.name) - self.mayReturnEmpty = False - self.mayIndexError = False - self.caseless = caseless - if caseless: - self.caselessmatch = match_string.upper() - identChars = identChars.upper() - self.identChars = set(identChars) - - def _generateDefaultName(self): - return repr(self.match) - - def parseImpl(self, instring, loc, doActions=True): - errmsg = self.errmsg - errloc = loc - if self.caseless: - if instring[loc : loc + self.matchLen].upper() == self.caselessmatch: - if loc == 0 or instring[loc - 1].upper() not in self.identChars: - if ( - loc >= len(instring) - self.matchLen - or instring[loc + self.matchLen].upper() not in self.identChars - ): - return loc + self.matchLen, self.match - else: - # followed by keyword char - errmsg += ", was immediately followed by keyword character" - errloc = loc + self.matchLen - else: - # preceded by keyword char - errmsg += ", keyword was immediately preceded by keyword character" - errloc = loc - 1 - # else no match just raise plain exception - - else: - if ( - instring[loc] == self.firstMatchChar - and self.matchLen == 1 - or instring.startswith(self.match, loc) - ): - if loc == 0 or instring[loc - 1] not in self.identChars: - if ( - loc >= len(instring) - self.matchLen - or instring[loc + self.matchLen] not in self.identChars - ): - return loc + self.matchLen, self.match - else: - # followed by keyword char - errmsg += ( - ", keyword was immediately followed by keyword character" - ) - errloc = loc + self.matchLen - else: - # preceded by keyword char - errmsg += ", keyword was immediately preceded by keyword character" - errloc = loc - 1 - # else no match just raise plain exception - - raise ParseException(instring, errloc, errmsg, self) - - @staticmethod - def set_default_keyword_chars(chars) -> None: - """ - Overrides the default characters used by :class:`Keyword` expressions. - """ - Keyword.DEFAULT_KEYWORD_CHARS = chars - - setDefaultKeywordChars = set_default_keyword_chars - - -class CaselessLiteral(Literal): - """ - Token to match a specified string, ignoring case of letters. - Note: the matched results will always be in the case of the given - match string, NOT the case of the input text. - - Example:: - - CaselessLiteral("CMD")[1, ...].parse_string("cmd CMD Cmd10") - # -> ['CMD', 'CMD', 'CMD'] - - (Contrast with example for :class:`CaselessKeyword`.) - """ - - def __init__(self, match_string: str = "", *, matchString: str = ""): - match_string = matchString or match_string - super().__init__(match_string.upper()) - # Preserve the defining literal. - self.returnString = match_string - self.errmsg = "Expected " + self.name - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc : loc + self.matchLen].upper() == self.match: - return loc + self.matchLen, self.returnString - raise ParseException(instring, loc, self.errmsg, self) - - -class CaselessKeyword(Keyword): - """ - Caseless version of :class:`Keyword`. - - Example:: - - CaselessKeyword("CMD")[1, ...].parse_string("cmd CMD Cmd10") - # -> ['CMD', 'CMD'] - - (Contrast with example for :class:`CaselessLiteral`.) - """ - - def __init__( - self, - match_string: str = "", - ident_chars: typing.Optional[str] = None, - *, - matchString: str = "", - identChars: typing.Optional[str] = None, - ): - identChars = identChars or ident_chars - match_string = matchString or match_string - super().__init__(match_string, identChars, caseless=True) - - -class CloseMatch(Token): - """A variation on :class:`Literal` which matches "close" matches, - that is, strings with at most 'n' mismatching characters. - :class:`CloseMatch` takes parameters: - - - ``match_string`` - string to be matched - - ``caseless`` - a boolean indicating whether to ignore casing when comparing characters - - ``max_mismatches`` - (``default=1``) maximum number of - mismatches allowed to count as a match - - The results from a successful parse will contain the matched text - from the input string and the following named results: - - - ``mismatches`` - a list of the positions within the - match_string where mismatches were found - - ``original`` - the original match_string used to compare - against the input string - - If ``mismatches`` is an empty list, then the match was an exact - match. - - Example:: - - patt = CloseMatch("ATCATCGAATGGA") - patt.parse_string("ATCATCGAAXGGA") # -> (['ATCATCGAAXGGA'], {'mismatches': [[9]], 'original': ['ATCATCGAATGGA']}) - patt.parse_string("ATCAXCGAAXGGA") # -> Exception: Expected 'ATCATCGAATGGA' (with up to 1 mismatches) (at char 0), (line:1, col:1) - - # exact match - patt.parse_string("ATCATCGAATGGA") # -> (['ATCATCGAATGGA'], {'mismatches': [[]], 'original': ['ATCATCGAATGGA']}) - - # close match allowing up to 2 mismatches - patt = CloseMatch("ATCATCGAATGGA", max_mismatches=2) - patt.parse_string("ATCAXCGAAXGGA") # -> (['ATCAXCGAAXGGA'], {'mismatches': [[4, 9]], 'original': ['ATCATCGAATGGA']}) - """ - - def __init__( - self, - match_string: str, - max_mismatches: int = None, - *, - maxMismatches: int = 1, - caseless=False, - ): - maxMismatches = max_mismatches if max_mismatches is not None else maxMismatches - super().__init__() - self.match_string = match_string - self.maxMismatches = maxMismatches - self.errmsg = "Expected {!r} (with up to {} mismatches)".format( - self.match_string, self.maxMismatches - ) - self.caseless = caseless - self.mayIndexError = False - self.mayReturnEmpty = False - - def _generateDefaultName(self): - return "{}:{!r}".format(type(self).__name__, self.match_string) - - def parseImpl(self, instring, loc, doActions=True): - start = loc - instrlen = len(instring) - maxloc = start + len(self.match_string) - - if maxloc <= instrlen: - match_string = self.match_string - match_stringloc = 0 - mismatches = [] - maxMismatches = self.maxMismatches - - for match_stringloc, s_m in enumerate( - zip(instring[loc:maxloc], match_string) - ): - src, mat = s_m - if self.caseless: - src, mat = src.lower(), mat.lower() - - if src != mat: - mismatches.append(match_stringloc) - if len(mismatches) > maxMismatches: - break - else: - loc = start + match_stringloc + 1 - results = ParseResults([instring[start:loc]]) - results["original"] = match_string - results["mismatches"] = mismatches - return loc, results - - raise ParseException(instring, loc, self.errmsg, self) - - -class Word(Token): - """Token for matching words composed of allowed character sets. - Parameters: - - ``init_chars`` - string of all characters that should be used to - match as a word; "ABC" will match "AAA", "ABAB", "CBAC", etc.; - if ``body_chars`` is also specified, then this is the string of - initial characters - - ``body_chars`` - string of characters that - can be used for matching after a matched initial character as - given in ``init_chars``; if omitted, same as the initial characters - (default=``None``) - - ``min`` - minimum number of characters to match (default=1) - - ``max`` - maximum number of characters to match (default=0) - - ``exact`` - exact number of characters to match (default=0) - - ``as_keyword`` - match as a keyword (default=``False``) - - ``exclude_chars`` - characters that might be - found in the input ``body_chars`` string but which should not be - accepted for matching ;useful to define a word of all - printables except for one or two characters, for instance - (default=``None``) - - :class:`srange` is useful for defining custom character set strings - for defining :class:`Word` expressions, using range notation from - regular expression character sets. - - A common mistake is to use :class:`Word` to match a specific literal - string, as in ``Word("Address")``. Remember that :class:`Word` - uses the string argument to define *sets* of matchable characters. - This expression would match "Add", "AAA", "dAred", or any other word - made up of the characters 'A', 'd', 'r', 'e', and 's'. To match an - exact literal string, use :class:`Literal` or :class:`Keyword`. - - pyparsing includes helper strings for building Words: - - - :class:`alphas` - - :class:`nums` - - :class:`alphanums` - - :class:`hexnums` - - :class:`alphas8bit` (alphabetic characters in ASCII range 128-255 - - accented, tilded, umlauted, etc.) - - :class:`punc8bit` (non-alphabetic characters in ASCII range - 128-255 - currency, symbols, superscripts, diacriticals, etc.) - - :class:`printables` (any non-whitespace character) - - ``alphas``, ``nums``, and ``printables`` are also defined in several - Unicode sets - see :class:`pyparsing_unicode``. - - Example:: - - # a word composed of digits - integer = Word(nums) # equivalent to Word("0123456789") or Word(srange("0-9")) - - # a word with a leading capital, and zero or more lowercase - capital_word = Word(alphas.upper(), alphas.lower()) - - # hostnames are alphanumeric, with leading alpha, and '-' - hostname = Word(alphas, alphanums + '-') - - # roman numeral (not a strict parser, accepts invalid mix of characters) - roman = Word("IVXLCDM") - - # any string of non-whitespace characters, except for ',' - csv_value = Word(printables, exclude_chars=",") - """ - - def __init__( - self, - init_chars: str = "", - body_chars: typing.Optional[str] = None, - min: int = 1, - max: int = 0, - exact: int = 0, - as_keyword: bool = False, - exclude_chars: typing.Optional[str] = None, - *, - initChars: typing.Optional[str] = None, - bodyChars: typing.Optional[str] = None, - asKeyword: bool = False, - excludeChars: typing.Optional[str] = None, - ): - initChars = initChars or init_chars - bodyChars = bodyChars or body_chars - asKeyword = asKeyword or as_keyword - excludeChars = excludeChars or exclude_chars - super().__init__() - if not initChars: - raise ValueError( - "invalid {}, initChars cannot be empty string".format( - type(self).__name__ - ) - ) - - initChars = set(initChars) - self.initChars = initChars - if excludeChars: - excludeChars = set(excludeChars) - initChars -= excludeChars - if bodyChars: - bodyChars = set(bodyChars) - excludeChars - self.initCharsOrig = "".join(sorted(initChars)) - - if bodyChars: - self.bodyCharsOrig = "".join(sorted(bodyChars)) - self.bodyChars = set(bodyChars) - else: - self.bodyCharsOrig = "".join(sorted(initChars)) - self.bodyChars = set(initChars) - - self.maxSpecified = max > 0 - - if min < 1: - raise ValueError( - "cannot specify a minimum length < 1; use Opt(Word()) if zero-length word is permitted" - ) - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.asKeyword = asKeyword - - # see if we can make a regex for this Word - if " " not in self.initChars | self.bodyChars and (min == 1 and exact == 0): - if self.bodyChars == self.initChars: - if max == 0: - repeat = "+" - elif max == 1: - repeat = "" - else: - repeat = "{{{},{}}}".format( - self.minLen, "" if self.maxLen == _MAX_INT else self.maxLen - ) - self.reString = "[{}]{}".format( - _collapse_string_to_ranges(self.initChars), - repeat, - ) - elif len(self.initChars) == 1: - if max == 0: - repeat = "*" - else: - repeat = "{{0,{}}}".format(max - 1) - self.reString = "{}[{}]{}".format( - re.escape(self.initCharsOrig), - _collapse_string_to_ranges(self.bodyChars), - repeat, - ) - else: - if max == 0: - repeat = "*" - elif max == 2: - repeat = "" - else: - repeat = "{{0,{}}}".format(max - 1) - self.reString = "[{}][{}]{}".format( - _collapse_string_to_ranges(self.initChars), - _collapse_string_to_ranges(self.bodyChars), - repeat, - ) - if self.asKeyword: - self.reString = r"\b" + self.reString + r"\b" - - try: - self.re = re.compile(self.reString) - except re.error: - self.re = None - else: - self.re_match = self.re.match - self.__class__ = _WordRegex - - def _generateDefaultName(self): - def charsAsStr(s): - max_repr_len = 16 - s = _collapse_string_to_ranges(s, re_escape=False) - if len(s) > max_repr_len: - return s[: max_repr_len - 3] + "..." - else: - return s - - if self.initChars != self.bodyChars: - base = "W:({}, {})".format( - charsAsStr(self.initChars), charsAsStr(self.bodyChars) - ) - else: - base = "W:({})".format(charsAsStr(self.initChars)) - - # add length specification - if self.minLen > 1 or self.maxLen != _MAX_INT: - if self.minLen == self.maxLen: - if self.minLen == 1: - return base[2:] - else: - return base + "{{{}}}".format(self.minLen) - elif self.maxLen == _MAX_INT: - return base + "{{{},...}}".format(self.minLen) - else: - return base + "{{{},{}}}".format(self.minLen, self.maxLen) - return base - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] not in self.initChars: - raise ParseException(instring, loc, self.errmsg, self) - - start = loc - loc += 1 - instrlen = len(instring) - bodychars = self.bodyChars - maxloc = start + self.maxLen - maxloc = min(maxloc, instrlen) - while loc < maxloc and instring[loc] in bodychars: - loc += 1 - - throwException = False - if loc - start < self.minLen: - throwException = True - elif self.maxSpecified and loc < instrlen and instring[loc] in bodychars: - throwException = True - elif self.asKeyword: - if ( - start > 0 - and instring[start - 1] in bodychars - or loc < instrlen - and instring[loc] in bodychars - ): - throwException = True - - if throwException: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class _WordRegex(Word): - def parseImpl(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - return loc, result.group() - - -class Char(_WordRegex): - """A short-cut class for defining :class:`Word` ``(characters, exact=1)``, - when defining a match of any single character in a string of - characters. - """ - - def __init__( - self, - charset: str, - as_keyword: bool = False, - exclude_chars: typing.Optional[str] = None, - *, - asKeyword: bool = False, - excludeChars: typing.Optional[str] = None, - ): - asKeyword = asKeyword or as_keyword - excludeChars = excludeChars or exclude_chars - super().__init__( - charset, exact=1, asKeyword=asKeyword, excludeChars=excludeChars - ) - self.reString = "[{}]".format(_collapse_string_to_ranges(self.initChars)) - if asKeyword: - self.reString = r"\b{}\b".format(self.reString) - self.re = re.compile(self.reString) - self.re_match = self.re.match - - -class Regex(Token): - r"""Token for matching strings that match a given regular - expression. Defined with string specifying the regular expression in - a form recognized by the stdlib Python `re module `_. - If the given regex contains named groups (defined using ``(?P...)``), - these will be preserved as named :class:`ParseResults`. - - If instead of the Python stdlib ``re`` module you wish to use a different RE module - (such as the ``regex`` module), you can do so by building your ``Regex`` object with - a compiled RE that was compiled using ``regex``. - - Example:: - - realnum = Regex(r"[+-]?\d+\.\d*") - # ref: https://stackoverflow.com/questions/267399/how-do-you-match-only-valid-roman-numerals-with-a-regular-expression - roman = Regex(r"M{0,4}(CM|CD|D?{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})") - - # named fields in a regex will be returned as named results - date = Regex(r'(?P\d{4})-(?P\d\d?)-(?P\d\d?)') - - # the Regex class will accept re's compiled using the regex module - import regex - parser = pp.Regex(regex.compile(r'[0-9]')) - """ - - def __init__( - self, - pattern: Any, - flags: Union[re.RegexFlag, int] = 0, - as_group_list: bool = False, - as_match: bool = False, - *, - asGroupList: bool = False, - asMatch: bool = False, - ): - """The parameters ``pattern`` and ``flags`` are passed - to the ``re.compile()`` function as-is. See the Python - `re module `_ module for an - explanation of the acceptable patterns and flags. - """ - super().__init__() - asGroupList = asGroupList or as_group_list - asMatch = asMatch or as_match - - if isinstance(pattern, str_type): - if not pattern: - raise ValueError("null string passed to Regex; use Empty() instead") - - self._re = None - self.reString = self.pattern = pattern - self.flags = flags - - elif hasattr(pattern, "pattern") and hasattr(pattern, "match"): - self._re = pattern - self.pattern = self.reString = pattern.pattern - self.flags = flags - - else: - raise TypeError( - "Regex may only be constructed with a string or a compiled RE object" - ) - - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.asGroupList = asGroupList - self.asMatch = asMatch - if self.asGroupList: - self.parseImpl = self.parseImplAsGroupList - if self.asMatch: - self.parseImpl = self.parseImplAsMatch - - @cached_property - def re(self): - if self._re: - return self._re - else: - try: - return re.compile(self.pattern, self.flags) - except re.error: - raise ValueError( - "invalid pattern ({!r}) passed to Regex".format(self.pattern) - ) - - @cached_property - def re_match(self): - return self.re.match - - @cached_property - def mayReturnEmpty(self): - return self.re_match("") is not None - - def _generateDefaultName(self): - return "Re:({})".format(repr(self.pattern).replace("\\\\", "\\")) - - def parseImpl(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = ParseResults(result.group()) - d = result.groupdict() - if d: - for k, v in d.items(): - ret[k] = v - return loc, ret - - def parseImplAsGroupList(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result.groups() - return loc, ret - - def parseImplAsMatch(self, instring, loc, doActions=True): - result = self.re_match(instring, loc) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result - return loc, ret - - def sub(self, repl: str) -> ParserElement: - r""" - Return :class:`Regex` with an attached parse action to transform the parsed - result as if called using `re.sub(expr, repl, string) `_. - - Example:: - - make_html = Regex(r"(\w+):(.*?):").sub(r"<\1>\2") - print(make_html.transform_string("h1:main title:")) - # prints "

      main title

      " - """ - if self.asGroupList: - raise TypeError("cannot use sub() with Regex(asGroupList=True)") - - if self.asMatch and callable(repl): - raise TypeError("cannot use sub() with a callable with Regex(asMatch=True)") - - if self.asMatch: - - def pa(tokens): - return tokens[0].expand(repl) - - else: - - def pa(tokens): - return self.re.sub(repl, tokens[0]) - - return self.add_parse_action(pa) - - -class QuotedString(Token): - r""" - Token for matching strings that are delimited by quoting characters. - - Defined with the following parameters: - - - ``quote_char`` - string of one or more characters defining the - quote delimiting string - - ``esc_char`` - character to re_escape quotes, typically backslash - (default= ``None``) - - ``esc_quote`` - special quote sequence to re_escape an embedded quote - string (such as SQL's ``""`` to re_escape an embedded ``"``) - (default= ``None``) - - ``multiline`` - boolean indicating whether quotes can span - multiple lines (default= ``False``) - - ``unquote_results`` - boolean indicating whether the matched text - should be unquoted (default= ``True``) - - ``end_quote_char`` - string of one or more characters defining the - end of the quote delimited string (default= ``None`` => same as - quote_char) - - ``convert_whitespace_escapes`` - convert escaped whitespace - (``'\t'``, ``'\n'``, etc.) to actual whitespace - (default= ``True``) - - Example:: - - qs = QuotedString('"') - print(qs.search_string('lsjdf "This is the quote" sldjf')) - complex_qs = QuotedString('{{', end_quote_char='}}') - print(complex_qs.search_string('lsjdf {{This is the "quote"}} sldjf')) - sql_qs = QuotedString('"', esc_quote='""') - print(sql_qs.search_string('lsjdf "This is the quote with ""embedded"" quotes" sldjf')) - - prints:: - - [['This is the quote']] - [['This is the "quote"']] - [['This is the quote with "embedded" quotes']] - """ - ws_map = ((r"\t", "\t"), (r"\n", "\n"), (r"\f", "\f"), (r"\r", "\r")) - - def __init__( - self, - quote_char: str = "", - esc_char: typing.Optional[str] = None, - esc_quote: typing.Optional[str] = None, - multiline: bool = False, - unquote_results: bool = True, - end_quote_char: typing.Optional[str] = None, - convert_whitespace_escapes: bool = True, - *, - quoteChar: str = "", - escChar: typing.Optional[str] = None, - escQuote: typing.Optional[str] = None, - unquoteResults: bool = True, - endQuoteChar: typing.Optional[str] = None, - convertWhitespaceEscapes: bool = True, - ): - super().__init__() - escChar = escChar or esc_char - escQuote = escQuote or esc_quote - unquoteResults = unquoteResults and unquote_results - endQuoteChar = endQuoteChar or end_quote_char - convertWhitespaceEscapes = ( - convertWhitespaceEscapes and convert_whitespace_escapes - ) - quote_char = quoteChar or quote_char - - # remove white space from quote chars - wont work anyway - quote_char = quote_char.strip() - if not quote_char: - raise ValueError("quote_char cannot be the empty string") - - if endQuoteChar is None: - endQuoteChar = quote_char - else: - endQuoteChar = endQuoteChar.strip() - if not endQuoteChar: - raise ValueError("endQuoteChar cannot be the empty string") - - self.quoteChar = quote_char - self.quoteCharLen = len(quote_char) - self.firstQuoteChar = quote_char[0] - self.endQuoteChar = endQuoteChar - self.endQuoteCharLen = len(endQuoteChar) - self.escChar = escChar - self.escQuote = escQuote - self.unquoteResults = unquoteResults - self.convertWhitespaceEscapes = convertWhitespaceEscapes - - sep = "" - inner_pattern = "" - - if escQuote: - inner_pattern += r"{}(?:{})".format(sep, re.escape(escQuote)) - sep = "|" - - if escChar: - inner_pattern += r"{}(?:{}.)".format(sep, re.escape(escChar)) - sep = "|" - self.escCharReplacePattern = re.escape(self.escChar) + "(.)" - - if len(self.endQuoteChar) > 1: - inner_pattern += ( - "{}(?:".format(sep) - + "|".join( - "(?:{}(?!{}))".format( - re.escape(self.endQuoteChar[:i]), - re.escape(self.endQuoteChar[i:]), - ) - for i in range(len(self.endQuoteChar) - 1, 0, -1) - ) - + ")" - ) - sep = "|" - - if multiline: - self.flags = re.MULTILINE | re.DOTALL - inner_pattern += r"{}(?:[^{}{}])".format( - sep, - _escape_regex_range_chars(self.endQuoteChar[0]), - (_escape_regex_range_chars(escChar) if escChar is not None else ""), - ) - else: - self.flags = 0 - inner_pattern += r"{}(?:[^{}\n\r{}])".format( - sep, - _escape_regex_range_chars(self.endQuoteChar[0]), - (_escape_regex_range_chars(escChar) if escChar is not None else ""), - ) - - self.pattern = "".join( - [ - re.escape(self.quoteChar), - "(?:", - inner_pattern, - ")*", - re.escape(self.endQuoteChar), - ] - ) - - try: - self.re = re.compile(self.pattern, self.flags) - self.reString = self.pattern - self.re_match = self.re.match - except re.error: - raise ValueError( - "invalid pattern {!r} passed to Regex".format(self.pattern) - ) - - self.errmsg = "Expected " + self.name - self.mayIndexError = False - self.mayReturnEmpty = True - - def _generateDefaultName(self): - if self.quoteChar == self.endQuoteChar and isinstance(self.quoteChar, str_type): - return "string enclosed in {!r}".format(self.quoteChar) - - return "quoted string, starting with {} ending with {}".format( - self.quoteChar, self.endQuoteChar - ) - - def parseImpl(self, instring, loc, doActions=True): - result = ( - instring[loc] == self.firstQuoteChar - and self.re_match(instring, loc) - or None - ) - if not result: - raise ParseException(instring, loc, self.errmsg, self) - - loc = result.end() - ret = result.group() - - if self.unquoteResults: - - # strip off quotes - ret = ret[self.quoteCharLen : -self.endQuoteCharLen] - - if isinstance(ret, str_type): - # replace escaped whitespace - if "\\" in ret and self.convertWhitespaceEscapes: - for wslit, wschar in self.ws_map: - ret = ret.replace(wslit, wschar) - - # replace escaped characters - if self.escChar: - ret = re.sub(self.escCharReplacePattern, r"\g<1>", ret) - - # replace escaped quotes - if self.escQuote: - ret = ret.replace(self.escQuote, self.endQuoteChar) - - return loc, ret - - -class CharsNotIn(Token): - """Token for matching words composed of characters *not* in a given - set (will include whitespace in matched characters if not listed in - the provided exclusion set - see example). Defined with string - containing all disallowed characters, and an optional minimum, - maximum, and/or exact length. The default value for ``min`` is - 1 (a minimum value < 1 is not valid); the default values for - ``max`` and ``exact`` are 0, meaning no maximum or exact - length restriction. - - Example:: - - # define a comma-separated-value as anything that is not a ',' - csv_value = CharsNotIn(',') - print(delimited_list(csv_value).parse_string("dkls,lsdkjf,s12 34,@!#,213")) - - prints:: - - ['dkls', 'lsdkjf', 's12 34', '@!#', '213'] - """ - - def __init__( - self, - not_chars: str = "", - min: int = 1, - max: int = 0, - exact: int = 0, - *, - notChars: str = "", - ): - super().__init__() - self.skipWhitespace = False - self.notChars = not_chars or notChars - self.notCharsSet = set(self.notChars) - - if min < 1: - raise ValueError( - "cannot specify a minimum length < 1; use " - "Opt(CharsNotIn()) if zero-length char group is permitted" - ) - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - self.errmsg = "Expected " + self.name - self.mayReturnEmpty = self.minLen == 0 - self.mayIndexError = False - - def _generateDefaultName(self): - not_chars_str = _collapse_string_to_ranges(self.notChars) - if len(not_chars_str) > 16: - return "!W:({}...)".format(self.notChars[: 16 - 3]) - else: - return "!W:({})".format(self.notChars) - - def parseImpl(self, instring, loc, doActions=True): - notchars = self.notCharsSet - if instring[loc] in notchars: - raise ParseException(instring, loc, self.errmsg, self) - - start = loc - loc += 1 - maxlen = min(start + self.maxLen, len(instring)) - while loc < maxlen and instring[loc] not in notchars: - loc += 1 - - if loc - start < self.minLen: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class White(Token): - """Special matching class for matching whitespace. Normally, - whitespace is ignored by pyparsing grammars. This class is included - when some whitespace structures are significant. Define with - a string containing the whitespace characters to be matched; default - is ``" \\t\\r\\n"``. Also takes optional ``min``, - ``max``, and ``exact`` arguments, as defined for the - :class:`Word` class. - """ - - whiteStrs = { - " ": "", - "\t": "", - "\n": "", - "\r": "", - "\f": "", - "\u00A0": "", - "\u1680": "", - "\u180E": "", - "\u2000": "", - "\u2001": "", - "\u2002": "", - "\u2003": "", - "\u2004": "", - "\u2005": "", - "\u2006": "", - "\u2007": "", - "\u2008": "", - "\u2009": "", - "\u200A": "", - "\u200B": "", - "\u202F": "", - "\u205F": "", - "\u3000": "", - } - - def __init__(self, ws: str = " \t\r\n", min: int = 1, max: int = 0, exact: int = 0): - super().__init__() - self.matchWhite = ws - self.set_whitespace_chars( - "".join(c for c in self.whiteStrs if c not in self.matchWhite), - copy_defaults=True, - ) - # self.leave_whitespace() - self.mayReturnEmpty = True - self.errmsg = "Expected " + self.name - - self.minLen = min - - if max > 0: - self.maxLen = max - else: - self.maxLen = _MAX_INT - - if exact > 0: - self.maxLen = exact - self.minLen = exact - - def _generateDefaultName(self): - return "".join(White.whiteStrs[c] for c in self.matchWhite) - - def parseImpl(self, instring, loc, doActions=True): - if instring[loc] not in self.matchWhite: - raise ParseException(instring, loc, self.errmsg, self) - start = loc - loc += 1 - maxloc = start + self.maxLen - maxloc = min(maxloc, len(instring)) - while loc < maxloc and instring[loc] in self.matchWhite: - loc += 1 - - if loc - start < self.minLen: - raise ParseException(instring, loc, self.errmsg, self) - - return loc, instring[start:loc] - - -class PositionToken(Token): - def __init__(self): - super().__init__() - self.mayReturnEmpty = True - self.mayIndexError = False - - -class GoToColumn(PositionToken): - """Token to advance to a specific column of input text; useful for - tabular report scraping. - """ - - def __init__(self, colno: int): - super().__init__() - self.col = colno - - def preParse(self, instring, loc): - if col(loc, instring) != self.col: - instrlen = len(instring) - if self.ignoreExprs: - loc = self._skipIgnorables(instring, loc) - while ( - loc < instrlen - and instring[loc].isspace() - and col(loc, instring) != self.col - ): - loc += 1 - return loc - - def parseImpl(self, instring, loc, doActions=True): - thiscol = col(loc, instring) - if thiscol > self.col: - raise ParseException(instring, loc, "Text not in expected column", self) - newloc = loc + self.col - thiscol - ret = instring[loc:newloc] - return newloc, ret - - -class LineStart(PositionToken): - r"""Matches if current position is at the beginning of a line within - the parse string - - Example:: - - test = '''\ - AAA this line - AAA and this line - AAA but not this one - B AAA and definitely not this one - ''' - - for t in (LineStart() + 'AAA' + restOfLine).search_string(test): - print(t) - - prints:: - - ['AAA', ' this line'] - ['AAA', ' and this line'] - - """ - - def __init__(self): - super().__init__() - self.leave_whitespace() - self.orig_whiteChars = set() | self.whiteChars - self.whiteChars.discard("\n") - self.skipper = Empty().set_whitespace_chars(self.whiteChars) - self.errmsg = "Expected start of line" - - def preParse(self, instring, loc): - if loc == 0: - return loc - else: - ret = self.skipper.preParse(instring, loc) - if "\n" in self.orig_whiteChars: - while instring[ret : ret + 1] == "\n": - ret = self.skipper.preParse(instring, ret + 1) - return ret - - def parseImpl(self, instring, loc, doActions=True): - if col(loc, instring) == 1: - return loc, [] - raise ParseException(instring, loc, self.errmsg, self) - - -class LineEnd(PositionToken): - """Matches if current position is at the end of a line within the - parse string - """ - - def __init__(self): - super().__init__() - self.whiteChars.discard("\n") - self.set_whitespace_chars(self.whiteChars, copy_defaults=False) - self.errmsg = "Expected end of line" - - def parseImpl(self, instring, loc, doActions=True): - if loc < len(instring): - if instring[loc] == "\n": - return loc + 1, "\n" - else: - raise ParseException(instring, loc, self.errmsg, self) - elif loc == len(instring): - return loc + 1, [] - else: - raise ParseException(instring, loc, self.errmsg, self) - - -class StringStart(PositionToken): - """Matches if current position is at the beginning of the parse - string - """ - - def __init__(self): - super().__init__() - self.errmsg = "Expected start of text" - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - # see if entire string up to here is just whitespace and ignoreables - if loc != self.preParse(instring, 0): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class StringEnd(PositionToken): - """ - Matches if current position is at the end of the parse string - """ - - def __init__(self): - super().__init__() - self.errmsg = "Expected end of text" - - def parseImpl(self, instring, loc, doActions=True): - if loc < len(instring): - raise ParseException(instring, loc, self.errmsg, self) - elif loc == len(instring): - return loc + 1, [] - elif loc > len(instring): - return loc, [] - else: - raise ParseException(instring, loc, self.errmsg, self) - - -class WordStart(PositionToken): - """Matches if the current position is at the beginning of a - :class:`Word`, and is not preceded by any character in a given - set of ``word_chars`` (default= ``printables``). To emulate the - ``\b`` behavior of regular expressions, use - ``WordStart(alphanums)``. ``WordStart`` will also match at - the beginning of the string being parsed, or at the beginning of - a line. - """ - - def __init__(self, word_chars: str = printables, *, wordChars: str = printables): - wordChars = word_chars if wordChars == printables else wordChars - super().__init__() - self.wordChars = set(wordChars) - self.errmsg = "Not at the start of a word" - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - if ( - instring[loc - 1] in self.wordChars - or instring[loc] not in self.wordChars - ): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class WordEnd(PositionToken): - """Matches if the current position is at the end of a :class:`Word`, - and is not followed by any character in a given set of ``word_chars`` - (default= ``printables``). To emulate the ``\b`` behavior of - regular expressions, use ``WordEnd(alphanums)``. ``WordEnd`` - will also match at the end of the string being parsed, or at the end - of a line. - """ - - def __init__(self, word_chars: str = printables, *, wordChars: str = printables): - wordChars = word_chars if wordChars == printables else wordChars - super().__init__() - self.wordChars = set(wordChars) - self.skipWhitespace = False - self.errmsg = "Not at the end of a word" - - def parseImpl(self, instring, loc, doActions=True): - instrlen = len(instring) - if instrlen > 0 and loc < instrlen: - if ( - instring[loc] in self.wordChars - or instring[loc - 1] not in self.wordChars - ): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - -class ParseExpression(ParserElement): - """Abstract subclass of ParserElement, for combining and - post-processing parsed tokens. - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = False): - super().__init__(savelist) - self.exprs: List[ParserElement] - if isinstance(exprs, _generatorType): - exprs = list(exprs) - - if isinstance(exprs, str_type): - self.exprs = [self._literalStringClass(exprs)] - elif isinstance(exprs, ParserElement): - self.exprs = [exprs] - elif isinstance(exprs, Iterable): - exprs = list(exprs) - # if sequence of strings provided, wrap with Literal - if any(isinstance(expr, str_type) for expr in exprs): - exprs = ( - self._literalStringClass(e) if isinstance(e, str_type) else e - for e in exprs - ) - self.exprs = list(exprs) - else: - try: - self.exprs = list(exprs) - except TypeError: - self.exprs = [exprs] - self.callPreparse = False - - def recurse(self) -> Sequence[ParserElement]: - return self.exprs[:] - - def append(self, other) -> ParserElement: - self.exprs.append(other) - self._defaultName = None - return self - - def leave_whitespace(self, recursive: bool = True) -> ParserElement: - """ - Extends ``leave_whitespace`` defined in base class, and also invokes ``leave_whitespace`` on - all contained expressions. - """ - super().leave_whitespace(recursive) - - if recursive: - self.exprs = [e.copy() for e in self.exprs] - for e in self.exprs: - e.leave_whitespace(recursive) - return self - - def ignore_whitespace(self, recursive: bool = True) -> ParserElement: - """ - Extends ``ignore_whitespace`` defined in base class, and also invokes ``leave_whitespace`` on - all contained expressions. - """ - super().ignore_whitespace(recursive) - if recursive: - self.exprs = [e.copy() for e in self.exprs] - for e in self.exprs: - e.ignore_whitespace(recursive) - return self - - def ignore(self, other) -> ParserElement: - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - super().ignore(other) - for e in self.exprs: - e.ignore(self.ignoreExprs[-1]) - else: - super().ignore(other) - for e in self.exprs: - e.ignore(self.ignoreExprs[-1]) - return self - - def _generateDefaultName(self): - return "{}:({})".format(self.__class__.__name__, str(self.exprs)) - - def streamline(self) -> ParserElement: - if self.streamlined: - return self - - super().streamline() - - for e in self.exprs: - e.streamline() - - # collapse nested :class:`And`'s of the form ``And(And(And(a, b), c), d)`` to ``And(a, b, c, d)`` - # but only if there are no parse actions or resultsNames on the nested And's - # (likewise for :class:`Or`'s and :class:`MatchFirst`'s) - if len(self.exprs) == 2: - other = self.exprs[0] - if ( - isinstance(other, self.__class__) - and not other.parseAction - and other.resultsName is None - and not other.debug - ): - self.exprs = other.exprs[:] + [self.exprs[1]] - self._defaultName = None - self.mayReturnEmpty |= other.mayReturnEmpty - self.mayIndexError |= other.mayIndexError - - other = self.exprs[-1] - if ( - isinstance(other, self.__class__) - and not other.parseAction - and other.resultsName is None - and not other.debug - ): - self.exprs = self.exprs[:-1] + other.exprs[:] - self._defaultName = None - self.mayReturnEmpty |= other.mayReturnEmpty - self.mayIndexError |= other.mayIndexError - - self.errmsg = "Expected " + str(self) - - return self - - def validate(self, validateTrace=None) -> None: - tmp = (validateTrace if validateTrace is not None else [])[:] + [self] - for e in self.exprs: - e.validate(tmp) - self._checkRecursion([]) - - def copy(self) -> ParserElement: - ret = super().copy() - ret.exprs = [e.copy() for e in self.exprs] - return ret - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_ungrouped_named_tokens_in_collection - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in self.suppress_warnings_ - ): - for e in self.exprs: - if ( - isinstance(e, ParserElement) - and e.resultsName - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in e.suppress_warnings_ - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "collides with {!r} on contained expression".format( - "warn_ungrouped_named_tokens_in_collection", - name, - type(self).__name__, - e.resultsName, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class And(ParseExpression): - """ - Requires all given :class:`ParseExpression` s to be found in the given order. - Expressions may be separated by whitespace. - May be constructed using the ``'+'`` operator. - May also be constructed using the ``'-'`` operator, which will - suppress backtracking. - - Example:: - - integer = Word(nums) - name_expr = Word(alphas)[1, ...] - - expr = And([integer("id"), name_expr("name"), integer("age")]) - # more easily written as: - expr = integer("id") + name_expr("name") + integer("age") - """ - - class _ErrorStop(Empty): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.leave_whitespace() - - def _generateDefaultName(self): - return "-" - - def __init__( - self, exprs_arg: typing.Iterable[ParserElement], savelist: bool = True - ): - exprs: List[ParserElement] = list(exprs_arg) - if exprs and Ellipsis in exprs: - tmp = [] - for i, expr in enumerate(exprs): - if expr is Ellipsis: - if i < len(exprs) - 1: - skipto_arg: ParserElement = (Empty() + exprs[i + 1]).exprs[-1] - tmp.append(SkipTo(skipto_arg)("_skipped*")) - else: - raise Exception( - "cannot construct And with sequence ending in ..." - ) - else: - tmp.append(expr) - exprs[:] = tmp - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - if not isinstance(self.exprs[0], White): - self.set_whitespace_chars( - self.exprs[0].whiteChars, - copy_defaults=self.exprs[0].copyDefaultWhiteChars, - ) - self.skipWhitespace = self.exprs[0].skipWhitespace - else: - self.skipWhitespace = False - else: - self.mayReturnEmpty = True - self.callPreparse = True - - def streamline(self) -> ParserElement: - # collapse any _PendingSkip's - if self.exprs: - if any( - isinstance(e, ParseExpression) - and e.exprs - and isinstance(e.exprs[-1], _PendingSkip) - for e in self.exprs[:-1] - ): - for i, e in enumerate(self.exprs[:-1]): - if e is None: - continue - if ( - isinstance(e, ParseExpression) - and e.exprs - and isinstance(e.exprs[-1], _PendingSkip) - ): - e.exprs[-1] = e.exprs[-1] + self.exprs[i + 1] - self.exprs[i + 1] = None - self.exprs = [e for e in self.exprs if e is not None] - - super().streamline() - - # link any IndentedBlocks to the prior expression - for prev, cur in zip(self.exprs, self.exprs[1:]): - # traverse cur or any first embedded expr of cur looking for an IndentedBlock - # (but watch out for recursive grammar) - seen = set() - while cur: - if id(cur) in seen: - break - seen.add(id(cur)) - if isinstance(cur, IndentedBlock): - prev.add_parse_action( - lambda s, l, t, cur_=cur: setattr( - cur_, "parent_anchor", col(l, s) - ) - ) - break - subs = cur.recurse() - cur = next(iter(subs), None) - - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - return self - - def parseImpl(self, instring, loc, doActions=True): - # pass False as callPreParse arg to _parse for first element, since we already - # pre-parsed the string as part of our And pre-parsing - loc, resultlist = self.exprs[0]._parse( - instring, loc, doActions, callPreParse=False - ) - errorStop = False - for e in self.exprs[1:]: - # if isinstance(e, And._ErrorStop): - if type(e) is And._ErrorStop: - errorStop = True - continue - if errorStop: - try: - loc, exprtokens = e._parse(instring, loc, doActions) - except ParseSyntaxException: - raise - except ParseBaseException as pe: - pe.__traceback__ = None - raise ParseSyntaxException._from_exception(pe) - except IndexError: - raise ParseSyntaxException( - instring, len(instring), self.errmsg, self - ) - else: - loc, exprtokens = e._parse(instring, loc, doActions) - if exprtokens or exprtokens.haskeys(): - resultlist += exprtokens - return loc, resultlist - - def __iadd__(self, other): - if isinstance(other, str_type): - other = self._literalStringClass(other) - return self.append(other) # And([self, other]) - - def _checkRecursion(self, parseElementList): - subRecCheckList = parseElementList[:] + [self] - for e in self.exprs: - e._checkRecursion(subRecCheckList) - if not e.mayReturnEmpty: - break - - def _generateDefaultName(self): - inner = " ".join(str(e) for e in self.exprs) - # strip off redundant inner {}'s - while len(inner) > 1 and inner[0 :: len(inner) - 1] == "{}": - inner = inner[1:-1] - return "{" + inner + "}" - - -class Or(ParseExpression): - """Requires that at least one :class:`ParseExpression` is found. If - two expressions match, the expression that matches the longest - string will be used. May be constructed using the ``'^'`` - operator. - - Example:: - - # construct Or using '^' operator - - number = Word(nums) ^ Combine(Word(nums) + '.' + Word(nums)) - print(number.search_string("123 3.1416 789")) - - prints:: - - [['123'], ['3.1416'], ['789']] - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = False): - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all(e.skipWhitespace for e in self.exprs) - else: - self.mayReturnEmpty = True - - def streamline(self) -> ParserElement: - super().streamline() - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.saveAsList = any(e.saveAsList for e in self.exprs) - self.skipWhitespace = all( - e.skipWhitespace and not isinstance(e, White) for e in self.exprs - ) - else: - self.saveAsList = False - return self - - def parseImpl(self, instring, loc, doActions=True): - maxExcLoc = -1 - maxException = None - matches = [] - fatals = [] - if all(e.callPreparse for e in self.exprs): - loc = self.preParse(instring, loc) - for e in self.exprs: - try: - loc2 = e.try_parse(instring, loc, raise_fatal=True) - except ParseFatalException as pfe: - pfe.__traceback__ = None - pfe.parserElement = e - fatals.append(pfe) - maxException = None - maxExcLoc = -1 - except ParseException as err: - if not fatals: - err.__traceback__ = None - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException( - instring, len(instring), e.errmsg, self - ) - maxExcLoc = len(instring) - else: - # save match among all matches, to retry longest to shortest - matches.append((loc2, e)) - - if matches: - # re-evaluate all matches in descending order of length of match, in case attached actions - # might change whether or how much they match of the input. - matches.sort(key=itemgetter(0), reverse=True) - - if not doActions: - # no further conditions or parse actions to change the selection of - # alternative, so the first match will be the best match - best_expr = matches[0][1] - return best_expr._parse(instring, loc, doActions) - - longest = -1, None - for loc1, expr1 in matches: - if loc1 <= longest[0]: - # already have a longer match than this one will deliver, we are done - return longest - - try: - loc2, toks = expr1._parse(instring, loc, doActions) - except ParseException as err: - err.__traceback__ = None - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - else: - if loc2 >= loc1: - return loc2, toks - # didn't match as much as before - elif loc2 > longest[0]: - longest = loc2, toks - - if longest != (-1, None): - return longest - - if fatals: - if len(fatals) > 1: - fatals.sort(key=lambda e: -e.loc) - if fatals[0].loc == fatals[1].loc: - fatals.sort(key=lambda e: (-e.loc, -len(str(e.parserElement)))) - max_fatal = fatals[0] - raise max_fatal - - if maxException is not None: - maxException.msg = self.errmsg - raise maxException - else: - raise ParseException( - instring, loc, "no defined alternatives to match", self - ) - - def __ixor__(self, other): - if isinstance(other, str_type): - other = self._literalStringClass(other) - return self.append(other) # Or([self, other]) - - def _generateDefaultName(self): - return "{" + " ^ ".join(str(e) for e in self.exprs) + "}" - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_multiple_tokens_in_named_alternation - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in self.suppress_warnings_ - ): - if any( - isinstance(e, And) - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in e.suppress_warnings_ - for e in self.exprs - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "will return a list of all parsed tokens in an And alternative, " - "in prior versions only the first token was returned; enclose " - "contained argument in Group".format( - "warn_multiple_tokens_in_named_alternation", - name, - type(self).__name__, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - -class MatchFirst(ParseExpression): - """Requires that at least one :class:`ParseExpression` is found. If - more than one expression matches, the first one listed is the one that will - match. May be constructed using the ``'|'`` operator. - - Example:: - - # construct MatchFirst using '|' operator - - # watch the order of expressions to match - number = Word(nums) | Combine(Word(nums) + '.' + Word(nums)) - print(number.search_string("123 3.1416 789")) # Fail! -> [['123'], ['3'], ['1416'], ['789']] - - # put more selective expression first - number = Combine(Word(nums) + '.' + Word(nums)) | Word(nums) - print(number.search_string("123 3.1416 789")) # Better -> [['123'], ['3.1416'], ['789']] - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = False): - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all(e.skipWhitespace for e in self.exprs) - else: - self.mayReturnEmpty = True - - def streamline(self) -> ParserElement: - if self.streamlined: - return self - - super().streamline() - if self.exprs: - self.saveAsList = any(e.saveAsList for e in self.exprs) - self.mayReturnEmpty = any(e.mayReturnEmpty for e in self.exprs) - self.skipWhitespace = all( - e.skipWhitespace and not isinstance(e, White) for e in self.exprs - ) - else: - self.saveAsList = False - self.mayReturnEmpty = True - return self - - def parseImpl(self, instring, loc, doActions=True): - maxExcLoc = -1 - maxException = None - - for e in self.exprs: - try: - return e._parse( - instring, - loc, - doActions, - ) - except ParseFatalException as pfe: - pfe.__traceback__ = None - pfe.parserElement = e - raise - except ParseException as err: - if err.loc > maxExcLoc: - maxException = err - maxExcLoc = err.loc - except IndexError: - if len(instring) > maxExcLoc: - maxException = ParseException( - instring, len(instring), e.errmsg, self - ) - maxExcLoc = len(instring) - - if maxException is not None: - maxException.msg = self.errmsg - raise maxException - else: - raise ParseException( - instring, loc, "no defined alternatives to match", self - ) - - def __ior__(self, other): - if isinstance(other, str_type): - other = self._literalStringClass(other) - return self.append(other) # MatchFirst([self, other]) - - def _generateDefaultName(self): - return "{" + " | ".join(str(e) for e in self.exprs) + "}" - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_multiple_tokens_in_named_alternation - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in self.suppress_warnings_ - ): - if any( - isinstance(e, And) - and Diagnostics.warn_multiple_tokens_in_named_alternation - not in e.suppress_warnings_ - for e in self.exprs - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "will return a list of all parsed tokens in an And alternative, " - "in prior versions only the first token was returned; enclose " - "contained argument in Group".format( - "warn_multiple_tokens_in_named_alternation", - name, - type(self).__name__, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - -class Each(ParseExpression): - """Requires all given :class:`ParseExpression` s to be found, but in - any order. Expressions may be separated by whitespace. - - May be constructed using the ``'&'`` operator. - - Example:: - - color = one_of("RED ORANGE YELLOW GREEN BLUE PURPLE BLACK WHITE BROWN") - shape_type = one_of("SQUARE CIRCLE TRIANGLE STAR HEXAGON OCTAGON") - integer = Word(nums) - shape_attr = "shape:" + shape_type("shape") - posn_attr = "posn:" + Group(integer("x") + ',' + integer("y"))("posn") - color_attr = "color:" + color("color") - size_attr = "size:" + integer("size") - - # use Each (using operator '&') to accept attributes in any order - # (shape and posn are required, color and size are optional) - shape_spec = shape_attr & posn_attr & Opt(color_attr) & Opt(size_attr) - - shape_spec.run_tests(''' - shape: SQUARE color: BLACK posn: 100, 120 - shape: CIRCLE size: 50 color: BLUE posn: 50,80 - color:GREEN size:20 shape:TRIANGLE posn:20,40 - ''' - ) - - prints:: - - shape: SQUARE color: BLACK posn: 100, 120 - ['shape:', 'SQUARE', 'color:', 'BLACK', 'posn:', ['100', ',', '120']] - - color: BLACK - - posn: ['100', ',', '120'] - - x: 100 - - y: 120 - - shape: SQUARE - - - shape: CIRCLE size: 50 color: BLUE posn: 50,80 - ['shape:', 'CIRCLE', 'size:', '50', 'color:', 'BLUE', 'posn:', ['50', ',', '80']] - - color: BLUE - - posn: ['50', ',', '80'] - - x: 50 - - y: 80 - - shape: CIRCLE - - size: 50 - - - color: GREEN size: 20 shape: TRIANGLE posn: 20,40 - ['color:', 'GREEN', 'size:', '20', 'shape:', 'TRIANGLE', 'posn:', ['20', ',', '40']] - - color: GREEN - - posn: ['20', ',', '40'] - - x: 20 - - y: 40 - - shape: TRIANGLE - - size: 20 - """ - - def __init__(self, exprs: typing.Iterable[ParserElement], savelist: bool = True): - super().__init__(exprs, savelist) - if self.exprs: - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - else: - self.mayReturnEmpty = True - self.skipWhitespace = True - self.initExprGroups = True - self.saveAsList = True - - def streamline(self) -> ParserElement: - super().streamline() - if self.exprs: - self.mayReturnEmpty = all(e.mayReturnEmpty for e in self.exprs) - else: - self.mayReturnEmpty = True - return self - - def parseImpl(self, instring, loc, doActions=True): - if self.initExprGroups: - self.opt1map = dict( - (id(e.expr), e) for e in self.exprs if isinstance(e, Opt) - ) - opt1 = [e.expr for e in self.exprs if isinstance(e, Opt)] - opt2 = [ - e - for e in self.exprs - if e.mayReturnEmpty and not isinstance(e, (Opt, Regex, ZeroOrMore)) - ] - self.optionals = opt1 + opt2 - self.multioptionals = [ - e.expr.set_results_name(e.resultsName, list_all_matches=True) - for e in self.exprs - if isinstance(e, _MultipleMatch) - ] - self.multirequired = [ - e.expr.set_results_name(e.resultsName, list_all_matches=True) - for e in self.exprs - if isinstance(e, OneOrMore) - ] - self.required = [ - e for e in self.exprs if not isinstance(e, (Opt, ZeroOrMore, OneOrMore)) - ] - self.required += self.multirequired - self.initExprGroups = False - - tmpLoc = loc - tmpReqd = self.required[:] - tmpOpt = self.optionals[:] - multis = self.multioptionals[:] - matchOrder = [] - - keepMatching = True - failed = [] - fatals = [] - while keepMatching: - tmpExprs = tmpReqd + tmpOpt + multis - failed.clear() - fatals.clear() - for e in tmpExprs: - try: - tmpLoc = e.try_parse(instring, tmpLoc, raise_fatal=True) - except ParseFatalException as pfe: - pfe.__traceback__ = None - pfe.parserElement = e - fatals.append(pfe) - failed.append(e) - except ParseException: - failed.append(e) - else: - matchOrder.append(self.opt1map.get(id(e), e)) - if e in tmpReqd: - tmpReqd.remove(e) - elif e in tmpOpt: - tmpOpt.remove(e) - if len(failed) == len(tmpExprs): - keepMatching = False - - # look for any ParseFatalExceptions - if fatals: - if len(fatals) > 1: - fatals.sort(key=lambda e: -e.loc) - if fatals[0].loc == fatals[1].loc: - fatals.sort(key=lambda e: (-e.loc, -len(str(e.parserElement)))) - max_fatal = fatals[0] - raise max_fatal - - if tmpReqd: - missing = ", ".join([str(e) for e in tmpReqd]) - raise ParseException( - instring, - loc, - "Missing one or more required elements ({})".format(missing), - ) - - # add any unmatched Opts, in case they have default values defined - matchOrder += [e for e in self.exprs if isinstance(e, Opt) and e.expr in tmpOpt] - - total_results = ParseResults([]) - for e in matchOrder: - loc, results = e._parse(instring, loc, doActions) - total_results += results - - return loc, total_results - - def _generateDefaultName(self): - return "{" + " & ".join(str(e) for e in self.exprs) + "}" - - -class ParseElementEnhance(ParserElement): - """Abstract subclass of :class:`ParserElement`, for combining and - post-processing parsed tokens. - """ - - def __init__(self, expr: Union[ParserElement, str], savelist: bool = False): - super().__init__(savelist) - if isinstance(expr, str_type): - if issubclass(self._literalStringClass, Token): - expr = self._literalStringClass(expr) - elif issubclass(type(self), self._literalStringClass): - expr = Literal(expr) - else: - expr = self._literalStringClass(Literal(expr)) - self.expr = expr - if expr is not None: - self.mayIndexError = expr.mayIndexError - self.mayReturnEmpty = expr.mayReturnEmpty - self.set_whitespace_chars( - expr.whiteChars, copy_defaults=expr.copyDefaultWhiteChars - ) - self.skipWhitespace = expr.skipWhitespace - self.saveAsList = expr.saveAsList - self.callPreparse = expr.callPreparse - self.ignoreExprs.extend(expr.ignoreExprs) - - def recurse(self) -> Sequence[ParserElement]: - return [self.expr] if self.expr is not None else [] - - def parseImpl(self, instring, loc, doActions=True): - if self.expr is not None: - return self.expr._parse(instring, loc, doActions, callPreParse=False) - else: - raise ParseException(instring, loc, "No expression defined", self) - - def leave_whitespace(self, recursive: bool = True) -> ParserElement: - super().leave_whitespace(recursive) - - if recursive: - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.leave_whitespace(recursive) - return self - - def ignore_whitespace(self, recursive: bool = True) -> ParserElement: - super().ignore_whitespace(recursive) - - if recursive: - self.expr = self.expr.copy() - if self.expr is not None: - self.expr.ignore_whitespace(recursive) - return self - - def ignore(self, other) -> ParserElement: - if isinstance(other, Suppress): - if other not in self.ignoreExprs: - super().ignore(other) - if self.expr is not None: - self.expr.ignore(self.ignoreExprs[-1]) - else: - super().ignore(other) - if self.expr is not None: - self.expr.ignore(self.ignoreExprs[-1]) - return self - - def streamline(self) -> ParserElement: - super().streamline() - if self.expr is not None: - self.expr.streamline() - return self - - def _checkRecursion(self, parseElementList): - if self in parseElementList: - raise RecursiveGrammarException(parseElementList + [self]) - subRecCheckList = parseElementList[:] + [self] - if self.expr is not None: - self.expr._checkRecursion(subRecCheckList) - - def validate(self, validateTrace=None) -> None: - if validateTrace is None: - validateTrace = [] - tmp = validateTrace[:] + [self] - if self.expr is not None: - self.expr.validate(tmp) - self._checkRecursion([]) - - def _generateDefaultName(self): - return "{}:({})".format(self.__class__.__name__, str(self.expr)) - - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class IndentedBlock(ParseElementEnhance): - """ - Expression to match one or more expressions at a given indentation level. - Useful for parsing text where structure is implied by indentation (like Python source code). - """ - - class _Indent(Empty): - def __init__(self, ref_col: int): - super().__init__() - self.errmsg = "expected indent at column {}".format(ref_col) - self.add_condition(lambda s, l, t: col(l, s) == ref_col) - - class _IndentGreater(Empty): - def __init__(self, ref_col: int): - super().__init__() - self.errmsg = "expected indent at column greater than {}".format(ref_col) - self.add_condition(lambda s, l, t: col(l, s) > ref_col) - - def __init__( - self, expr: ParserElement, *, recursive: bool = False, grouped: bool = True - ): - super().__init__(expr, savelist=True) - # if recursive: - # raise NotImplementedError("IndentedBlock with recursive is not implemented") - self._recursive = recursive - self._grouped = grouped - self.parent_anchor = 1 - - def parseImpl(self, instring, loc, doActions=True): - # advance parse position to non-whitespace by using an Empty() - # this should be the column to be used for all subsequent indented lines - anchor_loc = Empty().preParse(instring, loc) - - # see if self.expr matches at the current location - if not it will raise an exception - # and no further work is necessary - self.expr.try_parse(instring, anchor_loc, doActions) - - indent_col = col(anchor_loc, instring) - peer_detect_expr = self._Indent(indent_col) - - inner_expr = Empty() + peer_detect_expr + self.expr - if self._recursive: - sub_indent = self._IndentGreater(indent_col) - nested_block = IndentedBlock( - self.expr, recursive=self._recursive, grouped=self._grouped - ) - nested_block.set_debug(self.debug) - nested_block.parent_anchor = indent_col - inner_expr += Opt(sub_indent + nested_block) - - inner_expr.set_name(f"inner {hex(id(inner_expr))[-4:].upper()}@{indent_col}") - block = OneOrMore(inner_expr) - - trailing_undent = self._Indent(self.parent_anchor) | StringEnd() - - if self._grouped: - wrapper = Group - else: - wrapper = lambda expr: expr - return (wrapper(block) + Optional(trailing_undent)).parseImpl( - instring, anchor_loc, doActions - ) - - -class AtStringStart(ParseElementEnhance): - """Matches if expression matches at the beginning of the parse - string:: - - AtStringStart(Word(nums)).parse_string("123") - # prints ["123"] - - AtStringStart(Word(nums)).parse_string(" 123") - # raises ParseException - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - self.callPreparse = False - - def parseImpl(self, instring, loc, doActions=True): - if loc != 0: - raise ParseException(instring, loc, "not found at string start") - return super().parseImpl(instring, loc, doActions) - - -class AtLineStart(ParseElementEnhance): - r"""Matches if an expression matches at the beginning of a line within - the parse string - - Example:: - - test = '''\ - AAA this line - AAA and this line - AAA but not this one - B AAA and definitely not this one - ''' - - for t in (AtLineStart('AAA') + restOfLine).search_string(test): - print(t) - - prints:: - - ['AAA', ' this line'] - ['AAA', ' and this line'] - - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - self.callPreparse = False - - def parseImpl(self, instring, loc, doActions=True): - if col(loc, instring) != 1: - raise ParseException(instring, loc, "not found at line start") - return super().parseImpl(instring, loc, doActions) - - -class FollowedBy(ParseElementEnhance): - """Lookahead matching of the given parse expression. - ``FollowedBy`` does *not* advance the parsing position within - the input string, it only verifies that the specified parse - expression matches at the current position. ``FollowedBy`` - always returns a null token list. If any results names are defined - in the lookahead expression, those *will* be returned for access by - name. - - Example:: - - # use FollowedBy to match a label only if it is followed by a ':' - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - - attr_expr[1, ...].parse_string("shape: SQUARE color: BLACK posn: upper left").pprint() - - prints:: - - [['shape', 'SQUARE'], ['color', 'BLACK'], ['posn', 'upper left']] - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - # by using self._expr.parse and deleting the contents of the returned ParseResults list - # we keep any named results that were defined in the FollowedBy expression - _, ret = self.expr._parse(instring, loc, doActions=doActions) - del ret[:] - - return loc, ret - - -class PrecededBy(ParseElementEnhance): - """Lookbehind matching of the given parse expression. - ``PrecededBy`` does not advance the parsing position within the - input string, it only verifies that the specified parse expression - matches prior to the current position. ``PrecededBy`` always - returns a null token list, but if a results name is defined on the - given expression, it is returned. - - Parameters: - - - expr - expression that must match prior to the current parse - location - - retreat - (default= ``None``) - (int) maximum number of characters - to lookbehind prior to the current parse location - - If the lookbehind expression is a string, :class:`Literal`, - :class:`Keyword`, or a :class:`Word` or :class:`CharsNotIn` - with a specified exact or maximum length, then the retreat - parameter is not required. Otherwise, retreat must be specified to - give a maximum number of characters to look back from - the current parse position for a lookbehind match. - - Example:: - - # VB-style variable names with type prefixes - int_var = PrecededBy("#") + pyparsing_common.identifier - str_var = PrecededBy("$") + pyparsing_common.identifier - - """ - - def __init__( - self, expr: Union[ParserElement, str], retreat: typing.Optional[int] = None - ): - super().__init__(expr) - self.expr = self.expr().leave_whitespace() - self.mayReturnEmpty = True - self.mayIndexError = False - self.exact = False - if isinstance(expr, str_type): - retreat = len(expr) - self.exact = True - elif isinstance(expr, (Literal, Keyword)): - retreat = expr.matchLen - self.exact = True - elif isinstance(expr, (Word, CharsNotIn)) and expr.maxLen != _MAX_INT: - retreat = expr.maxLen - self.exact = True - elif isinstance(expr, PositionToken): - retreat = 0 - self.exact = True - self.retreat = retreat - self.errmsg = "not preceded by " + str(expr) - self.skipWhitespace = False - self.parseAction.append(lambda s, l, t: t.__delitem__(slice(None, None))) - - def parseImpl(self, instring, loc=0, doActions=True): - if self.exact: - if loc < self.retreat: - raise ParseException(instring, loc, self.errmsg) - start = loc - self.retreat - _, ret = self.expr._parse(instring, start) - else: - # retreat specified a maximum lookbehind window, iterate - test_expr = self.expr + StringEnd() - instring_slice = instring[max(0, loc - self.retreat) : loc] - last_expr = ParseException(instring, loc, self.errmsg) - for offset in range(1, min(loc, self.retreat + 1) + 1): - try: - # print('trying', offset, instring_slice, repr(instring_slice[loc - offset:])) - _, ret = test_expr._parse( - instring_slice, len(instring_slice) - offset - ) - except ParseBaseException as pbe: - last_expr = pbe - else: - break - else: - raise last_expr - return loc, ret - - -class Located(ParseElementEnhance): - """ - Decorates a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parse_with_tabs` - - Example:: - - wd = Word(alphas) - for match in Located(wd).search_string("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [0, ['ljsdf'], 5] - [8, ['lksdjjf'], 15] - [18, ['lkkjj'], 23] - - """ - - def parseImpl(self, instring, loc, doActions=True): - start = loc - loc, tokens = self.expr._parse(instring, start, doActions, callPreParse=False) - ret_tokens = ParseResults([start, tokens, loc]) - ret_tokens["locn_start"] = start - ret_tokens["value"] = tokens - ret_tokens["locn_end"] = loc - if self.resultsName: - # must return as a list, so that the name will be attached to the complete group - return loc, [ret_tokens] - else: - return loc, ret_tokens - - -class NotAny(ParseElementEnhance): - """ - Lookahead to disallow matching with the given parse expression. - ``NotAny`` does *not* advance the parsing position within the - input string, it only verifies that the specified parse expression - does *not* match at the current position. Also, ``NotAny`` does - *not* skip over leading whitespace. ``NotAny`` always returns - a null token list. May be constructed using the ``'~'`` operator. - - Example:: - - AND, OR, NOT = map(CaselessKeyword, "AND OR NOT".split()) - - # take care not to mistake keywords for identifiers - ident = ~(AND | OR | NOT) + Word(alphas) - boolean_term = Opt(NOT) + ident - - # very crude boolean expression - to support parenthesis groups and - # operation hierarchy, use infix_notation - boolean_expr = boolean_term + ((AND | OR) + boolean_term)[...] - - # integers that are followed by "." are actually floats - integer = Word(nums) + ~Char(".") - """ - - def __init__(self, expr: Union[ParserElement, str]): - super().__init__(expr) - # do NOT use self.leave_whitespace(), don't want to propagate to exprs - # self.leave_whitespace() - self.skipWhitespace = False - - self.mayReturnEmpty = True - self.errmsg = "Found unwanted token, " + str(self.expr) - - def parseImpl(self, instring, loc, doActions=True): - if self.expr.can_parse_next(instring, loc): - raise ParseException(instring, loc, self.errmsg, self) - return loc, [] - - def _generateDefaultName(self): - return "~{" + str(self.expr) + "}" - - -class _MultipleMatch(ParseElementEnhance): - def __init__( - self, - expr: ParserElement, - stop_on: typing.Optional[Union[ParserElement, str]] = None, - *, - stopOn: typing.Optional[Union[ParserElement, str]] = None, - ): - super().__init__(expr) - stopOn = stopOn or stop_on - self.saveAsList = True - ender = stopOn - if isinstance(ender, str_type): - ender = self._literalStringClass(ender) - self.stopOn(ender) - - def stopOn(self, ender) -> ParserElement: - if isinstance(ender, str_type): - ender = self._literalStringClass(ender) - self.not_ender = ~ender if ender is not None else None - return self - - def parseImpl(self, instring, loc, doActions=True): - self_expr_parse = self.expr._parse - self_skip_ignorables = self._skipIgnorables - check_ender = self.not_ender is not None - if check_ender: - try_not_ender = self.not_ender.tryParse - - # must be at least one (but first see if we are the stopOn sentinel; - # if so, fail) - if check_ender: - try_not_ender(instring, loc) - loc, tokens = self_expr_parse(instring, loc, doActions) - try: - hasIgnoreExprs = not not self.ignoreExprs - while 1: - if check_ender: - try_not_ender(instring, loc) - if hasIgnoreExprs: - preloc = self_skip_ignorables(instring, loc) - else: - preloc = loc - loc, tmptokens = self_expr_parse(instring, preloc, doActions) - if tmptokens or tmptokens.haskeys(): - tokens += tmptokens - except (ParseException, IndexError): - pass - - return loc, tokens - - def _setResultsName(self, name, listAllMatches=False): - if ( - __diag__.warn_ungrouped_named_tokens_in_collection - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in self.suppress_warnings_ - ): - for e in [self.expr] + self.expr.recurse(): - if ( - isinstance(e, ParserElement) - and e.resultsName - and Diagnostics.warn_ungrouped_named_tokens_in_collection - not in e.suppress_warnings_ - ): - warnings.warn( - "{}: setting results name {!r} on {} expression " - "collides with {!r} on contained expression".format( - "warn_ungrouped_named_tokens_in_collection", - name, - type(self).__name__, - e.resultsName, - ), - stacklevel=3, - ) - - return super()._setResultsName(name, listAllMatches) - - -class OneOrMore(_MultipleMatch): - """ - Repetition of one or more of the given expression. - - Parameters: - - expr - expression that must match one or more times - - stop_on - (default= ``None``) - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - - Example:: - - data_word = Word(alphas) - label = data_word + FollowedBy(':') - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word).set_parse_action(' '.join)) - - text = "shape: SQUARE posn: upper left color: BLACK" - attr_expr[1, ...].parse_string(text).pprint() # Fail! read 'color' as data instead of next label -> [['shape', 'SQUARE color']] - - # use stop_on attribute for OneOrMore to avoid reading label string as part of the data - attr_expr = Group(label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - OneOrMore(attr_expr).parse_string(text).pprint() # Better -> [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'BLACK']] - - # could also be written as - (attr_expr * (1,)).parse_string(text).pprint() - """ - - def _generateDefaultName(self): - return "{" + str(self.expr) + "}..." - - -class ZeroOrMore(_MultipleMatch): - """ - Optional repetition of zero or more of the given expression. - - Parameters: - - ``expr`` - expression that must match zero or more times - - ``stop_on`` - expression for a terminating sentinel - (only required if the sentinel would ordinarily match the repetition - expression) - (default= ``None``) - - Example: similar to :class:`OneOrMore` - """ - - def __init__( - self, - expr: ParserElement, - stop_on: typing.Optional[Union[ParserElement, str]] = None, - *, - stopOn: typing.Optional[Union[ParserElement, str]] = None, - ): - super().__init__(expr, stopOn=stopOn or stop_on) - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - try: - return super().parseImpl(instring, loc, doActions) - except (ParseException, IndexError): - return loc, ParseResults([], name=self.resultsName) - - def _generateDefaultName(self): - return "[" + str(self.expr) + "]..." - - -class _NullToken: - def __bool__(self): - return False - - def __str__(self): - return "" - - -class Opt(ParseElementEnhance): - """ - Optional matching of the given expression. - - Parameters: - - ``expr`` - expression that must match zero or more times - - ``default`` (optional) - value to be returned if the optional expression is not found. - - Example:: - - # US postal code can be a 5-digit zip, plus optional 4-digit qualifier - zip = Combine(Word(nums, exact=5) + Opt('-' + Word(nums, exact=4))) - zip.run_tests(''' - # traditional ZIP code - 12345 - - # ZIP+4 form - 12101-0001 - - # invalid ZIP - 98765- - ''') - - prints:: - - # traditional ZIP code - 12345 - ['12345'] - - # ZIP+4 form - 12101-0001 - ['12101-0001'] - - # invalid ZIP - 98765- - ^ - FAIL: Expected end of text (at char 5), (line:1, col:6) - """ - - __optionalNotMatched = _NullToken() - - def __init__( - self, expr: Union[ParserElement, str], default: Any = __optionalNotMatched - ): - super().__init__(expr, savelist=False) - self.saveAsList = self.expr.saveAsList - self.defaultValue = default - self.mayReturnEmpty = True - - def parseImpl(self, instring, loc, doActions=True): - self_expr = self.expr - try: - loc, tokens = self_expr._parse(instring, loc, doActions, callPreParse=False) - except (ParseException, IndexError): - default_value = self.defaultValue - if default_value is not self.__optionalNotMatched: - if self_expr.resultsName: - tokens = ParseResults([default_value]) - tokens[self_expr.resultsName] = default_value - else: - tokens = [default_value] - else: - tokens = [] - return loc, tokens - - def _generateDefaultName(self): - inner = str(self.expr) - # strip off redundant inner {}'s - while len(inner) > 1 and inner[0 :: len(inner) - 1] == "{}": - inner = inner[1:-1] - return "[" + inner + "]" - - -Optional = Opt - - -class SkipTo(ParseElementEnhance): - """ - Token for skipping over all undefined text until the matched - expression is found. - - Parameters: - - ``expr`` - target expression marking the end of the data to be skipped - - ``include`` - if ``True``, the target expression is also parsed - (the skipped text and target expression are returned as a 2-element - list) (default= ``False``). - - ``ignore`` - (default= ``None``) used to define grammars (typically quoted strings and - comments) that might contain false matches to the target expression - - ``fail_on`` - (default= ``None``) define expressions that are not allowed to be - included in the skipped test; if found before the target expression is found, - the :class:`SkipTo` is not a match - - Example:: - - report = ''' - Outstanding Issues Report - 1 Jan 2000 - - # | Severity | Description | Days Open - -----+----------+-------------------------------------------+----------- - 101 | Critical | Intermittent system crash | 6 - 94 | Cosmetic | Spelling error on Login ('log|n') | 14 - 79 | Minor | System slow when running too many reports | 47 - ''' - integer = Word(nums) - SEP = Suppress('|') - # use SkipTo to simply match everything up until the next SEP - # - ignore quoted strings, so that a '|' character inside a quoted string does not match - # - parse action will call token.strip() for each matched token, i.e., the description body - string_data = SkipTo(SEP, ignore=quoted_string) - string_data.set_parse_action(token_map(str.strip)) - ticket_expr = (integer("issue_num") + SEP - + string_data("sev") + SEP - + string_data("desc") + SEP - + integer("days_open")) - - for tkt in ticket_expr.search_string(report): - print tkt.dump() - - prints:: - - ['101', 'Critical', 'Intermittent system crash', '6'] - - days_open: '6' - - desc: 'Intermittent system crash' - - issue_num: '101' - - sev: 'Critical' - ['94', 'Cosmetic', "Spelling error on Login ('log|n')", '14'] - - days_open: '14' - - desc: "Spelling error on Login ('log|n')" - - issue_num: '94' - - sev: 'Cosmetic' - ['79', 'Minor', 'System slow when running too many reports', '47'] - - days_open: '47' - - desc: 'System slow when running too many reports' - - issue_num: '79' - - sev: 'Minor' - """ - - def __init__( - self, - other: Union[ParserElement, str], - include: bool = False, - ignore: bool = None, - fail_on: typing.Optional[Union[ParserElement, str]] = None, - *, - failOn: Union[ParserElement, str] = None, - ): - super().__init__(other) - failOn = failOn or fail_on - self.ignoreExpr = ignore - self.mayReturnEmpty = True - self.mayIndexError = False - self.includeMatch = include - self.saveAsList = False - if isinstance(failOn, str_type): - self.failOn = self._literalStringClass(failOn) - else: - self.failOn = failOn - self.errmsg = "No match found for " + str(self.expr) - - def parseImpl(self, instring, loc, doActions=True): - startloc = loc - instrlen = len(instring) - self_expr_parse = self.expr._parse - self_failOn_canParseNext = ( - self.failOn.canParseNext if self.failOn is not None else None - ) - self_ignoreExpr_tryParse = ( - self.ignoreExpr.tryParse if self.ignoreExpr is not None else None - ) - - tmploc = loc - while tmploc <= instrlen: - if self_failOn_canParseNext is not None: - # break if failOn expression matches - if self_failOn_canParseNext(instring, tmploc): - break - - if self_ignoreExpr_tryParse is not None: - # advance past ignore expressions - while 1: - try: - tmploc = self_ignoreExpr_tryParse(instring, tmploc) - except ParseBaseException: - break - - try: - self_expr_parse(instring, tmploc, doActions=False, callPreParse=False) - except (ParseException, IndexError): - # no match, advance loc in string - tmploc += 1 - else: - # matched skipto expr, done - break - - else: - # ran off the end of the input string without matching skipto expr, fail - raise ParseException(instring, loc, self.errmsg, self) - - # build up return values - loc = tmploc - skiptext = instring[startloc:loc] - skipresult = ParseResults(skiptext) - - if self.includeMatch: - loc, mat = self_expr_parse(instring, loc, doActions, callPreParse=False) - skipresult += mat - - return loc, skipresult - - -class Forward(ParseElementEnhance): - """ - Forward declaration of an expression to be defined later - - used for recursive grammars, such as algebraic infix notation. - When the expression is known, it is assigned to the ``Forward`` - variable using the ``'<<'`` operator. - - Note: take care when assigning to ``Forward`` not to overlook - precedence of operators. - - Specifically, ``'|'`` has a lower precedence than ``'<<'``, so that:: - - fwd_expr << a | b | c - - will actually be evaluated as:: - - (fwd_expr << a) | b | c - - thereby leaving b and c out as parseable alternatives. It is recommended that you - explicitly group the values inserted into the ``Forward``:: - - fwd_expr << (a | b | c) - - Converting to use the ``'<<='`` operator instead will avoid this problem. - - See :class:`ParseResults.pprint` for an example of a recursive - parser created using ``Forward``. - """ - - def __init__(self, other: typing.Optional[Union[ParserElement, str]] = None): - self.caller_frame = traceback.extract_stack(limit=2)[0] - super().__init__(other, savelist=False) - self.lshift_line = None - - def __lshift__(self, other): - if hasattr(self, "caller_frame"): - del self.caller_frame - if isinstance(other, str_type): - other = self._literalStringClass(other) - self.expr = other - self.mayIndexError = self.expr.mayIndexError - self.mayReturnEmpty = self.expr.mayReturnEmpty - self.set_whitespace_chars( - self.expr.whiteChars, copy_defaults=self.expr.copyDefaultWhiteChars - ) - self.skipWhitespace = self.expr.skipWhitespace - self.saveAsList = self.expr.saveAsList - self.ignoreExprs.extend(self.expr.ignoreExprs) - self.lshift_line = traceback.extract_stack(limit=2)[-2] - return self - - def __ilshift__(self, other): - return self << other - - def __or__(self, other): - caller_line = traceback.extract_stack(limit=2)[-2] - if ( - __diag__.warn_on_match_first_with_lshift_operator - and caller_line == self.lshift_line - and Diagnostics.warn_on_match_first_with_lshift_operator - not in self.suppress_warnings_ - ): - warnings.warn( - "using '<<' operator with '|' is probably an error, use '<<='", - stacklevel=2, - ) - ret = super().__or__(other) - return ret - - def __del__(self): - # see if we are getting dropped because of '=' reassignment of var instead of '<<=' or '<<' - if ( - self.expr is None - and __diag__.warn_on_assignment_to_Forward - and Diagnostics.warn_on_assignment_to_Forward not in self.suppress_warnings_ - ): - warnings.warn_explicit( - "Forward defined here but no expression attached later using '<<=' or '<<'", - UserWarning, - filename=self.caller_frame.filename, - lineno=self.caller_frame.lineno, - ) - - def parseImpl(self, instring, loc, doActions=True): - if ( - self.expr is None - and __diag__.warn_on_parse_using_empty_Forward - and Diagnostics.warn_on_parse_using_empty_Forward - not in self.suppress_warnings_ - ): - # walk stack until parse_string, scan_string, search_string, or transform_string is found - parse_fns = [ - "parse_string", - "scan_string", - "search_string", - "transform_string", - ] - tb = traceback.extract_stack(limit=200) - for i, frm in enumerate(reversed(tb), start=1): - if frm.name in parse_fns: - stacklevel = i + 1 - break - else: - stacklevel = 2 - warnings.warn( - "Forward expression was never assigned a value, will not parse any input", - stacklevel=stacklevel, - ) - if not ParserElement._left_recursion_enabled: - return super().parseImpl(instring, loc, doActions) - # ## Bounded Recursion algorithm ## - # Recursion only needs to be processed at ``Forward`` elements, since they are - # the only ones that can actually refer to themselves. The general idea is - # to handle recursion stepwise: We start at no recursion, then recurse once, - # recurse twice, ..., until more recursion offers no benefit (we hit the bound). - # - # The "trick" here is that each ``Forward`` gets evaluated in two contexts - # - to *match* a specific recursion level, and - # - to *search* the bounded recursion level - # and the two run concurrently. The *search* must *match* each recursion level - # to find the best possible match. This is handled by a memo table, which - # provides the previous match to the next level match attempt. - # - # See also "Left Recursion in Parsing Expression Grammars", Medeiros et al. - # - # There is a complication since we not only *parse* but also *transform* via - # actions: We do not want to run the actions too often while expanding. Thus, - # we expand using `doActions=False` and only run `doActions=True` if the next - # recursion level is acceptable. - with ParserElement.recursion_lock: - memo = ParserElement.recursion_memos - try: - # we are parsing at a specific recursion expansion - use it as-is - prev_loc, prev_result = memo[loc, self, doActions] - if isinstance(prev_result, Exception): - raise prev_result - return prev_loc, prev_result.copy() - except KeyError: - act_key = (loc, self, True) - peek_key = (loc, self, False) - # we are searching for the best recursion expansion - keep on improving - # both `doActions` cases must be tracked separately here! - prev_loc, prev_peek = memo[peek_key] = ( - loc - 1, - ParseException( - instring, loc, "Forward recursion without base case", self - ), - ) - if doActions: - memo[act_key] = memo[peek_key] - while True: - try: - new_loc, new_peek = super().parseImpl(instring, loc, False) - except ParseException: - # we failed before getting any match – do not hide the error - if isinstance(prev_peek, Exception): - raise - new_loc, new_peek = prev_loc, prev_peek - # the match did not get better: we are done - if new_loc <= prev_loc: - if doActions: - # replace the match for doActions=False as well, - # in case the action did backtrack - prev_loc, prev_result = memo[peek_key] = memo[act_key] - del memo[peek_key], memo[act_key] - return prev_loc, prev_result.copy() - del memo[peek_key] - return prev_loc, prev_peek.copy() - # the match did get better: see if we can improve further - else: - if doActions: - try: - memo[act_key] = super().parseImpl(instring, loc, True) - except ParseException as e: - memo[peek_key] = memo[act_key] = (new_loc, e) - raise - prev_loc, prev_peek = memo[peek_key] = new_loc, new_peek - - def leave_whitespace(self, recursive: bool = True) -> ParserElement: - self.skipWhitespace = False - return self - - def ignore_whitespace(self, recursive: bool = True) -> ParserElement: - self.skipWhitespace = True - return self - - def streamline(self) -> ParserElement: - if not self.streamlined: - self.streamlined = True - if self.expr is not None: - self.expr.streamline() - return self - - def validate(self, validateTrace=None) -> None: - if validateTrace is None: - validateTrace = [] - - if self not in validateTrace: - tmp = validateTrace[:] + [self] - if self.expr is not None: - self.expr.validate(tmp) - self._checkRecursion([]) - - def _generateDefaultName(self): - # Avoid infinite recursion by setting a temporary _defaultName - self._defaultName = ": ..." - - # Use the string representation of main expression. - retString = "..." - try: - if self.expr is not None: - retString = str(self.expr)[:1000] - else: - retString = "None" - finally: - return self.__class__.__name__ + ": " + retString - - def copy(self) -> ParserElement: - if self.expr is not None: - return super().copy() - else: - ret = Forward() - ret <<= self - return ret - - def _setResultsName(self, name, list_all_matches=False): - if ( - __diag__.warn_name_set_on_empty_Forward - and Diagnostics.warn_name_set_on_empty_Forward - not in self.suppress_warnings_ - ): - if self.expr is None: - warnings.warn( - "{}: setting results name {!r} on {} expression " - "that has no contained expression".format( - "warn_name_set_on_empty_Forward", name, type(self).__name__ - ), - stacklevel=3, - ) - - return super()._setResultsName(name, list_all_matches) - - ignoreWhitespace = ignore_whitespace - leaveWhitespace = leave_whitespace - - -class TokenConverter(ParseElementEnhance): - """ - Abstract subclass of :class:`ParseExpression`, for converting parsed results. - """ - - def __init__(self, expr: Union[ParserElement, str], savelist=False): - super().__init__(expr) # , savelist) - self.saveAsList = False - - -class Combine(TokenConverter): - """Converter to concatenate all matching tokens to a single string. - By default, the matching patterns must also be contiguous in the - input string; this can be disabled by specifying - ``'adjacent=False'`` in the constructor. - - Example:: - - real = Word(nums) + '.' + Word(nums) - print(real.parse_string('3.1416')) # -> ['3', '.', '1416'] - # will also erroneously match the following - print(real.parse_string('3. 1416')) # -> ['3', '.', '1416'] - - real = Combine(Word(nums) + '.' + Word(nums)) - print(real.parse_string('3.1416')) # -> ['3.1416'] - # no match when there are internal spaces - print(real.parse_string('3. 1416')) # -> Exception: Expected W:(0123...) - """ - - def __init__( - self, - expr: ParserElement, - join_string: str = "", - adjacent: bool = True, - *, - joinString: typing.Optional[str] = None, - ): - super().__init__(expr) - joinString = joinString if joinString is not None else join_string - # suppress whitespace-stripping in contained parse expressions, but re-enable it on the Combine itself - if adjacent: - self.leave_whitespace() - self.adjacent = adjacent - self.skipWhitespace = True - self.joinString = joinString - self.callPreparse = True - - def ignore(self, other) -> ParserElement: - if self.adjacent: - ParserElement.ignore(self, other) - else: - super().ignore(other) - return self - - def postParse(self, instring, loc, tokenlist): - retToks = tokenlist.copy() - del retToks[:] - retToks += ParseResults( - ["".join(tokenlist._asStringList(self.joinString))], modal=self.modalResults - ) - - if self.resultsName and retToks.haskeys(): - return [retToks] - else: - return retToks - - -class Group(TokenConverter): - """Converter to return the matched tokens as a list - useful for - returning tokens of :class:`ZeroOrMore` and :class:`OneOrMore` expressions. - - The optional ``aslist`` argument when set to True will return the - parsed tokens as a Python list instead of a pyparsing ParseResults. - - Example:: - - ident = Word(alphas) - num = Word(nums) - term = ident | num - func = ident + Opt(delimited_list(term)) - print(func.parse_string("fn a, b, 100")) - # -> ['fn', 'a', 'b', '100'] - - func = ident + Group(Opt(delimited_list(term))) - print(func.parse_string("fn a, b, 100")) - # -> ['fn', ['a', 'b', '100']] - """ - - def __init__(self, expr: ParserElement, aslist: bool = False): - super().__init__(expr) - self.saveAsList = True - self._asPythonList = aslist - - def postParse(self, instring, loc, tokenlist): - if self._asPythonList: - return ParseResults.List( - tokenlist.asList() - if isinstance(tokenlist, ParseResults) - else list(tokenlist) - ) - else: - return [tokenlist] - - -class Dict(TokenConverter): - """Converter to return a repetitive expression as a list, but also - as a dictionary. Each element can also be referenced using the first - token in the expression as its key. Useful for tabular report - scraping when the first column can be used as a item key. - - The optional ``asdict`` argument when set to True will return the - parsed tokens as a Python dict instead of a pyparsing ParseResults. - - Example:: - - data_word = Word(alphas) - label = data_word + FollowedBy(':') - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - - # print attributes as plain groups - print(attr_expr[1, ...].parse_string(text).dump()) - - # instead of OneOrMore(expr), parse using Dict(Group(expr)[1, ...]) - Dict will auto-assign names - result = Dict(Group(attr_expr)[1, ...]).parse_string(text) - print(result.dump()) - - # access named fields as dict entries, or output as dict - print(result['shape']) - print(result.as_dict()) - - prints:: - - ['shape', 'SQUARE', 'posn', 'upper left', 'color', 'light blue', 'texture', 'burlap'] - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - {'color': 'light blue', 'posn': 'upper left', 'texture': 'burlap', 'shape': 'SQUARE'} - - See more examples at :class:`ParseResults` of accessing fields by results name. - """ - - def __init__(self, expr: ParserElement, asdict: bool = False): - super().__init__(expr) - self.saveAsList = True - self._asPythonDict = asdict - - def postParse(self, instring, loc, tokenlist): - for i, tok in enumerate(tokenlist): - if len(tok) == 0: - continue - - ikey = tok[0] - if isinstance(ikey, int): - ikey = str(ikey).strip() - - if len(tok) == 1: - tokenlist[ikey] = _ParseResultsWithOffset("", i) - - elif len(tok) == 2 and not isinstance(tok[1], ParseResults): - tokenlist[ikey] = _ParseResultsWithOffset(tok[1], i) - - else: - try: - dictvalue = tok.copy() # ParseResults(i) - except Exception: - exc = TypeError( - "could not extract dict values from parsed results" - " - Dict expression must contain Grouped expressions" - ) - raise exc from None - - del dictvalue[0] - - if len(dictvalue) != 1 or ( - isinstance(dictvalue, ParseResults) and dictvalue.haskeys() - ): - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue, i) - else: - tokenlist[ikey] = _ParseResultsWithOffset(dictvalue[0], i) - - if self._asPythonDict: - return [tokenlist.as_dict()] if self.resultsName else tokenlist.as_dict() - else: - return [tokenlist] if self.resultsName else tokenlist - - -class Suppress(TokenConverter): - """Converter for ignoring the results of a parsed expression. - - Example:: - - source = "a, b, c,d" - wd = Word(alphas) - wd_list1 = wd + (',' + wd)[...] - print(wd_list1.parse_string(source)) - - # often, delimiters that are useful during parsing are just in the - # way afterward - use Suppress to keep them out of the parsed output - wd_list2 = wd + (Suppress(',') + wd)[...] - print(wd_list2.parse_string(source)) - - # Skipped text (using '...') can be suppressed as well - source = "lead in START relevant text END trailing text" - start_marker = Keyword("START") - end_marker = Keyword("END") - find_body = Suppress(...) + start_marker + ... + end_marker - print(find_body.parse_string(source) - - prints:: - - ['a', ',', 'b', ',', 'c', ',', 'd'] - ['a', 'b', 'c', 'd'] - ['START', 'relevant text ', 'END'] - - (See also :class:`delimited_list`.) - """ - - def __init__(self, expr: Union[ParserElement, str], savelist: bool = False): - if expr is ...: - expr = _PendingSkip(NoMatch()) - super().__init__(expr) - - def __add__(self, other) -> "ParserElement": - if isinstance(self.expr, _PendingSkip): - return Suppress(SkipTo(other)) + other - else: - return super().__add__(other) - - def __sub__(self, other) -> "ParserElement": - if isinstance(self.expr, _PendingSkip): - return Suppress(SkipTo(other)) - other - else: - return super().__sub__(other) - - def postParse(self, instring, loc, tokenlist): - return [] - - def suppress(self) -> ParserElement: - return self - - -def trace_parse_action(f: ParseAction) -> ParseAction: - """Decorator for debugging parse actions. - - When the parse action is called, this decorator will print - ``">> entering method-name(line:, , )"``. - When the parse action completes, the decorator will print - ``"<<"`` followed by the returned value, or any exception that the parse action raised. - - Example:: - - wd = Word(alphas) - - @trace_parse_action - def remove_duplicate_chars(tokens): - return ''.join(sorted(set(''.join(tokens)))) - - wds = wd[1, ...].set_parse_action(remove_duplicate_chars) - print(wds.parse_string("slkdjs sld sldd sdlf sdljf")) - - prints:: - - >>entering remove_duplicate_chars(line: 'slkdjs sld sldd sdlf sdljf', 0, (['slkdjs', 'sld', 'sldd', 'sdlf', 'sdljf'], {})) - < 3: - thisFunc = paArgs[0].__class__.__name__ + "." + thisFunc - sys.stderr.write( - ">>entering {}(line: {!r}, {}, {!r})\n".format(thisFunc, line(l, s), l, t) - ) - try: - ret = f(*paArgs) - except Exception as exc: - sys.stderr.write("< str: - r"""Helper to easily define string ranges for use in :class:`Word` - construction. Borrows syntax from regexp ``'[]'`` string range - definitions:: - - srange("[0-9]") -> "0123456789" - srange("[a-z]") -> "abcdefghijklmnopqrstuvwxyz" - srange("[a-z$_]") -> "abcdefghijklmnopqrstuvwxyz$_" - - The input string must be enclosed in []'s, and the returned string - is the expanded character set joined into a single string. The - values enclosed in the []'s may be: - - - a single character - - an escaped character with a leading backslash (such as ``\-`` - or ``\]``) - - an escaped hex character with a leading ``'\x'`` - (``\x21``, which is a ``'!'`` character) (``\0x##`` - is also supported for backwards compatibility) - - an escaped octal character with a leading ``'\0'`` - (``\041``, which is a ``'!'`` character) - - a range of any of the above, separated by a dash (``'a-z'``, - etc.) - - any combination of the above (``'aeiouy'``, - ``'a-zA-Z0-9_$'``, etc.) - """ - _expanded = ( - lambda p: p - if not isinstance(p, ParseResults) - else "".join(chr(c) for c in range(ord(p[0]), ord(p[1]) + 1)) - ) - try: - return "".join(_expanded(part) for part in _reBracketExpr.parse_string(s).body) - except Exception: - return "" - - -def token_map(func, *args) -> ParseAction: - """Helper to define a parse action by mapping a function to all - elements of a :class:`ParseResults` list. If any additional args are passed, - they are forwarded to the given function as additional arguments - after the token, as in - ``hex_integer = Word(hexnums).set_parse_action(token_map(int, 16))``, - which will convert the parsed data to an integer using base 16. - - Example (compare the last to example in :class:`ParserElement.transform_string`:: - - hex_ints = Word(hexnums)[1, ...].set_parse_action(token_map(int, 16)) - hex_ints.run_tests(''' - 00 11 22 aa FF 0a 0d 1a - ''') - - upperword = Word(alphas).set_parse_action(token_map(str.upper)) - upperword[1, ...].run_tests(''' - my kingdom for a horse - ''') - - wd = Word(alphas).set_parse_action(token_map(str.title)) - wd[1, ...].set_parse_action(' '.join).run_tests(''' - now is the winter of our discontent made glorious summer by this sun of york - ''') - - prints:: - - 00 11 22 aa FF 0a 0d 1a - [0, 17, 34, 170, 255, 10, 13, 26] - - my kingdom for a horse - ['MY', 'KINGDOM', 'FOR', 'A', 'HORSE'] - - now is the winter of our discontent made glorious summer by this sun of york - ['Now Is The Winter Of Our Discontent Made Glorious Summer By This Sun Of York'] - """ - - def pa(s, l, t): - return [func(tokn, *args) for tokn in t] - - func_name = getattr(func, "__name__", getattr(func, "__class__").__name__) - pa.__name__ = func_name - - return pa - - -def autoname_elements() -> None: - """ - Utility to simplify mass-naming of parser elements, for - generating railroad diagram with named subdiagrams. - """ - for name, var in sys._getframe().f_back.f_locals.items(): - if isinstance(var, ParserElement) and not var.customName: - var.set_name(name) - - -dbl_quoted_string = Combine( - Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"' -).set_name("string enclosed in double quotes") - -sgl_quoted_string = Combine( - Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'" -).set_name("string enclosed in single quotes") - -quoted_string = Combine( - Regex(r'"(?:[^"\n\r\\]|(?:"")|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*') + '"' - | Regex(r"'(?:[^'\n\r\\]|(?:'')|(?:\\(?:[^x]|x[0-9a-fA-F]+)))*") + "'" -).set_name("quotedString using single or double quotes") - -unicode_string = Combine("u" + quoted_string.copy()).set_name("unicode string literal") - - -alphas8bit = srange(r"[\0xc0-\0xd6\0xd8-\0xf6\0xf8-\0xff]") -punc8bit = srange(r"[\0xa1-\0xbf\0xd7\0xf7]") - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - -# backward compatibility names -tokenMap = token_map -conditionAsParseAction = condition_as_parse_action -nullDebugAction = null_debug_action -sglQuotedString = sgl_quoted_string -dblQuotedString = dbl_quoted_string -quotedString = quoted_string -unicodeString = unicode_string -lineStart = line_start -lineEnd = line_end -stringStart = string_start -stringEnd = string_end -traceParseAction = trace_parse_action diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py deleted file mode 100644 index 898644755..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/diagram/__init__.py +++ /dev/null @@ -1,642 +0,0 @@ -import railroad -import pyparsing -import typing -from typing import ( - List, - NamedTuple, - Generic, - TypeVar, - Dict, - Callable, - Set, - Iterable, -) -from jinja2 import Template -from io import StringIO -import inspect - - -jinja2_template_source = """\ - - - - {% if not head %} - - {% else %} - {{ head | safe }} - {% endif %} - - -{{ body | safe }} -{% for diagram in diagrams %} -
      -

      {{ diagram.title }}

      -
      {{ diagram.text }}
      -
      - {{ diagram.svg }} -
      -
      -{% endfor %} - - -""" - -template = Template(jinja2_template_source) - -# Note: ideally this would be a dataclass, but we're supporting Python 3.5+ so we can't do this yet -NamedDiagram = NamedTuple( - "NamedDiagram", - [("name", str), ("diagram", typing.Optional[railroad.DiagramItem]), ("index", int)], -) -""" -A simple structure for associating a name with a railroad diagram -""" - -T = TypeVar("T") - - -class EachItem(railroad.Group): - """ - Custom railroad item to compose a: - - Group containing a - - OneOrMore containing a - - Choice of the elements in the Each - with the group label indicating that all must be matched - """ - - all_label = "[ALL]" - - def __init__(self, *items): - choice_item = railroad.Choice(len(items) - 1, *items) - one_or_more_item = railroad.OneOrMore(item=choice_item) - super().__init__(one_or_more_item, label=self.all_label) - - -class AnnotatedItem(railroad.Group): - """ - Simple subclass of Group that creates an annotation label - """ - - def __init__(self, label: str, item): - super().__init__(item=item, label="[{}]".format(label) if label else label) - - -class EditablePartial(Generic[T]): - """ - Acts like a functools.partial, but can be edited. In other words, it represents a type that hasn't yet been - constructed. - """ - - # We need this here because the railroad constructors actually transform the data, so can't be called until the - # entire tree is assembled - - def __init__(self, func: Callable[..., T], args: list, kwargs: dict): - self.func = func - self.args = args - self.kwargs = kwargs - - @classmethod - def from_call(cls, func: Callable[..., T], *args, **kwargs) -> "EditablePartial[T]": - """ - If you call this function in the same way that you would call the constructor, it will store the arguments - as you expect. For example EditablePartial.from_call(Fraction, 1, 3)() == Fraction(1, 3) - """ - return EditablePartial(func=func, args=list(args), kwargs=kwargs) - - @property - def name(self): - return self.kwargs["name"] - - def __call__(self) -> T: - """ - Evaluate the partial and return the result - """ - args = self.args.copy() - kwargs = self.kwargs.copy() - - # This is a helpful hack to allow you to specify varargs parameters (e.g. *args) as keyword args (e.g. - # args=['list', 'of', 'things']) - arg_spec = inspect.getfullargspec(self.func) - if arg_spec.varargs in self.kwargs: - args += kwargs.pop(arg_spec.varargs) - - return self.func(*args, **kwargs) - - -def railroad_to_html(diagrams: List[NamedDiagram], **kwargs) -> str: - """ - Given a list of NamedDiagram, produce a single HTML string that visualises those diagrams - :params kwargs: kwargs to be passed in to the template - """ - data = [] - for diagram in diagrams: - if diagram.diagram is None: - continue - io = StringIO() - diagram.diagram.writeSvg(io.write) - title = diagram.name - if diagram.index == 0: - title += " (root)" - data.append({"title": title, "text": "", "svg": io.getvalue()}) - - return template.render(diagrams=data, **kwargs) - - -def resolve_partial(partial: "EditablePartial[T]") -> T: - """ - Recursively resolves a collection of Partials into whatever type they are - """ - if isinstance(partial, EditablePartial): - partial.args = resolve_partial(partial.args) - partial.kwargs = resolve_partial(partial.kwargs) - return partial() - elif isinstance(partial, list): - return [resolve_partial(x) for x in partial] - elif isinstance(partial, dict): - return {key: resolve_partial(x) for key, x in partial.items()} - else: - return partial - - -def to_railroad( - element: pyparsing.ParserElement, - diagram_kwargs: typing.Optional[dict] = None, - vertical: int = 3, - show_results_names: bool = False, - show_groups: bool = False, -) -> List[NamedDiagram]: - """ - Convert a pyparsing element tree into a list of diagrams. This is the recommended entrypoint to diagram - creation if you want to access the Railroad tree before it is converted to HTML - :param element: base element of the parser being diagrammed - :param diagram_kwargs: kwargs to pass to the Diagram() constructor - :param vertical: (optional) - int - limit at which number of alternatives should be - shown vertically instead of horizontally - :param show_results_names - bool to indicate whether results name annotations should be - included in the diagram - :param show_groups - bool to indicate whether groups should be highlighted with an unlabeled - surrounding box - """ - # Convert the whole tree underneath the root - lookup = ConverterState(diagram_kwargs=diagram_kwargs or {}) - _to_diagram_element( - element, - lookup=lookup, - parent=None, - vertical=vertical, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - root_id = id(element) - # Convert the root if it hasn't been already - if root_id in lookup: - if not element.customName: - lookup[root_id].name = "" - lookup[root_id].mark_for_extraction(root_id, lookup, force=True) - - # Now that we're finished, we can convert from intermediate structures into Railroad elements - diags = list(lookup.diagrams.values()) - if len(diags) > 1: - # collapse out duplicate diags with the same name - seen = set() - deduped_diags = [] - for d in diags: - # don't extract SkipTo elements, they are uninformative as subdiagrams - if d.name == "...": - continue - if d.name is not None and d.name not in seen: - seen.add(d.name) - deduped_diags.append(d) - resolved = [resolve_partial(partial) for partial in deduped_diags] - else: - # special case - if just one diagram, always display it, even if - # it has no name - resolved = [resolve_partial(partial) for partial in diags] - return sorted(resolved, key=lambda diag: diag.index) - - -def _should_vertical( - specification: int, exprs: Iterable[pyparsing.ParserElement] -) -> bool: - """ - Returns true if we should return a vertical list of elements - """ - if specification is None: - return False - else: - return len(_visible_exprs(exprs)) >= specification - - -class ElementState: - """ - State recorded for an individual pyparsing Element - """ - - # Note: this should be a dataclass, but we have to support Python 3.5 - def __init__( - self, - element: pyparsing.ParserElement, - converted: EditablePartial, - parent: EditablePartial, - number: int, - name: str = None, - parent_index: typing.Optional[int] = None, - ): - #: The pyparsing element that this represents - self.element: pyparsing.ParserElement = element - #: The name of the element - self.name: typing.Optional[str] = name - #: The output Railroad element in an unconverted state - self.converted: EditablePartial = converted - #: The parent Railroad element, which we store so that we can extract this if it's duplicated - self.parent: EditablePartial = parent - #: The order in which we found this element, used for sorting diagrams if this is extracted into a diagram - self.number: int = number - #: The index of this inside its parent - self.parent_index: typing.Optional[int] = parent_index - #: If true, we should extract this out into a subdiagram - self.extract: bool = False - #: If true, all of this element's children have been filled out - self.complete: bool = False - - def mark_for_extraction( - self, el_id: int, state: "ConverterState", name: str = None, force: bool = False - ): - """ - Called when this instance has been seen twice, and thus should eventually be extracted into a sub-diagram - :param el_id: id of the element - :param state: element/diagram state tracker - :param name: name to use for this element's text - :param force: If true, force extraction now, regardless of the state of this. Only useful for extracting the - root element when we know we're finished - """ - self.extract = True - - # Set the name - if not self.name: - if name: - # Allow forcing a custom name - self.name = name - elif self.element.customName: - self.name = self.element.customName - else: - self.name = "" - - # Just because this is marked for extraction doesn't mean we can do it yet. We may have to wait for children - # to be added - # Also, if this is just a string literal etc, don't bother extracting it - if force or (self.complete and _worth_extracting(self.element)): - state.extract_into_diagram(el_id) - - -class ConverterState: - """ - Stores some state that persists between recursions into the element tree - """ - - def __init__(self, diagram_kwargs: typing.Optional[dict] = None): - #: A dictionary mapping ParserElements to state relating to them - self._element_diagram_states: Dict[int, ElementState] = {} - #: A dictionary mapping ParserElement IDs to subdiagrams generated from them - self.diagrams: Dict[int, EditablePartial[NamedDiagram]] = {} - #: The index of the next unnamed element - self.unnamed_index: int = 1 - #: The index of the next element. This is used for sorting - self.index: int = 0 - #: Shared kwargs that are used to customize the construction of diagrams - self.diagram_kwargs: dict = diagram_kwargs or {} - self.extracted_diagram_names: Set[str] = set() - - def __setitem__(self, key: int, value: ElementState): - self._element_diagram_states[key] = value - - def __getitem__(self, key: int) -> ElementState: - return self._element_diagram_states[key] - - def __delitem__(self, key: int): - del self._element_diagram_states[key] - - def __contains__(self, key: int): - return key in self._element_diagram_states - - def generate_unnamed(self) -> int: - """ - Generate a number used in the name of an otherwise unnamed diagram - """ - self.unnamed_index += 1 - return self.unnamed_index - - def generate_index(self) -> int: - """ - Generate a number used to index a diagram - """ - self.index += 1 - return self.index - - def extract_into_diagram(self, el_id: int): - """ - Used when we encounter the same token twice in the same tree. When this - happens, we replace all instances of that token with a terminal, and - create a new subdiagram for the token - """ - position = self[el_id] - - # Replace the original definition of this element with a regular block - if position.parent: - ret = EditablePartial.from_call(railroad.NonTerminal, text=position.name) - if "item" in position.parent.kwargs: - position.parent.kwargs["item"] = ret - elif "items" in position.parent.kwargs: - position.parent.kwargs["items"][position.parent_index] = ret - - # If the element we're extracting is a group, skip to its content but keep the title - if position.converted.func == railroad.Group: - content = position.converted.kwargs["item"] - else: - content = position.converted - - self.diagrams[el_id] = EditablePartial.from_call( - NamedDiagram, - name=position.name, - diagram=EditablePartial.from_call( - railroad.Diagram, content, **self.diagram_kwargs - ), - index=position.number, - ) - - del self[el_id] - - -def _worth_extracting(element: pyparsing.ParserElement) -> bool: - """ - Returns true if this element is worth having its own sub-diagram. Simply, if any of its children - themselves have children, then its complex enough to extract - """ - children = element.recurse() - return any(child.recurse() for child in children) - - -def _apply_diagram_item_enhancements(fn): - """ - decorator to ensure enhancements to a diagram item (such as results name annotations) - get applied on return from _to_diagram_element (we do this since there are several - returns in _to_diagram_element) - """ - - def _inner( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, - ) -> typing.Optional[EditablePartial]: - - ret = fn( - element, - parent, - lookup, - vertical, - index, - name_hint, - show_results_names, - show_groups, - ) - - # apply annotation for results name, if present - if show_results_names and ret is not None: - element_results_name = element.resultsName - if element_results_name: - # add "*" to indicate if this is a "list all results" name - element_results_name += "" if element.modalResults else "*" - ret = EditablePartial.from_call( - railroad.Group, item=ret, label=element_results_name - ) - - return ret - - return _inner - - -def _visible_exprs(exprs: Iterable[pyparsing.ParserElement]): - non_diagramming_exprs = ( - pyparsing.ParseElementEnhance, - pyparsing.PositionToken, - pyparsing.And._ErrorStop, - ) - return [ - e - for e in exprs - if not (e.customName or e.resultsName or isinstance(e, non_diagramming_exprs)) - ] - - -@_apply_diagram_item_enhancements -def _to_diagram_element( - element: pyparsing.ParserElement, - parent: typing.Optional[EditablePartial], - lookup: ConverterState = None, - vertical: int = None, - index: int = 0, - name_hint: str = None, - show_results_names: bool = False, - show_groups: bool = False, -) -> typing.Optional[EditablePartial]: - """ - Recursively converts a PyParsing Element to a railroad Element - :param lookup: The shared converter state that keeps track of useful things - :param index: The index of this element within the parent - :param parent: The parent of this element in the output tree - :param vertical: Controls at what point we make a list of elements vertical. If this is an integer (the default), - it sets the threshold of the number of items before we go vertical. If True, always go vertical, if False, never - do so - :param name_hint: If provided, this will override the generated name - :param show_results_names: bool flag indicating whether to add annotations for results names - :returns: The converted version of the input element, but as a Partial that hasn't yet been constructed - :param show_groups: bool flag indicating whether to show groups using bounding box - """ - exprs = element.recurse() - name = name_hint or element.customName or element.__class__.__name__ - - # Python's id() is used to provide a unique identifier for elements - el_id = id(element) - - element_results_name = element.resultsName - - # Here we basically bypass processing certain wrapper elements if they contribute nothing to the diagram - if not element.customName: - if isinstance( - element, - ( - # pyparsing.TokenConverter, - # pyparsing.Forward, - pyparsing.Located, - ), - ): - # However, if this element has a useful custom name, and its child does not, we can pass it on to the child - if exprs: - if not exprs[0].customName: - propagated_name = name - else: - propagated_name = None - - return _to_diagram_element( - element.expr, - parent=parent, - lookup=lookup, - vertical=vertical, - index=index, - name_hint=propagated_name, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # If the element isn't worth extracting, we always treat it as the first time we say it - if _worth_extracting(element): - if el_id in lookup: - # If we've seen this element exactly once before, we are only just now finding out that it's a duplicate, - # so we have to extract it into a new diagram. - looked_up = lookup[el_id] - looked_up.mark_for_extraction(el_id, lookup, name=name_hint) - ret = EditablePartial.from_call(railroad.NonTerminal, text=looked_up.name) - return ret - - elif el_id in lookup.diagrams: - # If we have seen the element at least twice before, and have already extracted it into a subdiagram, we - # just put in a marker element that refers to the sub-diagram - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - return ret - - # Recursively convert child elements - # Here we find the most relevant Railroad element for matching pyparsing Element - # We use ``items=[]`` here to hold the place for where the child elements will go once created - if isinstance(element, pyparsing.And): - # detect And's created with ``expr*N`` notation - for these use a OneOrMore with a repeat - # (all will have the same name, and resultsName) - if not exprs: - return None - if len(set((e.name, e.resultsName) for e in exprs)) == 1: - ret = EditablePartial.from_call( - railroad.OneOrMore, item="", repeat=str(len(exprs)) - ) - elif _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Stack, items=[]) - else: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif isinstance(element, (pyparsing.Or, pyparsing.MatchFirst)): - if not exprs: - return None - if _should_vertical(vertical, exprs): - ret = EditablePartial.from_call(railroad.Choice, 0, items=[]) - else: - ret = EditablePartial.from_call(railroad.HorizontalChoice, items=[]) - elif isinstance(element, pyparsing.Each): - if not exprs: - return None - ret = EditablePartial.from_call(EachItem, items=[]) - elif isinstance(element, pyparsing.NotAny): - ret = EditablePartial.from_call(AnnotatedItem, label="NOT", item="") - elif isinstance(element, pyparsing.FollowedBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKAHEAD", item="") - elif isinstance(element, pyparsing.PrecededBy): - ret = EditablePartial.from_call(AnnotatedItem, label="LOOKBEHIND", item="") - elif isinstance(element, pyparsing.Group): - if show_groups: - ret = EditablePartial.from_call(AnnotatedItem, label="", item="") - else: - ret = EditablePartial.from_call(railroad.Group, label="", item="") - elif isinstance(element, pyparsing.TokenConverter): - ret = EditablePartial.from_call( - AnnotatedItem, label=type(element).__name__.lower(), item="" - ) - elif isinstance(element, pyparsing.Opt): - ret = EditablePartial.from_call(railroad.Optional, item="") - elif isinstance(element, pyparsing.OneOrMore): - ret = EditablePartial.from_call(railroad.OneOrMore, item="") - elif isinstance(element, pyparsing.ZeroOrMore): - ret = EditablePartial.from_call(railroad.ZeroOrMore, item="") - elif isinstance(element, pyparsing.Group): - ret = EditablePartial.from_call( - railroad.Group, item=None, label=element_results_name - ) - elif isinstance(element, pyparsing.Empty) and not element.customName: - # Skip unnamed "Empty" elements - ret = None - elif len(exprs) > 1: - ret = EditablePartial.from_call(railroad.Sequence, items=[]) - elif len(exprs) > 0 and not element_results_name: - ret = EditablePartial.from_call(railroad.Group, item="", label=name) - else: - terminal = EditablePartial.from_call(railroad.Terminal, element.defaultName) - ret = terminal - - if ret is None: - return - - # Indicate this element's position in the tree so we can extract it if necessary - lookup[el_id] = ElementState( - element=element, - converted=ret, - parent=parent, - parent_index=index, - number=lookup.generate_index(), - ) - if element.customName: - lookup[el_id].mark_for_extraction(el_id, lookup, element.customName) - - i = 0 - for expr in exprs: - # Add a placeholder index in case we have to extract the child before we even add it to the parent - if "items" in ret.kwargs: - ret.kwargs["items"].insert(i, None) - - item = _to_diagram_element( - expr, - parent=ret, - lookup=lookup, - vertical=vertical, - index=i, - show_results_names=show_results_names, - show_groups=show_groups, - ) - - # Some elements don't need to be shown in the diagram - if item is not None: - if "item" in ret.kwargs: - ret.kwargs["item"] = item - elif "items" in ret.kwargs: - # If we've already extracted the child, don't touch this index, since it's occupied by a nonterminal - ret.kwargs["items"][i] = item - i += 1 - elif "items" in ret.kwargs: - # If we're supposed to skip this element, remove it from the parent - del ret.kwargs["items"][i] - - # If all this items children are none, skip this item - if ret and ( - ("items" in ret.kwargs and len(ret.kwargs["items"]) == 0) - or ("item" in ret.kwargs and ret.kwargs["item"] is None) - ): - ret = EditablePartial.from_call(railroad.Terminal, name) - - # Mark this element as "complete", ie it has all of its children - if el_id in lookup: - lookup[el_id].complete = True - - if el_id in lookup and lookup[el_id].extract and lookup[el_id].complete: - lookup.extract_into_diagram(el_id) - if ret is not None: - ret = EditablePartial.from_call( - railroad.NonTerminal, text=lookup.diagrams[el_id].kwargs["name"] - ) - - return ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py deleted file mode 100644 index a38447bb0..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/exceptions.py +++ /dev/null @@ -1,267 +0,0 @@ -# exceptions.py - -import re -import sys -import typing - -from .util import col, line, lineno, _collapse_string_to_ranges -from .unicode import pyparsing_unicode as ppu - - -class ExceptionWordUnicode(ppu.Latin1, ppu.LatinA, ppu.LatinB, ppu.Greek, ppu.Cyrillic): - pass - - -_extract_alphanums = _collapse_string_to_ranges(ExceptionWordUnicode.alphanums) -_exception_word_extractor = re.compile("([" + _extract_alphanums + "]{1,16})|.") - - -class ParseBaseException(Exception): - """base exception class for all parsing runtime exceptions""" - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, - pstr: str, - loc: int = 0, - msg: typing.Optional[str] = None, - elem=None, - ): - self.loc = loc - if msg is None: - self.msg = pstr - self.pstr = "" - else: - self.msg = msg - self.pstr = pstr - self.parser_element = self.parserElement = elem - self.args = (pstr, loc, msg) - - @staticmethod - def explain_exception(exc, depth=16): - """ - Method to take an exception and translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - exc - exception raised during parsing (need not be a ParseException, in support - of Python exceptions that might be raised in a parse action) - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - """ - import inspect - from .core import ParserElement - - if depth is None: - depth = sys.getrecursionlimit() - ret = [] - if isinstance(exc, ParseBaseException): - ret.append(exc.line) - ret.append(" " * (exc.column - 1) + "^") - ret.append("{}: {}".format(type(exc).__name__, exc)) - - if depth > 0: - callers = inspect.getinnerframes(exc.__traceback__, context=depth) - seen = set() - for i, ff in enumerate(callers[-depth:]): - frm = ff[0] - - f_self = frm.f_locals.get("self", None) - if isinstance(f_self, ParserElement): - if frm.f_code.co_name not in ("parseImpl", "_parseNoCache"): - continue - if id(f_self) in seen: - continue - seen.add(id(f_self)) - - self_type = type(f_self) - ret.append( - "{}.{} - {}".format( - self_type.__module__, self_type.__name__, f_self - ) - ) - - elif f_self is not None: - self_type = type(f_self) - ret.append("{}.{}".format(self_type.__module__, self_type.__name__)) - - else: - code = frm.f_code - if code.co_name in ("wrapper", ""): - continue - - ret.append("{}".format(code.co_name)) - - depth -= 1 - if not depth: - break - - return "\n".join(ret) - - @classmethod - def _from_exception(cls, pe): - """ - internal factory method to simplify creating one type of ParseException - from another - avoids having __init__ signature conflicts among subclasses - """ - return cls(pe.pstr, pe.loc, pe.msg, pe.parserElement) - - @property - def line(self) -> str: - """ - Return the line of text where the exception occurred. - """ - return line(self.loc, self.pstr) - - @property - def lineno(self) -> int: - """ - Return the 1-based line number of text where the exception occurred. - """ - return lineno(self.loc, self.pstr) - - @property - def col(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - @property - def column(self) -> int: - """ - Return the 1-based column on the line of text where the exception occurred. - """ - return col(self.loc, self.pstr) - - def __str__(self) -> str: - if self.pstr: - if self.loc >= len(self.pstr): - foundstr = ", found end of text" - else: - # pull out next word at error location - found_match = _exception_word_extractor.match(self.pstr, self.loc) - if found_match is not None: - found = found_match.group(0) - else: - found = self.pstr[self.loc : self.loc + 1] - foundstr = (", found %r" % found).replace(r"\\", "\\") - else: - foundstr = "" - return "{}{} (at char {}), (line:{}, col:{})".format( - self.msg, foundstr, self.loc, self.lineno, self.column - ) - - def __repr__(self): - return str(self) - - def mark_input_line(self, marker_string: str = None, *, markerString=">!<") -> str: - """ - Extracts the exception line from the input string, and marks - the location of the exception with a special symbol. - """ - markerString = marker_string if marker_string is not None else markerString - line_str = self.line - line_column = self.column - 1 - if markerString: - line_str = "".join( - (line_str[:line_column], markerString, line_str[line_column:]) - ) - return line_str.strip() - - def explain(self, depth=16) -> str: - """ - Method to translate the Python internal traceback into a list - of the pyparsing expressions that caused the exception to be raised. - - Parameters: - - - depth (default=16) - number of levels back in the stack trace to list expression - and function names; if None, the full stack trace names will be listed; if 0, only - the failing input line, marker, and exception string will be shown - - Returns a multi-line string listing the ParserElements and/or function names in the - exception's stack trace. - - Example:: - - expr = pp.Word(pp.nums) * 3 - try: - expr.parse_string("123 456 A789") - except pp.ParseException as pe: - print(pe.explain(depth=0)) - - prints:: - - 123 456 A789 - ^ - ParseException: Expected W:(0-9), found 'A' (at char 8), (line:1, col:9) - - Note: the diagnostic output will include string representations of the expressions - that failed to parse. These representations will be more helpful if you use `set_name` to - give identifiable names to your expressions. Otherwise they will use the default string - forms, which may be cryptic to read. - - Note: pyparsing's default truncation of exception tracebacks may also truncate the - stack of expressions that are displayed in the ``explain`` output. To get the full listing - of parser expressions, you may have to set ``ParserElement.verbose_stacktrace = True`` - """ - return self.explain_exception(self, depth) - - markInputline = mark_input_line - - -class ParseException(ParseBaseException): - """ - Exception thrown when a parse expression doesn't match the input string - - Example:: - - try: - Word(nums).set_name("integer").parse_string("ABC") - except ParseException as pe: - print(pe) - print("column: {}".format(pe.column)) - - prints:: - - Expected integer (at char 0), (line:1, col:1) - column: 1 - - """ - - -class ParseFatalException(ParseBaseException): - """ - User-throwable exception thrown when inconsistent parse content - is found; stops all parsing immediately - """ - - -class ParseSyntaxException(ParseFatalException): - """ - Just like :class:`ParseFatalException`, but thrown internally - when an :class:`ErrorStop` ('-' operator) indicates - that parsing is to stop immediately because an unbacktrackable - syntax error has been found. - """ - - -class RecursiveGrammarException(Exception): - """ - Exception thrown by :class:`ParserElement.validate` if the - grammar could be left-recursive; parser may need to enable - left recursion using :class:`ParserElement.enable_left_recursion` - """ - - def __init__(self, parseElementList): - self.parseElementTrace = parseElementList - - def __str__(self) -> str: - return "RecursiveGrammarException: {}".format(self.parseElementTrace) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py deleted file mode 100644 index 9588b3b78..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/helpers.py +++ /dev/null @@ -1,1088 +0,0 @@ -# helpers.py -import html.entities -import re -import typing - -from . import __diag__ -from .core import * -from .util import _bslash, _flatten, _escape_regex_range_chars - - -# -# global helpers -# -def delimited_list( - expr: Union[str, ParserElement], - delim: Union[str, ParserElement] = ",", - combine: bool = False, - min: typing.Optional[int] = None, - max: typing.Optional[int] = None, - *, - allow_trailing_delim: bool = False, -) -> ParserElement: - """Helper to define a delimited list of expressions - the delimiter - defaults to ','. By default, the list elements and delimiters can - have intervening whitespace, and comments, but this can be - overridden by passing ``combine=True`` in the constructor. If - ``combine`` is set to ``True``, the matching tokens are - returned as a single token string, with the delimiters included; - otherwise, the matching tokens are returned as a list of tokens, - with the delimiters suppressed. - - If ``allow_trailing_delim`` is set to True, then the list may end with - a delimiter. - - Example:: - - delimited_list(Word(alphas)).parse_string("aa,bb,cc") # -> ['aa', 'bb', 'cc'] - delimited_list(Word(hexnums), delim=':', combine=True).parse_string("AA:BB:CC:DD:EE") # -> ['AA:BB:CC:DD:EE'] - """ - if isinstance(expr, str_type): - expr = ParserElement._literalStringClass(expr) - - dlName = "{expr} [{delim} {expr}]...{end}".format( - expr=str(expr.copy().streamline()), - delim=str(delim), - end=" [{}]".format(str(delim)) if allow_trailing_delim else "", - ) - - if not combine: - delim = Suppress(delim) - - if min is not None: - if min < 1: - raise ValueError("min must be greater than 0") - min -= 1 - if max is not None: - if min is not None and max <= min: - raise ValueError("max must be greater than, or equal to min") - max -= 1 - delimited_list_expr = expr + (delim + expr)[min, max] - - if allow_trailing_delim: - delimited_list_expr += Opt(delim) - - if combine: - return Combine(delimited_list_expr).set_name(dlName) - else: - return delimited_list_expr.set_name(dlName) - - -def counted_array( - expr: ParserElement, - int_expr: typing.Optional[ParserElement] = None, - *, - intExpr: typing.Optional[ParserElement] = None, -) -> ParserElement: - """Helper to define a counted list of expressions. - - This helper defines a pattern of the form:: - - integer expr expr expr... - - where the leading integer tells how many expr expressions follow. - The matched tokens returns the array of expr tokens as a list - the - leading count token is suppressed. - - If ``int_expr`` is specified, it should be a pyparsing expression - that produces an integer value. - - Example:: - - counted_array(Word(alphas)).parse_string('2 ab cd ef') # -> ['ab', 'cd'] - - # in this parser, the leading integer value is given in binary, - # '10' indicating that 2 values are in the array - binary_constant = Word('01').set_parse_action(lambda t: int(t[0], 2)) - counted_array(Word(alphas), int_expr=binary_constant).parse_string('10 ab cd ef') # -> ['ab', 'cd'] - - # if other fields must be parsed after the count but before the - # list items, give the fields results names and they will - # be preserved in the returned ParseResults: - count_with_metadata = integer + Word(alphas)("type") - typed_array = counted_array(Word(alphanums), int_expr=count_with_metadata)("items") - result = typed_array.parse_string("3 bool True True False") - print(result.dump()) - - # prints - # ['True', 'True', 'False'] - # - items: ['True', 'True', 'False'] - # - type: 'bool' - """ - intExpr = intExpr or int_expr - array_expr = Forward() - - def count_field_parse_action(s, l, t): - nonlocal array_expr - n = t[0] - array_expr <<= (expr * n) if n else Empty() - # clear list contents, but keep any named results - del t[:] - - if intExpr is None: - intExpr = Word(nums).set_parse_action(lambda t: int(t[0])) - else: - intExpr = intExpr.copy() - intExpr.set_name("arrayLen") - intExpr.add_parse_action(count_field_parse_action, call_during_try=True) - return (intExpr + array_expr).set_name("(len) " + str(expr) + "...") - - -def match_previous_literal(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_literal(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches a previous literal, will also match the leading - ``"1:1"`` in ``"1:10"``. If this is not desired, use - :class:`match_previous_expr`. Do *not* use with packrat parsing - enabled. - """ - rep = Forward() - - def copy_token_to_repeater(s, l, t): - if t: - if len(t) == 1: - rep << t[0] - else: - # flatten t tokens - tflat = _flatten(t.as_list()) - rep << And(Literal(tt) for tt in tflat) - else: - rep << Empty() - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def match_previous_expr(expr: ParserElement) -> ParserElement: - """Helper to define an expression that is indirectly defined from - the tokens matched in a previous expression, that is, it looks for - a 'repeat' of a previous expression. For example:: - - first = Word(nums) - second = match_previous_expr(first) - match_expr = first + ":" + second - - will match ``"1:1"``, but not ``"1:2"``. Because this - matches by expressions, will *not* match the leading ``"1:1"`` - in ``"1:10"``; the expressions are evaluated first, and then - compared, so ``"1"`` is compared with ``"10"``. Do *not* use - with packrat parsing enabled. - """ - rep = Forward() - e2 = expr.copy() - rep <<= e2 - - def copy_token_to_repeater(s, l, t): - matchTokens = _flatten(t.as_list()) - - def must_match_these_tokens(s, l, t): - theseTokens = _flatten(t.as_list()) - if theseTokens != matchTokens: - raise ParseException( - s, l, "Expected {}, found{}".format(matchTokens, theseTokens) - ) - - rep.set_parse_action(must_match_these_tokens, callDuringTry=True) - - expr.add_parse_action(copy_token_to_repeater, callDuringTry=True) - rep.set_name("(prev) " + str(expr)) - return rep - - -def one_of( - strs: Union[typing.Iterable[str], str], - caseless: bool = False, - use_regex: bool = True, - as_keyword: bool = False, - *, - useRegex: bool = True, - asKeyword: bool = False, -) -> ParserElement: - """Helper to quickly define a set of alternative :class:`Literal` s, - and makes sure to do longest-first testing when there is a conflict, - regardless of the input order, but returns - a :class:`MatchFirst` for best performance. - - Parameters: - - - ``strs`` - a string of space-delimited literals, or a collection of - string literals - - ``caseless`` - treat all literals as caseless - (default= ``False``) - - ``use_regex`` - as an optimization, will - generate a :class:`Regex` object; otherwise, will generate - a :class:`MatchFirst` object (if ``caseless=True`` or ``asKeyword=True``, or if - creating a :class:`Regex` raises an exception) - (default= ``True``) - - ``as_keyword`` - enforce :class:`Keyword`-style matching on the - generated expressions - (default= ``False``) - - ``asKeyword`` and ``useRegex`` are retained for pre-PEP8 compatibility, - but will be removed in a future release - - Example:: - - comp_oper = one_of("< = > <= >= !=") - var = Word(alphas) - number = Word(nums) - term = var | number - comparison_expr = term + comp_oper + term - print(comparison_expr.search_string("B = 12 AA=23 B<=AA AA>12")) - - prints:: - - [['B', '=', '12'], ['AA', '=', '23'], ['B', '<=', 'AA'], ['AA', '>', '12']] - """ - asKeyword = asKeyword or as_keyword - useRegex = useRegex and use_regex - - if ( - isinstance(caseless, str_type) - and __diag__.warn_on_multiple_string_args_to_oneof - ): - warnings.warn( - "More than one string argument passed to one_of, pass" - " choices as a list or space-delimited string", - stacklevel=2, - ) - - if caseless: - isequal = lambda a, b: a.upper() == b.upper() - masks = lambda a, b: b.upper().startswith(a.upper()) - parseElementClass = CaselessKeyword if asKeyword else CaselessLiteral - else: - isequal = lambda a, b: a == b - masks = lambda a, b: b.startswith(a) - parseElementClass = Keyword if asKeyword else Literal - - symbols: List[str] = [] - if isinstance(strs, str_type): - symbols = strs.split() - elif isinstance(strs, Iterable): - symbols = list(strs) - else: - raise TypeError("Invalid argument to one_of, expected string or iterable") - if not symbols: - return NoMatch() - - # reorder given symbols to take care to avoid masking longer choices with shorter ones - # (but only if the given symbols are not just single characters) - if any(len(sym) > 1 for sym in symbols): - i = 0 - while i < len(symbols) - 1: - cur = symbols[i] - for j, other in enumerate(symbols[i + 1 :]): - if isequal(other, cur): - del symbols[i + j + 1] - break - elif masks(cur, other): - del symbols[i + j + 1] - symbols.insert(i, other) - break - else: - i += 1 - - if useRegex: - re_flags: int = re.IGNORECASE if caseless else 0 - - try: - if all(len(sym) == 1 for sym in symbols): - # symbols are just single characters, create range regex pattern - patt = "[{}]".format( - "".join(_escape_regex_range_chars(sym) for sym in symbols) - ) - else: - patt = "|".join(re.escape(sym) for sym in symbols) - - # wrap with \b word break markers if defining as keywords - if asKeyword: - patt = r"\b(?:{})\b".format(patt) - - ret = Regex(patt, flags=re_flags).set_name(" | ".join(symbols)) - - if caseless: - # add parse action to return symbols as specified, not in random - # casing as found in input string - symbol_map = {sym.lower(): sym for sym in symbols} - ret.add_parse_action(lambda s, l, t: symbol_map[t[0].lower()]) - - return ret - - except re.error: - warnings.warn( - "Exception creating Regex for one_of, building MatchFirst", stacklevel=2 - ) - - # last resort, just use MatchFirst - return MatchFirst(parseElementClass(sym) for sym in symbols).set_name( - " | ".join(symbols) - ) - - -def dict_of(key: ParserElement, value: ParserElement) -> ParserElement: - """Helper to easily and clearly define a dictionary by specifying - the respective patterns for the key and value. Takes care of - defining the :class:`Dict`, :class:`ZeroOrMore`, and - :class:`Group` tokens in the proper order. The key pattern - can include delimiting markers or punctuation, as long as they are - suppressed, thereby leaving the significant key text. The value - pattern can include named results, so that the :class:`Dict` results - can include named token fields. - - Example:: - - text = "shape: SQUARE posn: upper left color: light blue texture: burlap" - attr_expr = (label + Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join)) - print(attr_expr[1, ...].parse_string(text).dump()) - - attr_label = label - attr_value = Suppress(':') + OneOrMore(data_word, stop_on=label).set_parse_action(' '.join) - - # similar to Dict, but simpler call format - result = dict_of(attr_label, attr_value).parse_string(text) - print(result.dump()) - print(result['shape']) - print(result.shape) # object attribute access works too - print(result.as_dict()) - - prints:: - - [['shape', 'SQUARE'], ['posn', 'upper left'], ['color', 'light blue'], ['texture', 'burlap']] - - color: 'light blue' - - posn: 'upper left' - - shape: 'SQUARE' - - texture: 'burlap' - SQUARE - SQUARE - {'color': 'light blue', 'shape': 'SQUARE', 'posn': 'upper left', 'texture': 'burlap'} - """ - return Dict(OneOrMore(Group(key + value))) - - -def original_text_for( - expr: ParserElement, as_string: bool = True, *, asString: bool = True -) -> ParserElement: - """Helper to return the original, untokenized text for a given - expression. Useful to restore the parsed fields of an HTML start - tag into the raw tag text itself, or to revert separate tokens with - intervening whitespace back to the original matching input text. By - default, returns astring containing the original parsed text. - - If the optional ``as_string`` argument is passed as - ``False``, then the return value is - a :class:`ParseResults` containing any results names that - were originally matched, and a single token containing the original - matched text from the input string. So if the expression passed to - :class:`original_text_for` contains expressions with defined - results names, you must set ``as_string`` to ``False`` if you - want to preserve those results name values. - - The ``asString`` pre-PEP8 argument is retained for compatibility, - but will be removed in a future release. - - Example:: - - src = "this is test bold text normal text " - for tag in ("b", "i"): - opener, closer = make_html_tags(tag) - patt = original_text_for(opener + SkipTo(closer) + closer) - print(patt.search_string(src)[0]) - - prints:: - - [' bold text '] - ['text'] - """ - asString = asString and as_string - - locMarker = Empty().set_parse_action(lambda s, loc, t: loc) - endlocMarker = locMarker.copy() - endlocMarker.callPreparse = False - matchExpr = locMarker("_original_start") + expr + endlocMarker("_original_end") - if asString: - extractText = lambda s, l, t: s[t._original_start : t._original_end] - else: - - def extractText(s, l, t): - t[:] = [s[t.pop("_original_start") : t.pop("_original_end")]] - - matchExpr.set_parse_action(extractText) - matchExpr.ignoreExprs = expr.ignoreExprs - matchExpr.suppress_warning(Diagnostics.warn_ungrouped_named_tokens_in_collection) - return matchExpr - - -def ungroup(expr: ParserElement) -> ParserElement: - """Helper to undo pyparsing's default grouping of And expressions, - even if all but one are non-empty. - """ - return TokenConverter(expr).add_parse_action(lambda t: t[0]) - - -def locatedExpr(expr: ParserElement) -> ParserElement: - """ - (DEPRECATED - future code should use the Located class) - Helper to decorate a returned token with its starting and ending - locations in the input string. - - This helper adds the following results names: - - - ``locn_start`` - location where matched expression begins - - ``locn_end`` - location where matched expression ends - - ``value`` - the actual parsed results - - Be careful if the input text contains ```` characters, you - may want to call :class:`ParserElement.parseWithTabs` - - Example:: - - wd = Word(alphas) - for match in locatedExpr(wd).searchString("ljsdf123lksdjjf123lkkjj1222"): - print(match) - - prints:: - - [[0, 'ljsdf', 5]] - [[8, 'lksdjjf', 15]] - [[18, 'lkkjj', 23]] - """ - locator = Empty().set_parse_action(lambda ss, ll, tt: ll) - return Group( - locator("locn_start") - + expr("value") - + locator.copy().leaveWhitespace()("locn_end") - ) - - -def nested_expr( - opener: Union[str, ParserElement] = "(", - closer: Union[str, ParserElement] = ")", - content: typing.Optional[ParserElement] = None, - ignore_expr: ParserElement = quoted_string(), - *, - ignoreExpr: ParserElement = quoted_string(), -) -> ParserElement: - """Helper method for defining nested lists enclosed in opening and - closing delimiters (``"("`` and ``")"`` are the default). - - Parameters: - - ``opener`` - opening character for a nested list - (default= ``"("``); can also be a pyparsing expression - - ``closer`` - closing character for a nested list - (default= ``")"``); can also be a pyparsing expression - - ``content`` - expression for items within the nested lists - (default= ``None``) - - ``ignore_expr`` - expression for ignoring opening and closing delimiters - (default= :class:`quoted_string`) - - ``ignoreExpr`` - this pre-PEP8 argument is retained for compatibility - but will be removed in a future release - - If an expression is not provided for the content argument, the - nested expression will capture all whitespace-delimited content - between delimiters as a list of separate values. - - Use the ``ignore_expr`` argument to define expressions that may - contain opening or closing characters that should not be treated as - opening or closing characters for nesting, such as quoted_string or - a comment expression. Specify multiple expressions using an - :class:`Or` or :class:`MatchFirst`. The default is - :class:`quoted_string`, but if no expressions are to be ignored, then - pass ``None`` for this argument. - - Example:: - - data_type = one_of("void int short long char float double") - decl_data_type = Combine(data_type + Opt(Word('*'))) - ident = Word(alphas+'_', alphanums+'_') - number = pyparsing_common.number - arg = Group(decl_data_type + ident) - LPAR, RPAR = map(Suppress, "()") - - code_body = nested_expr('{', '}', ignore_expr=(quoted_string | c_style_comment)) - - c_function = (decl_data_type("type") - + ident("name") - + LPAR + Opt(delimited_list(arg), [])("args") + RPAR - + code_body("body")) - c_function.ignore(c_style_comment) - - source_code = ''' - int is_odd(int x) { - return (x%2); - } - - int dec_to_hex(char hchar) { - if (hchar >= '0' && hchar <= '9') { - return (ord(hchar)-ord('0')); - } else { - return (10+ord(hchar)-ord('A')); - } - } - ''' - for func in c_function.search_string(source_code): - print("%(name)s (%(type)s) args: %(args)s" % func) - - - prints:: - - is_odd (int) args: [['int', 'x']] - dec_to_hex (int) args: [['char', 'hchar']] - """ - if ignoreExpr != ignore_expr: - ignoreExpr = ignore_expr if ignoreExpr == quoted_string() else ignoreExpr - if opener == closer: - raise ValueError("opening and closing strings cannot be the same") - if content is None: - if isinstance(opener, str_type) and isinstance(closer, str_type): - if len(opener) == 1 and len(closer) == 1: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS, - exact=1, - ) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = empty.copy() + CharsNotIn( - opener + closer + ParserElement.DEFAULT_WHITE_CHARS - ).set_parse_action(lambda t: t[0].strip()) - else: - if ignoreExpr is not None: - content = Combine( - OneOrMore( - ~ignoreExpr - + ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - content = Combine( - OneOrMore( - ~Literal(opener) - + ~Literal(closer) - + CharsNotIn(ParserElement.DEFAULT_WHITE_CHARS, exact=1) - ) - ).set_parse_action(lambda t: t[0].strip()) - else: - raise ValueError( - "opening and closing arguments must be strings if no content expression is given" - ) - ret = Forward() - if ignoreExpr is not None: - ret <<= Group( - Suppress(opener) + ZeroOrMore(ignoreExpr | ret | content) + Suppress(closer) - ) - else: - ret <<= Group(Suppress(opener) + ZeroOrMore(ret | content) + Suppress(closer)) - ret.set_name("nested %s%s expression" % (opener, closer)) - return ret - - -def _makeTags(tagStr, xml, suppress_LT=Suppress("<"), suppress_GT=Suppress(">")): - """Internal helper to construct opening and closing tag expressions, given a tag name""" - if isinstance(tagStr, str_type): - resname = tagStr - tagStr = Keyword(tagStr, caseless=not xml) - else: - resname = tagStr.name - - tagAttrName = Word(alphas, alphanums + "_-:") - if xml: - tagAttrValue = dbl_quoted_string.copy().set_parse_action(remove_quotes) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict(ZeroOrMore(Group(tagAttrName + Suppress("=") + tagAttrValue))) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - else: - tagAttrValue = quoted_string.copy().set_parse_action(remove_quotes) | Word( - printables, exclude_chars=">" - ) - openTag = ( - suppress_LT - + tagStr("tag") - + Dict( - ZeroOrMore( - Group( - tagAttrName.set_parse_action(lambda t: t[0].lower()) - + Opt(Suppress("=") + tagAttrValue) - ) - ) - ) - + Opt("/", default=[False])("empty").set_parse_action( - lambda s, l, t: t[0] == "/" - ) - + suppress_GT - ) - closeTag = Combine(Literal("", adjacent=False) - - openTag.set_name("<%s>" % resname) - # add start results name in parse action now that ungrouped names are not reported at two levels - openTag.add_parse_action( - lambda t: t.__setitem__( - "start" + "".join(resname.replace(":", " ").title().split()), t.copy() - ) - ) - closeTag = closeTag( - "end" + "".join(resname.replace(":", " ").title().split()) - ).set_name("" % resname) - openTag.tag = resname - closeTag.tag = resname - openTag.tag_body = SkipTo(closeTag()) - return openTag, closeTag - - -def make_html_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for HTML, - given a tag name. Matches tags in either upper or lower case, - attributes with namespaces and with quoted or unquoted values. - - Example:: - - text = 'More info at the pyparsing wiki page' - # make_html_tags returns pyparsing expressions for the opening and - # closing tags as a 2-tuple - a, a_end = make_html_tags("A") - link_expr = a + SkipTo(a_end)("link_text") + a_end - - for link in link_expr.search_string(text): - # attributes in the tag (like "href" shown here) are - # also accessible as named results - print(link.link_text, '->', link.href) - - prints:: - - pyparsing -> https://github.com/pyparsing/pyparsing/wiki - """ - return _makeTags(tag_str, False) - - -def make_xml_tags( - tag_str: Union[str, ParserElement] -) -> Tuple[ParserElement, ParserElement]: - """Helper to construct opening and closing tag expressions for XML, - given a tag name. Matches tags only in the given upper/lower case. - - Example: similar to :class:`make_html_tags` - """ - return _makeTags(tag_str, True) - - -any_open_tag: ParserElement -any_close_tag: ParserElement -any_open_tag, any_close_tag = make_html_tags( - Word(alphas, alphanums + "_:").set_name("any tag") -) - -_htmlEntityMap = {k.rstrip(";"): v for k, v in html.entities.html5.items()} -common_html_entity = Regex("&(?P" + "|".join(_htmlEntityMap) + ");").set_name( - "common HTML entity" -) - - -def replace_html_entity(t): - """Helper parser action to replace common HTML entities with their special characters""" - return _htmlEntityMap.get(t.entity) - - -class OpAssoc(Enum): - LEFT = 1 - RIGHT = 2 - - -InfixNotationOperatorArgType = Union[ - ParserElement, str, Tuple[Union[ParserElement, str], Union[ParserElement, str]] -] -InfixNotationOperatorSpec = Union[ - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - typing.Optional[ParseAction], - ], - Tuple[ - InfixNotationOperatorArgType, - int, - OpAssoc, - ], -] - - -def infix_notation( - base_expr: ParserElement, - op_list: List[InfixNotationOperatorSpec], - lpar: Union[str, ParserElement] = Suppress("("), - rpar: Union[str, ParserElement] = Suppress(")"), -) -> ParserElement: - """Helper method for constructing grammars of expressions made up of - operators working in a precedence hierarchy. Operators may be unary - or binary, left- or right-associative. Parse actions can also be - attached to operator expressions. The generated parser will also - recognize the use of parentheses to override operator precedences - (see example below). - - Note: if you define a deep operator list, you may see performance - issues when using infix_notation. See - :class:`ParserElement.enable_packrat` for a mechanism to potentially - improve your parser performance. - - Parameters: - - ``base_expr`` - expression representing the most basic operand to - be used in the expression - - ``op_list`` - list of tuples, one for each operator precedence level - in the expression grammar; each tuple is of the form ``(op_expr, - num_operands, right_left_assoc, (optional)parse_action)``, where: - - - ``op_expr`` is the pyparsing expression for the operator; may also - be a string, which will be converted to a Literal; if ``num_operands`` - is 3, ``op_expr`` is a tuple of two expressions, for the two - operators separating the 3 terms - - ``num_operands`` is the number of terms for this operator (must be 1, - 2, or 3) - - ``right_left_assoc`` is the indicator whether the operator is right - or left associative, using the pyparsing-defined constants - ``OpAssoc.RIGHT`` and ``OpAssoc.LEFT``. - - ``parse_action`` is the parse action to be associated with - expressions matching this operator expression (the parse action - tuple member may be omitted); if the parse action is passed - a tuple or list of functions, this is equivalent to calling - ``set_parse_action(*fn)`` - (:class:`ParserElement.set_parse_action`) - - ``lpar`` - expression for matching left-parentheses; if passed as a - str, then will be parsed as Suppress(lpar). If lpar is passed as - an expression (such as ``Literal('(')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress('(')``) - - ``rpar`` - expression for matching right-parentheses; if passed as a - str, then will be parsed as Suppress(rpar). If rpar is passed as - an expression (such as ``Literal(')')``), then it will be kept in - the parsed results, and grouped with them. (default= ``Suppress(')')``) - - Example:: - - # simple example of four-function arithmetic with ints and - # variable names - integer = pyparsing_common.signed_integer - varname = pyparsing_common.identifier - - arith_expr = infix_notation(integer | varname, - [ - ('-', 1, OpAssoc.RIGHT), - (one_of('* /'), 2, OpAssoc.LEFT), - (one_of('+ -'), 2, OpAssoc.LEFT), - ]) - - arith_expr.run_tests(''' - 5+3*6 - (5+3)*6 - -2--11 - ''', full_dump=False) - - prints:: - - 5+3*6 - [[5, '+', [3, '*', 6]]] - - (5+3)*6 - [[[5, '+', 3], '*', 6]] - - -2--11 - [[['-', 2], '-', ['-', 11]]] - """ - # captive version of FollowedBy that does not do parse actions or capture results names - class _FB(FollowedBy): - def parseImpl(self, instring, loc, doActions=True): - self.expr.try_parse(instring, loc) - return loc, [] - - _FB.__name__ = "FollowedBy>" - - ret = Forward() - if isinstance(lpar, str): - lpar = Suppress(lpar) - if isinstance(rpar, str): - rpar = Suppress(rpar) - - # if lpar and rpar are not suppressed, wrap in group - if not (isinstance(rpar, Suppress) and isinstance(rpar, Suppress)): - lastExpr = base_expr | Group(lpar + ret + rpar) - else: - lastExpr = base_expr | (lpar + ret + rpar) - - for i, operDef in enumerate(op_list): - opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4] - if isinstance(opExpr, str_type): - opExpr = ParserElement._literalStringClass(opExpr) - if arity == 3: - if not isinstance(opExpr, (tuple, list)) or len(opExpr) != 2: - raise ValueError( - "if numterms=3, opExpr must be a tuple or list of two expressions" - ) - opExpr1, opExpr2 = opExpr - term_name = "{}{} term".format(opExpr1, opExpr2) - else: - term_name = "{} term".format(opExpr) - - if not 1 <= arity <= 3: - raise ValueError("operator must be unary (1), binary (2), or ternary (3)") - - if rightLeftAssoc not in (OpAssoc.LEFT, OpAssoc.RIGHT): - raise ValueError("operator must indicate right or left associativity") - - thisExpr: Forward = Forward().set_name(term_name) - if rightLeftAssoc is OpAssoc.LEFT: - if arity == 1: - matchExpr = _FB(lastExpr + opExpr) + Group(lastExpr + opExpr[1, ...]) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + lastExpr) + Group( - lastExpr + (opExpr + lastExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + lastExpr) + Group(lastExpr[2, ...]) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr - ) + Group(lastExpr + OneOrMore(opExpr1 + lastExpr + opExpr2 + lastExpr)) - elif rightLeftAssoc is OpAssoc.RIGHT: - if arity == 1: - # try to avoid LR with this extra test - if not isinstance(opExpr, Opt): - opExpr = Opt(opExpr) - matchExpr = _FB(opExpr.expr + thisExpr) + Group(opExpr + thisExpr) - elif arity == 2: - if opExpr is not None: - matchExpr = _FB(lastExpr + opExpr + thisExpr) + Group( - lastExpr + (opExpr + thisExpr)[1, ...] - ) - else: - matchExpr = _FB(lastExpr + thisExpr) + Group( - lastExpr + thisExpr[1, ...] - ) - elif arity == 3: - matchExpr = _FB( - lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr - ) + Group(lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) - if pa: - if isinstance(pa, (tuple, list)): - matchExpr.set_parse_action(*pa) - else: - matchExpr.set_parse_action(pa) - thisExpr <<= (matchExpr | lastExpr).setName(term_name) - lastExpr = thisExpr - ret <<= lastExpr - return ret - - -def indentedBlock(blockStatementExpr, indentStack, indent=True, backup_stacks=[]): - """ - (DEPRECATED - use IndentedBlock class instead) - Helper method for defining space-delimited indentation blocks, - such as those used to define block statements in Python source code. - - Parameters: - - - ``blockStatementExpr`` - expression defining syntax of statement that - is repeated within the indented block - - ``indentStack`` - list created by caller to manage indentation stack - (multiple ``statementWithIndentedBlock`` expressions within a single - grammar should share a common ``indentStack``) - - ``indent`` - boolean indicating whether block must be indented beyond - the current level; set to ``False`` for block of left-most statements - (default= ``True``) - - A valid block must contain at least one ``blockStatement``. - - (Note that indentedBlock uses internal parse actions which make it - incompatible with packrat parsing.) - - Example:: - - data = ''' - def A(z): - A1 - B = 100 - G = A2 - A2 - A3 - B - def BB(a,b,c): - BB1 - def BBA(): - bba1 - bba2 - bba3 - C - D - def spam(x,y): - def eggs(z): - pass - ''' - - - indentStack = [1] - stmt = Forward() - - identifier = Word(alphas, alphanums) - funcDecl = ("def" + identifier + Group("(" + Opt(delimitedList(identifier)) + ")") + ":") - func_body = indentedBlock(stmt, indentStack) - funcDef = Group(funcDecl + func_body) - - rvalue = Forward() - funcCall = Group(identifier + "(" + Opt(delimitedList(rvalue)) + ")") - rvalue << (funcCall | identifier | Word(nums)) - assignment = Group(identifier + "=" + rvalue) - stmt << (funcDef | assignment | identifier) - - module_body = stmt[1, ...] - - parseTree = module_body.parseString(data) - parseTree.pprint() - - prints:: - - [['def', - 'A', - ['(', 'z', ')'], - ':', - [['A1'], [['B', '=', '100']], [['G', '=', 'A2']], ['A2'], ['A3']]], - 'B', - ['def', - 'BB', - ['(', 'a', 'b', 'c', ')'], - ':', - [['BB1'], [['def', 'BBA', ['(', ')'], ':', [['bba1'], ['bba2'], ['bba3']]]]]], - 'C', - 'D', - ['def', - 'spam', - ['(', 'x', 'y', ')'], - ':', - [[['def', 'eggs', ['(', 'z', ')'], ':', [['pass']]]]]]] - """ - backup_stacks.append(indentStack[:]) - - def reset_stack(): - indentStack[:] = backup_stacks[-1] - - def checkPeerIndent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if curCol != indentStack[-1]: - if curCol > indentStack[-1]: - raise ParseException(s, l, "illegal nesting") - raise ParseException(s, l, "not a peer entry") - - def checkSubIndent(s, l, t): - curCol = col(l, s) - if curCol > indentStack[-1]: - indentStack.append(curCol) - else: - raise ParseException(s, l, "not a subentry") - - def checkUnindent(s, l, t): - if l >= len(s): - return - curCol = col(l, s) - if not (indentStack and curCol in indentStack): - raise ParseException(s, l, "not an unindent") - if curCol < indentStack[-1]: - indentStack.pop() - - NL = OneOrMore(LineEnd().set_whitespace_chars("\t ").suppress()) - INDENT = (Empty() + Empty().set_parse_action(checkSubIndent)).set_name("INDENT") - PEER = Empty().set_parse_action(checkPeerIndent).set_name("") - UNDENT = Empty().set_parse_action(checkUnindent).set_name("UNINDENT") - if indent: - smExpr = Group( - Opt(NL) - + INDENT - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + UNDENT - ) - else: - smExpr = Group( - Opt(NL) - + OneOrMore(PEER + Group(blockStatementExpr) + Opt(NL)) - + Opt(UNDENT) - ) - - # add a parse action to remove backup_stack from list of backups - smExpr.add_parse_action( - lambda: backup_stacks.pop(-1) and None if backup_stacks else None - ) - smExpr.set_fail_action(lambda a, b, c, d: reset_stack()) - blockStatementExpr.ignore(_bslash + LineEnd()) - return smExpr.set_name("indented block") - - -# it's easy to get these comment structures wrong - they're very common, so may as well make them available -c_style_comment = Combine(Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/").set_name( - "C style comment" -) -"Comment of the form ``/* ... */``" - -html_comment = Regex(r"").set_name("HTML comment") -"Comment of the form ````" - -rest_of_line = Regex(r".*").leave_whitespace().set_name("rest of line") -dbl_slash_comment = Regex(r"//(?:\\\n|[^\n])*").set_name("// comment") -"Comment of the form ``// ... (to end of line)``" - -cpp_style_comment = Combine( - Regex(r"/\*(?:[^*]|\*(?!/))*") + "*/" | dbl_slash_comment -).set_name("C++ style comment") -"Comment of either form :class:`c_style_comment` or :class:`dbl_slash_comment`" - -java_style_comment = cpp_style_comment -"Same as :class:`cpp_style_comment`" - -python_style_comment = Regex(r"#.*").set_name("Python style comment") -"Comment of the form ``# ... (to end of line)``" - - -# build list of built-in expressions, for future reference if a global default value -# gets updated -_builtin_exprs: List[ParserElement] = [ - v for v in vars().values() if isinstance(v, ParserElement) -] - - -# pre-PEP8 compatible names -delimitedList = delimited_list -countedArray = counted_array -matchPreviousLiteral = match_previous_literal -matchPreviousExpr = match_previous_expr -oneOf = one_of -dictOf = dict_of -originalTextFor = original_text_for -nestedExpr = nested_expr -makeHTMLTags = make_html_tags -makeXMLTags = make_xml_tags -anyOpenTag, anyCloseTag = any_open_tag, any_close_tag -commonHTMLEntity = common_html_entity -replaceHTMLEntity = replace_html_entity -opAssoc = OpAssoc -infixNotation = infix_notation -cStyleComment = c_style_comment -htmlComment = html_comment -restOfLine = rest_of_line -dblSlashComment = dbl_slash_comment -cppStyleComment = cpp_style_comment -javaStyleComment = java_style_comment -pythonStyleComment = python_style_comment diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/results.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/results.py deleted file mode 100644 index 00c9421d3..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/results.py +++ /dev/null @@ -1,760 +0,0 @@ -# results.py -from collections.abc import MutableMapping, Mapping, MutableSequence, Iterator -import pprint -from weakref import ref as wkref -from typing import Tuple, Any - -str_type: Tuple[type, ...] = (str, bytes) -_generator_type = type((_ for _ in ())) - - -class _ParseResultsWithOffset: - __slots__ = ["tup"] - - def __init__(self, p1, p2): - self.tup = (p1, p2) - - def __getitem__(self, i): - return self.tup[i] - - def __getstate__(self): - return self.tup - - def __setstate__(self, *args): - self.tup = args[0] - - -class ParseResults: - """Structured parse results, to provide multiple means of access to - the parsed data: - - - as a list (``len(results)``) - - by list index (``results[0], results[1]``, etc.) - - by attribute (``results.`` - see :class:`ParserElement.set_results_name`) - - Example:: - - integer = Word(nums) - date_str = (integer.set_results_name("year") + '/' - + integer.set_results_name("month") + '/' - + integer.set_results_name("day")) - # equivalent form: - # date_str = (integer("year") + '/' - # + integer("month") + '/' - # + integer("day")) - - # parse_string returns a ParseResults object - result = date_str.parse_string("1999/12/31") - - def test(s, fn=repr): - print("{} -> {}".format(s, fn(eval(s)))) - test("list(result)") - test("result[0]") - test("result['month']") - test("result.day") - test("'month' in result") - test("'minutes' in result") - test("result.dump()", str) - - prints:: - - list(result) -> ['1999', '/', '12', '/', '31'] - result[0] -> '1999' - result['month'] -> '12' - result.day -> '31' - 'month' in result -> True - 'minutes' in result -> False - result.dump() -> ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - - _null_values: Tuple[Any, ...] = (None, [], "", ()) - - __slots__ = [ - "_name", - "_parent", - "_all_names", - "_modal", - "_toklist", - "_tokdict", - "__weakref__", - ] - - class List(list): - """ - Simple wrapper class to distinguish parsed list results that should be preserved - as actual Python lists, instead of being converted to :class:`ParseResults`: - - LBRACK, RBRACK = map(pp.Suppress, "[]") - element = pp.Forward() - item = ppc.integer - element_list = LBRACK + pp.delimited_list(element) + RBRACK - - # add parse actions to convert from ParseResults to actual Python collection types - def as_python_list(t): - return pp.ParseResults.List(t.as_list()) - element_list.add_parse_action(as_python_list) - - element <<= item | element_list - - element.run_tests(''' - 100 - [2,3,4] - [[2, 1],3,4] - [(2, 1),3,4] - (2,3,4) - ''', post_parse=lambda s, r: (r[0], type(r[0]))) - - prints: - - 100 - (100, ) - - [2,3,4] - ([2, 3, 4], ) - - [[2, 1],3,4] - ([[2, 1], 3, 4], ) - - (Used internally by :class:`Group` when `aslist=True`.) - """ - - def __new__(cls, contained=None): - if contained is None: - contained = [] - - if not isinstance(contained, list): - raise TypeError( - "{} may only be constructed with a list," - " not {}".format(cls.__name__, type(contained).__name__) - ) - - return list.__new__(cls) - - def __new__(cls, toklist=None, name=None, **kwargs): - if isinstance(toklist, ParseResults): - return toklist - self = object.__new__(cls) - self._name = None - self._parent = None - self._all_names = set() - - if toklist is None: - self._toklist = [] - elif isinstance(toklist, (list, _generator_type)): - self._toklist = ( - [toklist[:]] - if isinstance(toklist, ParseResults.List) - else list(toklist) - ) - else: - self._toklist = [toklist] - self._tokdict = dict() - return self - - # Performance tuning: we construct a *lot* of these, so keep this - # constructor as small and fast as possible - def __init__( - self, toklist=None, name=None, asList=True, modal=True, isinstance=isinstance - ): - self._modal = modal - if name is not None and name != "": - if isinstance(name, int): - name = str(name) - if not modal: - self._all_names = {name} - self._name = name - if toklist not in self._null_values: - if isinstance(toklist, (str_type, type)): - toklist = [toklist] - if asList: - if isinstance(toklist, ParseResults): - self[name] = _ParseResultsWithOffset( - ParseResults(toklist._toklist), 0 - ) - else: - self[name] = _ParseResultsWithOffset( - ParseResults(toklist[0]), 0 - ) - self[name]._name = name - else: - try: - self[name] = toklist[0] - except (KeyError, TypeError, IndexError): - if toklist is not self: - self[name] = toklist - else: - self._name = name - - def __getitem__(self, i): - if isinstance(i, (int, slice)): - return self._toklist[i] - else: - if i not in self._all_names: - return self._tokdict[i][-1][0] - else: - return ParseResults([v[0] for v in self._tokdict[i]]) - - def __setitem__(self, k, v, isinstance=isinstance): - if isinstance(v, _ParseResultsWithOffset): - self._tokdict[k] = self._tokdict.get(k, list()) + [v] - sub = v[0] - elif isinstance(k, (int, slice)): - self._toklist[k] = v - sub = v - else: - self._tokdict[k] = self._tokdict.get(k, list()) + [ - _ParseResultsWithOffset(v, 0) - ] - sub = v - if isinstance(sub, ParseResults): - sub._parent = wkref(self) - - def __delitem__(self, i): - if isinstance(i, (int, slice)): - mylen = len(self._toklist) - del self._toklist[i] - - # convert int to slice - if isinstance(i, int): - if i < 0: - i += mylen - i = slice(i, i + 1) - # get removed indices - removed = list(range(*i.indices(mylen))) - removed.reverse() - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for j in removed: - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position - (position > j) - ) - else: - del self._tokdict[i] - - def __contains__(self, k) -> bool: - return k in self._tokdict - - def __len__(self) -> int: - return len(self._toklist) - - def __bool__(self) -> bool: - return not not (self._toklist or self._tokdict) - - def __iter__(self) -> Iterator: - return iter(self._toklist) - - def __reversed__(self) -> Iterator: - return iter(self._toklist[::-1]) - - def keys(self): - return iter(self._tokdict) - - def values(self): - return (self[k] for k in self.keys()) - - def items(self): - return ((k, self[k]) for k in self.keys()) - - def haskeys(self) -> bool: - """ - Since ``keys()`` returns an iterator, this method is helpful in bypassing - code that looks for the existence of any defined results names.""" - return bool(self._tokdict) - - def pop(self, *args, **kwargs): - """ - Removes and returns item at specified index (default= ``last``). - Supports both ``list`` and ``dict`` semantics for ``pop()``. If - passed no argument or an integer argument, it will use ``list`` - semantics and pop tokens from the list of parsed tokens. If passed - a non-integer argument (most likely a string), it will use ``dict`` - semantics and pop the corresponding value from any defined results - names. A second default return value argument is supported, just as in - ``dict.pop()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - def remove_first(tokens): - tokens.pop(0) - numlist.add_parse_action(remove_first) - print(numlist.parse_string("0 123 321")) # -> ['123', '321'] - - label = Word(alphas) - patt = label("LABEL") + Word(nums)[1, ...] - print(patt.parse_string("AAB 123 321").dump()) - - # Use pop() in a parse action to remove named result (note that corresponding value is not - # removed from list form of results) - def remove_LABEL(tokens): - tokens.pop("LABEL") - return tokens - patt.add_parse_action(remove_LABEL) - print(patt.parse_string("AAB 123 321").dump()) - - prints:: - - ['AAB', '123', '321'] - - LABEL: 'AAB' - - ['AAB', '123', '321'] - """ - if not args: - args = [-1] - for k, v in kwargs.items(): - if k == "default": - args = (args[0], v) - else: - raise TypeError( - "pop() got an unexpected keyword argument {!r}".format(k) - ) - if isinstance(args[0], int) or len(args) == 1 or args[0] in self: - index = args[0] - ret = self[index] - del self[index] - return ret - else: - defaultvalue = args[1] - return defaultvalue - - def get(self, key, default_value=None): - """ - Returns named result matching the given key, or if there is no - such name, then returns the given ``default_value`` or ``None`` if no - ``default_value`` is specified. - - Similar to ``dict.get()``. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string("1999/12/31") - print(result.get("year")) # -> '1999' - print(result.get("hour", "not specified")) # -> 'not specified' - print(result.get("hour")) # -> None - """ - if key in self: - return self[key] - else: - return default_value - - def insert(self, index, ins_string): - """ - Inserts new element at location index in the list of parsed tokens. - - Similar to ``list.insert()``. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to insert the parse location in the front of the parsed results - def insert_locn(locn, tokens): - tokens.insert(0, locn) - numlist.add_parse_action(insert_locn) - print(numlist.parse_string("0 123 321")) # -> [0, '0', '123', '321'] - """ - self._toklist.insert(index, ins_string) - # fixup indices in token dictionary - for name, occurrences in self._tokdict.items(): - for k, (value, position) in enumerate(occurrences): - occurrences[k] = _ParseResultsWithOffset( - value, position + (position > index) - ) - - def append(self, item): - """ - Add single element to end of ``ParseResults`` list of elements. - - Example:: - - numlist = Word(nums)[...] - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321'] - - # use a parse action to compute the sum of the parsed integers, and add it to the end - def append_sum(tokens): - tokens.append(sum(map(int, tokens))) - numlist.add_parse_action(append_sum) - print(numlist.parse_string("0 123 321")) # -> ['0', '123', '321', 444] - """ - self._toklist.append(item) - - def extend(self, itemseq): - """ - Add sequence of elements to end of ``ParseResults`` list of elements. - - Example:: - - patt = Word(alphas)[1, ...] - - # use a parse action to append the reverse of the matched strings, to make a palindrome - def make_palindrome(tokens): - tokens.extend(reversed([t[::-1] for t in tokens])) - return ''.join(tokens) - patt.add_parse_action(make_palindrome) - print(patt.parse_string("lskdj sdlkjf lksd")) # -> 'lskdjsdlkjflksddsklfjkldsjdksl' - """ - if isinstance(itemseq, ParseResults): - self.__iadd__(itemseq) - else: - self._toklist.extend(itemseq) - - def clear(self): - """ - Clear all elements and results names. - """ - del self._toklist[:] - self._tokdict.clear() - - def __getattr__(self, name): - try: - return self[name] - except KeyError: - if name.startswith("__"): - raise AttributeError(name) - return "" - - def __add__(self, other) -> "ParseResults": - ret = self.copy() - ret += other - return ret - - def __iadd__(self, other) -> "ParseResults": - if other._tokdict: - offset = len(self._toklist) - addoffset = lambda a: offset if a < 0 else a + offset - otheritems = other._tokdict.items() - otherdictitems = [ - (k, _ParseResultsWithOffset(v[0], addoffset(v[1]))) - for k, vlist in otheritems - for v in vlist - ] - for k, v in otherdictitems: - self[k] = v - if isinstance(v[0], ParseResults): - v[0]._parent = wkref(self) - - self._toklist += other._toklist - self._all_names |= other._all_names - return self - - def __radd__(self, other) -> "ParseResults": - if isinstance(other, int) and other == 0: - # useful for merging many ParseResults using sum() builtin - return self.copy() - else: - # this may raise a TypeError - so be it - return other + self - - def __repr__(self) -> str: - return "{}({!r}, {})".format(type(self).__name__, self._toklist, self.as_dict()) - - def __str__(self) -> str: - return ( - "[" - + ", ".join( - [ - str(i) if isinstance(i, ParseResults) else repr(i) - for i in self._toklist - ] - ) - + "]" - ) - - def _asStringList(self, sep=""): - out = [] - for item in self._toklist: - if out and sep: - out.append(sep) - if isinstance(item, ParseResults): - out += item._asStringList() - else: - out.append(str(item)) - return out - - def as_list(self) -> list: - """ - Returns the parse results as a nested list of matching tokens, all converted to strings. - - Example:: - - patt = Word(alphas)[1, ...] - result = patt.parse_string("sldkj lsdkj sldkj") - # even though the result prints in string-like form, it is actually a pyparsing ParseResults - print(type(result), result) # -> ['sldkj', 'lsdkj', 'sldkj'] - - # Use as_list() to create an actual list - result_list = result.as_list() - print(type(result_list), result_list) # -> ['sldkj', 'lsdkj', 'sldkj'] - """ - return [ - res.as_list() if isinstance(res, ParseResults) else res - for res in self._toklist - ] - - def as_dict(self) -> dict: - """ - Returns the named parse results as a nested dictionary. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('12/31/1999') - print(type(result), repr(result)) # -> (['12', '/', '31', '/', '1999'], {'day': [('1999', 4)], 'year': [('12', 0)], 'month': [('31', 2)]}) - - result_dict = result.as_dict() - print(type(result_dict), repr(result_dict)) # -> {'day': '1999', 'year': '12', 'month': '31'} - - # even though a ParseResults supports dict-like access, sometime you just need to have a dict - import json - print(json.dumps(result)) # -> Exception: TypeError: ... is not JSON serializable - print(json.dumps(result.as_dict())) # -> {"month": "31", "day": "1999", "year": "12"} - """ - - def to_item(obj): - if isinstance(obj, ParseResults): - return obj.as_dict() if obj.haskeys() else [to_item(v) for v in obj] - else: - return obj - - return dict((k, to_item(v)) for k, v in self.items()) - - def copy(self) -> "ParseResults": - """ - Returns a new copy of a :class:`ParseResults` object. - """ - ret = ParseResults(self._toklist) - ret._tokdict = self._tokdict.copy() - ret._parent = self._parent - ret._all_names |= self._all_names - ret._name = self._name - return ret - - def get_name(self): - r""" - Returns the results name for this token expression. Useful when several - different expressions might match at a particular location. - - Example:: - - integer = Word(nums) - ssn_expr = Regex(r"\d\d\d-\d\d-\d\d\d\d") - house_number_expr = Suppress('#') + Word(nums, alphanums) - user_data = (Group(house_number_expr)("house_number") - | Group(ssn_expr)("ssn") - | Group(integer)("age")) - user_info = user_data[1, ...] - - result = user_info.parse_string("22 111-22-3333 #221B") - for item in result: - print(item.get_name(), ':', item[0]) - - prints:: - - age : 22 - ssn : 111-22-3333 - house_number : 221B - """ - if self._name: - return self._name - elif self._parent: - par = self._parent() - - def find_in_parent(sub): - return next( - ( - k - for k, vlist in par._tokdict.items() - for v, loc in vlist - if sub is v - ), - None, - ) - - return find_in_parent(self) if par else None - elif ( - len(self) == 1 - and len(self._tokdict) == 1 - and next(iter(self._tokdict.values()))[0][1] in (0, -1) - ): - return next(iter(self._tokdict.keys())) - else: - return None - - def dump(self, indent="", full=True, include_list=True, _depth=0) -> str: - """ - Diagnostic method for listing out the contents of - a :class:`ParseResults`. Accepts an optional ``indent`` argument so - that this string can be embedded in a nested display of other data. - - Example:: - - integer = Word(nums) - date_str = integer("year") + '/' + integer("month") + '/' + integer("day") - - result = date_str.parse_string('1999/12/31') - print(result.dump()) - - prints:: - - ['1999', '/', '12', '/', '31'] - - day: '31' - - month: '12' - - year: '1999' - """ - out = [] - NL = "\n" - out.append(indent + str(self.as_list()) if include_list else "") - - if full: - if self.haskeys(): - items = sorted((str(k), v) for k, v in self.items()) - for k, v in items: - if out: - out.append(NL) - out.append("{}{}- {}: ".format(indent, (" " * _depth), k)) - if isinstance(v, ParseResults): - if v: - out.append( - v.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ) - ) - else: - out.append(str(v)) - else: - out.append(repr(v)) - if any(isinstance(vv, ParseResults) for vv in self): - v = self - for i, vv in enumerate(v): - if isinstance(vv, ParseResults): - out.append( - "\n{}{}[{}]:\n{}{}{}".format( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - vv.dump( - indent=indent, - full=full, - include_list=include_list, - _depth=_depth + 1, - ), - ) - ) - else: - out.append( - "\n%s%s[%d]:\n%s%s%s" - % ( - indent, - (" " * (_depth)), - i, - indent, - (" " * (_depth + 1)), - str(vv), - ) - ) - - return "".join(out) - - def pprint(self, *args, **kwargs): - """ - Pretty-printer for parsed results as a list, using the - `pprint `_ module. - Accepts additional positional or keyword args as defined for - `pprint.pprint `_ . - - Example:: - - ident = Word(alphas, alphanums) - num = Word(nums) - func = Forward() - term = ident | num | Group('(' + func + ')') - func <<= ident + Group(Optional(delimited_list(term))) - result = func.parse_string("fna a,b,(fnb c,d,200),100") - result.pprint(width=40) - - prints:: - - ['fna', - ['a', - 'b', - ['(', 'fnb', ['c', 'd', '200'], ')'], - '100']] - """ - pprint.pprint(self.as_list(), *args, **kwargs) - - # add support for pickle protocol - def __getstate__(self): - return ( - self._toklist, - ( - self._tokdict.copy(), - self._parent is not None and self._parent() or None, - self._all_names, - self._name, - ), - ) - - def __setstate__(self, state): - self._toklist, (self._tokdict, par, inAccumNames, self._name) = state - self._all_names = set(inAccumNames) - if par is not None: - self._parent = wkref(par) - else: - self._parent = None - - def __getnewargs__(self): - return self._toklist, self._name - - def __dir__(self): - return dir(type(self)) + list(self.keys()) - - @classmethod - def from_dict(cls, other, name=None) -> "ParseResults": - """ - Helper classmethod to construct a ``ParseResults`` from a ``dict``, preserving the - name-value relations as results names. If an optional ``name`` argument is - given, a nested ``ParseResults`` will be returned. - """ - - def is_iterable(obj): - try: - iter(obj) - except Exception: - return False - else: - return not isinstance(obj, str_type) - - ret = cls([]) - for k, v in other.items(): - if isinstance(v, Mapping): - ret += cls.from_dict(v, name=k) - else: - ret += cls([v], name=k, asList=is_iterable(v)) - if name is not None: - ret = cls([ret], name=name) - return ret - - asList = as_list - asDict = as_dict - getName = get_name - - -MutableMapping.register(ParseResults) -MutableSequence.register(ParseResults) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py deleted file mode 100644 index 84a0ef170..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/testing.py +++ /dev/null @@ -1,331 +0,0 @@ -# testing.py - -from contextlib import contextmanager -import typing - -from .core import ( - ParserElement, - ParseException, - Keyword, - __diag__, - __compat__, -) - - -class pyparsing_test: - """ - namespace class for classes useful in writing unit tests - """ - - class reset_pyparsing_context: - """ - Context manager to be used when writing unit tests that modify pyparsing config values: - - packrat parsing - - bounded recursion parsing - - default whitespace characters. - - default keyword characters - - literal string auto-conversion class - - __diag__ settings - - Example:: - - with reset_pyparsing_context(): - # test that literals used to construct a grammar are automatically suppressed - ParserElement.inlineLiteralsUsing(Suppress) - - term = Word(alphas) | Word(nums) - group = Group('(' + term[...] + ')') - - # assert that the '()' characters are not included in the parsed tokens - self.assertParseAndCheckList(group, "(abc 123 def)", ['abc', '123', 'def']) - - # after exiting context manager, literals are converted to Literal expressions again - """ - - def __init__(self): - self._save_context = {} - - def save(self): - self._save_context["default_whitespace"] = ParserElement.DEFAULT_WHITE_CHARS - self._save_context["default_keyword_chars"] = Keyword.DEFAULT_KEYWORD_CHARS - - self._save_context[ - "literal_string_class" - ] = ParserElement._literalStringClass - - self._save_context["verbose_stacktrace"] = ParserElement.verbose_stacktrace - - self._save_context["packrat_enabled"] = ParserElement._packratEnabled - if ParserElement._packratEnabled: - self._save_context[ - "packrat_cache_size" - ] = ParserElement.packrat_cache.size - else: - self._save_context["packrat_cache_size"] = None - self._save_context["packrat_parse"] = ParserElement._parse - self._save_context[ - "recursion_enabled" - ] = ParserElement._left_recursion_enabled - - self._save_context["__diag__"] = { - name: getattr(__diag__, name) for name in __diag__._all_names - } - - self._save_context["__compat__"] = { - "collect_all_And_tokens": __compat__.collect_all_And_tokens - } - - return self - - def restore(self): - # reset pyparsing global state - if ( - ParserElement.DEFAULT_WHITE_CHARS - != self._save_context["default_whitespace"] - ): - ParserElement.set_default_whitespace_chars( - self._save_context["default_whitespace"] - ) - - ParserElement.verbose_stacktrace = self._save_context["verbose_stacktrace"] - - Keyword.DEFAULT_KEYWORD_CHARS = self._save_context["default_keyword_chars"] - ParserElement.inlineLiteralsUsing( - self._save_context["literal_string_class"] - ) - - for name, value in self._save_context["__diag__"].items(): - (__diag__.enable if value else __diag__.disable)(name) - - ParserElement._packratEnabled = False - if self._save_context["packrat_enabled"]: - ParserElement.enable_packrat(self._save_context["packrat_cache_size"]) - else: - ParserElement._parse = self._save_context["packrat_parse"] - ParserElement._left_recursion_enabled = self._save_context[ - "recursion_enabled" - ] - - __compat__.collect_all_And_tokens = self._save_context["__compat__"] - - return self - - def copy(self): - ret = type(self)() - ret._save_context.update(self._save_context) - return ret - - def __enter__(self): - return self.save() - - def __exit__(self, *args): - self.restore() - - class TestParseResultsAsserts: - """ - A mixin class to add parse results assertion methods to normal unittest.TestCase classes. - """ - - def assertParseResultsEquals( - self, result, expected_list=None, expected_dict=None, msg=None - ): - """ - Unit test assertion to compare a :class:`ParseResults` object with an optional ``expected_list``, - and compare any defined results names with an optional ``expected_dict``. - """ - if expected_list is not None: - self.assertEqual(expected_list, result.as_list(), msg=msg) - if expected_dict is not None: - self.assertEqual(expected_dict, result.as_dict(), msg=msg) - - def assertParseAndCheckList( - self, expr, test_string, expected_list, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asList()`` is equal to the ``expected_list``. - """ - result = expr.parse_string(test_string, parse_all=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_list=expected_list, msg=msg) - - def assertParseAndCheckDict( - self, expr, test_string, expected_dict, msg=None, verbose=True - ): - """ - Convenience wrapper assert to test a parser element and input string, and assert that - the resulting ``ParseResults.asDict()`` is equal to the ``expected_dict``. - """ - result = expr.parse_string(test_string, parseAll=True) - if verbose: - print(result.dump()) - else: - print(result.as_list()) - self.assertParseResultsEquals(result, expected_dict=expected_dict, msg=msg) - - def assertRunTestResults( - self, run_tests_report, expected_parse_results=None, msg=None - ): - """ - Unit test assertion to evaluate output of ``ParserElement.runTests()``. If a list of - list-dict tuples is given as the ``expected_parse_results`` argument, then these are zipped - with the report tuples returned by ``runTests`` and evaluated using ``assertParseResultsEquals``. - Finally, asserts that the overall ``runTests()`` success value is ``True``. - - :param run_tests_report: tuple(bool, [tuple(str, ParseResults or Exception)]) returned from runTests - :param expected_parse_results (optional): [tuple(str, list, dict, Exception)] - """ - run_test_success, run_test_results = run_tests_report - - if expected_parse_results is not None: - merged = [ - (*rpt, expected) - for rpt, expected in zip(run_test_results, expected_parse_results) - ] - for test_string, result, expected in merged: - # expected should be a tuple containing a list and/or a dict or an exception, - # and optional failure message string - # an empty tuple will skip any result validation - fail_msg = next( - (exp for exp in expected if isinstance(exp, str)), None - ) - expected_exception = next( - ( - exp - for exp in expected - if isinstance(exp, type) and issubclass(exp, Exception) - ), - None, - ) - if expected_exception is not None: - with self.assertRaises( - expected_exception=expected_exception, msg=fail_msg or msg - ): - if isinstance(result, Exception): - raise result - else: - expected_list = next( - (exp for exp in expected if isinstance(exp, list)), None - ) - expected_dict = next( - (exp for exp in expected if isinstance(exp, dict)), None - ) - if (expected_list, expected_dict) != (None, None): - self.assertParseResultsEquals( - result, - expected_list=expected_list, - expected_dict=expected_dict, - msg=fail_msg or msg, - ) - else: - # warning here maybe? - print("no validation for {!r}".format(test_string)) - - # do this last, in case some specific test results can be reported instead - self.assertTrue( - run_test_success, msg=msg if msg is not None else "failed runTests" - ) - - @contextmanager - def assertRaisesParseException(self, exc_type=ParseException, msg=None): - with self.assertRaises(exc_type, msg=msg): - yield - - @staticmethod - def with_line_numbers( - s: str, - start_line: typing.Optional[int] = None, - end_line: typing.Optional[int] = None, - expand_tabs: bool = True, - eol_mark: str = "|", - mark_spaces: typing.Optional[str] = None, - mark_control: typing.Optional[str] = None, - ) -> str: - """ - Helpful method for debugging a parser - prints a string with line and column numbers. - (Line and column numbers are 1-based.) - - :param s: tuple(bool, str - string to be printed with line and column numbers - :param start_line: int - (optional) starting line number in s to print (default=1) - :param end_line: int - (optional) ending line number in s to print (default=len(s)) - :param expand_tabs: bool - (optional) expand tabs to spaces, to match the pyparsing default - :param eol_mark: str - (optional) string to mark the end of lines, helps visualize trailing spaces (default="|") - :param mark_spaces: str - (optional) special character to display in place of spaces - :param mark_control: str - (optional) convert non-printing control characters to a placeholding - character; valid values: - - "unicode" - replaces control chars with Unicode symbols, such as "␍" and "␊" - - any single character string - replace control characters with given string - - None (default) - string is displayed as-is - - :return: str - input string with leading line numbers and column number headers - """ - if expand_tabs: - s = s.expandtabs() - if mark_control is not None: - if mark_control == "unicode": - tbl = str.maketrans( - {c: u for c, u in zip(range(0, 33), range(0x2400, 0x2433))} - | {127: 0x2421} - ) - eol_mark = "" - else: - tbl = str.maketrans( - {c: mark_control for c in list(range(0, 32)) + [127]} - ) - s = s.translate(tbl) - if mark_spaces is not None and mark_spaces != " ": - if mark_spaces == "unicode": - tbl = str.maketrans({9: 0x2409, 32: 0x2423}) - s = s.translate(tbl) - else: - s = s.replace(" ", mark_spaces) - if start_line is None: - start_line = 1 - if end_line is None: - end_line = len(s) - end_line = min(end_line, len(s)) - start_line = min(max(1, start_line), end_line) - - if mark_control != "unicode": - s_lines = s.splitlines()[start_line - 1 : end_line] - else: - s_lines = [line + "␊" for line in s.split("␊")[start_line - 1 : end_line]] - if not s_lines: - return "" - - lineno_width = len(str(end_line)) - max_line_len = max(len(line) for line in s_lines) - lead = " " * (lineno_width + 1) - if max_line_len >= 99: - header0 = ( - lead - + "".join( - "{}{}".format(" " * 99, (i + 1) % 100) - for i in range(max(max_line_len // 100, 1)) - ) - + "\n" - ) - else: - header0 = "" - header1 = ( - header0 - + lead - + "".join( - " {}".format((i + 1) % 10) - for i in range(-(-max_line_len // 10)) - ) - + "\n" - ) - header2 = lead + "1234567890" * (-(-max_line_len // 10)) + "\n" - return ( - header1 - + header2 - + "\n".join( - "{:{}d}:{}{}".format(i, lineno_width, line, eol_mark) - for i, line in enumerate(s_lines, start=start_line) - ) - + "\n" - ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py deleted file mode 100644 index 065262039..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/unicode.py +++ /dev/null @@ -1,352 +0,0 @@ -# unicode.py - -import sys -from itertools import filterfalse -from typing import List, Tuple, Union - - -class _lazyclassproperty: - def __init__(self, fn): - self.fn = fn - self.__doc__ = fn.__doc__ - self.__name__ = fn.__name__ - - def __get__(self, obj, cls): - if cls is None: - cls = type(obj) - if not hasattr(cls, "_intern") or any( - cls._intern is getattr(superclass, "_intern", []) - for superclass in cls.__mro__[1:] - ): - cls._intern = {} - attrname = self.fn.__name__ - if attrname not in cls._intern: - cls._intern[attrname] = self.fn(cls) - return cls._intern[attrname] - - -UnicodeRangeList = List[Union[Tuple[int, int], Tuple[int]]] - - -class unicode_set: - """ - A set of Unicode characters, for language-specific strings for - ``alphas``, ``nums``, ``alphanums``, and ``printables``. - A unicode_set is defined by a list of ranges in the Unicode character - set, in a class attribute ``_ranges``. Ranges can be specified using - 2-tuples or a 1-tuple, such as:: - - _ranges = [ - (0x0020, 0x007e), - (0x00a0, 0x00ff), - (0x0100,), - ] - - Ranges are left- and right-inclusive. A 1-tuple of (x,) is treated as (x, x). - - A unicode set can also be defined using multiple inheritance of other unicode sets:: - - class CJK(Chinese, Japanese, Korean): - pass - """ - - _ranges: UnicodeRangeList = [] - - @_lazyclassproperty - def _chars_for_ranges(cls): - ret = [] - for cc in cls.__mro__: - if cc is unicode_set: - break - for rr in getattr(cc, "_ranges", ()): - ret.extend(range(rr[0], rr[-1] + 1)) - return [chr(c) for c in sorted(set(ret))] - - @_lazyclassproperty - def printables(cls): - "all non-whitespace characters in this range" - return "".join(filterfalse(str.isspace, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphas(cls): - "all alphabetic characters in this range" - return "".join(filter(str.isalpha, cls._chars_for_ranges)) - - @_lazyclassproperty - def nums(cls): - "all numeric digit characters in this range" - return "".join(filter(str.isdigit, cls._chars_for_ranges)) - - @_lazyclassproperty - def alphanums(cls): - "all alphanumeric characters in this range" - return cls.alphas + cls.nums - - @_lazyclassproperty - def identchars(cls): - "all characters in this range that are valid identifier characters, plus underscore '_'" - return "".join( - sorted( - set( - "".join(filter(str.isidentifier, cls._chars_for_ranges)) - + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªµº" - + "ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ" - + "_" - ) - ) - ) - - @_lazyclassproperty - def identbodychars(cls): - """ - all characters in this range that are valid identifier body characters, - plus the digits 0-9 - """ - return "".join( - sorted( - set( - cls.identchars - + "0123456789" - + "".join( - [c for c in cls._chars_for_ranges if ("_" + c).isidentifier()] - ) - ) - ) - ) - - -class pyparsing_unicode(unicode_set): - """ - A namespace class for defining common language unicode_sets. - """ - - # fmt: off - - # define ranges in language character sets - _ranges: UnicodeRangeList = [ - (0x0020, sys.maxunicode), - ] - - class BasicMultilingualPlane(unicode_set): - "Unicode set for the Basic Multilingual Plane" - _ranges: UnicodeRangeList = [ - (0x0020, 0xFFFF), - ] - - class Latin1(unicode_set): - "Unicode set for Latin-1 Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0020, 0x007E), - (0x00A0, 0x00FF), - ] - - class LatinA(unicode_set): - "Unicode set for Latin-A Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0100, 0x017F), - ] - - class LatinB(unicode_set): - "Unicode set for Latin-B Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0180, 0x024F), - ] - - class Greek(unicode_set): - "Unicode set for Greek Unicode Character Ranges" - _ranges: UnicodeRangeList = [ - (0x0342, 0x0345), - (0x0370, 0x0377), - (0x037A, 0x037F), - (0x0384, 0x038A), - (0x038C,), - (0x038E, 0x03A1), - (0x03A3, 0x03E1), - (0x03F0, 0x03FF), - (0x1D26, 0x1D2A), - (0x1D5E,), - (0x1D60,), - (0x1D66, 0x1D6A), - (0x1F00, 0x1F15), - (0x1F18, 0x1F1D), - (0x1F20, 0x1F45), - (0x1F48, 0x1F4D), - (0x1F50, 0x1F57), - (0x1F59,), - (0x1F5B,), - (0x1F5D,), - (0x1F5F, 0x1F7D), - (0x1F80, 0x1FB4), - (0x1FB6, 0x1FC4), - (0x1FC6, 0x1FD3), - (0x1FD6, 0x1FDB), - (0x1FDD, 0x1FEF), - (0x1FF2, 0x1FF4), - (0x1FF6, 0x1FFE), - (0x2129,), - (0x2719, 0x271A), - (0xAB65,), - (0x10140, 0x1018D), - (0x101A0,), - (0x1D200, 0x1D245), - (0x1F7A1, 0x1F7A7), - ] - - class Cyrillic(unicode_set): - "Unicode set for Cyrillic Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0400, 0x052F), - (0x1C80, 0x1C88), - (0x1D2B,), - (0x1D78,), - (0x2DE0, 0x2DFF), - (0xA640, 0xA672), - (0xA674, 0xA69F), - (0xFE2E, 0xFE2F), - ] - - class Chinese(unicode_set): - "Unicode set for Chinese Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x2E80, 0x2E99), - (0x2E9B, 0x2EF3), - (0x31C0, 0x31E3), - (0x3400, 0x4DB5), - (0x4E00, 0x9FEF), - (0xA700, 0xA707), - (0xF900, 0xFA6D), - (0xFA70, 0xFAD9), - (0x16FE2, 0x16FE3), - (0x1F210, 0x1F212), - (0x1F214, 0x1F23B), - (0x1F240, 0x1F248), - (0x20000, 0x2A6D6), - (0x2A700, 0x2B734), - (0x2B740, 0x2B81D), - (0x2B820, 0x2CEA1), - (0x2CEB0, 0x2EBE0), - (0x2F800, 0x2FA1D), - ] - - class Japanese(unicode_set): - "Unicode set for Japanese Unicode Character Range, combining Kanji, Hiragana, and Katakana ranges" - _ranges: UnicodeRangeList = [] - - class Kanji(unicode_set): - "Unicode set for Kanji Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x4E00, 0x9FBF), - (0x3000, 0x303F), - ] - - class Hiragana(unicode_set): - "Unicode set for Hiragana Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x3041, 0x3096), - (0x3099, 0x30A0), - (0x30FC,), - (0xFF70,), - (0x1B001,), - (0x1B150, 0x1B152), - (0x1F200,), - ] - - class Katakana(unicode_set): - "Unicode set for Katakana Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x3099, 0x309C), - (0x30A0, 0x30FF), - (0x31F0, 0x31FF), - (0x32D0, 0x32FE), - (0xFF65, 0xFF9F), - (0x1B000,), - (0x1B164, 0x1B167), - (0x1F201, 0x1F202), - (0x1F213,), - ] - - class Hangul(unicode_set): - "Unicode set for Hangul (Korean) Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x1100, 0x11FF), - (0x302E, 0x302F), - (0x3131, 0x318E), - (0x3200, 0x321C), - (0x3260, 0x327B), - (0x327E,), - (0xA960, 0xA97C), - (0xAC00, 0xD7A3), - (0xD7B0, 0xD7C6), - (0xD7CB, 0xD7FB), - (0xFFA0, 0xFFBE), - (0xFFC2, 0xFFC7), - (0xFFCA, 0xFFCF), - (0xFFD2, 0xFFD7), - (0xFFDA, 0xFFDC), - ] - - Korean = Hangul - - class CJK(Chinese, Japanese, Hangul): - "Unicode set for combined Chinese, Japanese, and Korean (CJK) Unicode Character Range" - - class Thai(unicode_set): - "Unicode set for Thai Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0E01, 0x0E3A), - (0x0E3F, 0x0E5B) - ] - - class Arabic(unicode_set): - "Unicode set for Arabic Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0600, 0x061B), - (0x061E, 0x06FF), - (0x0700, 0x077F), - ] - - class Hebrew(unicode_set): - "Unicode set for Hebrew Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0591, 0x05C7), - (0x05D0, 0x05EA), - (0x05EF, 0x05F4), - (0xFB1D, 0xFB36), - (0xFB38, 0xFB3C), - (0xFB3E,), - (0xFB40, 0xFB41), - (0xFB43, 0xFB44), - (0xFB46, 0xFB4F), - ] - - class Devanagari(unicode_set): - "Unicode set for Devanagari Unicode Character Range" - _ranges: UnicodeRangeList = [ - (0x0900, 0x097F), - (0xA8E0, 0xA8FF) - ] - - # fmt: on - - -pyparsing_unicode.Japanese._ranges = ( - pyparsing_unicode.Japanese.Kanji._ranges - + pyparsing_unicode.Japanese.Hiragana._ranges - + pyparsing_unicode.Japanese.Katakana._ranges -) - -pyparsing_unicode.BMP = pyparsing_unicode.BasicMultilingualPlane - -# add language identifiers using language Unicode -pyparsing_unicode.العربية = pyparsing_unicode.Arabic -pyparsing_unicode.中文 = pyparsing_unicode.Chinese -pyparsing_unicode.кириллица = pyparsing_unicode.Cyrillic -pyparsing_unicode.Ελληνικά = pyparsing_unicode.Greek -pyparsing_unicode.עִברִית = pyparsing_unicode.Hebrew -pyparsing_unicode.日本語 = pyparsing_unicode.Japanese -pyparsing_unicode.Japanese.漢字 = pyparsing_unicode.Japanese.Kanji -pyparsing_unicode.Japanese.カタカナ = pyparsing_unicode.Japanese.Katakana -pyparsing_unicode.Japanese.ひらがな = pyparsing_unicode.Japanese.Hiragana -pyparsing_unicode.한국어 = pyparsing_unicode.Korean -pyparsing_unicode.ไทย = pyparsing_unicode.Thai -pyparsing_unicode.देवनागरी = pyparsing_unicode.Devanagari diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/util.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/util.py deleted file mode 100644 index 34ce092c6..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/_vendor/pyparsing/util.py +++ /dev/null @@ -1,235 +0,0 @@ -# util.py -import warnings -import types -import collections -import itertools -from functools import lru_cache -from typing import List, Union, Iterable - -_bslash = chr(92) - - -class __config_flags: - """Internal class for defining compatibility and debugging flags""" - - _all_names: List[str] = [] - _fixed_names: List[str] = [] - _type_desc = "configuration" - - @classmethod - def _set(cls, dname, value): - if dname in cls._fixed_names: - warnings.warn( - "{}.{} {} is {} and cannot be overridden".format( - cls.__name__, - dname, - cls._type_desc, - str(getattr(cls, dname)).upper(), - ) - ) - return - if dname in cls._all_names: - setattr(cls, dname, value) - else: - raise ValueError("no such {} {!r}".format(cls._type_desc, dname)) - - enable = classmethod(lambda cls, name: cls._set(name, True)) - disable = classmethod(lambda cls, name: cls._set(name, False)) - - -@lru_cache(maxsize=128) -def col(loc: int, strg: str) -> int: - """ - Returns current column within a string, counting newlines as line separators. - The first column is number 1. - - Note: the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See - :class:`ParserElement.parseString` for more - information on parsing strings containing ```` s, and suggested - methods to maintain a consistent view of the parsed string, the parse - location, and line and column positions within the parsed string. - """ - s = strg - return 1 if 0 < loc < len(s) and s[loc - 1] == "\n" else loc - s.rfind("\n", 0, loc) - - -@lru_cache(maxsize=128) -def lineno(loc: int, strg: str) -> int: - """Returns current line number within a string, counting newlines as line separators. - The first line is number 1. - - Note - the default parsing behavior is to expand tabs in the input string - before starting the parsing process. See :class:`ParserElement.parseString` - for more information on parsing strings containing ```` s, and - suggested methods to maintain a consistent view of the parsed string, the - parse location, and line and column positions within the parsed string. - """ - return strg.count("\n", 0, loc) + 1 - - -@lru_cache(maxsize=128) -def line(loc: int, strg: str) -> str: - """ - Returns the line of text containing loc within a string, counting newlines as line separators. - """ - last_cr = strg.rfind("\n", 0, loc) - next_cr = strg.find("\n", loc) - return strg[last_cr + 1 : next_cr] if next_cr >= 0 else strg[last_cr + 1 :] - - -class _UnboundedCache: - def __init__(self): - cache = {} - cache_get = cache.get - self.not_in_cache = not_in_cache = object() - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - - def clear(_): - cache.clear() - - self.size = None - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class _FifoCache: - def __init__(self, size): - self.not_in_cache = not_in_cache = object() - cache = collections.OrderedDict() - cache_get = cache.get - - def get(_, key): - return cache_get(key, not_in_cache) - - def set_(_, key, value): - cache[key] = value - while len(cache) > size: - cache.popitem(last=False) - - def clear(_): - cache.clear() - - self.size = size - self.get = types.MethodType(get, self) - self.set = types.MethodType(set_, self) - self.clear = types.MethodType(clear, self) - - -class LRUMemo: - """ - A memoizing mapping that retains `capacity` deleted items - - The memo tracks retained items by their access order; once `capacity` items - are retained, the least recently used item is discarded. - """ - - def __init__(self, capacity): - self._capacity = capacity - self._active = {} - self._memory = collections.OrderedDict() - - def __getitem__(self, key): - try: - return self._active[key] - except KeyError: - self._memory.move_to_end(key) - return self._memory[key] - - def __setitem__(self, key, value): - self._memory.pop(key, None) - self._active[key] = value - - def __delitem__(self, key): - try: - value = self._active.pop(key) - except KeyError: - pass - else: - while len(self._memory) >= self._capacity: - self._memory.popitem(last=False) - self._memory[key] = value - - def clear(self): - self._active.clear() - self._memory.clear() - - -class UnboundedMemo(dict): - """ - A memoizing mapping that retains all deleted items - """ - - def __delitem__(self, key): - pass - - -def _escape_regex_range_chars(s: str) -> str: - # escape these chars: ^-[] - for c in r"\^-[]": - s = s.replace(c, _bslash + c) - s = s.replace("\n", r"\n") - s = s.replace("\t", r"\t") - return str(s) - - -def _collapse_string_to_ranges( - s: Union[str, Iterable[str]], re_escape: bool = True -) -> str: - def is_consecutive(c): - c_int = ord(c) - is_consecutive.prev, prev = c_int, is_consecutive.prev - if c_int - prev > 1: - is_consecutive.value = next(is_consecutive.counter) - return is_consecutive.value - - is_consecutive.prev = 0 - is_consecutive.counter = itertools.count() - is_consecutive.value = -1 - - def escape_re_range_char(c): - return "\\" + c if c in r"\^-][" else c - - def no_escape_re_range_char(c): - return c - - if not re_escape: - escape_re_range_char = no_escape_re_range_char - - ret = [] - s = "".join(sorted(set(s))) - if len(s) > 3: - for _, chars in itertools.groupby(s, key=is_consecutive): - first = last = next(chars) - last = collections.deque( - itertools.chain(iter([last]), chars), maxlen=1 - ).pop() - if first == last: - ret.append(escape_re_range_char(first)) - else: - sep = "" if ord(last) == ord(first) + 1 else "-" - ret.append( - "{}{}{}".format( - escape_re_range_char(first), sep, escape_re_range_char(last) - ) - ) - else: - ret = [escape_re_range_char(c) for c in s] - - return "".join(ret) - - -def _flatten(ll: list) -> list: - ret = [] - for i in ll: - if isinstance(i, list): - ret.extend(_flatten(i)) - else: - ret.append(i) - return ret diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/build_meta.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/build_meta.py index 1fb4c3b10..618a5e8f5 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/build_meta.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/build_meta.py @@ -326,7 +326,7 @@ def _get_build_requires(self, config_settings, requirements): def run_setup(self, setup_script='setup.py'): # Note that we can reuse our build directory between calls # Correctness comes first, then optimization later - __file__ = setup_script + __file__ = os.path.abspath(setup_script) __name__ = '__main__' with _open_setup_script(__file__) as f: diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/bdist_egg.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/bdist_egg.py index 11a1c6be2..33f483cf5 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/bdist_egg.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/bdist_egg.py @@ -11,7 +11,6 @@ import textwrap import marshal -from pkg_resources import get_build_platform, Distribution from setuptools.extension import Library from setuptools import Command from .._path import ensure_directory @@ -64,7 +63,7 @@ class bdist_egg(Command): ('bdist-dir=', 'b', "temporary directory for creating the distribution"), ('plat-name=', 'p', "platform name to embed in generated filenames " - "(default: %s)" % get_build_platform()), + "(by default uses `pkg_resources.get_build_platform()`)"), ('exclude-source-files', None, "remove all .py files from the generated egg"), ('keep-temp', 'k', @@ -98,18 +97,18 @@ def finalize_options(self): self.bdist_dir = os.path.join(bdist_base, 'egg') if self.plat_name is None: + from pkg_resources import get_build_platform + self.plat_name = get_build_platform() self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) if self.egg_output is None: - # Compute filename of the output egg - basename = Distribution( - None, None, ei_cmd.egg_name, ei_cmd.egg_version, - get_python_version(), - self.distribution.has_ext_modules() and self.plat_name - ).egg_name() + basename = ei_cmd._get_egg_basename( + py_version=get_python_version(), + platform=self.distribution.has_ext_modules() and self.plat_name, + ) self.egg_output = os.path.join(self.dist_dir, basename + '.egg') diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/develop.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/develop.py index 24fb0a7c8..5630ca4cd 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/develop.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/develop.py @@ -5,8 +5,8 @@ import glob import io -import pkg_resources from setuptools.command.easy_install import easy_install +from setuptools import _path from setuptools import namespaces import setuptools @@ -42,6 +42,8 @@ def initialize_options(self): self.always_copy_from = '.' # always copy eggs installed in curdir def finalize_options(self): + import pkg_resources + ei = self.get_finalized_command("egg_info") if ei.broken_egg_info: template = "Please rename %r to %r before using 'develop'" @@ -61,10 +63,8 @@ def finalize_options(self): if self.egg_path is None: self.egg_path = os.path.abspath(ei.egg_base) - target = pkg_resources.normalize_path(self.egg_base) - egg_path = pkg_resources.normalize_path( - os.path.join(self.install_dir, self.egg_path) - ) + target = _path.normpath(self.egg_base) + egg_path = _path.normpath(os.path.join(self.install_dir, self.egg_path)) if egg_path != target: raise DistutilsOptionError( "--egg-path must be a relative path from the install" @@ -94,15 +94,16 @@ def _resolve_setup_path(egg_base, install_dir, egg_path): path_to_setup = egg_base.replace(os.sep, '/').rstrip('/') if path_to_setup != os.curdir: path_to_setup = '../' * (path_to_setup.count('/') + 1) - resolved = pkg_resources.normalize_path( + resolved = _path.normpath( os.path.join(install_dir, egg_path, path_to_setup) ) - if resolved != pkg_resources.normalize_path(os.curdir): + curdir = _path.normpath(os.curdir) + if resolved != curdir: raise DistutilsOptionError( "Can't get a consistent path to setup script from" " installation directory", resolved, - pkg_resources.normalize_path(os.curdir), + curdir, ) return path_to_setup diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/dist_info.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/dist_info.py index 0685c9459..40fdfd0a2 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/dist_info.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/dist_info.py @@ -4,23 +4,25 @@ """ import os -import re import shutil import sys import warnings from contextlib import contextmanager -from inspect import cleandoc +from distutils import log +from distutils.core import Command from pathlib import Path -from distutils.core import Command -from distutils import log -from setuptools.extern import packaging -from setuptools._deprecation_warning import SetuptoolsDeprecationWarning +from .. import _normalization +from .._deprecation_warning import SetuptoolsDeprecationWarning class dist_info(Command): + """ + This command is private and reserved for internal use of setuptools, + users should rely on ``setuptools.build_meta`` APIs. + """ - description = 'create a .dist-info directory' + description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create .dist-info directory" user_options = [ ('egg-base=', 'e', "directory containing .egg-info directories" @@ -72,8 +74,8 @@ def finalize_options(self): egg_info.finalize_options() self.egg_info = egg_info - name = _safe(dist.get_name()) - version = _version(dist.get_version()) + name = _normalization.safer_name(dist.get_name()) + version = _normalization.safer_best_effort_version(dist.get_version()) self.name = f"{name}-{version}" self.dist_info_dir = os.path.join(self.output_dir, f"{self.name}.dist-info") @@ -105,32 +107,6 @@ def run(self): bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir) -def _safe(component: str) -> str: - """Escape a component used to form a wheel name according to PEP 491""" - return re.sub(r"[^\w\d.]+", "_", component) - - -def _version(version: str) -> str: - """Convert an arbitrary string to a version string.""" - v = version.replace(' ', '.') - try: - return str(packaging.version.Version(v)).replace("-", "_") - except packaging.version.InvalidVersion: - msg = f"""Invalid version: {version!r}. - !!\n\n - ################### - # Invalid version # - ################### - {version!r} is not valid according to PEP 440.\n - Please make sure specify a valid version for your package. - Also note that future releases of setuptools may halt the build process - if an invalid version is given. - \n\n!! - """ - warnings.warn(cleandoc(msg)) - return _safe(v).strip("_") - - def _rm(dir_name, **opts): if os.path.isdir(dir_name): shutil.rmtree(dir_name, **opts) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/editable_wheel.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/editable_wheel.py index d60cfbebb..6fddf03d6 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/editable_wheel.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/editable_wheel.py @@ -12,7 +12,6 @@ import logging import os -import re import shutil import sys import traceback @@ -36,10 +35,17 @@ Union, ) -from setuptools import Command, SetuptoolsDeprecationWarning, errors, namespaces -from setuptools.command.build_py import build_py as build_py_cls -from setuptools.discovery import find_package_path -from setuptools.dist import Distribution +from .. import ( + Command, + SetuptoolsDeprecationWarning, + _normalization, + _path, + errors, + namespaces, +) +from ..discovery import find_package_path +from ..dist import Distribution +from .build_py import build_py as build_py_cls if TYPE_CHECKING: from wheel.wheelfile import WheelFile # noqa @@ -104,10 +110,11 @@ def convert(cls, mode: Optional[str]) -> "_EditableMode": class editable_wheel(Command): """Build 'editable' wheel for development. - (This command is reserved for internal use of setuptools). + This command is private and reserved for internal use of setuptools, + users should rely on ``setuptools.build_meta`` APIs. """ - description = "create a PEP 660 'editable' wheel" + description = "DO NOT CALL DIRECTLY, INTERNAL ONLY: create PEP 660 editable wheel" user_options = [ ("dist-dir=", "d", "directory to put final built distributions in"), @@ -138,20 +145,11 @@ def run(self): bdist_wheel.write_wheelfile(self.dist_info_dir) self._create_wheel_file(bdist_wheel) - except Exception as ex: + except Exception: traceback.print_exc() - msg = """ - Support for editable installs via PEP 660 was recently introduced - in `setuptools`. If you are seeing this error, please report to: - - https://github.com/pypa/setuptools/issues - - Meanwhile you can try the legacy behavior by setting an - environment variable and trying to install again: - - SETUPTOOLS_ENABLE_FEATURES="legacy-editable" - """ - raise errors.InternalError(cleandoc(msg)) from ex + project = self.distribution.name or self.distribution.get_name() + _DebuggingTips.warn(project) + raise def _ensure_dist_info(self): if self.dist_info_dir is None: @@ -490,7 +488,7 @@ def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str] )) name = f"__editable__.{self.name}.finder" - finder = _make_identifier(name) + finder = _normalization.safe_identifier(name) content = bytes(_finder_template(name, roots, namespaces_), "utf-8") wheel.writestr(f"{finder}.py", content) @@ -569,7 +567,7 @@ def _simple_layout( return set(package_dir) in ({}, {""}) parent = os.path.commonpath([_parent_path(k, v) for k, v in layout.items()]) return all( - _normalize_path(Path(parent, *key.split('.'))) == _normalize_path(value) + _path.same_path(Path(parent, *key.split('.')), value) for key, value in layout.items() ) @@ -698,21 +696,14 @@ def _is_nested(pkg: str, pkg_path: str, parent: str, parent_path: str) -> bool: >>> _is_nested("b.a", "path/b/a", "a", "path/a") False """ - norm_pkg_path = _normalize_path(pkg_path) + norm_pkg_path = _path.normpath(pkg_path) rest = pkg.replace(parent, "", 1).strip(".").split(".") return ( pkg.startswith(parent) - and norm_pkg_path == _normalize_path(Path(parent_path, *rest)) + and norm_pkg_path == _path.normpath(Path(parent_path, *rest)) ) -def _normalize_path(filename: _Path) -> str: - """Normalize a file/dir name for comparison purposes""" - # See pkg_resources.normalize_path - file = os.path.abspath(filename) if sys.platform == 'cygwin' else filename - return os.path.normcase(os.path.realpath(os.path.normpath(file))) - - def _empty_dir(dir_: _P) -> _P: """Create a directory ensured to be empty. Existing files may be removed.""" shutil.rmtree(dir_, ignore_errors=True) @@ -720,18 +711,6 @@ def _empty_dir(dir_: _P) -> _P: return dir_ -def _make_identifier(name: str) -> str: - """Make a string safe to be used as Python identifier. - >>> _make_identifier("12abc") - '_12abc' - >>> _make_identifier("__editable__.myns.pkg-78.9.3_local") - '__editable___myns_pkg_78_9_3_local' - """ - safe = re.sub(r'\W|^(?=\d)', '_', name) - assert safe.isidentifier() - return safe - - class _NamespaceInstaller(namespaces.Installer): def __init__(self, distribution, installation_dir, editable_name, src_root): self.distribution = distribution @@ -842,3 +821,36 @@ class InformationOnly(UserWarning): class LinksNotSupported(errors.FileError): """File system does not seem to support either symlinks or hard links.""" + + +class _DebuggingTips(InformationOnly): + @classmethod + def warn(cls, project: str): + msg = f"""An error happened while installing {project!r} in editable mode. + + ************************************************************************ + The following steps are recommended to help debugging this problem: + + - Try to install the project normally, without using the editable mode. + Does the error still persists? + (If it does, try fixing the problem before attempting the editable mode). + - If you are using binary extensions, make sure you have all OS-level + dependencies installed (e.g. compilers, toolchains, binary libraries, ...). + - Try the latest version of setuptools (maybe the error was already fixed). + - If you (or your project dependencies) are using any setuptools extension + or customization, make sure they support the editable mode. + + After following the steps above, if the problem still persist and + you think this is related to how setuptools handles editable installations, + please submit a reproducible example + (see https://stackoverflow.com/help/minimal-reproducible-example) to: + + https://github.com/pypa/setuptools/issues + + More information about editable installs can be found in the docs: + + https://setuptools.pypa.io/en/latest/userguide/development_mode.html + ************************************************************************ + """ + # We cannot use `add_notes` since pip hides PEP 678 notes + warnings.warn(msg, cls, stacklevel=2) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/egg_info.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/egg_info.py index 25888ed86..e40df9bba 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/egg_info.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/egg_info.py @@ -18,16 +18,13 @@ import collections from .._importlib import metadata -from .. import _entry_points +from .. import _entry_points, _normalization from setuptools import Command from setuptools.command.sdist import sdist from setuptools.command.sdist import walk_revctrl from setuptools.command.setopt import edit_config from setuptools.command import bdist_egg -from pkg_resources import ( - Requirement, safe_name, parse_version, - safe_version, to_filename) import setuptools.unicode_utils as unicode_utils from setuptools.glob import glob @@ -36,6 +33,9 @@ from setuptools import SetuptoolsDeprecationWarning +PY_MAJOR = '{}.{}'.format(*sys.version_info) + + def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME """ Translate a file path glob like '*.txt' in to a regular expression. @@ -125,10 +125,11 @@ class InfoCommon: @property def name(self): - return safe_name(self.distribution.get_name()) + return _normalization.safe_name(self.distribution.get_name()) def tagged_version(self): - return safe_version(self._maybe_tag(self.distribution.get_version())) + tagged = self._maybe_tag(self.distribution.get_version()) + return _normalization.best_effort_version(tagged) def _maybe_tag(self, version): """ @@ -148,14 +149,14 @@ def _already_tagged(self, version: str) -> bool: def _safe_tags(self) -> str: # To implement this we can rely on `safe_version` pretending to be version 0 # followed by tags. Then we simply discard the starting 0 (fake version number) - return safe_version(f"0{self.vtags}")[1:] + return _normalization.best_effort_version(f"0{self.vtags}")[1:] def tags(self) -> str: version = '' if self.tag_build: version += self.tag_build if self.tag_date: - version += time.strftime("-%Y%m%d") + version += time.strftime("%Y%m%d") return version vtags = property(tags) @@ -216,12 +217,12 @@ def finalize_options(self): # repercussions. self.egg_name = self.name self.egg_version = self.tagged_version() - parsed_version = parse_version(self.egg_version) + parsed_version = packaging.version.Version(self.egg_version) try: is_version = isinstance(parsed_version, packaging.version.Version) spec = "%s==%s" if is_version else "%s===%s" - Requirement(spec % (self.egg_name, self.egg_version)) + packaging.requirements.Requirement(spec % (self.egg_name, self.egg_version)) except ValueError as e: raise distutils.errors.DistutilsOptionError( "Invalid distribution name or version syntax: %s-%s" % @@ -233,7 +234,7 @@ def finalize_options(self): self.egg_base = (dirs or {}).get('', os.curdir) self.ensure_dirname('egg_base') - self.egg_info = to_filename(self.egg_name) + '.egg-info' + self.egg_info = _normalization.filename_component(self.egg_name) + '.egg-info' if self.egg_base != os.curdir: self.egg_info = os.path.join(self.egg_base, self.egg_info) if '-' in self.egg_name: @@ -249,11 +250,16 @@ def finalize_options(self): # to the version info # pd = self.distribution._patched_dist - if pd is not None and pd.key == self.egg_name.lower(): + key = getattr(pd, "key", None) or getattr(pd, "name", None) + if pd is not None and key == self.egg_name.lower(): pd._version = self.egg_version - pd._parsed_version = parse_version(self.egg_version) + pd._parsed_version = packaging.version.Version(self.egg_version) self.distribution._patched_dist = None + def _get_egg_basename(self, py_version=PY_MAJOR, platform=None): + """Compute filename of the output egg. Private API.""" + return _egg_basename(self.egg_name, self.egg_version, py_version, platform) + def write_or_delete_file(self, what, filename, data, force=False): """Write `data` to `filename` or delete if empty @@ -295,7 +301,11 @@ def delete_file(self, filename): def run(self): self.mkpath(self.egg_info) - os.utime(self.egg_info, None) + try: + os.utime(self.egg_info, None) + except OSError as e: + msg = f"Cannot update time stamp of directory '{self.egg_info}'" + raise distutils.errors.DistutilsFileError(msg) from e for ep in metadata.entry_points(group='egg_info.writers'): writer = ep.load() writer(self, ep.name, os.path.join(self.egg_info, ep.name)) @@ -561,6 +571,7 @@ def run(self): if os.path.exists(self.template): self.read_template() self.add_license_files() + self._add_referenced_files() self.prune_file_list() self.filelist.sort() self.filelist.remove_duplicates() @@ -615,9 +626,16 @@ def add_license_files(self): license_files = self.distribution.metadata.license_files or [] for lf in license_files: log.info("adding license file '%s'", lf) - pass self.filelist.extend(license_files) + def _add_referenced_files(self): + """Add files referenced by the config (e.g. `file:` directive) to filelist""" + referenced = getattr(self.distribution, '_referenced_files', []) + # ^-- fallback if dist comes from distutils or is a custom class + for rf in referenced: + log.debug("adding file referenced by config '%s'", rf) + self.filelist.extend(referenced) + def prune_file_list(self): build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() @@ -759,5 +777,15 @@ def get_pkg_info_revision(): return 0 +def _egg_basename(egg_name, egg_version, py_version=None, platform=None): + """Compute filename of the output egg. Private API.""" + name = _normalization.filename_component(egg_name) + version = _normalization.filename_component(egg_version) + egg = f"{name}-{version}-py{py_version or PY_MAJOR}" + if platform: + egg += f"-{platform}" + return egg + + class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning): """Deprecated behavior warning for EggInfo, bypassing suppression.""" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_egg_info.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_egg_info.py index 65ede406b..1c549c98e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_egg_info.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_egg_info.py @@ -5,7 +5,6 @@ from setuptools import namespaces from setuptools.archive_util import unpack_archive from .._path import ensure_directory -import pkg_resources class install_egg_info(namespaces.Installer, Command): @@ -24,9 +23,7 @@ def finalize_options(self): self.set_undefined_options('install_lib', ('install_dir', 'install_dir')) ei_cmd = self.get_finalized_command("egg_info") - basename = pkg_resources.Distribution( - None, None, ei_cmd.egg_name, ei_cmd.egg_version - ).egg_name() + '.egg-info' + basename = f"{ei_cmd._get_egg_basename()}.egg-info" self.source = ei_cmd.egg_info self.target = os.path.join(self.install_dir, basename) self.outputs = [] diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_scripts.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_scripts.py index aeb0e4240..8b3133f1f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_scripts.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/command/install_scripts.py @@ -4,7 +4,6 @@ import os import sys -from pkg_resources import Distribution, PathMetadata from .._path import ensure_directory @@ -16,8 +15,6 @@ def initialize_options(self): self.no_ep = False def run(self): - import setuptools.command.easy_install as ei - self.run_command("egg_info") if self.distribution.scripts: orig.install_scripts.run(self) # run first to set up self.outfiles @@ -26,6 +23,12 @@ def run(self): if self.no_ep: # don't install entry point scripts into .egg file! return + self._install_ep_scripts() + + def _install_ep_scripts(self): + # Delay import side-effects + from pkg_resources import Distribution, PathMetadata + from . import easy_install as ei ei_cmd = self.get_finalized_command("egg_info") dist = Distribution( diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py index 8af556169..a2b443657 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_apply_pyprojecttoml.py @@ -16,7 +16,7 @@ from itertools import chain from types import MappingProxyType from typing import (TYPE_CHECKING, Any, Callable, Dict, List, Optional, Set, Tuple, - Type, Union) + Type, Union, cast) from setuptools._deprecation_warning import SetuptoolsDeprecationWarning @@ -142,22 +142,29 @@ def _long_description(dist: "Distribution", val: _DictOrStr, root_dir: _Path): from setuptools.config import expand if isinstance(val, str): - text = expand.read_files(val, root_dir) + file: Union[str, list] = val + text = expand.read_files(file, root_dir) ctype = _guess_content_type(val) else: - text = val.get("text") or expand.read_files(val.get("file", []), root_dir) + file = val.get("file") or [] + text = val.get("text") or expand.read_files(file, root_dir) ctype = val["content-type"] _set_config(dist, "long_description", text) + if ctype: _set_config(dist, "long_description_content_type", ctype) + if file: + dist._referenced_files.add(cast(str, file)) + def _license(dist: "Distribution", val: dict, root_dir: _Path): from setuptools.config import expand if "file" in val: _set_config(dist, "license", expand.read_files([val["file"]], root_dir)) + dist._referenced_files.add(val["file"]) else: _set_config(dist, "license", val["text"]) @@ -272,6 +279,12 @@ def _normalise_cmd_options(desc: List[Tuple[str, Optional[str], str]]) -> Set[st return {_normalise_cmd_option_key(fancy_option[0]) for fancy_option in desc} +def _get_previous_entrypoints(dist: "Distribution") -> Dict[str, list]: + ignore = ("console_scripts", "gui_scripts") + value = getattr(dist, "entry_points", None) or {} + return {k: v for k, v in value.items() if k not in ignore} + + def _attrgetter(attr): """ Similar to ``operator.attrgetter`` but returns None if ``attr`` is not found @@ -336,7 +349,7 @@ def _acessor(obj): "keywords": _attrgetter("metadata.keywords"), "classifiers": _attrgetter("metadata.classifiers"), "urls": _attrgetter("metadata.project_urls"), - "entry-points": _attrgetter("entry_points"), + "entry-points": _get_previous_entrypoints, "dependencies": _some_attrgetter("_orig_install_requires", "install_requires"), "optional-dependencies": _some_attrgetter("_orig_extras_require", "extras_require"), } diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py index ad5ee31ef..52e18da24 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/fastjsonschema_validations.py @@ -10,7 +10,7 @@ # *** PLEASE DO NOT MODIFY DIRECTLY: Automatically generated code *** -VERSION = "2.15.3" +VERSION = "2.16.3" import re from .fastjsonschema_exceptions import JsonSchemaValueException @@ -30,7 +30,7 @@ def validate(data, custom_formats={}, name_prefix=None): def validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).'], 'default': ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -85,7 +85,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui data_keys.remove("tool") data__tool = data["tool"] if not isinstance(data__tool, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).'], 'default': ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') data__tool_is_dict = isinstance(data__tool, dict) if data__tool_is_dict: data__tool_keys = set(data__tool.keys()) @@ -98,12 +98,12 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui data__tool__setuptools = data__tool["setuptools"] validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data__tool__setuptools, custom_formats, (name_prefix or "data") + ".tool.setuptools") if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).'], 'default': ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') return data def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).'], 'default': ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -180,16 +180,12 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if data__packages_one_of_count1 < 2: try: if not isinstance(data__packages, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, rule='type') data__packages_is_list = isinstance(data__packages, (list, tuple)) if data__packages_is_list: data__packages_len = len(data__packages) for data__packages_x, data__packages_item in enumerate(data__packages): - if not isinstance(data__packages_item, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals()) + " must be string", value=data__packages_item, name="" + (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') - if isinstance(data__packages_item, str): - if not custom_formats["python-module-name"](data__packages_item): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals()) + " must be python-module-name", value=data__packages_item, name="" + (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals()) + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data__packages_item, custom_formats, (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals())) data__packages_one_of_count1 += 1 except JsonSchemaValueException: pass if data__packages_one_of_count1 < 2: @@ -198,12 +194,12 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data__packages_one_of_count1 += 1 except JsonSchemaValueException: pass if data__packages_one_of_count1 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') if "package-dir" in data_keys: data_keys.remove("package-dir") data__packagedir = data["package-dir"] if not isinstance(data__packagedir, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') data__packagedir_is_dict = isinstance(data__packagedir, dict) if data__packagedir_is_dict: data__packagedir_keys = set(data__packagedir.keys()) @@ -214,7 +210,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if not isinstance(data__packagedir_val, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + " must be string", value=data__packagedir_val, name="" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + "", definition={'type': 'string'}, rule='type') if data__packagedir_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') data__packagedir_len = len(data__packagedir) if data__packagedir_len != 0: data__packagedir_property_names = True @@ -223,23 +219,21 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data__packagedir_key_one_of_count2 = 0 if data__packagedir_key_one_of_count2 < 2: try: - if isinstance(data__packagedir_key, str): - if not custom_formats["python-module-name"](data__packagedir_key): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be python-module-name", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'format': 'python-module-name'}, rule='format') + if data__packagedir_key != "": + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be same as const definition: ", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'const': ''}, rule='const') data__packagedir_key_one_of_count2 += 1 except JsonSchemaValueException: pass if data__packagedir_key_one_of_count2 < 2: try: - if data__packagedir_key != "": - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be same as const definition: ", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'const': ''}, rule='const') + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data__packagedir_key, custom_formats, (name_prefix or "data") + ".package-dir") data__packagedir_key_one_of_count2 += 1 except JsonSchemaValueException: pass if data__packagedir_key_one_of_count2 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be valid exactly by one definition" + (" (" + str(data__packagedir_key_one_of_count2) + " matches found)"), value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, rule='oneOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be valid exactly by one definition" + (" (" + str(data__packagedir_key_one_of_count2) + " matches found)"), value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, rule='oneOf') except JsonSchemaValueException: data__packagedir_property_names = False if not data__packagedir_property_names: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') if "package-data" in data_keys: data_keys.remove("package-data") data__packagedata = data["package-data"] @@ -408,14 +402,13 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("license-files") data__licensefiles = data["license-files"] if not isinstance(data__licensefiles, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).'], 'default': ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, rule='type') data__licensefiles_is_list = isinstance(data__licensefiles, (list, tuple)) if data__licensefiles_is_list: data__licensefiles_len = len(data__licensefiles) for data__licensefiles_x, data__licensefiles_item in enumerate(data__licensefiles): if not isinstance(data__licensefiles_item, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + " must be string", value=data__licensefiles_item, name="" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') - else: data["license-files"] = ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'] if "dynamic" in data_keys: data_keys.remove("dynamic") data__dynamic = data["dynamic"] @@ -468,7 +461,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if REGEX_PATTERNS['.+'].search(data__dynamic__optionaldependencies_key): if data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies_keys: data__dynamic__optionaldependencies_keys.remove(data__dynamic__optionaldependencies_key) - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__optionaldependencies_val, custom_formats, (name_prefix or "data") + ".dynamic.optional-dependencies.{data__dynamic__optionaldependencies_key}") + validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__optionaldependencies_val, custom_formats, (name_prefix or "data") + ".dynamic.optional-dependencies.{data__dynamic__optionaldependencies_key}".format(**locals())) if data__dynamic__optionaldependencies_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must not contain "+str(data__dynamic__optionaldependencies_keys)+" properties", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='additionalProperties') data__dynamic__optionaldependencies_len = len(data__dynamic__optionaldependencies) @@ -514,7 +507,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if data__dynamic_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must not contain "+str(data__dynamic_keys)+" properties", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}, rule='additionalProperties') if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': ''}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).'], 'default': ['LICEN[CS]E*', ' COPYING*', ' NOTICE*', 'AUTHORS*'], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') return data def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data, custom_formats={}, name_prefix=None): @@ -630,6 +623,28 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__defi raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='additionalProperties') return data +def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data, custom_formats={}, name_prefix=None): + if not isinstance(data, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, rule='type') + data_any_of_count8 = 0 + if not data_any_of_count8: + try: + if isinstance(data, str): + if not custom_formats["python-module-name"](data): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name", value=data, name="" + (name_prefix or "data") + "", definition={'format': 'python-module-name'}, rule='format') + data_any_of_count8 += 1 + except JsonSchemaValueException: pass + if not data_any_of_count8: + try: + if isinstance(data, str): + if not custom_formats["pep561-stub-name"](data): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep561-stub-name", value=data, name="" + (name_prefix or "data") + "", definition={'format': 'pep561-stub-name'}, rule='format') + data_any_of_count8 += 1 + except JsonSchemaValueException: pass + if not data_any_of_count8: + raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, rule='anyOf') + return data + def validate_https___docs_python_org_3_install(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, rule='type') @@ -651,12 +666,12 @@ def validate_https___docs_python_org_3_install(data, custom_formats={}, name_pre def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_len = len(data) if not all(prop in data for prop in ['name']): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['name'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='required') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['name'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='required') data_keys = set(data.keys()) if "name" in data_keys: data_keys.remove("name") @@ -682,19 +697,19 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if "readme" in data_keys: data_keys.remove("readme") data__readme = data["readme"] - data__readme_one_of_count8 = 0 - if data__readme_one_of_count8 < 2: + data__readme_one_of_count9 = 0 + if data__readme_one_of_count9 < 2: try: if not isinstance(data__readme, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be string", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, rule='type') - data__readme_one_of_count8 += 1 + data__readme_one_of_count9 += 1 except JsonSchemaValueException: pass - if data__readme_one_of_count8 < 2: + if data__readme_one_of_count9 < 2: try: if not isinstance(data__readme, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be object", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}, rule='type') - data__readme_any_of_count9 = 0 - if not data__readme_any_of_count9: + data__readme_any_of_count10 = 0 + if not data__readme_any_of_count10: try: data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: @@ -707,9 +722,9 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro data__readme__file = data__readme["file"] if not isinstance(data__readme__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.file must be string", value=data__readme__file, name="" + (name_prefix or "data") + ".readme.file", definition={'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}, rule='type') - data__readme_any_of_count9 += 1 + data__readme_any_of_count10 += 1 except JsonSchemaValueException: pass - if not data__readme_any_of_count9: + if not data__readme_any_of_count10: try: data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: @@ -722,9 +737,9 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro data__readme__text = data__readme["text"] if not isinstance(data__readme__text, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.text must be string", value=data__readme__text, name="" + (name_prefix or "data") + ".readme.text", definition={'type': 'string', 'description': 'Full text describing the project.'}, rule='type') - data__readme_any_of_count9 += 1 + data__readme_any_of_count10 += 1 except JsonSchemaValueException: pass - if not data__readme_any_of_count9: + if not data__readme_any_of_count10: raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme cannot be validated by any definition", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, rule='anyOf') data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: @@ -737,10 +752,10 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro data__readme__contenttype = data__readme["content-type"] if not isinstance(data__readme__contenttype, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.content-type must be string", value=data__readme__contenttype, name="" + (name_prefix or "data") + ".readme.content-type", definition={'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}, rule='type') - data__readme_one_of_count8 += 1 + data__readme_one_of_count9 += 1 except JsonSchemaValueException: pass - if data__readme_one_of_count8 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count8) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') + if data__readme_one_of_count9 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count9) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') if "requires-python" in data_keys: data_keys.remove("requires-python") data__requirespython = data["requires-python"] @@ -752,8 +767,8 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if "license" in data_keys: data_keys.remove("license") data__license = data["license"] - data__license_one_of_count10 = 0 - if data__license_one_of_count10 < 2: + data__license_one_of_count11 = 0 + if data__license_one_of_count11 < 2: try: data__license_is_dict = isinstance(data__license, dict) if data__license_is_dict: @@ -766,9 +781,9 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro data__license__file = data__license["file"] if not isinstance(data__license__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.file must be string", value=data__license__file, name="" + (name_prefix or "data") + ".license.file", definition={'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}, rule='type') - data__license_one_of_count10 += 1 + data__license_one_of_count11 += 1 except JsonSchemaValueException: pass - if data__license_one_of_count10 < 2: + if data__license_one_of_count11 < 2: try: data__license_is_dict = isinstance(data__license, dict) if data__license_is_dict: @@ -781,30 +796,30 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro data__license__text = data__license["text"] if not isinstance(data__license__text, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.text must be string", value=data__license__text, name="" + (name_prefix or "data") + ".license.text", definition={'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}, rule='type') - data__license_one_of_count10 += 1 + data__license_one_of_count11 += 1 except JsonSchemaValueException: pass - if data__license_one_of_count10 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count10) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, rule='oneOf') + if data__license_one_of_count11 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count11) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, rule='oneOf') if "authors" in data_keys: data_keys.remove("authors") data__authors = data["authors"] if not isinstance(data__authors, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".authors must be array", value=data__authors, name="" + (name_prefix or "data") + ".authors", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".authors must be array", value=data__authors, name="" + (name_prefix or "data") + ".authors", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, rule='type') data__authors_is_list = isinstance(data__authors, (list, tuple)) if data__authors_is_list: data__authors_len = len(data__authors) for data__authors_x, data__authors_item in enumerate(data__authors): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__authors_item, custom_formats, (name_prefix or "data") + ".authors[{data__authors_x}]") + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__authors_item, custom_formats, (name_prefix or "data") + ".authors[{data__authors_x}]".format(**locals())) if "maintainers" in data_keys: data_keys.remove("maintainers") data__maintainers = data["maintainers"] if not isinstance(data__maintainers, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".maintainers must be array", value=data__maintainers, name="" + (name_prefix or "data") + ".maintainers", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".maintainers must be array", value=data__maintainers, name="" + (name_prefix or "data") + ".maintainers", definition={'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, rule='type') data__maintainers_is_list = isinstance(data__maintainers, (list, tuple)) if data__maintainers_is_list: data__maintainers_len = len(data__maintainers) for data__maintainers_x, data__maintainers_item in enumerate(data__maintainers): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__maintainers_item, custom_formats, (name_prefix or "data") + ".maintainers[{data__maintainers_x}]") + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__maintainers_item, custom_formats, (name_prefix or "data") + ".maintainers[{data__maintainers_x}]".format(**locals())) if "keywords" in data_keys: data_keys.remove("keywords") data__keywords = data["keywords"] @@ -867,7 +882,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if REGEX_PATTERNS['^.+$'].search(data__entrypoints_key): if data__entrypoints_key in data__entrypoints_keys: data__entrypoints_keys.remove(data__entrypoints_key) - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__entrypoints_val, custom_formats, (name_prefix or "data") + ".entry-points.{data__entrypoints_key}") + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__entrypoints_val, custom_formats, (name_prefix or "data") + ".entry-points.{data__entrypoints_key}".format(**locals())) if data__entrypoints_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must not contain "+str(data__entrypoints_keys)+" properties", value=data__entrypoints, name="" + (name_prefix or "data") + ".entry-points", definition={'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, rule='additionalProperties') data__entrypoints_len = len(data__entrypoints) @@ -891,7 +906,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__dependencies_is_list: data__dependencies_len = len(data__dependencies) for data__dependencies_x, data__dependencies_item in enumerate(data__dependencies): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__dependencies_item, custom_formats, (name_prefix or "data") + ".dependencies[{data__dependencies_x}]") + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__dependencies_item, custom_formats, (name_prefix or "data") + ".dependencies[{data__dependencies_x}]".format(**locals())) if "optional-dependencies" in data_keys: data_keys.remove("optional-dependencies") data__optionaldependencies = data["optional-dependencies"] @@ -910,7 +925,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__optionaldependencies_val_is_list: data__optionaldependencies_val_len = len(data__optionaldependencies_val) for data__optionaldependencies_val_x, data__optionaldependencies_val_item in enumerate(data__optionaldependencies_val): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__optionaldependencies_val_item, custom_formats, (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}[{data__optionaldependencies_val_x}]") + validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__optionaldependencies_val_item, custom_formats, (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}[{data__optionaldependencies_val_x}]".format(**locals())) if data__optionaldependencies_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must not contain "+str(data__optionaldependencies_keys)+" properties", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') data__optionaldependencies_len = len(data__optionaldependencies) @@ -937,7 +952,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__dynamic_item not in ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']: raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']", value=data__dynamic_item, name="" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + "", definition={'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}, rule='enum') if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='additionalProperties') try: try: data_is_dict = isinstance(data, dict) @@ -1015,7 +1030,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://www.python.org/dev/peps/pep-0621/#authors-maintainers', 'type': 'object', 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -1032,4 +1047,6 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if isinstance(data__email, str): if not REGEX_PATTERNS["idn-email_re_pattern"].match(data__email): raise JsonSchemaValueException("" + (name_prefix or "data") + ".email must be idn-email", value=data__email, name="" + (name_prefix or "data") + ".email", definition={'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}, rule='format') + if data_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, rule='additionalProperties') return data \ No newline at end of file diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py index 638ac1195..486d52602 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/_validate_pyproject/formats.py @@ -5,6 +5,9 @@ import typing from itertools import chain as _chain +if typing.TYPE_CHECKING: + from typing_extensions import Literal + _logger = logging.getLogger(__name__) # ------------------------------------------------------------------------------------- @@ -131,8 +134,10 @@ class _TroveClassifier: option (classifiers will be validated anyway during the upload to PyPI). """ + downloaded: typing.Union[None, "Literal[False]", typing.Set[str]] + def __init__(self): - self.downloaded: typing.Union[None, False, typing.Set[str]] = None + self.downloaded = None self._skip_download = False # None => not cached yet # False => cache not available @@ -181,6 +186,17 @@ def trove_classifier(value: str) -> bool: trove_classifier = _TroveClassifier() +# ------------------------------------------------------------------------------------- +# Stub packages - PEP 561 + + +def pep561_stub_name(value: str) -> bool: + top, *children = value.split(".") + if not top.endswith("-stubs"): + return False + return python_module_name(".".join([top[: -len("-stubs")], *children])) + + # ------------------------------------------------------------------------------------- # Non-PEP related diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/pyprojecttoml.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/pyprojecttoml.py index d995f0bcc..9ce550222 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/pyprojecttoml.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/pyprojecttoml.py @@ -8,7 +8,7 @@ import warnings from contextlib import contextmanager from functools import partial -from typing import TYPE_CHECKING, Callable, Dict, Optional, Mapping, Union +from typing import TYPE_CHECKING, Callable, Dict, Optional, Mapping, Set, Union from setuptools.errors import FileError, OptionError @@ -84,8 +84,8 @@ def read_configuration( :param Distribution|None: Distribution object to which the configuration refers. If not given a dummy object will be created and discarded after the - configuration is read. This is used for auto-discovery of packages in the case - a dynamic configuration (e.g. ``attr`` or ``cmdclass``) is expanded. + configuration is read. This is used for auto-discovery of packages and in the + case a dynamic configuration (e.g. ``attr`` or ``cmdclass``) is expanded. When ``expand=False`` this object is simply ignored. :rtype: dict @@ -112,7 +112,7 @@ def read_configuration( # `ini2toml` backfills include_package_data=False when nothing is explicitly given, # therefore setting a default here is backwards compatible. orig_setuptools_table = setuptools_table.copy() - if dist and getattr(dist, "include_package_data") is not None: + if dist and getattr(dist, "include_package_data", None) is not None: setuptools_table.setdefault("include-package-data", dist.include_package_data) else: setuptools_table.setdefault("include-package-data", True) @@ -211,6 +211,7 @@ def __init__( self.dynamic_cfg = self.setuptools_cfg.get("dynamic", {}) self.ignore_option_errors = ignore_option_errors self._dist = dist + self._referenced_files: Set[str] = set() def _ensure_dist(self) -> "Distribution": from setuptools.dist import Distribution @@ -241,6 +242,7 @@ def expand(self): self._expand_cmdclass(package_dir) self._expand_all_dynamic(dist, package_dir) + dist._referenced_files.update(self._referenced_files) return self.config def _expand_packages(self): @@ -307,9 +309,12 @@ def _ensure_previously_set(self, dist: "Distribution", field: str): def _expand_directive( self, specifier: str, directive, package_dir: Mapping[str, str] ): + from setuptools.extern.more_itertools import always_iterable # type: ignore + with _ignore_errors(self.ignore_option_errors): root_dir = self.root_dir if "file" in directive: + self._referenced_files.update(always_iterable(directive["file"])) return _expand.read_files(directive["file"], root_dir) if "attr" in directive: return _expand.read_attr(directive["attr"], package_dir, root_dir) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/setupcfg.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/setupcfg.py index c2a974de6..03a446fd2 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/setupcfg.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/config/setupcfg.py @@ -11,11 +11,24 @@ from collections import defaultdict from functools import partial from functools import wraps -from typing import (TYPE_CHECKING, Callable, Any, Dict, Generic, Iterable, List, - Optional, Tuple, TypeVar, Union) +from typing import ( + TYPE_CHECKING, + Callable, + Any, + Dict, + Generic, + Iterable, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, +) from distutils.errors import DistutilsOptionError, DistutilsFileError from setuptools.extern.packaging.requirements import Requirement, InvalidRequirement +from setuptools.extern.packaging.markers import default_environment as marker_env from setuptools.extern.packaging.version import Version, InvalidVersion from setuptools.extern.packaging.specifiers import SpecifierSet from setuptools._deprecation_warning import SetuptoolsDeprecationWarning @@ -38,9 +51,7 @@ def read_configuration( - filepath: _Path, - find_others=False, - ignore_option_errors=False + filepath: _Path, find_others=False, ignore_option_errors=False ) -> dict: """Read given configuration file and returns options from it as a dict. @@ -75,7 +86,8 @@ def apply_configuration(dist: "Distribution", filepath: _Path) -> "Distribution" def _apply( - dist: "Distribution", filepath: _Path, + dist: "Distribution", + filepath: _Path, other_files: Iterable[_Path] = (), ignore_option_errors: bool = False, ) -> Tuple["ConfigHandler", ...]: @@ -136,7 +148,7 @@ def configuration_to_dict(handlers: Tuple["ConfigHandler", ...]) -> dict: def parse_configuration( distribution: "Distribution", command_options: AllCommandOptions, - ignore_option_errors=False + ignore_option_errors=False, ) -> Tuple["ConfigMetadataHandler", "ConfigOptionsHandler"]: """Performs additional parsing of configuration options for a distribution. @@ -172,6 +184,9 @@ def parse_configuration( distribution.src_root, ) meta.parse() + distribution._referenced_files.update( + options._referenced_files, meta._referenced_files + ) return meta, options @@ -196,17 +211,21 @@ def _warn_accidental_env_marker_misconfig(label: str, orig_value: str, parsed: l if "\n" in orig_value or len(parsed) != 2: return - with contextlib.suppress(InvalidRequirement): - original_requirements_str = ";".join(parsed) - req = Requirement(original_requirements_str) - if req.marker is not None: - msg = ( - f"One of the parsed requirements in `{label}` " - f"looks like a valid environment marker: '{parsed[1]}'\n" - "Make sure that the config is correct and check " - "https://setuptools.pypa.io/en/latest/userguide/declarative_config.html#opt-2" # noqa: E501 - ) - warnings.warn(msg, UserWarning) + markers = marker_env().keys() + msg = ( + f"One of the parsed requirements in `{label}` " + f"looks like a valid environment marker: '{parsed[1]}'\n" + "Make sure that the config is correct and check " + "https://setuptools.pypa.io/en/latest/userguide/declarative_config.html#opt-2" # noqa: E501 + ) + + try: + req = Requirement(parsed[1]) + if req.name in markers: + warnings.warn(msg) + except InvalidRequirement as ex: + if any(parsed[1].startswith(marker) for marker in markers): + raise InvalidRequirement(msg) from ex class ConfigHandler(Generic[Target]): @@ -232,21 +251,23 @@ def __init__( ignore_option_errors, ensure_discovered: expand.EnsurePackagesDiscovered, ): - sections: AllCommandOptions = {} - - section_prefix = self.section_prefix - for section_name, section_options in options.items(): - if not section_name.startswith(section_prefix): - continue - - section_name = section_name.replace(section_prefix, '').strip('.') - sections[section_name] = section_options - self.ignore_option_errors = ignore_option_errors self.target_obj = target_obj - self.sections = sections + self.sections = dict(self._section_options(options)) self.set_options: List[str] = [] self.ensure_discovered = ensure_discovered + self._referenced_files: Set[str] = set() + """After parsing configurations, this property will enumerate + all files referenced by the "file:" directive. Private API for setuptools only. + """ + + @classmethod + def _section_options(cls, options: AllCommandOptions): + for full_name, value in options.items(): + pre, sep, name = full_name.partition(cls.section_prefix) + if pre: + continue + yield name.lstrip('.'), value @property def parsers(self): @@ -256,40 +277,28 @@ def parsers(self): ) def __setitem__(self, option_name, value): - unknown = tuple() target_obj = self.target_obj # Translate alias into real name. option_name = self.aliases.get(option_name, option_name) - current_value = getattr(target_obj, option_name, unknown) - - if current_value is unknown: + try: + current_value = getattr(target_obj, option_name) + except AttributeError: raise KeyError(option_name) if current_value: # Already inhabited. Skipping. return - skip_option = False - parser = self.parsers.get(option_name) - if parser: - try: - value = parser(value) - - except Exception: - skip_option = True - if not self.ignore_option_errors: - raise - - if skip_option: + try: + parsed = self.parsers.get(option_name, lambda x: x)(value) + except (Exception,) * self.ignore_option_errors: return - setter = getattr(target_obj, 'set_%s' % option_name, None) - if setter is None: - setattr(target_obj, option_name, value) - else: - setter(value) + simple_setter = functools.partial(target_obj.__setattr__, option_name) + setter = getattr(target_obj, 'set_%s' % option_name, simple_setter) + setter(parsed) self.set_options.append(option_name) @@ -365,8 +374,7 @@ def parser(value): return parser - @classmethod - def _parse_file(cls, value, root_dir: _Path): + def _parse_file(self, value, root_dir: _Path): """Represents value as a string, allowing including text from nearest files using `file:` directive. @@ -388,7 +396,8 @@ def _parse_file(cls, value, root_dir: _Path): return value spec = value[len(include_directive) :] - filepaths = (path.strip() for path in spec.split(',')) + filepaths = [path.strip() for path in spec.split(',')] + self._referenced_files.update(filepaths) return expand.read_files(filepaths, root_dir) def _parse_attr(self, value, package_dir, root_dir: _Path): @@ -464,7 +473,7 @@ def parse_section(self, section_options): :param dict section_options: """ - for (name, (_, value)) in section_options.items(): + for name, (_, value) in section_options.items(): with contextlib.suppress(KeyError): # Keep silent for a new option may appear anytime. self[name] = value @@ -475,7 +484,6 @@ def parse(self): """ for section_name, section_options in self.sections.items(): - method_postfix = '' if section_name: # [section.option] variant method_postfix = '_%s' % section_name @@ -512,7 +520,6 @@ def config_handler(*args, **kwargs): class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]): - section_prefix = 'metadata' aliases = { @@ -535,7 +542,7 @@ def __init__( ignore_option_errors: bool, ensure_discovered: expand.EnsurePackagesDiscovered, package_dir: Optional[dict] = None, - root_dir: _Path = os.curdir + root_dir: _Path = os.curdir, ): super().__init__(target_obj, options, ignore_option_errors, ensure_discovered) self.package_dir = package_dir @@ -603,7 +610,6 @@ def _parse_version(self, value): class ConfigOptionsHandler(ConfigHandler["Distribution"]): - section_prefix = 'options' def __init__( @@ -748,7 +754,7 @@ def parse_section_extras_require(self, section_options): """ parsed = self._parse_section_to_dict_with_key( section_options, - lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v) + lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v), ) self['extras_require'] = parsed diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/discovery.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/discovery.py index 98fc2a7f4..3110b7279 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/discovery.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/discovery.py @@ -44,7 +44,6 @@ from pathlib import Path from typing import ( TYPE_CHECKING, - Callable, Dict, Iterable, Iterator, @@ -61,7 +60,6 @@ from distutils.util import convert_path _Path = Union[str, os.PathLike] -_Filter = Callable[[str], bool] StrIter = Iterator[str] chain_iter = itertools.chain.from_iterable @@ -75,6 +73,22 @@ def _valid_name(path: _Path) -> bool: return os.path.basename(path).isidentifier() +class _Filter: + """ + Given a list of patterns, create a callable that will be true only if + the input matches at least one of the patterns. + """ + + def __init__(self, *patterns: str): + self._patterns = dict.fromkeys(patterns) + + def __call__(self, item: str) -> bool: + return any(fnmatchcase(item, pat) for pat in self._patterns) + + def __contains__(self, item: str) -> bool: + return item in self._patterns + + class _Finder: """Base class that exposes functionality for module/package finders""" @@ -111,8 +125,8 @@ def find( return list( cls._find_iter( convert_path(str(where)), - cls._build_filter(*cls.ALWAYS_EXCLUDE, *exclude), - cls._build_filter(*include), + _Filter(*cls.ALWAYS_EXCLUDE, *exclude), + _Filter(*include), ) ) @@ -120,14 +134,6 @@ def find( def _find_iter(cls, where: _Path, exclude: _Filter, include: _Filter) -> StrIter: raise NotImplementedError - @staticmethod - def _build_filter(*patterns: str) -> _Filter: - """ - Given a list of patterns, return a callable that will be true only if - the input matches at least one of the patterns. - """ - return lambda name: any(fnmatchcase(name, pat) for pat in patterns) - class PackageFinder(_Finder): """ @@ -160,6 +166,10 @@ def _find_iter(cls, where: _Path, exclude: _Filter, include: _Filter) -> StrIter if include(package) and not exclude(package): yield package + # Early pruning if there is nothing else to be scanned + if f"{package}*" in exclude or f"{package}.*" in exclude: + continue + # Keep searching subdirectories, as there may be more packages # down there, even if the parent was excluded. dirs.append(dir) @@ -234,6 +244,7 @@ class FlatLayoutPackageFinder(PEP420PackageFinder): "benchmarks", "exercise", "exercises", + "htmlcov", # Coverage.py # ---- Hidden directories/Private packages ---- "[._]*", ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/dist.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/dist.py index 1c71e5eed..eb59f3a0a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/dist.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/dist.py @@ -17,7 +17,8 @@ from glob import iglob import itertools import textwrap -from typing import List, Optional, TYPE_CHECKING +from contextlib import suppress +from typing import List, Optional, Set, TYPE_CHECKING from pathlib import Path from collections import defaultdict @@ -32,7 +33,7 @@ from ._importlib import metadata -from . import SetuptoolsDeprecationWarning +from . import SetuptoolsDeprecationWarning, _normalization import setuptools import setuptools.command @@ -41,7 +42,6 @@ from setuptools.config import setupcfg, pyprojecttoml from setuptools.discovery import ConfigDiscovery -import pkg_resources from setuptools.extern.packaging import version from . import _reqs from . import _entry_points @@ -280,7 +280,9 @@ def check_nsp(dist, attr, value): ) msg = ( "The namespace_packages parameter is deprecated, " - "consider using implicit namespaces instead (PEP 420)." + "consider using implicit namespaces instead (PEP 420). " + "See https://setuptools.pypa.io/en/latest/references/" + "keywords.html#keyword-namespace-packages" ) warnings.warn(msg, SetuptoolsDeprecationWarning) @@ -299,11 +301,21 @@ def check_extras(dist, attr, value): def _check_extra(extra, reqs): name, sep, marker = extra.partition(':') - if marker and pkg_resources.invalid_marker(marker): - raise DistutilsSetupError("Invalid environment marker: " + marker) + try: + _check_marker(marker) + except packaging.markers.InvalidMarker: + msg = f"Invalid environment marker: {marker} ({extra!r})" + raise DistutilsSetupError(msg) from None list(_reqs.parse(reqs)) +def _check_marker(marker): + if not marker: + return + m = packaging.markers.Marker(marker) + m.evaluate() + + def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: @@ -453,11 +465,12 @@ def patch_missing_pkg_info(self, attrs): # if not attrs or 'name' not in attrs or 'version' not in attrs: return - key = pkg_resources.safe_name(str(attrs['name'])).lower() - dist = pkg_resources.working_set.by_key.get(key) - if dist is not None and not dist.has_metadata('PKG-INFO'): - dist._version = pkg_resources.safe_version(str(attrs['version'])) - self._patched_dist = dist + name = _normalization.safe_name(str(attrs['name'])).lower() + with suppress(metadata.PackageNotFoundError): + dist = metadata.distribution(name) + if dist is not None and not dist.read_text('PKG-INFO'): + dist._version = _normalization.safe_version(str(attrs['version'])) + self._patched_dist = dist def __init__(self, attrs=None): have_package_data = hasattr(self, "package_data") @@ -481,6 +494,11 @@ def __init__(self, attrs=None): }, ) + # Private API (setuptools-use only, not restricted to Distribution) + # Stores files that are referenced by the configuration and need to be in the + # sdist (e.g. `version = file: VERSION.txt`) + self._referenced_files: Set[str] = set() + # Save the original dependencies before they are processed into the egg format self._orig_extras_require = {} self._orig_install_requires = [] @@ -871,14 +889,9 @@ def parse_config_files(self, filenames=None, ignore_option_errors=False): def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" - resolved_dists = pkg_resources.working_set.resolve( - _reqs.parse(requires), - installer=self.fetch_build_egg, - replace_conflicting=True, - ) - for dist in resolved_dists: - pkg_resources.working_set.add(dist, replace=True) - return resolved_dists + from setuptools.installer import _fetch_build_eggs + + return _fetch_build_eggs(self, requires) def finalize_options(self): """ diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/extern/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/extern/__init__.py index d3a6dc99f..bfd4d2d48 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/extern/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/extern/__init__.py @@ -70,7 +70,14 @@ def install(self): names = ( - 'packaging', 'pyparsing', 'ordered_set', 'more_itertools', 'importlib_metadata', - 'zipp', 'importlib_resources', 'jaraco', 'typing_extensions', 'tomli', + 'packaging', + 'ordered_set', + 'more_itertools', + 'importlib_metadata', + 'zipp', + 'importlib_resources', + 'jaraco', + 'typing_extensions', + 'tomli', ) VendorImporter(__name__, names, 'setuptools._vendor').install() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/installer.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/installer.py index b7096df14..e9a7567ab 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/installer.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/installer.py @@ -6,9 +6,10 @@ import warnings from distutils import log from distutils.errors import DistutilsError +from functools import partial -import pkg_resources -from setuptools.wheel import Wheel +from . import _reqs +from .wheel import Wheel from ._deprecation_warning import SetuptoolsDeprecationWarning @@ -20,20 +21,34 @@ def _fixup_find_links(find_links): return find_links -def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME +def fetch_build_egg(dist, req): """Fetch an egg needed for building. Use pip/wheel to fetch/build a wheel.""" - warnings.warn( - "setuptools.installer is deprecated. Requirements should " - "be satisfied by a PEP 517 installer.", - SetuptoolsDeprecationWarning, + _DeprecatedInstaller.warn(stacklevel=2) + _warn_wheel_not_available(dist) + return _fetch_build_egg_no_warn(dist, req) + + +def _fetch_build_eggs(dist, requires): + import pkg_resources # Delay import to avoid unnecessary side-effects + + _DeprecatedInstaller.warn(stacklevel=3) + _warn_wheel_not_available(dist) + + resolved_dists = pkg_resources.working_set.resolve( + _reqs.parse(requires, pkg_resources.Requirement), # required for compatibility + installer=partial(_fetch_build_egg_no_warn, dist), # avoid warning twice + replace_conflicting=True, ) - # Warn if wheel is not available - try: - pkg_resources.get_distribution('wheel') - except pkg_resources.DistributionNotFound: - dist.announce('WARNING: The wheel package is not available.', log.WARN) + for dist in resolved_dists: + pkg_resources.working_set.add(dist, replace=True) + return resolved_dists + + +def _fetch_build_egg_no_warn(dist, req): # noqa: C901 # is too complex (16) # FIXME + import pkg_resources # Delay import to avoid unnecessary side-effects + # Ignore environment markers; if supplied, it is required. req = strip_marker(req) # Take easy_install options into account, but do not override relevant @@ -98,7 +113,30 @@ def strip_marker(req): calling pip with something like `babel; extra == "i18n"`, which would always be ignored. """ + import pkg_resources # Delay import to avoid unnecessary side-effects + # create a copy to avoid mutating the input req = pkg_resources.Requirement.parse(str(req)) req.marker = None return req + + +def _warn_wheel_not_available(dist): + import pkg_resources # Delay import to avoid unnecessary side-effects + + try: + pkg_resources.get_distribution('wheel') + except pkg_resources.DistributionNotFound: + dist.announce('WARNING: The wheel package is not available.', log.WARN) + + +class _DeprecatedInstaller(SetuptoolsDeprecationWarning): + @classmethod + def warn(cls, stacklevel=1): + warnings.warn( + "setuptools.installer and fetch_build_eggs are deprecated. " + "Requirements should be satisfied by a PEP 517 installer. " + "If you are using pip, you can try `pip install --use-pep517`.", + cls, + stacklevel=stacklevel+1 + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/monkey.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/monkey.py index 77a7adcf8..50653fc7e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/monkey.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/monkey.py @@ -157,9 +157,3 @@ def patch_params(mod_name, func_name): patch_func(*msvc14('_get_vc_env')) except ImportError: pass - - try: - # Patch distutils._msvccompiler.gen_lib_options for Numpy - patch_func(*msvc14('gen_lib_options')) - except ImportError: - pass diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/msvc.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/msvc.py index 5d4d7759c..26d4e74f9 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/msvc.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/msvc.py @@ -15,17 +15,13 @@ from io import open from os import listdir, pathsep from os.path import join, isfile, isdir, dirname -import sys import contextlib import platform import itertools import subprocess import distutils.errors -from setuptools.extern.packaging.version import LegacyVersion from setuptools.extern.more_itertools import unique_everseen -from .monkey import get_unpatched - if platform.system() == 'Windows': import winreg from os import environ @@ -217,19 +213,6 @@ def msvc14_get_vc_env(plat_spec): raise -def msvc14_gen_lib_options(*args, **kwargs): - """ - Patched "distutils._msvccompiler.gen_lib_options" for fix - compatibility between "numpy.distutils" and "distutils._msvccompiler" - (for Numpy < 1.11.2) - """ - if "numpy.distutils" in sys.modules: - import numpy as np - if LegacyVersion(np.__version__) < LegacyVersion('1.11.2'): - return np.distutils.ccompiler.gen_lib_options(*args, **kwargs) - return get_unpatched(msvc14_gen_lib_options)(*args, **kwargs) - - def _augment_exception(exc, version, arch=''): """ Add details to the exception message to help guide the user diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/package_index.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/package_index.py index 362e26f3e..bec418353 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/package_index.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/package_index.py @@ -169,39 +169,35 @@ def distros_for_filename(filename, metadata=None): def interpret_distro_name( location, basename, metadata, py_version=None, precedence=SOURCE_DIST, platform=None ): - """Generate alternative interpretations of a source distro name + """Generate the interpretation of a source distro name Note: if `location` is a filesystem filename, you should call ``pkg_resources.normalize_path()`` on it before passing it to this routine! """ - # Generate alternative interpretations of a source distro name - # Because some packages are ambiguous as to name/versions split - # e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc. - # So, we generate each possible interpretation (e.g. "adns, python-1.1.0" - # "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice, - # the spurious interpretations should be ignored, because in the event - # there's also an "adns" package, the spurious "python-1.1.0" version will - # compare lower than any numeric version number, and is therefore unlikely - # to match a request for it. It's still a potential problem, though, and - # in the long run PyPI and the distutils should go for "safe" names and - # versions in distribution archive names (sdist and bdist). parts = basename.split('-') if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]): # it is a bdist_dumb, not an sdist -- bail out return - for p in range(1, len(parts) + 1): - yield Distribution( - location, - metadata, - '-'.join(parts[:p]), - '-'.join(parts[p:]), - py_version=py_version, - precedence=precedence, - platform=platform, - ) + # find the pivot (p) that splits the name from the version. + # infer the version as the first item that has a digit. + for p in range(len(parts)): + if parts[p][:1].isdigit(): + break + else: + p = len(parts) + + yield Distribution( + location, + metadata, + '-'.join(parts[:p]), + '-'.join(parts[p:]), + py_version=py_version, + precedence=precedence, + platform=platform + ) def unique_values(func): diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/version.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/version.py index 95e186965..75b2a1495 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/version.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/version.py @@ -1,6 +1,6 @@ -import pkg_resources +from ._importlib import metadata try: - __version__ = pkg_resources.get_distribution('setuptools').version + __version__ = metadata.version('setuptools') except Exception: __version__ = 'unknown' diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/wheel.py b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/wheel.py index 527ed3b23..850e43cd0 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/setuptools/wheel.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/setuptools/wheel.py @@ -2,6 +2,7 @@ import email import itertools +import functools import os import posixpath import re @@ -10,12 +11,11 @@ from distutils.util import get_platform -import pkg_resources import setuptools -from pkg_resources import parse_version +from setuptools.extern.packaging.version import Version as parse_version from setuptools.extern.packaging.tags import sys_tags from setuptools.extern.packaging.utils import canonicalize_name -from setuptools.command.egg_info import write_requirements +from setuptools.command.egg_info import write_requirements, _egg_basename from setuptools.archive_util import _unpack_zipfile_obj @@ -29,6 +29,14 @@ "__import__('pkg_resources').declare_namespace(__name__)\n" +@functools.lru_cache(maxsize=None) +def _get_supported_tags(): + # We calculate the supported tags only once, otherwise calling + # this method on thousands of wheels takes seconds instead of + # milliseconds. + return {(t.interpreter, t.abi, t.platform) for t in sys_tags()} + + def unpack(src_dir, dst_dir): '''Move everything under `src_dir` to `dst_dir`, and delete the former.''' for dirpath, dirnames, filenames in os.walk(src_dir): @@ -83,16 +91,15 @@ def tags(self): ) def is_compatible(self): - '''Is the wheel is compatible with the current platform?''' - supported_tags = set( - (t.interpreter, t.abi, t.platform) for t in sys_tags()) - return next((True for t in self.tags() if t in supported_tags), False) + '''Is the wheel compatible with the current platform?''' + return next((True for t in self.tags() if t in _get_supported_tags()), False) def egg_name(self): - return pkg_resources.Distribution( - project_name=self.project_name, version=self.version, + return _egg_basename( + self.project_name, + self.version, platform=(None if self.platform == 'any' else get_platform()), - ).egg_name() + '.egg' + ) + ".egg" def get_dist_info(self, zf): # find the correct name of the .dist-info dir in the wheel file @@ -121,6 +128,8 @@ def _install_as_egg(self, destination_eggdir, zf): @staticmethod def _convert_metadata(zf, destination_eggdir, dist_info, egg_info): + import pkg_resources + def get_metadata(name): with zf.open(posixpath.join(dist_info, name)) as fp: value = fp.read().decode('utf-8') diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE deleted file mode 100644 index de6633112..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2010-2020 Benjamin Peterson - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA deleted file mode 100644 index 6d7525c2e..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/METADATA +++ /dev/null @@ -1,49 +0,0 @@ -Metadata-Version: 2.1 -Name: six -Version: 1.16.0 -Summary: Python 2 and 3 compatibility utilities -Home-page: https://github.com/benjaminp/six -Author: Benjamin Peterson -Author-email: benjamin@python.org -License: MIT -Platform: UNKNOWN -Classifier: Development Status :: 5 - Production/Stable -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 3 -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Topic :: Software Development :: Libraries -Classifier: Topic :: Utilities -Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.* - -.. image:: https://img.shields.io/pypi/v/six.svg - :target: https://pypi.org/project/six/ - :alt: six on PyPI - -.. image:: https://travis-ci.org/benjaminp/six.svg?branch=master - :target: https://travis-ci.org/benjaminp/six - :alt: six on TravisCI - -.. image:: https://readthedocs.org/projects/six/badge/?version=latest - :target: https://six.readthedocs.io/ - :alt: six's documentation on Read the Docs - -.. image:: https://img.shields.io/badge/license-MIT-green.svg - :target: https://github.com/benjaminp/six/blob/master/LICENSE - :alt: MIT License badge - -Six is a Python 2 and 3 compatibility library. It provides utility functions -for smoothing over the differences between the Python versions with the goal of -writing Python code that is compatible on both Python versions. See the -documentation for more information on what is provided. - -Six supports Python 2.7 and 3.3+. It is contained in only one Python -file, so it can be easily copied into your project. (The copyright and license -notice must be retained.) - -Online documentation is at https://six.readthedocs.io/. - -Bugs can be reported to https://github.com/benjaminp/six. The code can also -be found there. - - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD deleted file mode 100644 index ed0b59873..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -__pycache__/six.cpython-310.pyc,, -six-1.16.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -six-1.16.0.dist-info/LICENSE,sha256=i7hQxWWqOJ_cFvOkaWWtI9gq3_YPI5P8J2K2MYXo5sk,1066 -six-1.16.0.dist-info/METADATA,sha256=VQcGIFCAEmfZcl77E5riPCN4v2TIsc_qtacnjxKHJoI,1795 -six-1.16.0.dist-info/RECORD,, -six-1.16.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 -six-1.16.0.dist-info/top_level.txt,sha256=_iVH_iYEtEXnD8nYGQYpYFUvkUW9sEO1GYbkeKSAais,4 -six.py,sha256=TOOfQi7nFGfMrIvtdr6wX4wyHH8M7aknmuLfo2cBBrM,34549 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL deleted file mode 100644 index 01b8fc7d4..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.36.2) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt deleted file mode 100644 index ffe2fce49..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six-1.16.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -six diff --git a/dependencies/windows_amd64/python/Lib/site-packages/six.py b/dependencies/windows_amd64/python/Lib/site-packages/six.py deleted file mode 100644 index 4e15675d8..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/six.py +++ /dev/null @@ -1,998 +0,0 @@ -# Copyright (c) 2010-2020 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.16.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - -if PY34: - from importlib.util import spec_from_loader -else: - spec_from_loader = None - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def find_spec(self, fullname, path, target=None): - if fullname in self.known_modules: - return spec_from_loader(fullname, self) - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - - def create_module(self, spec): - return self.load_module(spec.name) - - def exec_module(self, module): - pass - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - del io - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" - _assertNotRegex = "assertNotRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - _assertNotRegex = "assertNotRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -def assertNotRegex(self, *args, **kwargs): - return getattr(self, _assertNotRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] > (3,): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - # This does exactly the same what the :func:`py3:functools.update_wrapper` - # function does on Python versions after 3.2. It sets the ``__wrapped__`` - # attribute on ``wrapper`` object and it doesn't raise an error if any of - # the attributes mentioned in ``assigned`` and ``updated`` are missing on - # ``wrapped`` object. - def _update_wrapper(wrapper, wrapped, - assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - for attr in assigned: - try: - value = getattr(wrapped, attr) - except AttributeError: - continue - else: - setattr(wrapper, attr, value) - for attr in updated: - getattr(wrapper, attr).update(getattr(wrapped, attr, {})) - wrapper.__wrapped__ = wrapped - return wrapper - _update_wrapper.__doc__ = functools.update_wrapper.__doc__ - - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - return functools.partial(_update_wrapper, wrapped=wrapped, - assigned=assigned, updated=updated) - wraps.__doc__ = functools.wraps.__doc__ - -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - if sys.version_info[:2] >= (3, 7): - # This version introduced PEP 560 that requires a bit - # of extra care (we mimic what is done by __build_class__). - resolved_bases = types.resolve_bases(bases) - if resolved_bases is not bases: - d['__orig_bases__'] = bases - else: - resolved_bases = bases - return meta(name, resolved_bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - if hasattr(cls, '__qualname__'): - orig_vars['__qualname__'] = cls.__qualname__ - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def ensure_binary(s, encoding='utf-8', errors='strict'): - """Coerce **s** to six.binary_type. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> encoded to `bytes` - - `bytes` -> `bytes` - """ - if isinstance(s, binary_type): - return s - if isinstance(s, text_type): - return s.encode(encoding, errors) - raise TypeError("not expecting type '%s'" % type(s)) - - -def ensure_str(s, encoding='utf-8', errors='strict'): - """Coerce *s* to `str`. - - For Python 2: - - `unicode` -> encoded to `str` - - `str` -> `str` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - # Optimization: Fast return for the common case. - if type(s) is str: - return s - if PY2 and isinstance(s, text_type): - return s.encode(encoding, errors) - elif PY3 and isinstance(s, binary_type): - return s.decode(encoding, errors) - elif not isinstance(s, (text_type, binary_type)): - raise TypeError("not expecting type '%s'" % type(s)) - return s - - -def ensure_text(s, encoding='utf-8', errors='strict'): - """Coerce *s* to six.text_type. - - For Python 2: - - `unicode` -> `unicode` - - `str` -> `unicode` - - For Python 3: - - `str` -> `str` - - `bytes` -> decoded to `str` - """ - if isinstance(s, binary_type): - return s.decode(encoding, errors) - elif isinstance(s, text_type): - return s - else: - raise TypeError("not expecting type '%s'" % type(s)) - - -def python_2_unicode_compatible(klass): - """ - A class decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/INSTALLER deleted file mode 100644 index a1b589e38..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD deleted file mode 100644 index 5c2f70a15..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/RECORD +++ /dev/null @@ -1,43 +0,0 @@ -../../Scripts/wheel.exe,sha256=JPGdDoSzEi6F9BWdUC6piDtUCwaDdIvNcEWSoDjYIu4,108438 -wheel-0.38.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -wheel-0.38.4.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107 -wheel-0.38.4.dist-info/METADATA,sha256=3j4KgVZCY7eZyOrwDKYoTuAcfr_gXAbxx1yGhR9DssA,2110 -wheel-0.38.4.dist-info/RECORD,, -wheel-0.38.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -wheel-0.38.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92 -wheel-0.38.4.dist-info/entry_points.txt,sha256=krg-iHKefnsk1qvNLDkZP3-4Aq3J0F_zJaathht0JBI,107 -wheel-0.38.4.dist-info/top_level.txt,sha256=HxSBIbgEstMPe4eFawhA66Mq-QYHMopXVoAncfjb_1c,6 -wheel/__init__.py,sha256=2wJrg-twJVHIbVXveZjxyMtxjelZOVff9bnhTBt3eec,59 -wheel/__main__.py,sha256=NkMUnuTCGcOkgY0IBLgBCVC_BGGcWORx2K8jYGS12UE,455 -wheel/__pycache__/__init__.cpython-310.pyc,, -wheel/__pycache__/__main__.cpython-310.pyc,, -wheel/__pycache__/_setuptools_logging.cpython-310.pyc,, -wheel/__pycache__/bdist_wheel.cpython-310.pyc,, -wheel/__pycache__/macosx_libfile.cpython-310.pyc,, -wheel/__pycache__/metadata.cpython-310.pyc,, -wheel/__pycache__/util.cpython-310.pyc,, -wheel/__pycache__/wheelfile.cpython-310.pyc,, -wheel/_setuptools_logging.py,sha256=NoCnjJ4DFEZ45Eo-2BdXLsWJCwGkait1tp_17paleVw,746 -wheel/bdist_wheel.py,sha256=k_gee2yY4TfDr_dRODcyCso6ItpidTLj6JZfbvZ93qk,19293 -wheel/cli/__init__.py,sha256=NVh8x79QGybwZpL5VMQgQv-zwSg_6uKsYVXbIVRkpQ4,2384 -wheel/cli/__pycache__/__init__.cpython-310.pyc,, -wheel/cli/__pycache__/convert.cpython-310.pyc,, -wheel/cli/__pycache__/pack.cpython-310.pyc,, -wheel/cli/__pycache__/unpack.cpython-310.pyc,, -wheel/cli/convert.py,sha256=skUf4TuZcksqG75J-_KUkFXdmYDxTJpP311O16cNJ50,9427 -wheel/cli/pack.py,sha256=zQ1zmJouN8Y86hCJ3lTaKh6g7SbqgA2MuKP1wY-vjfw,3383 -wheel/cli/unpack.py,sha256=QU_OVMCvYWtrjQa18Z5ZKaZwGBImAqJIzQokYt2u3bI,659 -wheel/macosx_libfile.py,sha256=OXM6OTx1O_ACLGBE2Q9prIHj47uWkZSZSuM_756W89Q,16145 -wheel/metadata.py,sha256=-6n1-hH8YtmUV8zsrRf206iXPJJWyE9tqlqZv5XGpSg,3727 -wheel/util.py,sha256=e0jpnsbbM9QhaaMSyap-_ZgUxcxwpyLDk6RHcrduPLg,621 -wheel/vendored/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -wheel/vendored/__pycache__/__init__.cpython-310.pyc,, -wheel/vendored/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -wheel/vendored/packaging/__pycache__/__init__.cpython-310.pyc,, -wheel/vendored/packaging/__pycache__/_manylinux.cpython-310.pyc,, -wheel/vendored/packaging/__pycache__/_musllinux.cpython-310.pyc,, -wheel/vendored/packaging/__pycache__/tags.cpython-310.pyc,, -wheel/vendored/packaging/_manylinux.py,sha256=1OWKAD6xtgTgOGVEhujFWqF35JWqMvQLxZEZ2QlHI9g,11489 -wheel/vendored/packaging/_musllinux.py,sha256=k9vZj4tmx0ElF-2y8h7gbz09zCfhEjZ9ZQbeZr5sVds,4374 -wheel/vendored/packaging/tags.py,sha256=M_DQI4zGnPq3hsRV9QWYf5SoMJmBB91gqqG9Jtyv-m8,15612 -wheel/wheelfile.py,sha256=9iWWOWcvVXSx26YdfK9QptRA1OHeRRjqEo7PEb631mI,7536 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/REQUESTED b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/REQUESTED deleted file mode 100644 index e69de29bb..000000000 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/WHEEL deleted file mode 100644 index 57e3d840d..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.38.4) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/entry_points.txt deleted file mode 100644 index 251855d95..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/entry_points.txt +++ /dev/null @@ -1,5 +0,0 @@ -[console_scripts] -wheel = wheel.cli:main - -[distutils.commands] -bdist_wheel = wheel.bdist_wheel:bdist_wheel diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/top_level.txt b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/top_level.txt deleted file mode 100644 index 2309722a9..000000000 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -wheel diff --git a/dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/INSTALLER b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/INSTALLER similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/cryptography-39.0.0.dist-info/INSTALLER rename to dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/INSTALLER diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/LICENSE.txt b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/LICENSE.txt similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/LICENSE.txt rename to dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/LICENSE.txt diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/METADATA b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/METADATA similarity index 87% rename from dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/METADATA rename to dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/METADATA index 7ca74eb5e..f018f474f 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.38.4.dist-info/METADATA +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/METADATA @@ -1,17 +1,12 @@ Metadata-Version: 2.1 Name: wheel -Version: 0.38.4 +Version: 0.40.0 Summary: A built-package format for Python -Home-page: https://github.com/pypa/wheel -Author: Daniel Holth -Author-email: dholth@fastmail.fm -Maintainer: Alex Grönholm -Maintainer-email: alex.gronholm@nextday.fi -License: MIT -Project-URL: Documentation, https://wheel.readthedocs.io/ -Project-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html -Project-URL: Issue Tracker, https://github.com/pypa/wheel/issues Keywords: wheel,packaging +Author-email: Daniel Holth +Maintainer-email: Alex Grönholm +Requires-Python: >=3.7 +Description-Content-Type: text/x-rst Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Topic :: System :: Archiving :: Packaging @@ -23,10 +18,11 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 -Requires-Python: >=3.7 -License-File: LICENSE.txt +Requires-Dist: pytest >= 6.0.0 ; extra == "test" +Project-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html +Project-URL: Documentation, https://wheel.readthedocs.io/ +Project-URL: Issue Tracker, https://github.com/pypa/wheel/issues Provides-Extra: test -Requires-Dist: pytest (>=3.0.0) ; extra == 'test' wheel ===== @@ -60,3 +56,4 @@ Everyone interacting in the wheel project's codebases, issue trackers, chat rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. .. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/RECORD b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/RECORD new file mode 100644 index 000000000..883d98353 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/RECORD @@ -0,0 +1,63 @@ +../../Scripts/wheel.exe,sha256=5hNhejgez5VRsy2kFzdD2KcuA69G-NgUB0owbkN6ZuY,108438 +wheel-0.40.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +wheel-0.40.0.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107 +wheel-0.40.0.dist-info/METADATA,sha256=lgTWaV1e5PLefpgNsml_Q9IhZOPDXqIMvI_1wEUxU1Y,2051 +wheel-0.40.0.dist-info/RECORD,, +wheel-0.40.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wheel-0.40.0.dist-info/WHEEL,sha256=rSgq_JpHF9fHR1lx53qwg_1-2LypZE_qmcuXbVUq948,81 +wheel-0.40.0.dist-info/entry_points.txt,sha256=rTY1BbkPHhkGMm4Q3F0pIzJBzW2kMxoG1oriffvGdA0,104 +wheel/__init__.py,sha256=KBZxcKJe72-jXEN_P3BdiGkSQqrcuMyKLVTxJ8lXREE,59 +wheel/__main__.py,sha256=NkMUnuTCGcOkgY0IBLgBCVC_BGGcWORx2K8jYGS12UE,455 +wheel/__pycache__/__init__.cpython-310.pyc,, +wheel/__pycache__/__main__.cpython-310.pyc,, +wheel/__pycache__/_setuptools_logging.cpython-310.pyc,, +wheel/__pycache__/bdist_wheel.cpython-310.pyc,, +wheel/__pycache__/macosx_libfile.cpython-310.pyc,, +wheel/__pycache__/metadata.cpython-310.pyc,, +wheel/__pycache__/util.cpython-310.pyc,, +wheel/__pycache__/wheelfile.cpython-310.pyc,, +wheel/_setuptools_logging.py,sha256=NoCnjJ4DFEZ45Eo-2BdXLsWJCwGkait1tp_17paleVw,746 +wheel/bdist_wheel.py,sha256=YQAoEJCZYudEqbto6LffrXgXAByFk1ArgcH_lRuF4mw,19868 +wheel/cli/__init__.py,sha256=C_b6gaHShwH-gilEcFxkVz9w7mjfsGK22mvGw6ucfw0,3932 +wheel/cli/__pycache__/__init__.cpython-310.pyc,, +wheel/cli/__pycache__/convert.cpython-310.pyc,, +wheel/cli/__pycache__/pack.cpython-310.pyc,, +wheel/cli/__pycache__/tags.cpython-310.pyc,, +wheel/cli/__pycache__/unpack.cpython-310.pyc,, +wheel/cli/convert.py,sha256=skUf4TuZcksqG75J-_KUkFXdmYDxTJpP311O16cNJ50,9427 +wheel/cli/pack.py,sha256=j6mMTDkR29E-QSdGD4eziG9UHwtRpaNoCNc2CtoXlxM,4338 +wheel/cli/tags.py,sha256=x0bF_qlI2JO2GiQierWZmIm5anSS7VKMQ9ZXTXRBQTk,5124 +wheel/cli/unpack.py,sha256=Y_J7ynxPSoFFTT7H0fMgbBlVErwyDGcObgme5MBuz58,1021 +wheel/macosx_libfile.py,sha256=mKH4GW3FILt0jLgm5LPgj7D5XyEvBU2Fgc-jCxMfSng,16143 +wheel/metadata.py,sha256=9Vg4rCGRQlrp4CMsyjvGwe3AE8jfXtX7-0Ig03gTcRI,5889 +wheel/util.py,sha256=e0jpnsbbM9QhaaMSyap-_ZgUxcxwpyLDk6RHcrduPLg,621 +wheel/vendored/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wheel/vendored/__pycache__/__init__.cpython-310.pyc,, +wheel/vendored/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wheel/vendored/packaging/__pycache__/__init__.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/_elffile.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/_manylinux.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/_musllinux.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/_parser.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/_structures.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/_tokenizer.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/markers.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/requirements.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/specifiers.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/tags.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/utils.cpython-310.pyc,, +wheel/vendored/packaging/__pycache__/version.cpython-310.pyc,, +wheel/vendored/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266 +wheel/vendored/packaging/_manylinux.py,sha256=uZ821PBqQrokhUbwe7E0UodEraMHqzoSgTvfJ8MIl30,8813 +wheel/vendored/packaging/_musllinux.py,sha256=mvPk7FNjjILKRLIdMxR7IvJ1uggLgCszo-L9rjfpi0M,2524 +wheel/vendored/packaging/_parser.py,sha256=jjFjSqNf7W2-Ta6YUkywK0P4d2i0Bz_MqLOfl7O1Tkw,9399 +wheel/vendored/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 +wheel/vendored/packaging/_tokenizer.py,sha256=czGibL-4oPofx1pCSt_hrozNbHlOPrqGv6m-0d-iTdo,5148 +wheel/vendored/packaging/markers.py,sha256=HDPXE0_MPBSwsw_9upez8t8mdrqUGrgiOG_qyQy-W30,8161 +wheel/vendored/packaging/requirements.py,sha256=4nOKheaBbVEQXTGSqaOGTy1Tkg7J_sEno3u8jxC-baw,3264 +wheel/vendored/packaging/specifiers.py,sha256=NX3JjilBf4Bs1abjIG8-ZKGv0QFs5xc43vO8GokHxXE,39047 +wheel/vendored/packaging/tags.py,sha256=fOKnZVfiU3oc9CPSzjJUsMk5VTfgOfpNhWobUH0sAlg,18065 +wheel/vendored/packaging/utils.py,sha256=es0cCezKspzriQ-3V88h3yJzxz028euV2sUwM61kE-o,4355 +wheel/vendored/packaging/version.py,sha256=_ULefmddLDLJ9VKRFAXhshEd0zP8OYPhcjCPfYolUbo,16295 +wheel/vendored/vendor.txt,sha256=D8elx6ZKLANY-irWC6duLu0MUph8_wUrdHHZvOgCfKs,16 +wheel/wheelfile.py,sha256=XGi8I1BMBx4chHtK0GVJ3XEFgOYXHEdtNstDIQtFHfE,7674 diff --git a/dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/REQUESTED b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/REQUESTED similarity index 100% rename from dependencies/windows_amd64/python/Lib/site-packages/setuptools-65.6.3.dist-info/REQUESTED rename to dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/REQUESTED diff --git a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/WHEEL b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/WHEEL similarity index 64% rename from dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/WHEEL rename to dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/WHEEL index becc9a66e..db4a255f3 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/pip-22.3.1.dist-info/WHEEL +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/WHEEL @@ -1,5 +1,4 @@ Wheel-Version: 1.0 -Generator: bdist_wheel (0.37.1) +Generator: flit 3.8.0 Root-Is-Purelib: true Tag: py3-none-any - diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/entry_points.txt b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/entry_points.txt new file mode 100644 index 000000000..06c9f69de --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel-0.40.0.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +wheel=wheel.cli:main + +[distutils.commands] +bdist_wheel=wheel.bdist_wheel:bdist_wheel + diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/__init__.py index ace3fc60e..39e37b83b 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/__init__.py @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "0.38.4" +__version__ = "0.40.0" diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/bdist_wheel.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/bdist_wheel.py index 7fcf4a379..28a9050bd 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/bdist_wheel.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/bdist_wheel.py @@ -21,7 +21,7 @@ from shutil import rmtree from zipfile import ZIP_DEFLATED, ZIP_STORED -import pkg_resources +import setuptools from setuptools import Command from . import __version__ as wheel_version @@ -29,13 +29,30 @@ from .metadata import pkginfo_to_metadata from .util import log from .vendored.packaging import tags +from .vendored.packaging import version as _packaging_version from .wheelfile import WheelFile -safe_name = pkg_resources.safe_name -safe_version = pkg_resources.safe_version -setuptools_major_version = int( - pkg_resources.get_distribution("setuptools").version.split(".")[0] -) + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub("[^A-Za-z0-9.]+", "-", name) + + +def safe_version(version): + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(_packaging_version.Version(version)) + except _packaging_version.InvalidVersion: + version = version.replace(" ", ".") + return re.sub("[^A-Za-z0-9.]+", "-", version) + + +setuptools_major_version = int(setuptools.__version__.split(".")[0]) PY_LIMITED_API_PATTERN = r"cp3\d" @@ -120,7 +137,6 @@ def remove_readonly(func, path, excinfo): class bdist_wheel(Command): - description = "create a wheel distribution" supported_compressions = OrderedDict( @@ -285,7 +301,9 @@ def get_tag(self): ): plat_name = "linux_i686" - plat_name = plat_name.lower().replace("-", "_").replace(".", "_") + plat_name = ( + plat_name.lower().replace("-", "_").replace(".", "_").replace(" ", "_") + ) if self.root_is_pure: if self.universal: @@ -529,7 +547,7 @@ def adios(p): # delete dependency_links if it is only whitespace dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt") - with open(dependency_links_path) as dependency_links_file: + with open(dependency_links_path, encoding="utf-8") as dependency_links_file: dependency_links = dependency_links_file.read().strip() if not dependency_links: adios(dependency_links_path) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/__init__.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/__init__.py index c0fb8c44a..fa1f10bc7 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/__init__.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/__init__.py @@ -31,12 +31,40 @@ def convert_f(args): convert(args.files, args.dest_dir, args.verbose) +def tags_f(args): + from .tags import tags + + names = ( + tags( + wheel, + args.python_tag, + args.abi_tag, + args.platform_tag, + args.build, + args.remove, + ) + for wheel in args.wheel + ) + + for name in names: + print(name) + + def version_f(args): from .. import __version__ print("wheel %s" % __version__) +TAGS_HELP = """\ +Make a new wheel with given tags. Any tags unspecified will remain the same. +Starting the tags with a "+" will append to the existing tags. Starting with a +"-" will remove a tag (use --option=-TAG syntax). Multiple tags can be +separated by ".". The original file will remain unless --remove is given. The +output filename(s) will be displayed on stdout for further processing. +""" + + def parser(): p = argparse.ArgumentParser() s = p.add_subparsers(help="commands") @@ -72,6 +100,27 @@ def parser(): convert_parser.add_argument("--verbose", "-v", action="store_true") convert_parser.set_defaults(func=convert_f) + tags_parser = s.add_parser( + "tags", help="Add or replace the tags on a wheel", description=TAGS_HELP + ) + tags_parser.add_argument("wheel", nargs="*", help="Existing wheel(s) to retag") + tags_parser.add_argument( + "--remove", + action="store_true", + help="Remove the original files, keeping only the renamed ones", + ) + tags_parser.add_argument( + "--python-tag", metavar="TAG", help="Specify an interpreter tag(s)" + ) + tags_parser.add_argument("--abi-tag", metavar="TAG", help="Specify an ABI tag(s)") + tags_parser.add_argument( + "--platform-tag", metavar="TAG", help="Specify a platform tag(s)" + ) + tags_parser.add_argument( + "--build", type=int, metavar="NUMBER", help="Specify a build number" + ) + tags_parser.set_defaults(func=tags_f) + version_parser = s.add_parser("version", help="Print version and exit") version_parser.set_defaults(func=version_f) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/pack.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/pack.py index 1949d4cf2..7c75c63fe 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/pack.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/pack.py @@ -37,13 +37,8 @@ def pack(directory: str, dest_dir: str, build_number: str | None): # Read the tags and the existing build number from .dist-info/WHEEL existing_build_number = None wheel_file_path = os.path.join(directory, dist_info_dir, "WHEEL") - with open(wheel_file_path) as f: - tags = [] - for line in f: - if line.startswith("Tag: "): - tags.append(line.split(" ")[1].rstrip()) - elif line.startswith("Build: "): - existing_build_number = line.split(" ")[1].rstrip() + with open(wheel_file_path, "rb") as f: + tags, existing_build_number = read_tags(f.read()) if not tags: raise WheelError( @@ -58,28 +53,16 @@ def pack(directory: str, dest_dir: str, build_number: str | None): name_version += "-" + build_number if build_number != existing_build_number: - replacement = ( - ("Build: %s\r\n" % build_number).encode("ascii") - if build_number - else b"" - ) with open(wheel_file_path, "rb+") as f: wheel_file_content = f.read() - wheel_file_content, num_replaced = BUILD_NUM_RE.subn( - replacement, wheel_file_content - ) - if not num_replaced: - wheel_file_content += replacement + wheel_file_content = set_build_number(wheel_file_content, build_number) f.seek(0) f.truncate() f.write(wheel_file_content) # Reassemble the tags for the wheel file - impls = sorted({tag.split("-")[0] for tag in tags}) - abivers = sorted({tag.split("-")[1] for tag in tags}) - platforms = sorted({tag.split("-")[2] for tag in tags}) - tagline = "-".join([".".join(impls), ".".join(abivers), ".".join(platforms)]) + tagline = compute_tagline(tags) # Repack the wheel wheel_path = os.path.join(dest_dir, f"{name_version}-{tagline}.whl") @@ -88,3 +71,54 @@ def pack(directory: str, dest_dir: str, build_number: str | None): wf.write_files(directory) print("OK") + + +def read_tags(input_str: bytes) -> tuple[list[str], str | None]: + """Read tags from a string. + + :param input_str: A string containing one or more tags, separated by spaces + :return: A list of tags and a list of build tags + """ + + tags = [] + existing_build_number = None + for line in input_str.splitlines(): + if line.startswith(b"Tag: "): + tags.append(line.split(b" ")[1].rstrip().decode("ascii")) + elif line.startswith(b"Build: "): + existing_build_number = line.split(b" ")[1].rstrip().decode("ascii") + + return tags, existing_build_number + + +def set_build_number(wheel_file_content: bytes, build_number: str | None) -> bytes: + """Compute a build tag and add/replace/remove as necessary. + + :param wheel_file_content: The contents of .dist-info/WHEEL + :param build_number: The build tags present in .dist-info/WHEEL + :return: The (modified) contents of .dist-info/WHEEL + """ + replacement = ( + ("Build: %s\r\n" % build_number).encode("ascii") if build_number else b"" + ) + + wheel_file_content, num_replaced = BUILD_NUM_RE.subn( + replacement, wheel_file_content + ) + + if not num_replaced: + wheel_file_content += replacement + + return wheel_file_content + + +def compute_tagline(tags: list[str]) -> str: + """Compute a tagline from a list of tags. + + :param tags: A list of tags + :return: A tagline + """ + impls = sorted({tag.split("-")[0] for tag in tags}) + abivers = sorted({tag.split("-")[1] for tag in tags}) + platforms = sorted({tag.split("-")[2] for tag in tags}) + return "-".join([".".join(impls), ".".join(abivers), ".".join(platforms)]) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/tags.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/tags.py new file mode 100644 index 000000000..0ea0f4472 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/tags.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +import itertools +import os +from collections.abc import Iterable + +from ..wheelfile import WheelFile +from .pack import read_tags, set_build_number + + +def _compute_tags(original_tags: Iterable[str], new_tags: str | None) -> set[str]: + """Add or replace tags. Supports dot-separated tags""" + if new_tags is None: + return set(original_tags) + + if new_tags.startswith("+"): + return {*original_tags, *new_tags[1:].split(".")} + + if new_tags.startswith("-"): + return set(original_tags) - set(new_tags[1:].split(".")) + + return set(new_tags.split(".")) + + +def tags( + wheel: str, + python_tags: str | None = None, + abi_tags: str | None = None, + platform_tags: str | None = None, + build_number: int | None = None, + remove: bool = False, +) -> str: + """Change the tags on a wheel file. + + The tags are left unchanged if they are not specified. To specify "none", + use ["none"]. To append to the previous tags, a tag should start with a + "+". If a tag starts with "-", it will be removed from existing tags. + Processing is done left to right. + + :param wheel: The paths to the wheels + :param python_tags: The Python tags to set + :param abi_tags: The ABI tags to set + :param platform_tags: The platform tags to set + :param build_number: The build number to set + :param remove: Remove the original wheel + """ + with WheelFile(wheel, "r") as f: + assert f.filename, f"{f.filename} must be available" + + wheel_info = f.read(f.dist_info_path + "/WHEEL") + + original_wheel_name = os.path.basename(f.filename) + namever = f.parsed_filename.group("namever") + build = f.parsed_filename.group("build") + original_python_tags = f.parsed_filename.group("pyver").split(".") + original_abi_tags = f.parsed_filename.group("abi").split(".") + original_plat_tags = f.parsed_filename.group("plat").split(".") + + tags, existing_build_number = read_tags(wheel_info) + + impls = {tag.split("-")[0] for tag in tags} + abivers = {tag.split("-")[1] for tag in tags} + platforms = {tag.split("-")[2] for tag in tags} + + if impls != set(original_python_tags): + msg = f"Wheel internal tags {impls!r} != filename tags {original_python_tags!r}" + raise AssertionError(msg) + + if abivers != set(original_abi_tags): + msg = f"Wheel internal tags {abivers!r} != filename tags {original_abi_tags!r}" + raise AssertionError(msg) + + if platforms != set(original_plat_tags): + msg = ( + f"Wheel internal tags {platforms!r} != filename tags {original_plat_tags!r}" + ) + raise AssertionError(msg) + + if existing_build_number != build: + msg = ( + f"Incorrect filename '{build}' " + "& *.dist-info/WHEEL '{existing_build_number}' build numbers" + ) + raise AssertionError(msg) + + # Start changing as needed + if build_number is not None: + build = str(build_number) + + final_python_tags = sorted(_compute_tags(original_python_tags, python_tags)) + final_abi_tags = sorted(_compute_tags(original_abi_tags, abi_tags)) + final_plat_tags = sorted(_compute_tags(original_plat_tags, platform_tags)) + + final_tags = [ + namever, + ".".join(final_python_tags), + ".".join(final_abi_tags), + ".".join(final_plat_tags), + ] + if build: + final_tags.insert(1, build) + + final_wheel_name = "-".join(final_tags) + ".whl" + + if original_wheel_name != final_wheel_name: + tags = [ + f"{a}-{b}-{c}" + for a, b, c in itertools.product( + final_python_tags, final_abi_tags, final_plat_tags + ) + ] + + original_wheel_path = os.path.join( + os.path.dirname(f.filename), original_wheel_name + ) + final_wheel_path = os.path.join(os.path.dirname(f.filename), final_wheel_name) + + with WheelFile(original_wheel_path, "r") as fin, WheelFile( + final_wheel_path, "w" + ) as fout: + fout.comment = fin.comment # preserve the comment + for item in fin.infolist(): + if item.filename == f.dist_info_path + "/RECORD": + continue + if item.filename == f.dist_info_path + "/WHEEL": + content = fin.read(item) + content = set_tags(content, tags) + content = set_build_number(content, build) + fout.writestr(item, content) + else: + fout.writestr(item, fin.read(item)) + + if remove: + os.remove(original_wheel_path) + + return final_wheel_name + + +def set_tags(in_string: bytes, tags: Iterable[str]) -> bytes: + """Set the tags in the .dist-info/WHEEL file contents. + + :param in_string: The string to modify. + :param tags: The tags to set. + """ + + lines = [line for line in in_string.splitlines() if not line.startswith(b"Tag:")] + for tag in tags: + lines.append(b"Tag: " + tag.encode("ascii")) + in_string = b"\r\n".join(lines) + b"\r\n" + + return in_string diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/unpack.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/unpack.py index c6409d4be..d48840e6e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/unpack.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/cli/unpack.py @@ -18,6 +18,13 @@ def unpack(path: str, dest: str = ".") -> None: namever = wf.parsed_filename.group("namever") destination = Path(dest) / namever print(f"Unpacking to: {destination}...", end="", flush=True) - wf.extractall(destination) + for zinfo in wf.filelist: + wf.extract(zinfo, destination) + + # Set permissions to the same values as they were set in the archive + # We have to do this manually due to + # https://github.com/python/cpython/issues/59999 + permissions = zinfo.external_attr >> 16 & 0o777 + destination.joinpath(zinfo.filename).chmod(permissions) print("OK") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/macosx_libfile.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/macosx_libfile.py index 4d085742a..3d1998481 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/macosx_libfile.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/macosx_libfile.py @@ -423,7 +423,7 @@ def calculate_macosx_platform_tag(archive_root, platform_tag): assert len(base_version) == 2 start_version = base_version versions_dict = {} - for (dirpath, _dirnames, filenames) in os.walk(archive_root): + for dirpath, _dirnames, filenames in os.walk(archive_root): for filename in filenames: if filename.endswith(".dylib") or filename.endswith(".so"): lib_path = os.path.join(dirpath, filename) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/metadata.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/metadata.py index 159ff0aa6..b391c962e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/metadata.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/metadata.py @@ -3,13 +3,83 @@ """ from __future__ import annotations +import functools +import itertools import os.path +import re import textwrap from email.message import Message from email.parser import Parser from typing import Iterator -from pkg_resources import Requirement, safe_extra, split_sections +from .vendored.packaging.requirements import Requirement + + +def _nonblank(str): + return str and not str.startswith("#") + + +@functools.singledispatch +def yield_lines(iterable): + r""" + Yield valid lines of a string or iterable. + >>> list(yield_lines('')) + [] + >>> list(yield_lines(['foo', 'bar'])) + ['foo', 'bar'] + >>> list(yield_lines('foo\nbar')) + ['foo', 'bar'] + >>> list(yield_lines('\nfoo\n#bar\nbaz #comment')) + ['foo', 'baz #comment'] + >>> list(yield_lines(['foo\nbar', 'baz', 'bing\n\n\n'])) + ['foo', 'bar', 'baz', 'bing'] + """ + return itertools.chain.from_iterable(map(yield_lines, iterable)) + + +@yield_lines.register(str) +def _(text): + return filter(_nonblank, map(str.strip, text.splitlines())) + + +def split_sections(s): + """Split a string or iterable thereof into (section, content) pairs + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def safe_extra(extra): + """Convert an arbitrary string to a standard 'extra' name + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub("[^A-Za-z0-9.-]+", "_", extra).lower() + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub("[^A-Za-z0-9.]+", "-", name) def requires_to_requires_dist(requirement: Requirement) -> str: @@ -18,8 +88,8 @@ def requires_to_requires_dist(requirement: Requirement) -> str: return " @ " + requirement.url requires_dist = [] - for op, ver in requirement.specs: - requires_dist.append(op + ver) + for spec in requirement.specifier: + requires_dist.append(spec.operator + spec.version) if requires_dist: return " (" + ",".join(sorted(requires_dist)) + ")" @@ -30,13 +100,13 @@ def requires_to_requires_dist(requirement: Requirement) -> str: def convert_requirements(requirements: list[str]) -> Iterator[str]: """Yield Requires-Dist: strings for parsed requirements strings.""" for req in requirements: - parsed_requirement = Requirement.parse(req) + parsed_requirement = Requirement(req) spec = requires_to_requires_dist(parsed_requirement) - extras = ",".join(sorted(parsed_requirement.extras)) + extras = ",".join(sorted(safe_extra(e) for e in parsed_requirement.extras)) if extras: extras = f"[{extras}]" - yield parsed_requirement.project_name + extras + spec + yield safe_name(parsed_requirement.name) + extras + spec def generate_requirements( @@ -82,7 +152,7 @@ def pkginfo_to_metadata(egg_info_path: str, pkginfo_path: str) -> Message: del pkg_info["Requires-Dist"] requires_path = os.path.join(egg_info_path, "requires.txt") if os.path.exists(requires_path): - with open(requires_path) as requires_file: + with open(requires_path, encoding="utf-8") as requires_file: requires = requires_file.read() parsed_requirements = sorted(split_sections(requires), key=lambda x: x[0] or "") diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_elffile.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_elffile.py new file mode 100644 index 000000000..6fb19b30b --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +import enum +import os +import struct +from typing import IO, Optional, Tuple + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error: + raise ELFInvalid("unable to parse identification") + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or " + f"encoding ({self.encoding})" + ) + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> Tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> Optional[str]: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_manylinux.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_manylinux.py index 4934ba8b9..2f0cc7439 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_manylinux.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_manylinux.py @@ -1,123 +1,58 @@ -from __future__ import annotations - import collections +import contextlib import functools import os import re -import struct import sys import warnings -from typing import IO, Iterator, NamedTuple - - -# Python does not provide platform information at sufficient granularity to -# identify the architecture of the running executable in some cases, so we -# determine it dynamically by reading the information from the running -# process. This only applies on Linux, which uses the ELF format. -class _ELFFileHeader: - # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header - class _InvalidELFFileHeader(ValueError): - """ - An invalid ELF file header was found. - """ - - ELF_MAGIC_NUMBER = 0x7F454C46 - ELFCLASS32 = 1 - ELFCLASS64 = 2 - ELFDATA2LSB = 1 - ELFDATA2MSB = 2 - EM_386 = 3 - EM_S390 = 22 - EM_ARM = 40 - EM_X86_64 = 62 - EF_ARM_ABIMASK = 0xFF000000 - EF_ARM_ABI_VER5 = 0x05000000 - EF_ARM_ABI_FLOAT_HARD = 0x00000400 - - def __init__(self, file: IO[bytes]) -> None: - def unpack(fmt: str) -> int: - try: - data = file.read(struct.calcsize(fmt)) - result: tuple[int, ...] = struct.unpack(fmt, data) - except struct.error: - raise _ELFFileHeader._InvalidELFFileHeader() - return result[0] - - self.e_ident_magic = unpack(">I") - if self.e_ident_magic != self.ELF_MAGIC_NUMBER: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_class = unpack("B") - if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_data = unpack("B") - if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}: - raise _ELFFileHeader._InvalidELFFileHeader() - self.e_ident_version = unpack("B") - self.e_ident_osabi = unpack("B") - self.e_ident_abiversion = unpack("B") - self.e_ident_pad = file.read(7) - format_h = "H" - format_i = "I" - format_q = "Q" - format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q - self.e_type = unpack(format_h) - self.e_machine = unpack(format_h) - self.e_version = unpack(format_i) - self.e_entry = unpack(format_p) - self.e_phoff = unpack(format_p) - self.e_shoff = unpack(format_p) - self.e_flags = unpack(format_i) - self.e_ehsize = unpack(format_h) - self.e_phentsize = unpack(format_h) - self.e_phnum = unpack(format_h) - self.e_shentsize = unpack(format_h) - self.e_shnum = unpack(format_h) - self.e_shstrndx = unpack(format_h) - - -def _get_elf_header() -> _ELFFileHeader | None: +from typing import Dict, Generator, Iterator, NamedTuple, Optional, Tuple + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + + +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[Optional[ELFFile], None, None]: try: - with open(sys.executable, "rb") as f: - elf_header = _ELFFileHeader(f) - except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader): - return None - return elf_header + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None -def _is_linux_armhf() -> bool: +def _is_linux_armhf(executable: str) -> bool: # hard-float ABI can be detected from the ELF header of the running # process # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_ARM - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABIMASK - ) == elf_header.EF_ARM_ABI_VER5 - result &= ( - elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD - ) == elf_header.EF_ARM_ABI_FLOAT_HARD - return result - - -def _is_linux_i686() -> bool: - elf_header = _get_elf_header() - if elf_header is None: - return False - result = elf_header.e_ident_class == elf_header.ELFCLASS32 - result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB - result &= elf_header.e_machine == elf_header.EM_386 - return result + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) -def _have_compatible_abi(arch: str) -> bool: +def _have_compatible_abi(executable: str, arch: str) -> bool: if arch == "armv7l": - return _is_linux_armhf() + return _is_linux_armhf(executable) if arch == "i686": - return _is_linux_i686() + return _is_linux_i686(executable) return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"} @@ -126,7 +61,7 @@ def _have_compatible_abi(arch: str) -> bool: # For now, guess what the highest minor version might be, assume it will # be 50 for testing. Once this actually happens, update the dictionary # with the actual value. -_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50) +_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50) class _GLibCVersion(NamedTuple): @@ -134,7 +69,7 @@ class _GLibCVersion(NamedTuple): minor: int -def _glibc_version_string_confstr() -> str | None: +def _glibc_version_string_confstr() -> Optional[str]: """ Primary implementation of glibc_version_string using os.confstr. """ @@ -143,17 +78,17 @@ def _glibc_version_string_confstr() -> str | None: # platform module. # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 try: - # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17". - version_string = os.confstr("CS_GNU_LIBC_VERSION") + # Should be a string like "glibc 2.17". + version_string: str = getattr(os, "confstr")("CS_GNU_LIBC_VERSION") assert version_string is not None - _, version = version_string.split() + _, version = version_string.rsplit() except (AssertionError, AttributeError, OSError, ValueError): # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... return None return version -def _glibc_version_string_ctypes() -> str | None: +def _glibc_version_string_ctypes() -> Optional[str]: """ Fallback implementation of glibc_version_string using ctypes. """ @@ -197,12 +132,12 @@ def _glibc_version_string_ctypes() -> str | None: return version_str -def _glibc_version_string() -> str | None: +def _glibc_version_string() -> Optional[str]: """Returns glibc version string, or None if not using glibc.""" return _glibc_version_string_confstr() or _glibc_version_string_ctypes() -def _parse_glibc_version(version_str: str) -> tuple[int, int]: +def _parse_glibc_version(version_str: str) -> Tuple[int, int]: """Parse glibc version. We use a regexp instead of str.split because we want to discard any @@ -213,8 +148,8 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]: m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) if not m: warnings.warn( - "Expected glibc version with 2 components major.minor," - " got: %s" % version_str, + f"Expected glibc version with 2 components major.minor," + f" got: {version_str}", RuntimeWarning, ) return -1, -1 @@ -222,7 +157,7 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]: @functools.lru_cache() -def _get_glibc_version() -> tuple[int, int]: +def _get_glibc_version() -> Tuple[int, int]: version_str = _glibc_version_string() if version_str is None: return (-1, -1) @@ -267,7 +202,7 @@ def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool: def platform_tags(linux: str, arch: str) -> Iterator[str]: - if not _have_compatible_abi(arch): + if not _have_compatible_abi(sys.executable, arch): return # Oldest glibc to be supported regardless of architecture is (2, 17). too_old_glibc2 = _GLibCVersion(2, 16) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_musllinux.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_musllinux.py index 7946c9b29..706ba600a 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_musllinux.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_musllinux.py @@ -4,70 +4,13 @@ linked against musl, and what musl version is used. """ -from __future__ import annotations - -import contextlib import functools -import operator -import os import re -import struct import subprocess import sys -from typing import IO, Iterator, NamedTuple - - -def _read_unpacked(f: IO[bytes], fmt: str) -> tuple[int, ...]: - return struct.unpack(fmt, f.read(struct.calcsize(fmt))) +from typing import Iterator, NamedTuple, Optional - -def _parse_ld_musl_from_elf(f: IO[bytes]) -> str | None: - """Detect musl libc location by parsing the Python executable. - - Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca - ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html - """ - f.seek(0) - try: - ident = _read_unpacked(f, "16B") - except struct.error: - return None - if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF. - return None - f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version. - - try: - # e_fmt: Format for program header. - # p_fmt: Format for section header. - # p_idx: Indexes to find p_type, p_offset, and p_filesz. - e_fmt, p_fmt, p_idx = { - 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit. - 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit. - }[ident[4]] - except KeyError: - return None - else: - p_get = operator.itemgetter(*p_idx) - - # Find the interpreter section and return its content. - try: - _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt) - except struct.error: - return None - for i in range(e_phnum + 1): - f.seek(e_phoff + e_phentsize * i) - try: - p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt)) - except struct.error: - return None - if p_type != 3: # Not PT_INTERP. - continue - f.seek(p_offset) - interpreter = os.fsdecode(f.read(p_filesz)).strip("\0") - if "musl" not in interpreter: - return None - return interpreter - return None +from ._elffile import ELFFile class _MuslVersion(NamedTuple): @@ -75,7 +18,7 @@ class _MuslVersion(NamedTuple): minor: int -def _parse_musl_version(output: str) -> _MuslVersion | None: +def _parse_musl_version(output: str) -> Optional[_MuslVersion]: lines = [n for n in (n.strip() for n in output.splitlines()) if n] if len(lines) < 2 or lines[0][:4] != "musl": return None @@ -86,7 +29,7 @@ def _parse_musl_version(output: str) -> _MuslVersion | None: @functools.lru_cache() -def _get_musl_version(executable: str) -> _MuslVersion | None: +def _get_musl_version(executable: str) -> Optional[_MuslVersion]: """Detect currently-running musl runtime version. This is done by checking the specified executable's dynamic linking @@ -97,15 +40,14 @@ def _get_musl_version(executable: str) -> _MuslVersion | None: Version 1.2.2 Dynamic Program Loader """ - with contextlib.ExitStack() as stack: - try: - f = stack.enter_context(open(executable, "rb")) - except OSError: - return None - ld = _parse_ld_musl_from_elf(f) - if not ld: + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: return None - proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True) + proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True) return _parse_musl_version(proc.stderr) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_parser.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_parser.py new file mode 100644 index 000000000..2bc6a8f98 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_parser.py @@ -0,0 +1,328 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains ENBF-inspired grammar representing +the implementation. +""" + +import ast +from typing import Any, List, NamedTuple, Optional, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}('{self}')>" + + def serialize(self) -> str: + raise NotImplementedError + + +class Variable(Node): + def serialize(self) -> str: + return str(self) + + +class Value(Node): + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + def serialize(self) -> str: + return str(self) + + +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +# MarkerAtom = Union[MarkerItem, List["MarkerAtom"]] +# MarkerList = List[Union["MarkerList", MarkerAtom, str]] +# mypy does not support recursive type definition +# https://github.com/python/mypy/issues/731 +MarkerAtom = Any +MarkerList = List[Any] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: List[str] + specifier: str + marker: Optional[MarkerList] + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> Tuple[str, str, Optional[MarkerList]]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, span_start=url_start, after="URL and whitespace" + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + after=( + "version specifier" + if specifier + else "name and no valid version specifier" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, after: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected end or semicolon (after {after})", + span_start=span_start, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> List[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens("LEFT_BRACKET", "RIGHT_BRACKET"): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> List[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: List[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens("LEFT_PARENTHESIS", "RIGHT_PARENTHESIS"): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if ( + env_var == "platform_python_implementation" + or env_var == "python_implementation" + ): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of " + "<=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_structures.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_structures.py new file mode 100644 index 000000000..90a6465f9 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_structures.py @@ -0,0 +1,61 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + + +class InfinityType: + def __repr__(self) -> str: + return "Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return False + + def __le__(self, other: object) -> bool: + return False + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return True + + def __ge__(self, other: object) -> bool: + return True + + def __neg__(self: object) -> "NegativeInfinityType": + return NegativeInfinity + + +Infinity = InfinityType() + + +class NegativeInfinityType: + def __repr__(self) -> str: + return "-Infinity" + + def __hash__(self) -> int: + return hash(repr(self)) + + def __lt__(self, other: object) -> bool: + return True + + def __le__(self, other: object) -> bool: + return True + + def __eq__(self, other: object) -> bool: + return isinstance(other, self.__class__) + + def __gt__(self, other: object) -> bool: + return False + + def __ge__(self, other: object) -> bool: + return False + + def __neg__(self: object) -> InfinityType: + return Infinity + + +NegativeInfinity = NegativeInfinityType() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_tokenizer.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_tokenizer.py new file mode 100644 index 000000000..b1fb207c7 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/_tokenizer.py @@ -0,0 +1,188 @@ +import contextlib +import re +from dataclasses import dataclass +from typing import Dict, Iterator, NoReturn, Optional, Tuple, Union + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: Tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return "\n ".join([self.message, self.source, marker]) + + +DEFAULT_RULES: "Dict[str, Union[str, re.Pattern[str]]]" = { + "LEFT_PARENTHESIS": r"\(", + "RIGHT_PARENTHESIS": r"\)", + "LEFT_BRACKET": r"\[", + "RIGHT_BRACKET": r"\]", + "SEMICOLON": r";", + "COMMA": r",", + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": r"(===|==|~=|!=|<=|>=|<|>)", + "BOOLOP": r"\b(or|and)\b", + "IN": r"\bin\b", + "NOT": r"\bnot\b", + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extra + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._operator_regex_str + Specifier._version_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": r"\@", + "URL": r"[^ \t]+", + "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", + "WS": r"[ \t]+", + "END": r"$", +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: "Dict[str, Union[str, re.Pattern[str]]]", + ) -> None: + self.source = source + self.rules: Dict[str, re.Pattern[str]] = { + name: re.compile(pattern) for name, pattern in rules.items() + } + self.next_token: Optional[Token] = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert ( + self.next_token is None + ), f"Cannot check for {name!r}, already have {self.next_token!r}" + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: Optional[int] = None, + span_end: Optional[int] = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens(self, open_token: str, close_token: str) -> Iterator[bool]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield open_position is not None + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected closing {close_token}", + span_start=open_position, + ) + + self.read() diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/markers.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/markers.py new file mode 100644 index 000000000..68369c981 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/markers.py @@ -0,0 +1,245 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import operator +import os +import platform +import sys +from typing import Any, Callable, Dict, List, Optional, Tuple, Union + +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable, parse_marker +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +__all__ = [ + "InvalidMarker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "Marker", + "default_environment", +] + +Operator = Callable[[str, str], bool] + + +class InvalidMarker(ValueError): + """ + An invalid marker was found, users should refer to PEP 508. + """ + + +class UndefinedComparison(ValueError): + """ + An invalid operation was attempted on a value that doesn't support it. + """ + + +class UndefinedEnvironmentName(ValueError): + """ + A name was attempted to be used that does not exist inside of the + environment. + """ + + +def _normalize_extra_values(results: Any) -> Any: + """ + Normalize extra values. + """ + if isinstance(results[0], tuple): + lhs, op, rhs = results[0] + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + results[0] = lhs, op, rhs + return results + + +def _format_marker( + marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True +) -> str: + + assert isinstance(marker, (list, tuple, str)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators: Dict[str, Operator] = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": operator.lt, + "<=": operator.le, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.ge, + ">": operator.gt, +} + + +def _eval_op(lhs: str, op: Op, rhs: str) -> bool: + try: + spec = Specifier("".join([op.serialize(), rhs])) + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Optional[Operator] = _operators.get(op.serialize()) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize(*values: str, key: str) -> Tuple[str, ...]: + # PEP 685 – Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + return tuple(canonicalize_name(v) for v in values) + + # other environment markers don't have such standards + return values + + +def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool: + groups: List[List[bool]] = [[]] + + for marker in markers: + assert isinstance(marker, (list, tuple, str)) + + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = environment[environment_key] + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = environment[environment_key] + + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + groups[-1].append(_eval_op(lhs_value, op, rhs_value)) + else: + assert marker in ["and", "or"] + if marker == "or": + groups.append([]) + + return any(all(item) for item in groups) + + +def format_full_version(info: "sys._version_info") -> str: + version = "{0.major}.{0.minor}.{0.micro}".format(info) + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment() -> Dict[str, str]: + iver = format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker: + def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + try: + self._markers = _normalize_extra_values(parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool: + """Evaluate a marker. + + Return the boolean from evaluating the given marker against the + environment. environment is an optional argument to override all or + part of the determined environment. + + The environment is determined from the current Python process. + """ + current_environment = default_environment() + current_environment["extra"] = "" + if environment is not None: + current_environment.update(environment) + # The API used to allow setting extra to None. We need to handle this + # case for backwards compatibility. + if current_environment["extra"] is None: + current_environment["extra"] = "" + + return _evaluate_markers(self._markers, current_environment) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/requirements.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/requirements.py new file mode 100644 index 000000000..a9f9b9c7c --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/requirements.py @@ -0,0 +1,95 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import urllib.parse +from typing import Any, List, Optional, Set + +from ._parser import parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +class Requirement: + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string: str) -> None: + try: + parsed = parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + if parsed.url: + parsed_url = urllib.parse.urlparse(parsed.url) + if parsed_url.scheme == "file": + if urllib.parse.urlunparse(parsed_url) != parsed.url: + raise InvalidRequirement("Invalid URL given") + elif not (parsed_url.scheme and parsed_url.netloc) or ( + not parsed_url.scheme and not parsed_url.netloc + ): + raise InvalidRequirement(f"Invalid URL: {parsed.url}") + self.url: Optional[str] = parsed.url + else: + self.url = None + self.extras: Set[str] = set(parsed.extras if parsed.extras else []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Optional[Marker] = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def __str__(self) -> str: + parts: List[str] = [self.name] + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + parts.append(f"[{formatted_extras}]") + + if self.specifier: + parts.append(str(self.specifier)) + + if self.url: + parts.append(f"@ {self.url}") + if self.marker: + parts.append(" ") + + if self.marker: + parts.append(f"; {self.marker}") + + return "".join(parts) + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash((self.__class__.__name__, str(self))) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + self.name == other.name + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/specifiers.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/specifiers.py new file mode 100644 index 000000000..bd7903de3 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/specifiers.py @@ -0,0 +1,1006 @@ +# This file is dual licensed under the terms of the Apache License, Version + +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" + +import abc +import itertools +import re +from typing import ( + Callable, + Iterable, + Iterator, + List, + Optional, + Set, + Tuple, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import Version + +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] + + +def _coerce_version(version: UnparsedVersion) -> Version: + if not isinstance(version, Version): + version = Version(version) + return version + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier-like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier-like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier-like + objects are equal. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> Optional[bool]: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @abc.abstractmethod + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + """ + + _operator_regex_str = r""" + (?P(~=|==|!=|<=|>=|<|>|===)) + """ + _version_regex_str = r""" + (?P + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + (?<====) # Only match for the identity operator + \s* + [^\s;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?<===|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?<=~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + match = self._regex.search(spec) + if not match: + raise InvalidSpecifier(f"Invalid specifier: '{spec}'") + + self._spec: Tuple[str, str] = ( + match.group("operator").strip(), + match.group("version").strip(), + ) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + @property + def prereleases(self) -> bool: + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Look at all of our specifiers and determine if they are inclusive + # operators, and if they are if they are including an explicit + # prerelease. + operator, version = self._spec + if operator in ["==", ">=", "<=", "~=", "==="]: + # The == specifier can include a trailing .*, if it does we + # want to remove before parsing. + if operator == "==" and version.endswith(".*"): + version = version[:-2] + + # Parse the version, and if it is a pre-release than this + # specifier allows pre-releases. + if Version(version).is_prerelease: + return True + + return False + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> Tuple[str, str]: + canonical_version = canonicalize_version( + self._spec[1], + strip_trailing_zero=(self._spec[0] != "~="), + ) + return self._spec[0], canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: str) -> bool: + + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore suffix segments. + prefix = ".".join( + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( + prospective, prefix + ) + + def _compare_equal(self, prospective: Version, spec: str) -> bool: + + # We need special logic to handle prefix matching + if spec.endswith(".*"): + # In the case of prefix matching we want to ignore local segment. + normalized_prospective = canonicalize_version(prospective.public) + # Get the normalized version string ignoring the trailing .* + normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) + # Split the spec out by dots, and pretend that there is an implicit + # dot in between a release segment and a pre-release segment. + split_spec = _version_split(normalized_spec) + + # Split the prospective version out by dots, and pretend that there + # is an implicit dot in between a release segment and a pre-release + # segment. + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective, _ = _pad_version(split_prospective, split_spec) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + shortened_prospective = padded_prospective[: len(split_spec)] + + return shortened_prospective == split_spec + else: + # Convert our spec string into a Version + spec_version = Version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec_version.local: + prospective = Version(prospective.public) + + return prospective == spec_version + + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) <= Version(spec) + + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: + + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return Version(prospective.public) >= Version(spec) + + def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a pre-release version, that we do not accept pre-release + # versions for the version mentioned in the specifier (e.g. <3.1 should + # not match 3.1.dev0, but should match 3.0.dev0). + if not spec.is_prerelease and prospective.is_prerelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: + + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = Version(spec_str) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # This special case is here so that, unless the specifier itself + # includes is a post-release version, that we do not accept + # post-release versions for the version mentioned in the specifier + # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). + if not spec.is_postrelease and prospective.is_postrelease: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # Ensure that we do not allow a local version of the version mentioned + # in the specifier, which is technically greater than, to match. + if prospective.local is not None: + if Version(prospective.base_version) == Version(spec.base_version): + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + def __contains__(self, item: Union[str, Version]) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, item: UnparsedVersion, prereleases: Optional[bool] = None + ) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + False + >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") + True + >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) + True + """ + + # Determine if prereleases are to be allowed or not. + if prereleases is None: + prereleases = self.prereleases + + # Normalize item to a Version, this allows us to have a shortcut for + # "2.0" in Specifier(">=2") + normalized_item = _coerce_version(item) + + # Determine if we should be supporting prereleases in this specifier + # or not, if we do not support prereleases than we can short circuit + # logic if this version is a prereleases. + if normalized_item.is_prerelease and not prereleases: + return False + + # Actually do the comparison to determine if this item is contained + # within this Specifier or not. + operator_callable: CallableOperator = self._get_operator(self.operator) + return operator_callable(normalized_item, self.version) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(Specifier().contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + """ + + yielded = False + found_prereleases = [] + + kw = {"prereleases": prereleases if prereleases is not None else True} + + # Attempt to iterate over all the values in the iterable and if any of + # them match, yield them. + for version in iterable: + parsed_version = _coerce_version(version) + + if self.contains(parsed_version, **kw): + # If our version is a prerelease, and we were not set to allow + # prereleases, then we'll store it for later in case nothing + # else matches this specifier. + if parsed_version.is_prerelease and not ( + prereleases or self.prereleases + ): + found_prereleases.append(version) + # Either this is not a prerelease, or we should have been + # accepting prereleases from the beginning. + else: + yielded = True + yield version + + # Now that we've iterated over everything, determine if we've yielded + # any values, and if we have not and we have any prereleases stored up + # then we will go ahead and yield the prereleases. + if not yielded and found_prereleases: + for version in found_prereleases: + yield version + + +_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") + + +def _version_split(version: str) -> List[str]: + result: List[str] = [] + for item in version.split("."): + match = _prefix_regex.search(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: + left_split, right_split = [], [] + + # Get the release segment of our versions + left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) + right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) + + # Get the rest of our versions + left_split.append(left[len(left_split[0]) :]) + right_split.append(right[len(right_split[0]) :]) + + # Insert our padding + left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) + right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) + + return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + """ + + def __init__( + self, specifiers: str = "", prereleases: Optional[bool] = None + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + # Split on `,` to break each individual specifier into it's own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + # Parsed each individual specifier, attempting first to make it a + # Specifier. + parsed: Set[Specifier] = set() + for specifier in split_specifiers: + parsed.add(Specifier(specifier)) + + # Turn our parsed specifiers into a frozen set and save them for later. + self._specs = frozenset(parsed) + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + @property + def prereleases(self) -> Optional[bool]: + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + return any(s.prereleases for s in self._specs) + + @prereleases.setter + def prereleases(self, value: bool) -> None: + self._prereleases = value + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(sorted(str(s) for s in self._specs)) + + def __hash__(self) -> int: + return hash(self._specs) + + def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + if isinstance(other, str): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = frozenset(self._specs | other._specs) + + if self._prereleases is None and other._prereleases is not None: + specifier._prereleases = other._prereleases + elif self._prereleases is not None and other._prereleases is None: + specifier._prereleases = self._prereleases + elif self._prereleases == other._prereleases: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease " + "overrides." + ) + + return specifier + + def __eq__(self, other: object) -> bool: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._specs == other._specs + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: Optional[bool] = None, + installed: Optional[bool] = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it uses :attr:`prereleases` to determine + whether or not prereleases are allowed. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + # Ensure that our item is a Version instance. + if not isinstance(item, Version): + item = Version(item) + + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # We can determine if we're going to allow pre-releases by looking to + # see if any of the underlying items supports them. If none of them do + # and this item is a pre-release then we do not allow it and we can + # short circuit that here. + # Note: This means that 1.0.dev1 would not be contained in something + # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 + if not prereleases and item.is_prerelease: + return False + + if installed and item.is_prerelease: + item = Version(item.base_version) + + # We simply dispatch to the underlying specs here to make sure that the + # given version is contained within all of them. + # Note: This use of all() here means that an empty set of specifiers + # will always return True, this is an explicit design decision. + return all(s.contains(item, prereleases=prereleases) for s in self._specs) + + def filter( + self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None + ) -> Iterator[UnparsedVersionVar]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will be intelligently decide whether to allow + prereleases or not (based on the :attr:`prereleases` attribute, and + whether the only versions matching are prereleases). + + This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` + because it implements the rule from :pep:`440` that a prerelease item + SHOULD be accepted if no other versions match the given specifier. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + [] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None: + prereleases = self.prereleases + + # If we have any specifiers, then we want to wrap our iterable in the + # filter method for each one, this will act as a logical AND amongst + # each specifier. + if self._specs: + for spec in self._specs: + iterable = spec.filter(iterable, prereleases=bool(prereleases)) + return iter(iterable) + # If we do not have any specifiers, then we need to have a rough filter + # which will filter out any pre-releases, unless there are no final + # releases. + else: + filtered: List[UnparsedVersionVar] = [] + found_prereleases: List[UnparsedVersionVar] = [] + + for item in iterable: + parsed_version = _coerce_version(item) + + # Store any item which is a pre-release for later unless we've + # already found a final version or we are accepting prereleases + if parsed_version.is_prerelease and not prereleases: + if not filtered: + found_prereleases.append(item) + else: + filtered.append(item) + + # If we've found no items except for pre-releases, then we'll go + # ahead and use the pre-releases + if not filtered and found_prereleases and prereleases is None: + return iter(found_prereleases) + + return iter(filtered) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/tags.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/tags.py index 4e003a95c..19ccbde3e 100644 --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/tags.py +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/tags.py @@ -2,14 +2,24 @@ # 2.0, and the BSD License. See the LICENSE file in the root of this repository # for complete details. -from __future__ import annotations - import logging import platform +import subprocess import sys import sysconfig from importlib.machinery import EXTENSION_SUFFIXES -from typing import Iterable, Iterator, Sequence, Tuple, cast +from typing import ( + Dict, + FrozenSet, + Iterable, + Iterator, + List, + Optional, + Sequence, + Tuple, + Union, + cast, +) from . import _manylinux, _musllinux @@ -18,7 +28,7 @@ PythonVersion = Sequence[int] MacVersion = Tuple[int, int] -INTERPRETER_SHORT_NAMES: dict[str, str] = { +INTERPRETER_SHORT_NAMES: Dict[str, str] = { "python": "py", # Generic. "cpython": "cp", "pypy": "pp", @@ -27,7 +37,7 @@ } -_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32 +_32_BIT_INTERPRETER = sys.maxsize <= 2**32 class Tag: @@ -84,7 +94,7 @@ def __repr__(self) -> str: return f"<{self} @ {id(self)}>" -def parse_tag(tag: str) -> frozenset[Tag]: +def parse_tag(tag: str) -> FrozenSet[Tag]: """ Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances. @@ -100,7 +110,7 @@ def parse_tag(tag: str) -> frozenset[Tag]: return frozenset(tags) -def _get_config_var(name: str, warn: bool = False) -> int | str | None: +def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]: value = sysconfig.get_config_var(name) if value is None and warn: logger.debug( @@ -122,7 +132,7 @@ def _abi3_applies(python_version: PythonVersion) -> bool: return len(python_version) > 1 and tuple(python_version) >= (3, 2) -def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]: py_version = tuple(py_version) # To allow for version comparison. abis = [] version = _version_nodot(py_version[:2]) @@ -159,9 +169,9 @@ def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: def cpython_tags( - python_version: PythonVersion | None = None, - abis: Iterable[str] | None = None, - platforms: Iterable[str] | None = None, + python_version: Optional[PythonVersion] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, *, warn: bool = False, ) -> Iterator[Tag]: @@ -215,16 +225,51 @@ def cpython_tags( yield Tag(interpreter, "abi3", platform_) -def _generic_abi() -> Iterator[str]: - abi = sysconfig.get_config_var("SOABI") - if abi: - yield _normalize_string(abi) +def _generic_abi() -> List[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] def generic_tags( - interpreter: str | None = None, - abis: Iterable[str] | None = None, - platforms: Iterable[str] | None = None, + interpreter: Optional[str] = None, + abis: Optional[Iterable[str]] = None, + platforms: Optional[Iterable[str]] = None, *, warn: bool = False, ) -> Iterator[Tag]: @@ -242,8 +287,9 @@ def generic_tags( interpreter = "".join([interp_name, interp_version]) if abis is None: abis = _generic_abi() + else: + abis = list(abis) platforms = list(platforms or platform_tags()) - abis = list(abis) if "none" not in abis: abis.append("none") for abi in abis: @@ -267,9 +313,9 @@ def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: def compatible_tags( - python_version: PythonVersion | None = None, - interpreter: str | None = None, - platforms: Iterable[str] | None = None, + python_version: Optional[PythonVersion] = None, + interpreter: Optional[str] = None, + platforms: Optional[Iterable[str]] = None, ) -> Iterator[Tag]: """ Yields the sequence of tags that are compatible with a specific version of Python. @@ -301,7 +347,7 @@ def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: return "i386" -def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: +def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]: formats = [cpu_arch] if cpu_arch == "x86_64": if version < (10, 4): @@ -334,7 +380,7 @@ def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> list[str]: def mac_platforms( - version: MacVersion | None = None, arch: str | None = None + version: Optional[MacVersion] = None, arch: Optional[str] = None ) -> Iterator[str]: """ Yields the platform tags for a macOS system. @@ -347,6 +393,22 @@ def mac_platforms( version_str, _, cpu_arch = platform.mac_ver() if version is None: version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2]))) else: version = version if arch is None: @@ -437,6 +499,9 @@ def platform_tags() -> Iterator[str]: def interpreter_name() -> str: """ Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. """ name = sys.implementation.name return INTERPRETER_SHORT_NAMES.get(name) or name @@ -473,6 +538,9 @@ def sys_tags(*, warn: bool = False) -> Iterator[Tag]: yield from generic_tags() if interp_name == "pp": - yield from compatible_tags(interpreter="pp3") + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) else: - yield from compatible_tags() + interp = None + yield from compatible_tags(interpreter=interp) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/utils.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/utils.py new file mode 100644 index 000000000..33c613b74 --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/utils.py @@ -0,0 +1,141 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +import re +from typing import FrozenSet, NewType, Tuple, Union, cast + +from .tags import Tag, parse_tag +from .version import InvalidVersion, Version + +BuildTag = Union[Tuple[()], Tuple[int, str]] +NormalizedName = NewType("NormalizedName", str) + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + +_canonicalize_regex = re.compile(r"[-_.]+") +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)") + + +def canonicalize_name(name: str) -> NormalizedName: + # This is taken from PEP 503. + value = _canonicalize_regex.sub("-", name).lower() + return cast(NormalizedName, value) + + +def canonicalize_version( + version: Union[Version, str], *, strip_trailing_zero: bool = True +) -> str: + """ + This is very similar to Version.__str__, but has one subtle difference + with the way it handles the release segment. + """ + if isinstance(version, str): + try: + parsed = Version(version) + except InvalidVersion: + # Legacy versions cannot be normalized + return version + else: + parsed = version + + parts = [] + + # Epoch + if parsed.epoch != 0: + parts.append(f"{parsed.epoch}!") + + # Release segment + release_segment = ".".join(str(x) for x in parsed.release) + if strip_trailing_zero: + # NB: This strips trailing '.0's to normalize + release_segment = re.sub(r"(\.0)+$", "", release_segment) + parts.append(release_segment) + + # Pre-release + if parsed.pre is not None: + parts.append("".join(str(x) for x in parsed.pre)) + + # Post-release + if parsed.post is not None: + parts.append(f".post{parsed.post}") + + # Development release + if parsed.dev is not None: + parts.append(f".dev{parsed.dev}") + + # Local version segment + if parsed.local is not None: + parts.append(f"+{parsed.local}") + + return "".join(parts) + + +def parse_wheel_filename( + filename: str, +) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]: + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename}") + name = canonicalize_name(name_part) + version = Version(parts[1]) + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in '{filename}'" + ) + build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tags = parse_tag(parts[-1]) + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]: + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename}") + + name = canonicalize_name(name_part) + version = Version(version_part) + return (name, version) diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/version.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/version.py new file mode 100644 index 000000000..e5c738cfd --- /dev/null +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/packaging/version.py @@ -0,0 +1,563 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from packaging.version import parse, Version +""" + +import collections +import itertools +import re +from typing import Callable, Optional, SupportsInt, Tuple, Union + +from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType + +__all__ = ["VERSION_PATTERN", "parse", "Version", "InvalidVersion"] + +InfiniteTypes = Union[InfinityType, NegativeInfinityType] +PrePostDevType = Union[InfiniteTypes, Tuple[str, int]] +SubLocalType = Union[InfiniteTypes, int, str] +LocalType = Union[ + NegativeInfinityType, + Tuple[ + Union[ + SubLocalType, + Tuple[SubLocalType, str], + Tuple[NegativeInfinityType, SubLocalType], + ], + ..., + ], +] +CmpKey = Tuple[ + int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + +_Version = collections.namedtuple( + "_Version", ["epoch", "release", "dev", "pre", "post", "local"] +) + + +def parse(version: str) -> "Version": + """Parse the given version string. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + _key: CmpKey + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: "_BaseVersion") -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse +_VERSION_PATTERN = r""" + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                                # pre-release
      +            [-_\.]?
      +            (?P(a|b|c|rc|alpha|beta|pre|preview))
      +            [-_\.]?
      +            (?P[0-9]+)?
      +        )?
      +        (?P                                         # post release
      +            (?:-(?P[0-9]+))
      +            |
      +            (?:
      +                [-_\.]?
      +                (?Ppost|rev|r)
      +                [-_\.]?
      +                (?P[0-9]+)?
      +            )
      +        )?
      +        (?P                                          # dev release
      +            [-_\.]?
      +            (?Pdev)
      +            [-_\.]?
      +            (?P[0-9]+)?
      +        )?
      +    )
      +    (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
      +"""
      +
      +VERSION_PATTERN = _VERSION_PATTERN
      +"""
      +A string containing the regular expression used to match a valid version.
      +
      +The pattern is not anchored at either end, and is intended for embedding in larger
      +expressions (for example, matching a version number as part of a file name). The
      +regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
      +flags set.
      +
      +:meta hide-value:
      +"""
      +
      +
      +class Version(_BaseVersion):
      +    """This class abstracts handling of a project's versions.
      +
      +    A :class:`Version` instance is comparison aware and can be compared and
      +    sorted using the standard Python interfaces.
      +
      +    >>> v1 = Version("1.0a5")
      +    >>> v2 = Version("1.0")
      +    >>> v1
      +    
      +    >>> v2
      +    
      +    >>> v1 < v2
      +    True
      +    >>> v1 == v2
      +    False
      +    >>> v1 > v2
      +    False
      +    >>> v1 >= v2
      +    False
      +    >>> v1 <= v2
      +    True
      +    """
      +
      +    _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
      +
      +    def __init__(self, version: str) -> None:
      +        """Initialize a Version object.
      +
      +        :param version:
      +            The string representation of a version which will be parsed and normalized
      +            before use.
      +        :raises InvalidVersion:
      +            If the ``version`` does not conform to PEP 440 in any way then this
      +            exception will be raised.
      +        """
      +
      +        # Validate the version and parse it into pieces
      +        match = self._regex.search(version)
      +        if not match:
      +            raise InvalidVersion(f"Invalid version: '{version}'")
      +
      +        # Store the parsed out pieces of the version
      +        self._version = _Version(
      +            epoch=int(match.group("epoch")) if match.group("epoch") else 0,
      +            release=tuple(int(i) for i in match.group("release").split(".")),
      +            pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
      +            post=_parse_letter_version(
      +                match.group("post_l"), match.group("post_n1") or match.group("post_n2")
      +            ),
      +            dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
      +            local=_parse_local_version(match.group("local")),
      +        )
      +
      +        # Generate a key which will be used for sorting
      +        self._key = _cmpkey(
      +            self._version.epoch,
      +            self._version.release,
      +            self._version.pre,
      +            self._version.post,
      +            self._version.dev,
      +            self._version.local,
      +        )
      +
      +    def __repr__(self) -> str:
      +        """A representation of the Version that shows all internal state.
      +
      +        >>> Version('1.0.0')
      +        
      +        """
      +        return f""
      +
      +    def __str__(self) -> str:
      +        """A string representation of the version that can be rounded-tripped.
      +
      +        >>> str(Version("1.0a5"))
      +        '1.0a5'
      +        """
      +        parts = []
      +
      +        # Epoch
      +        if self.epoch != 0:
      +            parts.append(f"{self.epoch}!")
      +
      +        # Release segment
      +        parts.append(".".join(str(x) for x in self.release))
      +
      +        # Pre-release
      +        if self.pre is not None:
      +            parts.append("".join(str(x) for x in self.pre))
      +
      +        # Post-release
      +        if self.post is not None:
      +            parts.append(f".post{self.post}")
      +
      +        # Development release
      +        if self.dev is not None:
      +            parts.append(f".dev{self.dev}")
      +
      +        # Local version segment
      +        if self.local is not None:
      +            parts.append(f"+{self.local}")
      +
      +        return "".join(parts)
      +
      +    @property
      +    def epoch(self) -> int:
      +        """The epoch of the version.
      +
      +        >>> Version("2.0.0").epoch
      +        0
      +        >>> Version("1!2.0.0").epoch
      +        1
      +        """
      +        _epoch: int = self._version.epoch
      +        return _epoch
      +
      +    @property
      +    def release(self) -> Tuple[int, ...]:
      +        """The components of the "release" segment of the version.
      +
      +        >>> Version("1.2.3").release
      +        (1, 2, 3)
      +        >>> Version("2.0.0").release
      +        (2, 0, 0)
      +        >>> Version("1!2.0.0.post0").release
      +        (2, 0, 0)
      +
      +        Includes trailing zeroes but not the epoch or any pre-release / development /
      +        post-release suffixes.
      +        """
      +        _release: Tuple[int, ...] = self._version.release
      +        return _release
      +
      +    @property
      +    def pre(self) -> Optional[Tuple[str, int]]:
      +        """The pre-release segment of the version.
      +
      +        >>> print(Version("1.2.3").pre)
      +        None
      +        >>> Version("1.2.3a1").pre
      +        ('a', 1)
      +        >>> Version("1.2.3b1").pre
      +        ('b', 1)
      +        >>> Version("1.2.3rc1").pre
      +        ('rc', 1)
      +        """
      +        _pre: Optional[Tuple[str, int]] = self._version.pre
      +        return _pre
      +
      +    @property
      +    def post(self) -> Optional[int]:
      +        """The post-release number of the version.
      +
      +        >>> print(Version("1.2.3").post)
      +        None
      +        >>> Version("1.2.3.post1").post
      +        1
      +        """
      +        return self._version.post[1] if self._version.post else None
      +
      +    @property
      +    def dev(self) -> Optional[int]:
      +        """The development number of the version.
      +
      +        >>> print(Version("1.2.3").dev)
      +        None
      +        >>> Version("1.2.3.dev1").dev
      +        1
      +        """
      +        return self._version.dev[1] if self._version.dev else None
      +
      +    @property
      +    def local(self) -> Optional[str]:
      +        """The local version segment of the version.
      +
      +        >>> print(Version("1.2.3").local)
      +        None
      +        >>> Version("1.2.3+abc").local
      +        'abc'
      +        """
      +        if self._version.local:
      +            return ".".join(str(x) for x in self._version.local)
      +        else:
      +            return None
      +
      +    @property
      +    def public(self) -> str:
      +        """The public portion of the version.
      +
      +        >>> Version("1.2.3").public
      +        '1.2.3'
      +        >>> Version("1.2.3+abc").public
      +        '1.2.3'
      +        >>> Version("1.2.3+abc.dev1").public
      +        '1.2.3'
      +        """
      +        return str(self).split("+", 1)[0]
      +
      +    @property
      +    def base_version(self) -> str:
      +        """The "base version" of the version.
      +
      +        >>> Version("1.2.3").base_version
      +        '1.2.3'
      +        >>> Version("1.2.3+abc").base_version
      +        '1.2.3'
      +        >>> Version("1!1.2.3+abc.dev1").base_version
      +        '1!1.2.3'
      +
      +        The "base version" is the public version of the project without any pre or post
      +        release markers.
      +        """
      +        parts = []
      +
      +        # Epoch
      +        if self.epoch != 0:
      +            parts.append(f"{self.epoch}!")
      +
      +        # Release segment
      +        parts.append(".".join(str(x) for x in self.release))
      +
      +        return "".join(parts)
      +
      +    @property
      +    def is_prerelease(self) -> bool:
      +        """Whether this version is a pre-release.
      +
      +        >>> Version("1.2.3").is_prerelease
      +        False
      +        >>> Version("1.2.3a1").is_prerelease
      +        True
      +        >>> Version("1.2.3b1").is_prerelease
      +        True
      +        >>> Version("1.2.3rc1").is_prerelease
      +        True
      +        >>> Version("1.2.3dev1").is_prerelease
      +        True
      +        """
      +        return self.dev is not None or self.pre is not None
      +
      +    @property
      +    def is_postrelease(self) -> bool:
      +        """Whether this version is a post-release.
      +
      +        >>> Version("1.2.3").is_postrelease
      +        False
      +        >>> Version("1.2.3.post1").is_postrelease
      +        True
      +        """
      +        return self.post is not None
      +
      +    @property
      +    def is_devrelease(self) -> bool:
      +        """Whether this version is a development release.
      +
      +        >>> Version("1.2.3").is_devrelease
      +        False
      +        >>> Version("1.2.3.dev1").is_devrelease
      +        True
      +        """
      +        return self.dev is not None
      +
      +    @property
      +    def major(self) -> int:
      +        """The first item of :attr:`release` or ``0`` if unavailable.
      +
      +        >>> Version("1.2.3").major
      +        1
      +        """
      +        return self.release[0] if len(self.release) >= 1 else 0
      +
      +    @property
      +    def minor(self) -> int:
      +        """The second item of :attr:`release` or ``0`` if unavailable.
      +
      +        >>> Version("1.2.3").minor
      +        2
      +        >>> Version("1").minor
      +        0
      +        """
      +        return self.release[1] if len(self.release) >= 2 else 0
      +
      +    @property
      +    def micro(self) -> int:
      +        """The third item of :attr:`release` or ``0`` if unavailable.
      +
      +        >>> Version("1.2.3").micro
      +        3
      +        >>> Version("1").micro
      +        0
      +        """
      +        return self.release[2] if len(self.release) >= 3 else 0
      +
      +
      +def _parse_letter_version(
      +    letter: str, number: Union[str, bytes, SupportsInt]
      +) -> Optional[Tuple[str, int]]:
      +
      +    if letter:
      +        # We consider there to be an implicit 0 in a pre-release if there is
      +        # not a numeral associated with it.
      +        if number is None:
      +            number = 0
      +
      +        # We normalize any letters to their lower case form
      +        letter = letter.lower()
      +
      +        # We consider some words to be alternate spellings of other words and
      +        # in those cases we want to normalize the spellings to our preferred
      +        # spelling.
      +        if letter == "alpha":
      +            letter = "a"
      +        elif letter == "beta":
      +            letter = "b"
      +        elif letter in ["c", "pre", "preview"]:
      +            letter = "rc"
      +        elif letter in ["rev", "r"]:
      +            letter = "post"
      +
      +        return letter, int(number)
      +    if not letter and number:
      +        # We assume if we are given a number, but we are not given a letter
      +        # then this is using the implicit post release syntax (e.g. 1.0-1)
      +        letter = "post"
      +
      +        return letter, int(number)
      +
      +    return None
      +
      +
      +_local_version_separators = re.compile(r"[\._-]")
      +
      +
      +def _parse_local_version(local: str) -> Optional[LocalType]:
      +    """
      +    Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
      +    """
      +    if local is not None:
      +        return tuple(
      +            part.lower() if not part.isdigit() else int(part)
      +            for part in _local_version_separators.split(local)
      +        )
      +    return None
      +
      +
      +def _cmpkey(
      +    epoch: int,
      +    release: Tuple[int, ...],
      +    pre: Optional[Tuple[str, int]],
      +    post: Optional[Tuple[str, int]],
      +    dev: Optional[Tuple[str, int]],
      +    local: Optional[Tuple[SubLocalType]],
      +) -> CmpKey:
      +
      +    # When we compare a release version, we want to compare it with all of the
      +    # trailing zeros removed. So we'll use a reverse the list, drop all the now
      +    # leading zeros until we come to something non zero, then take the rest
      +    # re-reverse it back into the correct order and make it a tuple and use
      +    # that for our sorting key.
      +    _release = tuple(
      +        reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
      +    )
      +
      +    # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
      +    # We'll do this by abusing the pre segment, but we _only_ want to do this
      +    # if there is not a pre or a post segment. If we have one of those then
      +    # the normal sorting rules will handle this case correctly.
      +    if pre is None and post is None and dev is not None:
      +        _pre: PrePostDevType = NegativeInfinity
      +    # Versions without a pre-release (except as noted above) should sort after
      +    # those with one.
      +    elif pre is None:
      +        _pre = Infinity
      +    else:
      +        _pre = pre
      +
      +    # Versions without a post segment should sort before those with one.
      +    if post is None:
      +        _post: PrePostDevType = NegativeInfinity
      +
      +    else:
      +        _post = post
      +
      +    # Versions without a development segment should sort after those with one.
      +    if dev is None:
      +        _dev: PrePostDevType = Infinity
      +
      +    else:
      +        _dev = dev
      +
      +    if local is None:
      +        # Versions without a local segment should sort before those with one.
      +        _local: LocalType = NegativeInfinity
      +    else:
      +        # Versions with a local segment need that segment parsed to implement
      +        # the sorting rules in PEP440.
      +        # - Alpha numeric segments sort before numeric segments
      +        # - Alpha numeric segments sort lexicographically
      +        # - Numeric segments sort numerically
      +        # - Shorter versions sort before longer versions when the prefixes
      +        #   match exactly
      +        _local = tuple(
      +            (i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
      +        )
      +
      +    return epoch, _release, _pre, _post, _dev, _local
      diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/vendor.txt b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/vendor.txt
      new file mode 100644
      index 000000000..7eba1b48c
      --- /dev/null
      +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/vendored/vendor.txt
      @@ -0,0 +1 @@
      +packaging==23.0
      diff --git a/dependencies/windows_amd64/python/Lib/site-packages/wheel/wheelfile.py b/dependencies/windows_amd64/python/Lib/site-packages/wheel/wheelfile.py
      index 8ae97336b..25129ce44 100644
      --- a/dependencies/windows_amd64/python/Lib/site-packages/wheel/wheelfile.py
      +++ b/dependencies/windows_amd64/python/Lib/site-packages/wheel/wheelfile.py
      @@ -153,6 +153,13 @@ def write(self, filename, arcname=None, compress_type=None):
               self.writestr(zinfo, data, compress_type)
       
           def writestr(self, zinfo_or_arcname, data, compress_type=None):
      +        if isinstance(zinfo_or_arcname, str):
      +            zinfo_or_arcname = ZipInfo(
      +                zinfo_or_arcname, date_time=get_zipinfo_datetime()
      +            )
      +            zinfo_or_arcname.compress_type = self.compression
      +            zinfo_or_arcname.external_attr = (0o664 | stat.S_IFREG) << 16
      +
               if isinstance(data, str):
                   data = data.encode("utf-8")
       
      @@ -183,9 +190,6 @@ def close(self):
                       )
                   )
                   writer.writerow((format(self.record_path), "", ""))
      -            zinfo = ZipInfo(self.record_path, date_time=get_zipinfo_datetime())
      -            zinfo.compress_type = self.compression
      -            zinfo.external_attr = 0o664 << 16
      -            self.writestr(zinfo, data.getvalue())
      +            self.writestr(self.record_path, data.getvalue())
       
               ZipFile.close(self)
      diff --git a/dependencies/windows_amd64/python/Scripts/espefuse.exe b/dependencies/windows_amd64/python/Scripts/espefuse.exe
      deleted file mode 100644
      index 877add0e0c75b4583b455d3ba2d9743d48d19503..0000000000000000000000000000000000000000
      GIT binary patch
      literal 0
      HcmV?d00001
      
      literal 108448
      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
      
      diff --git a/dependencies/windows_amd64/python/Scripts/espsecure.exe b/dependencies/windows_amd64/python/Scripts/espsecure.exe
      deleted file mode 100644
      index 228b40f5b01aaeb12b0b442a3f3163567123080a..0000000000000000000000000000000000000000
      GIT binary patch
      literal 0
      HcmV?d00001
      
      literal 108449
      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
      GuK7PI`$y*h
      
      diff --git a/dependencies/windows_amd64/python/Scripts/pip.exe b/dependencies/windows_amd64/python/Scripts/pip.exe
      index 81a80f8950760a147efe63f6c98f635cb2394ba0..dda03159a019e0decec65becc00ced65ba197a19 100644
      GIT binary patch
      delta 29
      icmZ2{o^A1YwuUW?*~^&U)pkwKTgLba%t&6&=l}rH+YP4x
      
      delta 29
      icmZ2{o^A1YwuUW?*~^%{XJ}2&TgLba%t&6&=l}q{eGAtB
      
      diff --git a/dependencies/windows_amd64/python/Scripts/pip3.10.exe b/dependencies/windows_amd64/python/Scripts/pip3.10.exe
      index 81a80f8950760a147efe63f6c98f635cb2394ba0..dda03159a019e0decec65becc00ced65ba197a19 100644
      GIT binary patch
      delta 29
      icmZ2{o^A1YwuUW?*~^&U)pkwKTgLba%t&6&=l}rH+YP4x
      
      delta 29
      icmZ2{o^A1YwuUW?*~^%{XJ}2&TgLba%t&6&=l}q{eGAtB
      
      diff --git a/dependencies/windows_amd64/python/Scripts/pip3.exe b/dependencies/windows_amd64/python/Scripts/pip3.exe
      index 81a80f8950760a147efe63f6c98f635cb2394ba0..dda03159a019e0decec65becc00ced65ba197a19 100644
      GIT binary patch
      delta 29
      icmZ2{o^A1YwuUW?*~^&U)pkwKTgLba%t&6&=l}rH+YP4x
      
      delta 29
      icmZ2{o^A1YwuUW?*~^%{XJ}2&TgLba%t&6&=l}q{eGAtB
      
      diff --git a/dependencies/windows_amd64/python/Scripts/pyserial-miniterm.exe b/dependencies/windows_amd64/python/Scripts/pyserial-miniterm.exe
      index 6bca4d12080af0b5b505fd4c8c54d7025b7fc967..1b7e0c7f3098a0c6de65cc3e6fb832196b45a3f8 100644
      GIT binary patch
      delta 29
      icmZ22wuUW?*~^&U*LF?MTgLbi%t%_!=l}rHuMMRD
      
      delta 29
      icmZ22wuUW?*~^%HW@t^%TgLbi%t%_!=l}q{Q47@o
      
      diff --git a/dependencies/windows_amd64/python/Scripts/pyserial-ports.exe b/dependencies/windows_amd64/python/Scripts/pyserial-ports.exe
      index a80b1fccbbba3a0603bcb5a935c28f7288677639..62fa3fd049de0014a4c67157fd6fde2b150a343b 100644
      GIT binary patch
      delta 29
      icmZ2-o^8o_wuUW?*~^&U*LF?MTgLbq%t%?z=l}rIISs7<
      
      delta 29
      icmZ2-o^8o_wuUW?*~^%HW@t^%TgLbq%t%?z=l}q{+Y8+Q
      
      diff --git a/dependencies/windows_amd64/python/Scripts/wheel.exe b/dependencies/windows_amd64/python/Scripts/wheel.exe
      index 3ab4bc63c381ad146c868d228cfa479bf09fbf74..c2467d8e3f70c6db0b3e07bbb27eda31136f46fd 100644
      GIT binary patch
      delta 29
      icmbPso^9HBwuUW?*~^$;)pkwKTgLbl%m`b~=l}rD_6=(Q
      
      delta 29
      icmbPso^9HBwuUW?*~^$cW@t^%TgLbl%m`b~=l}q@u?w95
      
      
      From 8d9df34d36266738114ee5261fe1529b09adbe65 Mon Sep 17 00:00:00 2001
      From: Julius Jurgelenas 
      Date: Wed, 19 Apr 2023 22:18:24 +0300
      Subject: [PATCH 28/54] Implement support for flasher.pyz
      
      ---
       src/api/index.ts                              |  3 +-
       .../BinaryConfigurator/index.ts               |  6 +-
       .../CloudBinariesCache/index.ts               |  2 +-
       .../DeviceDescriptionsLoader/index.ts         | 74 ++++++++++++-------
       .../services/BinaryFlashingStrategy/index.ts  | 14 +++-
       .../FlashingStrategyLocator/artefacts.ts      |  3 +
       6 files changed, 69 insertions(+), 33 deletions(-)
      
      diff --git a/src/api/index.ts b/src/api/index.ts
      index 6917716e4..cfd2a16e5 100644
      --- a/src/api/index.ts
      +++ b/src/api/index.ts
      @@ -141,7 +141,8 @@ export default class ApiServer {
           const deviceDescriptionsLoader = new DeviceDescriptionsLoader(
             logger,
             config.PATH,
      -      path.join(config.userDataPath, 'firmwares', 'binary-targets')
      +      path.join(config.userDataPath, 'firmwares', 'binary-targets'),
      +      path.join(config.userDataPath, 'firmwares', 'device-options')
           );
           const cloudBinariesCache = new CloudBinariesCache(
             config.cloudCacheServer,
      diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      index ba4b804bb..0715da783 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      @@ -1,4 +1,3 @@
      -import path from 'path';
       import { BuildFlashFirmwareParams } from '../../FlashingStrategyLocator/BuildFlashFirmwareParams';
       import BuildJobType from '../../../models/enum/BuildJobType';
       import UserDefine from '../../../models/UserDefine';
      @@ -150,21 +149,24 @@ export default class BinaryConfigurator {
         async run(
           firmwareSourcePath: string,
           hardwareDefinitionsPath: string,
      +    flasherPath: string,
           firmwareBinaryPath: string,
           flags: string[][],
           onUpdate: OnOutputFunc = NoOpFunc
         ) {
           this.logger.log('flags', {
      +      firmwareSourcePath,
             flags: maskSensitiveFlags(flags),
             hardwareDefinitionsPath,
             firmwareBinaryPath,
      +      flasherPath,
           });
           const binaryConfiguratorArgs = [
             ...this.stringifyFlags([...flags, ['--dir', hardwareDefinitionsPath]]),
             firmwareBinaryPath,
           ];
           return this.python.runPythonScript(
      -      path.join(firmwareSourcePath, 'python', 'binary_configurator.py'),
      +      flasherPath,
             binaryConfiguratorArgs,
             onUpdate,
             {
      diff --git a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts
      index 9312c546a..e8eef1cea 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/CloudBinariesCache/index.ts
      @@ -15,7 +15,7 @@ export default class CloudBinariesCache {
             commitHash
           );
           await mkdirp(workingDir);
      -    const firmwareCacheDir = path.join(workingDir, 'dist', 'firmware');
      +    const firmwareCacheDir = path.join(workingDir, 'firmware');
       
           // if we have firmware in our local cache already no need to download it the second time.
           if (fs.existsSync(firmwareCacheDir)) {
      diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts
      index ae81afa8a..f685627b2 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts
      @@ -36,14 +36,18 @@ export interface FirmwareVersion {
       
       @Service()
       export default class DeviceDescriptionsLoader {
      -  mutex: Mutex;
      +  targetsMutex: Mutex;
      +
      +  deviceOptionsMutex: Mutex;
       
         constructor(
           private logger: LoggerService,
           private PATH: string,
      -    private targetStoragePath: string
      +    private targetStorageGitPath: string,
      +    private deviceOptionsGitPath: string
         ) {
      -    this.mutex = new Mutex();
      +    this.targetsMutex = new Mutex();
      +    this.deviceOptionsMutex = new Mutex();
         }
       
         private uploadMethodToFlashingMethod(uploadMethod: string): FlashingMethod {
      @@ -92,31 +96,32 @@ export default class DeviceDescriptionsLoader {
           args: TargetArgs,
           gitRepository: GitRepository
         ): Promise {
      -    await this.mutex.tryLockWithTimeout(60000);
      +    let targetsDataDirectory = '';
      +    await this.targetsMutex.tryLockWithTimeout(15 * 60 * 1000);
           try {
      -      const targetsDataDirectory = await this.loadTargetsData(
      +      targetsDataDirectory = await this.loadTargetsData(
      +        this.targetStorageGitPath,
               args,
               gitRepository
             );
      -
      -      const targetsJSONLoader = new TargetsJSONLoader(this.logger);
      -      const targetsJSONPath = path.join(targetsDataDirectory, 'targets.json');
      -      const data = await targetsJSONLoader.loadDeviceDescriptions(
      -        targetsJSONPath
      -      );
      -      const devices: Device[] = [];
      -      Object.keys(data).forEach((id) => {
      -        devices.push(
      -          this.configToDevice(id, data[id].category, data[id].config)
      -        );
      -      });
      -      return devices;
           } finally {
      -      this.mutex.unlock();
      +      this.targetsMutex.unlock();
           }
      +
      +    const targetsJSONLoader = new TargetsJSONLoader(this.logger);
      +    const targetsJSONPath = path.join(targetsDataDirectory, 'targets.json');
      +    const data = await targetsJSONLoader.loadDeviceDescriptions(
      +      targetsJSONPath
      +    );
      +    const devices: Device[] = [];
      +    Object.keys(data).forEach((id) => {
      +      devices.push(this.configToDevice(id, data[id].category, data[id].config));
      +    });
      +    return devices;
         }
       
         private async loadTargetsData(
      +    gitRepositoryPath: string,
           args: FirmwareVersion,
           gitRepository: GitRepository
         ): Promise {
      @@ -136,7 +141,7 @@ export default class DeviceDescriptionsLoader {
       
           const firmwareDownload = new GitFirmwareDownloader(
             {
      -        baseDirectory: this.targetStoragePath,
      +        baseDirectory: gitRepositoryPath,
               gitBinaryLocation: gitPath,
             },
             this.logger
      @@ -202,10 +207,18 @@ export default class DeviceDescriptionsLoader {
           args: UserDefineFilters,
           gitRepository: GitRepository
         ): Promise {
      -    const targetsDataDirectory = await this.loadTargetsData(
      -      args,
      -      gitRepository
      -    );
      +    let targetsDataDirectory = '';
      +    await this.deviceOptionsMutex.tryLockWithTimeout(15 * 60 * 1000);
      +    try {
      +      targetsDataDirectory = await this.loadTargetsData(
      +        this.deviceOptionsGitPath,
      +        args,
      +        gitRepository
      +      );
      +    } finally {
      +      this.deviceOptionsMutex.unlock();
      +    }
      +
           const targetsJSONLoader = new TargetsJSONLoader(this.logger);
           const targetsJSONPath = path.join(targetsDataDirectory, 'targets.json');
           const data = await targetsJSONLoader.loadDeviceDescriptions(
      @@ -320,11 +333,18 @@ export default class DeviceDescriptionsLoader {
         }
       
         async clearCache() {
      -    await this.mutex.tryLockWithTimeout(60000);
      +    await this.targetsMutex.tryLockWithTimeout(60000);
      +    try {
      +      return await removeDirectoryContents(this.targetStorageGitPath);
      +    } finally {
      +      this.targetsMutex.unlock();
      +    }
      +
      +    await this.deviceOptionsMutex.tryLockWithTimeout(60000);
           try {
      -      return await removeDirectoryContents(this.targetStoragePath);
      +      return await removeDirectoryContents(this.deviceOptionsGitPath);
           } finally {
      -      this.mutex.unlock();
      +      this.deviceOptionsMutex.unlock();
           }
         }
       }
      diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts
      index 1289a7e9c..ea510464d 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts
      @@ -4,6 +4,7 @@ import { PubSubEngine } from 'graphql-subscriptions';
       import * as os from 'os';
       import semver from 'semver';
       import path from 'path';
      +import fs from 'fs';
       import UserDefine from '../../models/UserDefine';
       import FirmwareSource from '../../models/enum/FirmwareSource';
       import Mutex from '../../library/Mutex';
      @@ -409,6 +410,11 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
             );
       
             let firmwareBinaryPath = '';
      +      let flasherPath = path.join(
      +        firmwareSourcePath,
      +        'python',
      +        'binary_configurator.py'
      +      );
             if (this.isRequestCompatibleWithCache(params)) {
               const currentCommitHash = await this.getCurrentSourceCommit(
                 gitRepositoryUrl
      @@ -426,6 +432,11 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
                   params.userDefines
                 );
                 firmwareBinaryPath = path.join(cacheLocation, cachedBinaryPath);
      +
      +          const flasherPyzPath = path.join(cacheLocation, 'flasher.pyz');
      +          if (fs.existsSync(flasherPyzPath)) {
      +            flasherPath = flasherPyzPath;
      +          }
               } catch (e) {
                 this.logger.log(
                   'failed to get cached build, reverting to building firmware locally',
      @@ -464,13 +475,12 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
       
             const hardwareDefinitionsPath = firmwareSourcePath;
       
      -      // TODO: pip3 install pyserial esptool
      -      // python-serial
             const flags: string[][] =
               this.binaryConfigurator.buildBinaryConfigFlags(params);
             const binaryConfiguratorResult = await this.binaryConfigurator.run(
               firmwareSourcePath,
               hardwareDefinitionsPath,
      +        flasherPath,
               firmwareBinaryPath,
               flags,
               (output) => {
      diff --git a/src/api/src/services/FlashingStrategyLocator/artefacts.ts b/src/api/src/services/FlashingStrategyLocator/artefacts.ts
      index be372f474..e4792a72e 100644
      --- a/src/api/src/services/FlashingStrategyLocator/artefacts.ts
      +++ b/src/api/src/services/FlashingStrategyLocator/artefacts.ts
      @@ -102,6 +102,9 @@ const listFiles = async (directory: string): Promise => {
       };
       
       export const removeDirectoryContents = async (firmwaresPath: string) => {
      +  if (!fs.existsSync(firmwaresPath)) {
      +    return;
      +  }
         const files = await listFiles(firmwaresPath);
         if (files.length > 4) {
           throw new Error(`unexpected number of files to remove: ${files}`);
      
      From fc4797ad67028f4e78947da3deb81b13cc32afa6 Mon Sep 17 00:00:00 2001
      From: Julius Jurgelenas 
      Date: Wed, 19 Apr 2023 22:58:38 +0300
      Subject: [PATCH 29/54] Support all realeases starting 3.0.0
      
      ---
       src/api/src/services/BinaryFlashingStrategy/index.ts | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts
      index ea510464d..5da9ad365 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts
      @@ -127,7 +127,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
       
           if (
             params.source === FirmwareSource.GitTag &&
      -      semver.lte(params.gitTag, '3.2.0')
      +      semver.lt(params.gitTag, '3.0.0')
           ) {
             return false;
           }
      
      From e6f364cc5b0dcc82b35912ee379d9f6b01d75d0d Mon Sep 17 00:00:00 2001
      From: Julius Jurgelenas 
      Date: Thu, 20 Apr 2023 00:12:01 +0300
      Subject: [PATCH 30/54] Fix betaflight passthrough flashing method
      
      ---
       .../BinaryConfigurator/index.ts                | 18 +++++++++++++++++-
       1 file changed, 17 insertions(+), 1 deletion(-)
      
      diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      index 0715da783..828dcdee9 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      @@ -29,6 +29,22 @@ export default class BinaryConfigurator {
           });
         }
       
      +  // Map targets.json flashing methods to binary_flash.py
      +  // https://github.com/ExpressLRS/ExpressLRS/blame/master/src/python/binary_flash.py
      +  mapUploadMethod(input: string): string {
      +    const mapped: { [n: string]: string } = {
      +      uart: 'uart',
      +      betaflight: 'bf',
      +      wifi: 'wifi',
      +      etx: 'etx',
      +      stlink: 'stlink',
      +    };
      +    if (typeof mapped[input] === 'string') {
      +      return mapped[input];
      +    }
      +    return input;
      +  }
      +
         buildBinaryConfigFlags(params: BuildFlashFirmwareParams): string[][] {
           const flags: string[][] = [];
           const [manufacturer, subType, device, uploadMethod] =
      @@ -47,7 +63,7 @@ export default class BinaryConfigurator {
             params.type === BuildJobType.BuildAndFlash ||
             params.type === BuildJobType.ForceFlash
           ) {
      -      flags.push(['--flash', uploadMethod]);
      +      flags.push(['--flash', this.mapUploadMethod(uploadMethod)]);
           }
       
           if (
      
      From c64ca47bd662331da3d366fe4dc8ded6397edc02 Mon Sep 17 00:00:00 2001
      From: Julius Jurgelenas 
      Date: Thu, 20 Apr 2023 08:17:15 +0300
      Subject: [PATCH 31/54] Clone inner flags array
      
      ---
       .../BinaryFlashingStrategy/BinaryConfigurator/index.ts    | 8 +++++---
       1 file changed, 5 insertions(+), 3 deletions(-)
      
      diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      index 828dcdee9..38abfe827 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts
      @@ -8,11 +8,13 @@ import { LoggerService } from '../../../logger';
       
       const maskSensitiveFlags = (data: string[][]): string[][] => {
         const sensitiveData = ['--phrase', '--ssid', '--password'];
      -  const result = [...data];
      +  const result: string[][] = [];
         for (let i = 0; i < data.length; i++) {
      -    if (sensitiveData.includes(data[i][0])) {
      -      result[i][1] = '***';
      +    const item = [...data[i]];
      +    if (sensitiveData.includes(item[0])) {
      +      item[1] = '***';
           }
      +    result.push(item);
         }
         return result;
       };
      
      From 2fc9175d7cd822e1607cb36e6121dafe721f11ea Mon Sep 17 00:00:00 2001
      From: Julius Jurgelenas 
      Date: Thu, 20 Apr 2023 11:01:54 +0300
      Subject: [PATCH 32/54] Use hardware defines path from cached binary
      
      ---
       src/api/src/services/BinaryFlashingStrategy/index.ts | 4 ++--
       1 file changed, 2 insertions(+), 2 deletions(-)
      
      diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts
      index 5da9ad365..e77874aee 100644
      --- a/src/api/src/services/BinaryFlashingStrategy/index.ts
      +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts
      @@ -410,6 +410,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
             );
       
             let firmwareBinaryPath = '';
      +      let hardwareDefinitionsPath = firmwareSourcePath;
             let flasherPath = path.join(
               firmwareSourcePath,
               'python',
      @@ -437,6 +438,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
                 if (fs.existsSync(flasherPyzPath)) {
                   flasherPath = flasherPyzPath;
                 }
      +          hardwareDefinitionsPath = cacheLocation;
               } catch (e) {
                 this.logger.log(
                   'failed to get cached build, reverting to building firmware locally',
      @@ -473,8 +475,6 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy {
               BuildFirmwareStep.BUILDING_FIRMWARE
             );
       
      -      const hardwareDefinitionsPath = firmwareSourcePath;
      -
             const flags: string[][] =
               this.binaryConfigurator.buildBinaryConfigFlags(params);
             const binaryConfiguratorResult = await this.binaryConfigurator.run(
      
      From f594adcd7494ef51294f5bffc2d0dcc541ca3eb2 Mon Sep 17 00:00:00 2001
      From: Paul Kendall 
      Date: Fri, 21 Apr 2023 09:19:31 +1200
      Subject: [PATCH 33/54] Add pyserial dependency for MacOS
      
      ---
       .../bin/pyserial-miniterm                     |    8 +
       .../bin/pyserial-ports                        |    8 +
       .../pyserial-3.5.dist-info/DESCRIPTION.rst    |   13 +
       .../pyserial-3.5.dist-info/INSTALLER          |    1 +
       .../pyserial-3.5.dist-info/METADATA           |   47 +
       .../pyserial-3.5.dist-info/RECORD             |   66 +
       .../pyserial-3.5.dist-info/WHEEL              |    6 +
       .../pyserial-3.5.dist-info/entry_points.txt   |    4 +
       .../pyserial-3.5.dist-info/metadata.json      |    1 +
       .../pyserial-3.5.dist-info/top_level.txt      |    1 +
       .../site-packages/serial/__init__.py          |   91 ++
       .../site-packages/serial/__main__.py          |    3 +
       .../python3.8/site-packages/serial/rfc2217.py | 1351 +++++++++++++++++
       .../python3.8/site-packages/serial/rs485.py   |   94 ++
       .../site-packages/serial/serialcli.py         |  253 +++
       .../site-packages/serial/serialjava.py        |  251 +++
       .../site-packages/serial/serialposix.py       |  900 +++++++++++
       .../site-packages/serial/serialutil.py        |  697 +++++++++
       .../site-packages/serial/serialwin32.py       |  477 ++++++
       .../site-packages/serial/threaded/__init__.py |  297 ++++
       .../site-packages/serial/tools/__init__.py    |    0
       .../serial/tools/hexlify_codec.py             |  126 ++
       .../site-packages/serial/tools/list_ports.py  |  110 ++
       .../serial/tools/list_ports_common.py         |  121 ++
       .../serial/tools/list_ports_linux.py          |  109 ++
       .../serial/tools/list_ports_osx.py            |  299 ++++
       .../serial/tools/list_ports_posix.py          |  119 ++
       .../serial/tools/list_ports_windows.py        |  427 ++++++
       .../site-packages/serial/tools/miniterm.py    | 1042 +++++++++++++
       .../serial/urlhandler/__init__.py             |    0
       .../serial/urlhandler/protocol_alt.py         |   57 +
       .../serial/urlhandler/protocol_cp2110.py      |  258 ++++
       .../serial/urlhandler/protocol_hwgrep.py      |   91 ++
       .../serial/urlhandler/protocol_loop.py        |  308 ++++
       .../serial/urlhandler/protocol_rfc2217.py     |   12 +
       .../serial/urlhandler/protocol_socket.py      |  359 +++++
       .../serial/urlhandler/protocol_spy.py         |  290 ++++
       .../python3.8/site-packages/serial/win32.py   |  366 +++++
       38 files changed, 8663 insertions(+)
       create mode 100755 dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-miniterm
       create mode 100755 dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-ports
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__init__.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__main__.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rfc2217.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rs485.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialcli.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialjava.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialposix.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialutil.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialwin32.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/threaded/__init__.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/__init__.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/hexlify_codec.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_common.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_linux.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_osx.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_posix.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_windows.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/miniterm.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/__init__.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_loop.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_rfc2217.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_socket.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_spy.py
       create mode 100644 dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/win32.py
      
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-miniterm b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-miniterm
      new file mode 100755
      index 000000000..d41cc4e91
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-miniterm
      @@ -0,0 +1,8 @@
      +#!/Users/paul/Projects/Quad/ExpressLRS-Configurator/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/python3
      +# -*- coding: utf-8 -*-
      +import re
      +import sys
      +from serial.tools.miniterm import main
      +if __name__ == '__main__':
      +    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
      +    sys.exit(main())
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-ports b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-ports
      new file mode 100755
      index 000000000..798e8c5f6
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/pyserial-ports
      @@ -0,0 +1,8 @@
      +#!/Users/paul/Projects/Quad/ExpressLRS-Configurator/dependencies/darwin_amd64/python-portable-darwin-3.8.4/bin/python3
      +# -*- coding: utf-8 -*-
      +import re
      +import sys
      +from serial.tools.list_ports import main
      +if __name__ == '__main__':
      +    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
      +    sys.exit(main())
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst
      new file mode 100644
      index 000000000..606dcaaa7
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/DESCRIPTION.rst
      @@ -0,0 +1,13 @@
      +Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython
      +
      +Stable:
      +
      +- Documentation: http://pythonhosted.org/pyserial/
      +- Download Page: https://pypi.python.org/pypi/pyserial
      +
      +Latest:
      +
      +- Documentation: http://pyserial.readthedocs.io/en/latest/
      +- Project Homepage: https://github.com/pyserial/pyserial
      +
      +
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER
      new file mode 100644
      index 000000000..a1b589e38
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/INSTALLER
      @@ -0,0 +1 @@
      +pip
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA
      new file mode 100644
      index 000000000..363d8e1b1
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/METADATA
      @@ -0,0 +1,47 @@
      +Metadata-Version: 2.0
      +Name: pyserial
      +Version: 3.5
      +Summary: Python Serial Port Extension
      +Home-page: https://github.com/pyserial/pyserial
      +Author: Chris Liechti
      +Author-email: cliechti@gmx.net
      +License: BSD
      +Platform: any
      +Classifier: Development Status :: 5 - Production/Stable
      +Classifier: Intended Audience :: Developers
      +Classifier: Intended Audience :: End Users/Desktop
      +Classifier: License :: OSI Approved :: BSD License
      +Classifier: Natural Language :: English
      +Classifier: Operating System :: POSIX
      +Classifier: Operating System :: Microsoft :: Windows
      +Classifier: Operating System :: MacOS :: MacOS X
      +Classifier: Programming Language :: Python
      +Classifier: Programming Language :: Python :: 2
      +Classifier: Programming Language :: Python :: 2.7
      +Classifier: Programming Language :: Python :: 3
      +Classifier: Programming Language :: Python :: 3.4
      +Classifier: Programming Language :: Python :: 3.5
      +Classifier: Programming Language :: Python :: 3.6
      +Classifier: Programming Language :: Python :: 3.7
      +Classifier: Programming Language :: Python :: 3.8
      +Classifier: Topic :: Communications
      +Classifier: Topic :: Software Development :: Libraries
      +Classifier: Topic :: Software Development :: Libraries :: Python Modules
      +Classifier: Topic :: Terminals :: Serial
      +Provides-Extra: cp2110
      +Provides-Extra: cp2110
      +Requires-Dist: hidapi; extra == 'cp2110'
      +
      +Python Serial Port Extension for Win32, OSX, Linux, BSD, Jython, IronPython
      +
      +Stable:
      +
      +- Documentation: http://pythonhosted.org/pyserial/
      +- Download Page: https://pypi.python.org/pypi/pyserial
      +
      +Latest:
      +
      +- Documentation: http://pyserial.readthedocs.io/en/latest/
      +- Project Homepage: https://github.com/pyserial/pyserial
      +
      +
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD
      new file mode 100644
      index 000000000..2c9b2062a
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/RECORD
      @@ -0,0 +1,66 @@
      +../../../bin/pyserial-miniterm,sha256=U_k5pTsF8kULXNFgMdONxU020RIgE06Xs7vHEVqhndg,320
      +../../../bin/pyserial-ports,sha256=Rsc7kHaNn2yJkhDlAJWGxbOi9wUU46O4Oj0gQGsy7U0,322
      +pyserial-3.5.dist-info/DESCRIPTION.rst,sha256=rXXIUFeAsfXq2YS7DGkztGmXez-G7gAwbwdBL8t9KME,320
      +pyserial-3.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
      +pyserial-3.5.dist-info/METADATA,sha256=QqirfpTvC3uqfpTNrGXWuSVMYIR29jASDJkAB79HKUM,1650
      +pyserial-3.5.dist-info/RECORD,,
      +pyserial-3.5.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
      +pyserial-3.5.dist-info/entry_points.txt,sha256=-AQ3oVmIn7rtW5Dh0Oup90Hq0qkIlMj79qGmdDIXk9U,112
      +pyserial-3.5.dist-info/metadata.json,sha256=s5rFXxQKL9QXO3UMXRmYoMXGRQt2lol67rf_64S1v10,1647
      +pyserial-3.5.dist-info/top_level.txt,sha256=FSjfWHWw-VjPiEqOhttbiP-F8OHn-liixq1wKL2fWOA,7
      +serial/__init__.py,sha256=XeyJf970Wg6vY-rNoeAdYuHnVJAwJYSuAjj3U3ZZI0Q,3212
      +serial/__main__.py,sha256=oSpVknDS2Yqn2JdXlDs5Fk0E8ccdiLIJaXvPWUizQj0,45
      +serial/__pycache__/__init__.cpython-38.pyc,,
      +serial/__pycache__/__main__.cpython-38.pyc,,
      +serial/__pycache__/rfc2217.cpython-38.pyc,,
      +serial/__pycache__/rs485.cpython-38.pyc,,
      +serial/__pycache__/serialcli.cpython-38.pyc,,
      +serial/__pycache__/serialjava.cpython-38.pyc,,
      +serial/__pycache__/serialposix.cpython-38.pyc,,
      +serial/__pycache__/serialutil.cpython-38.pyc,,
      +serial/__pycache__/serialwin32.cpython-38.pyc,,
      +serial/__pycache__/win32.cpython-38.pyc,,
      +serial/rfc2217.py,sha256=ncG_5Ts42M_Tm_7XN3Q7iE24y-lGcwu2jC3MFSEv6Bc,59700
      +serial/rs485.py,sha256=9t6yuGcte36gk8G1U6NgboKVGtJUFqtbpAOXj7vYxM0,3305
      +serial/serialcli.py,sha256=u5QnG90UxttqsGG9nYgkj0GUyb0wIOxzlUgxJ4gCczg,9190
      +serial/serialjava.py,sha256=AcHLp2D_sAihu7L_wCcg8mtk7etf6zAyB4L_tuthVo8,8480
      +serial/serialposix.py,sha256=XVb5hRM5HhdmoYR6BOLhICztQXKhUoA7ocgjoUmptvk,35127
      +serial/serialutil.py,sha256=PIT4x8MZ8WGoXW-Ntb7cT2UlVxRYm9y-7m8tr2WhfAo,21797
      +serial/serialwin32.py,sha256=F2geqaZQEgxx2xqum4iBMBpA04xHs0uw2gUOP1v7vgA,20284
      +serial/threaded/__init__.py,sha256=ikXlKYejRlzzCze9kwxR_uABKa1YfuTyqcPjw3VRV1I,9319
      +serial/threaded/__pycache__/__init__.cpython-38.pyc,,
      +serial/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
      +serial/tools/__pycache__/__init__.cpython-38.pyc,,
      +serial/tools/__pycache__/hexlify_codec.cpython-38.pyc,,
      +serial/tools/__pycache__/list_ports.cpython-38.pyc,,
      +serial/tools/__pycache__/list_ports_common.cpython-38.pyc,,
      +serial/tools/__pycache__/list_ports_linux.cpython-38.pyc,,
      +serial/tools/__pycache__/list_ports_osx.cpython-38.pyc,,
      +serial/tools/__pycache__/list_ports_posix.cpython-38.pyc,,
      +serial/tools/__pycache__/list_ports_windows.cpython-38.pyc,,
      +serial/tools/__pycache__/miniterm.cpython-38.pyc,,
      +serial/tools/hexlify_codec.py,sha256=FRJSO8pfjM6AR9_SBqL34e50LVkvlzfFKdmCScGn408,3677
      +serial/tools/list_ports.py,sha256=eDDoyIhoS3f9D3CVpthqlQUqiR2l-X0VTGGOBjuM4ew,3389
      +serial/tools/list_ports_common.py,sha256=x5HIghG4NIz-Xf5iX6Gk7xZfdeads2tqCsfyJhh3Ifs,3736
      +serial/tools/list_ports_linux.py,sha256=UnU1VYP1NJI7J8Zn7gY-A2mbi1lugbFZSVztfX8P1pU,4503
      +serial/tools/list_ports_osx.py,sha256=eoefMGiuJqC-OCu9aAWqqJX75wGlBzoqZ6kdmMA82LM,11178
      +serial/tools/list_ports_posix.py,sha256=EYqD5kRbk0f2a5scaRS4tgWGBynkpVH77ja_G6S3UhE,4535
      +serial/tools/list_ports_windows.py,sha256=U4EzcOAiU66LWoPdMXI2oOM4LCd5vKwO3DgeTT6M3qc,16021
      +serial/tools/miniterm.py,sha256=fXvkEU9FEyU7HPSNE8bdX2OCgiGYgCee066yF58nots,37840
      +serial/urlhandler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
      +serial/urlhandler/__pycache__/__init__.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_alt.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_cp2110.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_hwgrep.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_loop.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_rfc2217.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_socket.cpython-38.pyc,,
      +serial/urlhandler/__pycache__/protocol_spy.cpython-38.pyc,,
      +serial/urlhandler/protocol_alt.py,sha256=-kYoCgy9GyMWN6wC8Oew8FL04LjL4Ntx3HHVqSTKGcQ,2033
      +serial/urlhandler/protocol_cp2110.py,sha256=iULOT4Vdw20P_w2jfSWdt0roUY1Ku8xJVHHYc6d3ImY,8540
      +serial/urlhandler/protocol_hwgrep.py,sha256=GdKdQ9tExKRHJzsiPcQ9ExmaLa6-A71q52i4jQxmoBk,3159
      +serial/urlhandler/protocol_loop.py,sha256=5barru_hfwNkayjjBz4w3snBJn0G7C-fG7-QmHaeTWo,10623
      +serial/urlhandler/protocol_rfc2217.py,sha256=IPO8r3pFN6yEDl1Zv2jgUnfIa0tQ0iY0ZsD8_xhAUeQ,317
      +serial/urlhandler/protocol_socket.py,sha256=QotaHCPd6t903W_9fa2Lv_5uElq0noir2Ci10a987XM,14299
      +serial/urlhandler/protocol_spy.py,sha256=FdUaU43-bl1KXTy_S4HhzyGV5hpUsopm8IqTrX2VX-4,9130
      +serial/win32.py,sha256=lk6rod9mHkqzgchmaqB3ygiTkmAWTNQ00IJ985ZjvTI,11138
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL
      new file mode 100644
      index 000000000..7332a419c
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/WHEEL
      @@ -0,0 +1,6 @@
      +Wheel-Version: 1.0
      +Generator: bdist_wheel (0.30.0)
      +Root-Is-Purelib: true
      +Tag: py2-none-any
      +Tag: py3-none-any
      +
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt
      new file mode 100644
      index 000000000..b69a613dc
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/entry_points.txt
      @@ -0,0 +1,4 @@
      +[console_scripts]
      +pyserial-miniterm = serial.tools.miniterm:main
      +pyserial-ports = serial.tools.list_ports:main
      +
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json
      new file mode 100644
      index 000000000..5064763b3
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/metadata.json
      @@ -0,0 +1 @@
      +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: End Users/Desktop", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: POSIX", "Operating System :: Microsoft :: Windows", "Operating System :: MacOS :: MacOS X", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Communications", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Terminals :: Serial"], "extensions": {"python.commands": {"wrap_console": {"pyserial-miniterm": "serial.tools.miniterm:main", "pyserial-ports": "serial.tools.list_ports:main"}}, "python.details": {"contacts": [{"email": "cliechti@gmx.net", "name": "Chris Liechti", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/pyserial/pyserial"}}, "python.exports": {"console_scripts": {"pyserial-miniterm": "serial.tools.miniterm:main", "pyserial-ports": "serial.tools.list_ports:main"}}}, "extras": ["cp2110"], "generator": "bdist_wheel (0.30.0)", "license": "BSD", "metadata_version": "2.0", "name": "pyserial", "platform": "any", "run_requires": [{"extra": "cp2110", "requires": ["hidapi"]}], "summary": "Python Serial Port Extension", "version": "3.5"}
      \ No newline at end of file
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt
      new file mode 100644
      index 000000000..b6be06a45
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/pyserial-3.5.dist-info/top_level.txt
      @@ -0,0 +1 @@
      +serial
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__init__.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__init__.py
      new file mode 100644
      index 000000000..caa4de1f5
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__init__.py
      @@ -0,0 +1,91 @@
      +#!/usr/bin/env python
      +#
      +# This is a wrapper module for different platform implementations
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2001-2020 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +import sys
      +import importlib
      +
      +from serial.serialutil import *
      +#~ SerialBase, SerialException, to_bytes, iterbytes
      +
      +__version__ = '3.5'
      +
      +VERSION = __version__
      +
      +# pylint: disable=wrong-import-position
      +if sys.platform == 'cli':
      +    from serial.serialcli import Serial
      +else:
      +    import os
      +    # chose an implementation, depending on os
      +    if os.name == 'nt':  # sys.platform == 'win32':
      +        from serial.serialwin32 import Serial
      +    elif os.name == 'posix':
      +        from serial.serialposix import Serial, PosixPollSerial, VTIMESerial  # noqa
      +    elif os.name == 'java':
      +        from serial.serialjava import Serial
      +    else:
      +        raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
      +
      +
      +protocol_handler_packages = [
      +    'serial.urlhandler',
      +]
      +
      +
      +def serial_for_url(url, *args, **kwargs):
      +    """\
      +    Get an instance of the Serial class, depending on port/url. The port is not
      +    opened when the keyword parameter 'do_not_open' is true, by default it
      +    is. All other parameters are directly passed to the __init__ method when
      +    the port is instantiated.
      +
      +    The list of package names that is searched for protocol handlers is kept in
      +    ``protocol_handler_packages``.
      +
      +    e.g. we want to support a URL ``foobar://``. A module
      +    ``my_handlers.protocol_foobar`` is provided by the user. Then
      +    ``protocol_handler_packages.append("my_handlers")`` would extend the search
      +    path so that ``serial_for_url("foobar://"))`` would work.
      +    """
      +    # check and remove extra parameter to not confuse the Serial class
      +    do_open = not kwargs.pop('do_not_open', False)
      +    # the default is to use the native implementation
      +    klass = Serial
      +    try:
      +        url_lowercase = url.lower()
      +    except AttributeError:
      +        # it's not a string, use default
      +        pass
      +    else:
      +        # if it is an URL, try to import the handler module from the list of possible packages
      +        if '://' in url_lowercase:
      +            protocol = url_lowercase.split('://', 1)[0]
      +            module_name = '.protocol_{}'.format(protocol)
      +            for package_name in protocol_handler_packages:
      +                try:
      +                    importlib.import_module(package_name)
      +                    handler_module = importlib.import_module(module_name, package_name)
      +                except ImportError:
      +                    continue
      +                else:
      +                    if hasattr(handler_module, 'serial_class_for_url'):
      +                        url, klass = handler_module.serial_class_for_url(url)
      +                    else:
      +                        klass = handler_module.Serial
      +                    break
      +            else:
      +                raise ValueError('invalid URL, protocol {!r} not known'.format(protocol))
      +    # instantiate and open when desired
      +    instance = klass(None, *args, **kwargs)
      +    instance.port = url
      +    if do_open:
      +        instance.open()
      +    return instance
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__main__.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__main__.py
      new file mode 100644
      index 000000000..bd0a2e63b
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/__main__.py
      @@ -0,0 +1,3 @@
      +from .tools import miniterm
      +
      +miniterm.main()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rfc2217.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rfc2217.py
      new file mode 100644
      index 000000000..2ae188edd
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rfc2217.py
      @@ -0,0 +1,1351 @@
      +#! python
      +#
      +# This module implements a RFC2217 compatible client. RF2217 descibes a
      +# protocol to access serial ports over TCP/IP and allows setting the baud rate,
      +# modem control lines etc.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2001-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +# TODO:
      +# - setting control line -> answer is not checked (had problems with one of the
      +#   severs). consider implementing a compatibility mode flag to make check
      +#   conditional
      +# - write timeout not implemented at all
      +
      +# ###########################################################################
      +# observations and issues with servers
      +# ===========================================================================
      +# sredird V2.2.1
      +# - http://www.ibiblio.org/pub/Linux/system/serial/   sredird-2.2.2.tar.gz
      +# - does not acknowledge SET_CONTROL (RTS/DTR) correctly, always responding
      +#   [105 1] instead of the actual value.
      +# - SET_BAUDRATE answer contains 4 extra null bytes -> probably for larger
      +#   numbers than 2**32?
      +# - To get the signature [COM_PORT_OPTION 0] has to be sent.
      +# - run a server: while true; do nc -l -p 7000 -c "sredird debug /dev/ttyUSB0 /var/lock/sredir"; done
      +# ===========================================================================
      +# telnetcpcd (untested)
      +# - http://ftp.wayne.edu/kermit/sredird/telnetcpcd-1.09.tar.gz
      +# - To get the signature [COM_PORT_OPTION] w/o data has to be sent.
      +# ===========================================================================
      +# ser2net
      +# - does not negotiate BINARY or COM_PORT_OPTION for his side but at least
      +#   acknowledges that the client activates these options
      +# - The configuration may be that the server prints a banner. As this client
      +#   implementation does a flushInput on connect, this banner is hidden from
      +#   the user application.
      +# - NOTIFY_MODEMSTATE: the poll interval of the server seems to be one
      +#   second.
      +# - To get the signature [COM_PORT_OPTION 0] has to be sent.
      +# - run a server: run ser2net daemon, in /etc/ser2net.conf:
      +#     2000:telnet:0:/dev/ttyS0:9600 remctl banner
      +# ###########################################################################
      +
      +# How to identify ports? pySerial might want to support other protocols in the
      +# future, so lets use an URL scheme.
      +# for RFC2217 compliant servers we will use this:
      +#    rfc2217://:[?option[&option...]]
      +#
      +# options:
      +# - "logging" set log level print diagnostic messages (e.g. "logging=debug")
      +# - "ign_set_control": do not look at the answers to SET_CONTROL
      +# - "poll_modem": issue NOTIFY_MODEMSTATE requests when CTS/DTR/RI/CD is read.
      +#   Without this option it expects that the server sends notifications
      +#   automatically on change (which most servers do and is according to the
      +#   RFC).
      +# the order of the options is not relevant
      +
      +from __future__ import absolute_import
      +
      +import logging
      +import socket
      +import struct
      +import threading
      +import time
      +try:
      +    import urlparse
      +except ImportError:
      +    import urllib.parse as urlparse
      +try:
      +    import Queue
      +except ImportError:
      +    import queue as Queue
      +
      +import serial
      +from serial.serialutil import SerialBase, SerialException, to_bytes, \
      +    iterbytes, PortNotOpenError, Timeout
      +
      +# port string is expected to be something like this:
      +# rfc2217://host:port
      +# host may be an IP or including domain, whatever.
      +# port is 0...65535
      +
      +# map log level names to constants. used in from_url()
      +LOGGER_LEVELS = {
      +    'debug': logging.DEBUG,
      +    'info': logging.INFO,
      +    'warning': logging.WARNING,
      +    'error': logging.ERROR,
      +}
      +
      +
      +# telnet protocol characters
      +SE = b'\xf0'    # Subnegotiation End
      +NOP = b'\xf1'   # No Operation
      +DM = b'\xf2'    # Data Mark
      +BRK = b'\xf3'   # Break
      +IP = b'\xf4'    # Interrupt process
      +AO = b'\xf5'    # Abort output
      +AYT = b'\xf6'   # Are You There
      +EC = b'\xf7'    # Erase Character
      +EL = b'\xf8'    # Erase Line
      +GA = b'\xf9'    # Go Ahead
      +SB = b'\xfa'    # Subnegotiation Begin
      +WILL = b'\xfb'
      +WONT = b'\xfc'
      +DO = b'\xfd'
      +DONT = b'\xfe'
      +IAC = b'\xff'   # Interpret As Command
      +IAC_DOUBLED = b'\xff\xff'
      +
      +# selected telnet options
      +BINARY = b'\x00'    # 8-bit data path
      +ECHO = b'\x01'      # echo
      +SGA = b'\x03'       # suppress go ahead
      +
      +# RFC2217
      +COM_PORT_OPTION = b'\x2c'
      +
      +# Client to Access Server
      +SET_BAUDRATE = b'\x01'
      +SET_DATASIZE = b'\x02'
      +SET_PARITY = b'\x03'
      +SET_STOPSIZE = b'\x04'
      +SET_CONTROL = b'\x05'
      +NOTIFY_LINESTATE = b'\x06'
      +NOTIFY_MODEMSTATE = b'\x07'
      +FLOWCONTROL_SUSPEND = b'\x08'
      +FLOWCONTROL_RESUME = b'\x09'
      +SET_LINESTATE_MASK = b'\x0a'
      +SET_MODEMSTATE_MASK = b'\x0b'
      +PURGE_DATA = b'\x0c'
      +
      +SERVER_SET_BAUDRATE = b'\x65'
      +SERVER_SET_DATASIZE = b'\x66'
      +SERVER_SET_PARITY = b'\x67'
      +SERVER_SET_STOPSIZE = b'\x68'
      +SERVER_SET_CONTROL = b'\x69'
      +SERVER_NOTIFY_LINESTATE = b'\x6a'
      +SERVER_NOTIFY_MODEMSTATE = b'\x6b'
      +SERVER_FLOWCONTROL_SUSPEND = b'\x6c'
      +SERVER_FLOWCONTROL_RESUME = b'\x6d'
      +SERVER_SET_LINESTATE_MASK = b'\x6e'
      +SERVER_SET_MODEMSTATE_MASK = b'\x6f'
      +SERVER_PURGE_DATA = b'\x70'
      +
      +RFC2217_ANSWER_MAP = {
      +    SET_BAUDRATE: SERVER_SET_BAUDRATE,
      +    SET_DATASIZE: SERVER_SET_DATASIZE,
      +    SET_PARITY: SERVER_SET_PARITY,
      +    SET_STOPSIZE: SERVER_SET_STOPSIZE,
      +    SET_CONTROL: SERVER_SET_CONTROL,
      +    NOTIFY_LINESTATE: SERVER_NOTIFY_LINESTATE,
      +    NOTIFY_MODEMSTATE: SERVER_NOTIFY_MODEMSTATE,
      +    FLOWCONTROL_SUSPEND: SERVER_FLOWCONTROL_SUSPEND,
      +    FLOWCONTROL_RESUME: SERVER_FLOWCONTROL_RESUME,
      +    SET_LINESTATE_MASK: SERVER_SET_LINESTATE_MASK,
      +    SET_MODEMSTATE_MASK: SERVER_SET_MODEMSTATE_MASK,
      +    PURGE_DATA: SERVER_PURGE_DATA,
      +}
      +
      +SET_CONTROL_REQ_FLOW_SETTING = b'\x00'        # Request Com Port Flow Control Setting (outbound/both)
      +SET_CONTROL_USE_NO_FLOW_CONTROL = b'\x01'     # Use No Flow Control (outbound/both)
      +SET_CONTROL_USE_SW_FLOW_CONTROL = b'\x02'     # Use XON/XOFF Flow Control (outbound/both)
      +SET_CONTROL_USE_HW_FLOW_CONTROL = b'\x03'     # Use HARDWARE Flow Control (outbound/both)
      +SET_CONTROL_REQ_BREAK_STATE = b'\x04'         # Request BREAK State
      +SET_CONTROL_BREAK_ON = b'\x05'                # Set BREAK State ON
      +SET_CONTROL_BREAK_OFF = b'\x06'               # Set BREAK State OFF
      +SET_CONTROL_REQ_DTR = b'\x07'                 # Request DTR Signal State
      +SET_CONTROL_DTR_ON = b'\x08'                  # Set DTR Signal State ON
      +SET_CONTROL_DTR_OFF = b'\x09'                 # Set DTR Signal State OFF
      +SET_CONTROL_REQ_RTS = b'\x0a'                 # Request RTS Signal State
      +SET_CONTROL_RTS_ON = b'\x0b'                  # Set RTS Signal State ON
      +SET_CONTROL_RTS_OFF = b'\x0c'                 # Set RTS Signal State OFF
      +SET_CONTROL_REQ_FLOW_SETTING_IN = b'\x0d'     # Request Com Port Flow Control Setting (inbound)
      +SET_CONTROL_USE_NO_FLOW_CONTROL_IN = b'\x0e'  # Use No Flow Control (inbound)
      +SET_CONTROL_USE_SW_FLOW_CONTOL_IN = b'\x0f'   # Use XON/XOFF Flow Control (inbound)
      +SET_CONTROL_USE_HW_FLOW_CONTOL_IN = b'\x10'   # Use HARDWARE Flow Control (inbound)
      +SET_CONTROL_USE_DCD_FLOW_CONTROL = b'\x11'    # Use DCD Flow Control (outbound/both)
      +SET_CONTROL_USE_DTR_FLOW_CONTROL = b'\x12'    # Use DTR Flow Control (inbound)
      +SET_CONTROL_USE_DSR_FLOW_CONTROL = b'\x13'    # Use DSR Flow Control (outbound/both)
      +
      +LINESTATE_MASK_TIMEOUT = 128        # Time-out Error
      +LINESTATE_MASK_SHIFTREG_EMPTY = 64  # Transfer Shift Register Empty
      +LINESTATE_MASK_TRANSREG_EMPTY = 32  # Transfer Holding Register Empty
      +LINESTATE_MASK_BREAK_DETECT = 16    # Break-detect Error
      +LINESTATE_MASK_FRAMING_ERROR = 8    # Framing Error
      +LINESTATE_MASK_PARTIY_ERROR = 4     # Parity Error
      +LINESTATE_MASK_OVERRUN_ERROR = 2    # Overrun Error
      +LINESTATE_MASK_DATA_READY = 1       # Data Ready
      +
      +MODEMSTATE_MASK_CD = 128            # Receive Line Signal Detect (also known as Carrier Detect)
      +MODEMSTATE_MASK_RI = 64             # Ring Indicator
      +MODEMSTATE_MASK_DSR = 32            # Data-Set-Ready Signal State
      +MODEMSTATE_MASK_CTS = 16            # Clear-To-Send Signal State
      +MODEMSTATE_MASK_CD_CHANGE = 8       # Delta Receive Line Signal Detect
      +MODEMSTATE_MASK_RI_CHANGE = 4       # Trailing-edge Ring Detector
      +MODEMSTATE_MASK_DSR_CHANGE = 2      # Delta Data-Set-Ready
      +MODEMSTATE_MASK_CTS_CHANGE = 1      # Delta Clear-To-Send
      +
      +PURGE_RECEIVE_BUFFER = b'\x01'      # Purge access server receive data buffer
      +PURGE_TRANSMIT_BUFFER = b'\x02'     # Purge access server transmit data buffer
      +PURGE_BOTH_BUFFERS = b'\x03'        # Purge both the access server receive data
      +                                    # buffer and the access server transmit data buffer
      +
      +
      +RFC2217_PARITY_MAP = {
      +    serial.PARITY_NONE: 1,
      +    serial.PARITY_ODD: 2,
      +    serial.PARITY_EVEN: 3,
      +    serial.PARITY_MARK: 4,
      +    serial.PARITY_SPACE: 5,
      +}
      +RFC2217_REVERSE_PARITY_MAP = dict((v, k) for k, v in RFC2217_PARITY_MAP.items())
      +
      +RFC2217_STOPBIT_MAP = {
      +    serial.STOPBITS_ONE: 1,
      +    serial.STOPBITS_ONE_POINT_FIVE: 3,
      +    serial.STOPBITS_TWO: 2,
      +}
      +RFC2217_REVERSE_STOPBIT_MAP = dict((v, k) for k, v in RFC2217_STOPBIT_MAP.items())
      +
      +# Telnet filter states
      +M_NORMAL = 0
      +M_IAC_SEEN = 1
      +M_NEGOTIATE = 2
      +
      +# TelnetOption and TelnetSubnegotiation states
      +REQUESTED = 'REQUESTED'
      +ACTIVE = 'ACTIVE'
      +INACTIVE = 'INACTIVE'
      +REALLY_INACTIVE = 'REALLY_INACTIVE'
      +
      +
      +class TelnetOption(object):
      +    """Manage a single telnet option, keeps track of DO/DONT WILL/WONT."""
      +
      +    def __init__(self, connection, name, option, send_yes, send_no, ack_yes,
      +                 ack_no, initial_state, activation_callback=None):
      +        """\
      +        Initialize option.
      +        :param connection: connection used to transmit answers
      +        :param name: a readable name for debug outputs
      +        :param send_yes: what to send when option is to be enabled.
      +        :param send_no: what to send when option is to be disabled.
      +        :param ack_yes: what to expect when remote agrees on option.
      +        :param ack_no: what to expect when remote disagrees on option.
      +        :param initial_state: options initialized with REQUESTED are tried to
      +            be enabled on startup. use INACTIVE for all others.
      +        """
      +        self.connection = connection
      +        self.name = name
      +        self.option = option
      +        self.send_yes = send_yes
      +        self.send_no = send_no
      +        self.ack_yes = ack_yes
      +        self.ack_no = ack_no
      +        self.state = initial_state
      +        self.active = False
      +        self.activation_callback = activation_callback
      +
      +    def __repr__(self):
      +        """String for debug outputs"""
      +        return "{o.name}:{o.active}({o.state})".format(o=self)
      +
      +    def process_incoming(self, command):
      +        """\
      +        A DO/DONT/WILL/WONT was received for this option, update state and
      +        answer when needed.
      +        """
      +        if command == self.ack_yes:
      +            if self.state is REQUESTED:
      +                self.state = ACTIVE
      +                self.active = True
      +                if self.activation_callback is not None:
      +                    self.activation_callback()
      +            elif self.state is ACTIVE:
      +                pass
      +            elif self.state is INACTIVE:
      +                self.state = ACTIVE
      +                self.connection.telnet_send_option(self.send_yes, self.option)
      +                self.active = True
      +                if self.activation_callback is not None:
      +                    self.activation_callback()
      +            elif self.state is REALLY_INACTIVE:
      +                self.connection.telnet_send_option(self.send_no, self.option)
      +            else:
      +                raise ValueError('option in illegal state {!r}'.format(self))
      +        elif command == self.ack_no:
      +            if self.state is REQUESTED:
      +                self.state = INACTIVE
      +                self.active = False
      +            elif self.state is ACTIVE:
      +                self.state = INACTIVE
      +                self.connection.telnet_send_option(self.send_no, self.option)
      +                self.active = False
      +            elif self.state is INACTIVE:
      +                pass
      +            elif self.state is REALLY_INACTIVE:
      +                pass
      +            else:
      +                raise ValueError('option in illegal state {!r}'.format(self))
      +
      +
      +class TelnetSubnegotiation(object):
      +    """\
      +    A object to handle subnegotiation of options. In this case actually
      +    sub-sub options for RFC 2217. It is used to track com port options.
      +    """
      +
      +    def __init__(self, connection, name, option, ack_option=None):
      +        if ack_option is None:
      +            ack_option = option
      +        self.connection = connection
      +        self.name = name
      +        self.option = option
      +        self.value = None
      +        self.ack_option = ack_option
      +        self.state = INACTIVE
      +
      +    def __repr__(self):
      +        """String for debug outputs."""
      +        return "{sn.name}:{sn.state}".format(sn=self)
      +
      +    def set(self, value):
      +        """\
      +        Request a change of the value. a request is sent to the server. if
      +        the client needs to know if the change is performed he has to check the
      +        state of this object.
      +        """
      +        self.value = value
      +        self.state = REQUESTED
      +        self.connection.rfc2217_send_subnegotiation(self.option, self.value)
      +        if self.connection.logger:
      +            self.connection.logger.debug("SB Requesting {} -> {!r}".format(self.name, self.value))
      +
      +    def is_ready(self):
      +        """\
      +        Check if answer from server has been received. when server rejects
      +        the change, raise a ValueError.
      +        """
      +        if self.state == REALLY_INACTIVE:
      +            raise ValueError("remote rejected value for option {!r}".format(self.name))
      +        return self.state == ACTIVE
      +    # add property to have a similar interface as TelnetOption
      +    active = property(is_ready)
      +
      +    def wait(self, timeout=3):
      +        """\
      +        Wait until the subnegotiation has been acknowledged or timeout. It
      +        can also throw a value error when the answer from the server does not
      +        match the value sent.
      +        """
      +        timeout_timer = Timeout(timeout)
      +        while not timeout_timer.expired():
      +            time.sleep(0.05)    # prevent 100% CPU load
      +            if self.is_ready():
      +                break
      +        else:
      +            raise SerialException("timeout while waiting for option {!r}".format(self.name))
      +
      +    def check_answer(self, suboption):
      +        """\
      +        Check an incoming subnegotiation block. The parameter already has
      +        cut off the header like sub option number and com port option value.
      +        """
      +        if self.value == suboption[:len(self.value)]:
      +            self.state = ACTIVE
      +        else:
      +            # error propagation done in is_ready
      +            self.state = REALLY_INACTIVE
      +        if self.connection.logger:
      +            self.connection.logger.debug("SB Answer {} -> {!r} -> {}".format(self.name, suboption, self.state))
      +
      +
      +class Serial(SerialBase):
      +    """Serial port implementation for RFC 2217 remote serial ports."""
      +
      +    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
      +                 9600, 19200, 38400, 57600, 115200)
      +
      +    def __init__(self, *args, **kwargs):
      +        self._thread = None
      +        self._socket = None
      +        self._linestate = 0
      +        self._modemstate = None
      +        self._modemstate_timeout = Timeout(-1)
      +        self._remote_suspend_flow = False
      +        self._write_lock = None
      +        self.logger = None
      +        self._ignore_set_control_answer = False
      +        self._poll_modem_state = False
      +        self._network_timeout = 3
      +        self._telnet_options = None
      +        self._rfc2217_port_settings = None
      +        self._rfc2217_options = None
      +        self._read_buffer = None
      +        super(Serial, self).__init__(*args, **kwargs)  # must be last call in case of auto-open
      +
      +    def open(self):
      +        """\
      +        Open port with current settings. This may throw a SerialException
      +        if the port cannot be opened.
      +        """
      +        self.logger = None
      +        self._ignore_set_control_answer = False
      +        self._poll_modem_state = False
      +        self._network_timeout = 3
      +        if self._port is None:
      +            raise SerialException("Port must be configured before it can be used.")
      +        if self.is_open:
      +            raise SerialException("Port is already open.")
      +        try:
      +            self._socket = socket.create_connection(self.from_url(self.portstr), timeout=5)  # XXX good value?
      +            self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
      +        except Exception as msg:
      +            self._socket = None
      +            raise SerialException("Could not open port {}: {}".format(self.portstr, msg))
      +
      +        # use a thread save queue as buffer. it also simplifies implementing
      +        # the read timeout
      +        self._read_buffer = Queue.Queue()
      +        # to ensure that user writes does not interfere with internal
      +        # telnet/rfc2217 options establish a lock
      +        self._write_lock = threading.Lock()
      +        # name the following separately so that, below, a check can be easily done
      +        mandadory_options = [
      +            TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
      +            TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED),
      +        ]
      +        # all supported telnet options
      +        self._telnet_options = [
      +            TelnetOption(self, 'ECHO', ECHO, DO, DONT, WILL, WONT, REQUESTED),
      +            TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
      +            TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, REQUESTED),
      +            TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, INACTIVE),
      +            TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, REQUESTED),
      +        ] + mandadory_options
      +        # RFC 2217 specific states
      +        # COM port settings
      +        self._rfc2217_port_settings = {
      +            'baudrate': TelnetSubnegotiation(self, 'baudrate', SET_BAUDRATE, SERVER_SET_BAUDRATE),
      +            'datasize': TelnetSubnegotiation(self, 'datasize', SET_DATASIZE, SERVER_SET_DATASIZE),
      +            'parity':   TelnetSubnegotiation(self, 'parity',   SET_PARITY,   SERVER_SET_PARITY),
      +            'stopsize': TelnetSubnegotiation(self, 'stopsize', SET_STOPSIZE, SERVER_SET_STOPSIZE),
      +        }
      +        # There are more subnegotiation objects, combine all in one dictionary
      +        # for easy access
      +        self._rfc2217_options = {
      +            'purge':    TelnetSubnegotiation(self, 'purge',    PURGE_DATA,   SERVER_PURGE_DATA),
      +            'control':  TelnetSubnegotiation(self, 'control',  SET_CONTROL,  SERVER_SET_CONTROL),
      +        }
      +        self._rfc2217_options.update(self._rfc2217_port_settings)
      +        # cache for line and modem states that the server sends to us
      +        self._linestate = 0
      +        self._modemstate = None
      +        self._modemstate_timeout = Timeout(-1)
      +        # RFC 2217 flow control between server and client
      +        self._remote_suspend_flow = False
      +
      +        self.is_open = True
      +        self._thread = threading.Thread(target=self._telnet_read_loop)
      +        self._thread.setDaemon(True)
      +        self._thread.setName('pySerial RFC 2217 reader thread for {}'.format(self._port))
      +        self._thread.start()
      +
      +        try:    # must clean-up if open fails
      +            # negotiate Telnet/RFC 2217 -> send initial requests
      +            for option in self._telnet_options:
      +                if option.state is REQUESTED:
      +                    self.telnet_send_option(option.send_yes, option.option)
      +            # now wait until important options are negotiated
      +            timeout = Timeout(self._network_timeout)
      +            while not timeout.expired():
      +                time.sleep(0.05)    # prevent 100% CPU load
      +                if sum(o.active for o in mandadory_options) == sum(o.state != INACTIVE for o in mandadory_options):
      +                    break
      +            else:
      +                raise SerialException(
      +                    "Remote does not seem to support RFC2217 or BINARY mode {!r}".format(mandadory_options))
      +            if self.logger:
      +                self.logger.info("Negotiated options: {}".format(self._telnet_options))
      +
      +            # fine, go on, set RFC 2217 specific things
      +            self._reconfigure_port()
      +            # all things set up get, now a clean start
      +            if not self._dsrdtr:
      +                self._update_dtr_state()
      +            if not self._rtscts:
      +                self._update_rts_state()
      +            self.reset_input_buffer()
      +            self.reset_output_buffer()
      +        except:
      +            self.close()
      +            raise
      +
      +    def _reconfigure_port(self):
      +        """Set communication parameters on opened port."""
      +        if self._socket is None:
      +            raise SerialException("Can only operate on open ports")
      +
      +        # if self._timeout != 0 and self._interCharTimeout is not None:
      +            # XXX
      +
      +        if self._write_timeout is not None:
      +            raise NotImplementedError('write_timeout is currently not supported')
      +            # XXX
      +
      +        # Setup the connection
      +        # to get good performance, all parameter changes are sent first...
      +        if not 0 < self._baudrate < 2 ** 32:
      +            raise ValueError("invalid baudrate: {!r}".format(self._baudrate))
      +        self._rfc2217_port_settings['baudrate'].set(struct.pack(b'!I', self._baudrate))
      +        self._rfc2217_port_settings['datasize'].set(struct.pack(b'!B', self._bytesize))
      +        self._rfc2217_port_settings['parity'].set(struct.pack(b'!B', RFC2217_PARITY_MAP[self._parity]))
      +        self._rfc2217_port_settings['stopsize'].set(struct.pack(b'!B', RFC2217_STOPBIT_MAP[self._stopbits]))
      +
      +        # and now wait until parameters are active
      +        items = self._rfc2217_port_settings.values()
      +        if self.logger:
      +            self.logger.debug("Negotiating settings: {}".format(items))
      +        timeout = Timeout(self._network_timeout)
      +        while not timeout.expired():
      +            time.sleep(0.05)    # prevent 100% CPU load
      +            if sum(o.active for o in items) == len(items):
      +                break
      +        else:
      +            raise SerialException("Remote does not accept parameter change (RFC2217): {!r}".format(items))
      +        if self.logger:
      +            self.logger.info("Negotiated settings: {}".format(items))
      +
      +        if self._rtscts and self._xonxoff:
      +            raise ValueError('xonxoff and rtscts together are not supported')
      +        elif self._rtscts:
      +            self.rfc2217_set_control(SET_CONTROL_USE_HW_FLOW_CONTROL)
      +        elif self._xonxoff:
      +            self.rfc2217_set_control(SET_CONTROL_USE_SW_FLOW_CONTROL)
      +        else:
      +            self.rfc2217_set_control(SET_CONTROL_USE_NO_FLOW_CONTROL)
      +
      +    def close(self):
      +        """Close port"""
      +        self.is_open = False
      +        if self._socket:
      +            try:
      +                self._socket.shutdown(socket.SHUT_RDWR)
      +                self._socket.close()
      +            except:
      +                # ignore errors.
      +                pass
      +        if self._thread:
      +            self._thread.join(7)  # XXX more than socket timeout
      +            self._thread = None
      +            # in case of quick reconnects, give the server some time
      +            time.sleep(0.3)
      +        self._socket = None
      +
      +    def from_url(self, url):
      +        """\
      +        extract host and port from an URL string, other settings are extracted
      +        an stored in instance
      +        """
      +        parts = urlparse.urlsplit(url)
      +        if parts.scheme != "rfc2217":
      +            raise SerialException(
      +                'expected a string in the form '
      +                '"rfc2217://:[?option[&option...]]": '
      +                'not starting with rfc2217:// ({!r})'.format(parts.scheme))
      +        try:
      +            # process options now, directly altering self
      +            for option, values in urlparse.parse_qs(parts.query, True).items():
      +                if option == 'logging':
      +                    logging.basicConfig()   # XXX is that good to call it here?
      +                    self.logger = logging.getLogger('pySerial.rfc2217')
      +                    self.logger.setLevel(LOGGER_LEVELS[values[0]])
      +                    self.logger.debug('enabled logging')
      +                elif option == 'ign_set_control':
      +                    self._ignore_set_control_answer = True
      +                elif option == 'poll_modem':
      +                    self._poll_modem_state = True
      +                elif option == 'timeout':
      +                    self._network_timeout = float(values[0])
      +                else:
      +                    raise ValueError('unknown option: {!r}'.format(option))
      +            if not 0 <= parts.port < 65536:
      +                raise ValueError("port not in range 0...65535")
      +        except ValueError as e:
      +            raise SerialException(
      +                'expected a string in the form '
      +                '"rfc2217://:[?option[&option...]]": {}'.format(e))
      +        return (parts.hostname, parts.port)
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    @property
      +    def in_waiting(self):
      +        """Return the number of bytes currently in the input buffer."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return self._read_buffer.qsize()
      +
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        data = bytearray()
      +        try:
      +            timeout = Timeout(self._timeout)
      +            while len(data) < size:
      +                if self._thread is None or not self._thread.is_alive():
      +                    raise SerialException('connection failed (reader thread died)')
      +                buf = self._read_buffer.get(True, timeout.time_left())
      +                if buf is None:
      +                    return bytes(data)
      +                data += buf
      +                if timeout.expired():
      +                    break
      +        except Queue.Empty:  # -> timeout
      +            pass
      +        return bytes(data)
      +
      +    def write(self, data):
      +        """\
      +        Output the given byte string over the serial port. Can block if the
      +        connection is blocked. May raise SerialException if the connection is
      +        closed.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        with self._write_lock:
      +            try:
      +                self._socket.sendall(to_bytes(data).replace(IAC, IAC_DOUBLED))
      +            except socket.error as e:
      +                raise SerialException("connection failed (socket error): {}".format(e))
      +        return len(data)
      +
      +    def reset_input_buffer(self):
      +        """Clear input buffer, discarding all that is in the buffer."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self.rfc2217_send_purge(PURGE_RECEIVE_BUFFER)
      +        # empty read buffer
      +        while self._read_buffer.qsize():
      +            self._read_buffer.get(False)
      +
      +    def reset_output_buffer(self):
      +        """\
      +        Clear output buffer, aborting the current output and
      +        discarding all that is in the buffer.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self.rfc2217_send_purge(PURGE_TRANSMIT_BUFFER)
      +
      +    def _update_break_state(self):
      +        """\
      +        Set break: Controls TXD. When active, to transmitting is
      +        possible.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if self.logger:
      +            self.logger.info('set BREAK to {}'.format('active' if self._break_state else 'inactive'))
      +        if self._break_state:
      +            self.rfc2217_set_control(SET_CONTROL_BREAK_ON)
      +        else:
      +            self.rfc2217_set_control(SET_CONTROL_BREAK_OFF)
      +
      +    def _update_rts_state(self):
      +        """Set terminal status line: Request To Send."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if self.logger:
      +            self.logger.info('set RTS to {}'.format('active' if self._rts_state else 'inactive'))
      +        if self._rts_state:
      +            self.rfc2217_set_control(SET_CONTROL_RTS_ON)
      +        else:
      +            self.rfc2217_set_control(SET_CONTROL_RTS_OFF)
      +
      +    def _update_dtr_state(self):
      +        """Set terminal status line: Data Terminal Ready."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if self.logger:
      +            self.logger.info('set DTR to {}'.format('active' if self._dtr_state else 'inactive'))
      +        if self._dtr_state:
      +            self.rfc2217_set_control(SET_CONTROL_DTR_ON)
      +        else:
      +            self.rfc2217_set_control(SET_CONTROL_DTR_OFF)
      +
      +    @property
      +    def cts(self):
      +        """Read terminal status line: Clear To Send."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return bool(self.get_modem_state() & MODEMSTATE_MASK_CTS)
      +
      +    @property
      +    def dsr(self):
      +        """Read terminal status line: Data Set Ready."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return bool(self.get_modem_state() & MODEMSTATE_MASK_DSR)
      +
      +    @property
      +    def ri(self):
      +        """Read terminal status line: Ring Indicator."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return bool(self.get_modem_state() & MODEMSTATE_MASK_RI)
      +
      +    @property
      +    def cd(self):
      +        """Read terminal status line: Carrier Detect."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return bool(self.get_modem_state() & MODEMSTATE_MASK_CD)
      +
      +    # - - - platform specific - - -
      +    # None so far
      +
      +    # - - - RFC2217 specific - - -
      +
      +    def _telnet_read_loop(self):
      +        """Read loop for the socket."""
      +        mode = M_NORMAL
      +        suboption = None
      +        try:
      +            while self.is_open:
      +                try:
      +                    data = self._socket.recv(1024)
      +                except socket.timeout:
      +                    # just need to get out of recv form time to time to check if
      +                    # still alive
      +                    continue
      +                except socket.error as e:
      +                    # connection fails -> terminate loop
      +                    if self.logger:
      +                        self.logger.debug("socket error in reader thread: {}".format(e))
      +                    self._read_buffer.put(None)
      +                    break
      +                if not data:
      +                    self._read_buffer.put(None)
      +                    break  # lost connection
      +                for byte in iterbytes(data):
      +                    if mode == M_NORMAL:
      +                        # interpret as command or as data
      +                        if byte == IAC:
      +                            mode = M_IAC_SEEN
      +                        else:
      +                            # store data in read buffer or sub option buffer
      +                            # depending on state
      +                            if suboption is not None:
      +                                suboption += byte
      +                            else:
      +                                self._read_buffer.put(byte)
      +                    elif mode == M_IAC_SEEN:
      +                        if byte == IAC:
      +                            # interpret as command doubled -> insert character
      +                            # itself
      +                            if suboption is not None:
      +                                suboption += IAC
      +                            else:
      +                                self._read_buffer.put(IAC)
      +                            mode = M_NORMAL
      +                        elif byte == SB:
      +                            # sub option start
      +                            suboption = bytearray()
      +                            mode = M_NORMAL
      +                        elif byte == SE:
      +                            # sub option end -> process it now
      +                            self._telnet_process_subnegotiation(bytes(suboption))
      +                            suboption = None
      +                            mode = M_NORMAL
      +                        elif byte in (DO, DONT, WILL, WONT):
      +                            # negotiation
      +                            telnet_command = byte
      +                            mode = M_NEGOTIATE
      +                        else:
      +                            # other telnet commands
      +                            self._telnet_process_command(byte)
      +                            mode = M_NORMAL
      +                    elif mode == M_NEGOTIATE:  # DO, DONT, WILL, WONT was received, option now following
      +                        self._telnet_negotiate_option(telnet_command, byte)
      +                        mode = M_NORMAL
      +        finally:
      +            if self.logger:
      +                self.logger.debug("read thread terminated")
      +
      +    # - incoming telnet commands and options
      +
      +    def _telnet_process_command(self, command):
      +        """Process commands other than DO, DONT, WILL, WONT."""
      +        # Currently none. RFC2217 only uses negotiation and subnegotiation.
      +        if self.logger:
      +            self.logger.warning("ignoring Telnet command: {!r}".format(command))
      +
      +    def _telnet_negotiate_option(self, command, option):
      +        """Process incoming DO, DONT, WILL, WONT."""
      +        # check our registered telnet options and forward command to them
      +        # they know themselves if they have to answer or not
      +        known = False
      +        for item in self._telnet_options:
      +            # can have more than one match! as some options are duplicated for
      +            # 'us' and 'them'
      +            if item.option == option:
      +                item.process_incoming(command)
      +                known = True
      +        if not known:
      +            # handle unknown options
      +            # only answer to positive requests and deny them
      +            if command == WILL or command == DO:
      +                self.telnet_send_option((DONT if command == WILL else WONT), option)
      +                if self.logger:
      +                    self.logger.warning("rejected Telnet option: {!r}".format(option))
      +
      +    def _telnet_process_subnegotiation(self, suboption):
      +        """Process subnegotiation, the data between IAC SB and IAC SE."""
      +        if suboption[0:1] == COM_PORT_OPTION:
      +            if suboption[1:2] == SERVER_NOTIFY_LINESTATE and len(suboption) >= 3:
      +                self._linestate = ord(suboption[2:3])  # ensure it is a number
      +                if self.logger:
      +                    self.logger.info("NOTIFY_LINESTATE: {}".format(self._linestate))
      +            elif suboption[1:2] == SERVER_NOTIFY_MODEMSTATE and len(suboption) >= 3:
      +                self._modemstate = ord(suboption[2:3])  # ensure it is a number
      +                if self.logger:
      +                    self.logger.info("NOTIFY_MODEMSTATE: {}".format(self._modemstate))
      +                # update time when we think that a poll would make sense
      +                self._modemstate_timeout.restart(0.3)
      +            elif suboption[1:2] == FLOWCONTROL_SUSPEND:
      +                self._remote_suspend_flow = True
      +            elif suboption[1:2] == FLOWCONTROL_RESUME:
      +                self._remote_suspend_flow = False
      +            else:
      +                for item in self._rfc2217_options.values():
      +                    if item.ack_option == suboption[1:2]:
      +                        #~ print "processing COM_PORT_OPTION: %r" % list(suboption[1:])
      +                        item.check_answer(bytes(suboption[2:]))
      +                        break
      +                else:
      +                    if self.logger:
      +                        self.logger.warning("ignoring COM_PORT_OPTION: {!r}".format(suboption))
      +        else:
      +            if self.logger:
      +                self.logger.warning("ignoring subnegotiation: {!r}".format(suboption))
      +
      +    # - outgoing telnet commands and options
      +
      +    def _internal_raw_write(self, data):
      +        """internal socket write with no data escaping. used to send telnet stuff."""
      +        with self._write_lock:
      +            self._socket.sendall(data)
      +
      +    def telnet_send_option(self, action, option):
      +        """Send DO, DONT, WILL, WONT."""
      +        self._internal_raw_write(IAC + action + option)
      +
      +    def rfc2217_send_subnegotiation(self, option, value=b''):
      +        """Subnegotiation of RFC2217 parameters."""
      +        value = value.replace(IAC, IAC_DOUBLED)
      +        self._internal_raw_write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE)
      +
      +    def rfc2217_send_purge(self, value):
      +        """\
      +        Send purge request to the remote.
      +        (PURGE_RECEIVE_BUFFER / PURGE_TRANSMIT_BUFFER / PURGE_BOTH_BUFFERS)
      +        """
      +        item = self._rfc2217_options['purge']
      +        item.set(value)  # transmit desired purge type
      +        item.wait(self._network_timeout)  # wait for acknowledge from the server
      +
      +    def rfc2217_set_control(self, value):
      +        """transmit change of control line to remote"""
      +        item = self._rfc2217_options['control']
      +        item.set(value)  # transmit desired control type
      +        if self._ignore_set_control_answer:
      +            # answers are ignored when option is set. compatibility mode for
      +            # servers that answer, but not the expected one... (or no answer
      +            # at all) i.e. sredird
      +            time.sleep(0.1)  # this helps getting the unit tests passed
      +        else:
      +            item.wait(self._network_timeout)  # wait for acknowledge from the server
      +
      +    def rfc2217_flow_server_ready(self):
      +        """\
      +        check if server is ready to receive data. block for some time when
      +        not.
      +        """
      +        #~ if self._remote_suspend_flow:
      +        #~     wait---
      +
      +    def get_modem_state(self):
      +        """\
      +        get last modem state (cached value. If value is "old", request a new
      +        one. This cache helps that we don't issue to many requests when e.g. all
      +        status lines, one after the other is queried by the user (CTS, DSR
      +        etc.)
      +        """
      +        # active modem state polling enabled? is the value fresh enough?
      +        if self._poll_modem_state and self._modemstate_timeout.expired():
      +            if self.logger:
      +                self.logger.debug('polling modem state')
      +            # when it is older, request an update
      +            self.rfc2217_send_subnegotiation(NOTIFY_MODEMSTATE)
      +            timeout = Timeout(self._network_timeout)
      +            while not timeout.expired():
      +                time.sleep(0.05)    # prevent 100% CPU load
      +                # when expiration time is updated, it means that there is a new
      +                # value
      +                if not self._modemstate_timeout.expired():
      +                    break
      +            else:
      +                if self.logger:
      +                    self.logger.warning('poll for modem state failed')
      +            # even when there is a timeout, do not generate an error just
      +            # return the last known value. this way we can support buggy
      +            # servers that do not respond to polls, but send automatic
      +            # updates.
      +        if self._modemstate is not None:
      +            if self.logger:
      +                self.logger.debug('using cached modem state')
      +            return self._modemstate
      +        else:
      +            # never received a notification from the server
      +            raise SerialException("remote sends no NOTIFY_MODEMSTATE")
      +
      +
      +#############################################################################
      +# The following is code that helps implementing an RFC 2217 server.
      +
      +class PortManager(object):
      +    """\
      +    This class manages the state of Telnet and RFC 2217. It needs a serial
      +    instance and a connection to work with. Connection is expected to implement
      +    a (thread safe) write function, that writes the string to the network.
      +    """
      +
      +    def __init__(self, serial_port, connection, logger=None):
      +        self.serial = serial_port
      +        self.connection = connection
      +        self.logger = logger
      +        self._client_is_rfc2217 = False
      +
      +        # filter state machine
      +        self.mode = M_NORMAL
      +        self.suboption = None
      +        self.telnet_command = None
      +
      +        # states for modem/line control events
      +        self.modemstate_mask = 255
      +        self.last_modemstate = None
      +        self.linstate_mask = 0
      +
      +        # all supported telnet options
      +        self._telnet_options = [
      +            TelnetOption(self, 'ECHO', ECHO, WILL, WONT, DO, DONT, REQUESTED),
      +            TelnetOption(self, 'we-SGA', SGA, WILL, WONT, DO, DONT, REQUESTED),
      +            TelnetOption(self, 'they-SGA', SGA, DO, DONT, WILL, WONT, INACTIVE),
      +            TelnetOption(self, 'we-BINARY', BINARY, WILL, WONT, DO, DONT, INACTIVE),
      +            TelnetOption(self, 'they-BINARY', BINARY, DO, DONT, WILL, WONT, REQUESTED),
      +            TelnetOption(self, 'we-RFC2217', COM_PORT_OPTION, WILL, WONT, DO, DONT, REQUESTED, self._client_ok),
      +            TelnetOption(self, 'they-RFC2217', COM_PORT_OPTION, DO, DONT, WILL, WONT, INACTIVE, self._client_ok),
      +        ]
      +
      +        # negotiate Telnet/RFC2217 -> send initial requests
      +        if self.logger:
      +            self.logger.debug("requesting initial Telnet/RFC 2217 options")
      +        for option in self._telnet_options:
      +            if option.state is REQUESTED:
      +                self.telnet_send_option(option.send_yes, option.option)
      +        # issue 1st modem state notification
      +
      +    def _client_ok(self):
      +        """\
      +        callback of telnet option. It gets called when option is activated.
      +        This one here is used to detect when the client agrees on RFC 2217. A
      +        flag is set so that other functions like check_modem_lines know if the
      +        client is OK.
      +        """
      +        # The callback is used for we and they so if one party agrees, we're
      +        # already happy. it seems not all servers do the negotiation correctly
      +        # and i guess there are incorrect clients too.. so be happy if client
      +        # answers one or the other positively.
      +        self._client_is_rfc2217 = True
      +        if self.logger:
      +            self.logger.info("client accepts RFC 2217")
      +        # this is to ensure that the client gets a notification, even if there
      +        # was no change
      +        self.check_modem_lines(force_notification=True)
      +
      +    # - outgoing telnet commands and options
      +
      +    def telnet_send_option(self, action, option):
      +        """Send DO, DONT, WILL, WONT."""
      +        self.connection.write(IAC + action + option)
      +
      +    def rfc2217_send_subnegotiation(self, option, value=b''):
      +        """Subnegotiation of RFC 2217 parameters."""
      +        value = value.replace(IAC, IAC_DOUBLED)
      +        self.connection.write(IAC + SB + COM_PORT_OPTION + option + value + IAC + SE)
      +
      +    # - check modem lines, needs to be called periodically from user to
      +    # establish polling
      +
      +    def check_modem_lines(self, force_notification=False):
      +        """\
      +        read control lines from serial port and compare the last value sent to remote.
      +        send updates on changes.
      +        """
      +        modemstate = (
      +            (self.serial.cts and MODEMSTATE_MASK_CTS) |
      +            (self.serial.dsr and MODEMSTATE_MASK_DSR) |
      +            (self.serial.ri and MODEMSTATE_MASK_RI) |
      +            (self.serial.cd and MODEMSTATE_MASK_CD))
      +        # check what has changed
      +        deltas = modemstate ^ (self.last_modemstate or 0)  # when last is None -> 0
      +        if deltas & MODEMSTATE_MASK_CTS:
      +            modemstate |= MODEMSTATE_MASK_CTS_CHANGE
      +        if deltas & MODEMSTATE_MASK_DSR:
      +            modemstate |= MODEMSTATE_MASK_DSR_CHANGE
      +        if deltas & MODEMSTATE_MASK_RI:
      +            modemstate |= MODEMSTATE_MASK_RI_CHANGE
      +        if deltas & MODEMSTATE_MASK_CD:
      +            modemstate |= MODEMSTATE_MASK_CD_CHANGE
      +        # if new state is different and the mask allows this change, send
      +        # notification. suppress notifications when client is not rfc2217
      +        if modemstate != self.last_modemstate or force_notification:
      +            if (self._client_is_rfc2217 and (modemstate & self.modemstate_mask)) or force_notification:
      +                self.rfc2217_send_subnegotiation(
      +                    SERVER_NOTIFY_MODEMSTATE,
      +                    to_bytes([modemstate & self.modemstate_mask]))
      +                if self.logger:
      +                    self.logger.info("NOTIFY_MODEMSTATE: {}".format(modemstate))
      +            # save last state, but forget about deltas.
      +            # otherwise it would also notify about changing deltas which is
      +            # probably not very useful
      +            self.last_modemstate = modemstate & 0xf0
      +
      +    # - outgoing data escaping
      +
      +    def escape(self, data):
      +        """\
      +        This generator function is for the user. All outgoing data has to be
      +        properly escaped, so that no IAC character in the data stream messes up
      +        the Telnet state machine in the server.
      +
      +        socket.sendall(escape(data))
      +        """
      +        for byte in iterbytes(data):
      +            if byte == IAC:
      +                yield IAC
      +                yield IAC
      +            else:
      +                yield byte
      +
      +    # - incoming data filter
      +
      +    def filter(self, data):
      +        """\
      +        Handle a bunch of incoming bytes. This is a generator. It will yield
      +        all characters not of interest for Telnet/RFC 2217.
      +
      +        The idea is that the reader thread pushes data from the socket through
      +        this filter:
      +
      +        for byte in filter(socket.recv(1024)):
      +            # do things like CR/LF conversion/whatever
      +            # and write data to the serial port
      +            serial.write(byte)
      +
      +        (socket error handling code left as exercise for the reader)
      +        """
      +        for byte in iterbytes(data):
      +            if self.mode == M_NORMAL:
      +                # interpret as command or as data
      +                if byte == IAC:
      +                    self.mode = M_IAC_SEEN
      +                else:
      +                    # store data in sub option buffer or pass it to our
      +                    # consumer depending on state
      +                    if self.suboption is not None:
      +                        self.suboption += byte
      +                    else:
      +                        yield byte
      +            elif self.mode == M_IAC_SEEN:
      +                if byte == IAC:
      +                    # interpret as command doubled -> insert character
      +                    # itself
      +                    if self.suboption is not None:
      +                        self.suboption += byte
      +                    else:
      +                        yield byte
      +                    self.mode = M_NORMAL
      +                elif byte == SB:
      +                    # sub option start
      +                    self.suboption = bytearray()
      +                    self.mode = M_NORMAL
      +                elif byte == SE:
      +                    # sub option end -> process it now
      +                    self._telnet_process_subnegotiation(bytes(self.suboption))
      +                    self.suboption = None
      +                    self.mode = M_NORMAL
      +                elif byte in (DO, DONT, WILL, WONT):
      +                    # negotiation
      +                    self.telnet_command = byte
      +                    self.mode = M_NEGOTIATE
      +                else:
      +                    # other telnet commands
      +                    self._telnet_process_command(byte)
      +                    self.mode = M_NORMAL
      +            elif self.mode == M_NEGOTIATE:  # DO, DONT, WILL, WONT was received, option now following
      +                self._telnet_negotiate_option(self.telnet_command, byte)
      +                self.mode = M_NORMAL
      +
      +    # - incoming telnet commands and options
      +
      +    def _telnet_process_command(self, command):
      +        """Process commands other than DO, DONT, WILL, WONT."""
      +        # Currently none. RFC2217 only uses negotiation and subnegotiation.
      +        if self.logger:
      +            self.logger.warning("ignoring Telnet command: {!r}".format(command))
      +
      +    def _telnet_negotiate_option(self, command, option):
      +        """Process incoming DO, DONT, WILL, WONT."""
      +        # check our registered telnet options and forward command to them
      +        # they know themselves if they have to answer or not
      +        known = False
      +        for item in self._telnet_options:
      +            # can have more than one match! as some options are duplicated for
      +            # 'us' and 'them'
      +            if item.option == option:
      +                item.process_incoming(command)
      +                known = True
      +        if not known:
      +            # handle unknown options
      +            # only answer to positive requests and deny them
      +            if command == WILL or command == DO:
      +                self.telnet_send_option((DONT if command == WILL else WONT), option)
      +                if self.logger:
      +                    self.logger.warning("rejected Telnet option: {!r}".format(option))
      +
      +    def _telnet_process_subnegotiation(self, suboption):
      +        """Process subnegotiation, the data between IAC SB and IAC SE."""
      +        if suboption[0:1] == COM_PORT_OPTION:
      +            if self.logger:
      +                self.logger.debug('received COM_PORT_OPTION: {!r}'.format(suboption))
      +            if suboption[1:2] == SET_BAUDRATE:
      +                backup = self.serial.baudrate
      +                try:
      +                    (baudrate,) = struct.unpack(b"!I", suboption[2:6])
      +                    if baudrate != 0:
      +                        self.serial.baudrate = baudrate
      +                except ValueError as e:
      +                    if self.logger:
      +                        self.logger.error("failed to set baud rate: {}".format(e))
      +                    self.serial.baudrate = backup
      +                else:
      +                    if self.logger:
      +                        self.logger.info("{} baud rate: {}".format('set' if baudrate else 'get', self.serial.baudrate))
      +                self.rfc2217_send_subnegotiation(SERVER_SET_BAUDRATE, struct.pack(b"!I", self.serial.baudrate))
      +            elif suboption[1:2] == SET_DATASIZE:
      +                backup = self.serial.bytesize
      +                try:
      +                    (datasize,) = struct.unpack(b"!B", suboption[2:3])
      +                    if datasize != 0:
      +                        self.serial.bytesize = datasize
      +                except ValueError as e:
      +                    if self.logger:
      +                        self.logger.error("failed to set data size: {}".format(e))
      +                    self.serial.bytesize = backup
      +                else:
      +                    if self.logger:
      +                        self.logger.info("{} data size: {}".format('set' if datasize else 'get', self.serial.bytesize))
      +                self.rfc2217_send_subnegotiation(SERVER_SET_DATASIZE, struct.pack(b"!B", self.serial.bytesize))
      +            elif suboption[1:2] == SET_PARITY:
      +                backup = self.serial.parity
      +                try:
      +                    parity = struct.unpack(b"!B", suboption[2:3])[0]
      +                    if parity != 0:
      +                        self.serial.parity = RFC2217_REVERSE_PARITY_MAP[parity]
      +                except ValueError as e:
      +                    if self.logger:
      +                        self.logger.error("failed to set parity: {}".format(e))
      +                    self.serial.parity = backup
      +                else:
      +                    if self.logger:
      +                        self.logger.info("{} parity: {}".format('set' if parity else 'get', self.serial.parity))
      +                self.rfc2217_send_subnegotiation(
      +                    SERVER_SET_PARITY,
      +                    struct.pack(b"!B", RFC2217_PARITY_MAP[self.serial.parity]))
      +            elif suboption[1:2] == SET_STOPSIZE:
      +                backup = self.serial.stopbits
      +                try:
      +                    stopbits = struct.unpack(b"!B", suboption[2:3])[0]
      +                    if stopbits != 0:
      +                        self.serial.stopbits = RFC2217_REVERSE_STOPBIT_MAP[stopbits]
      +                except ValueError as e:
      +                    if self.logger:
      +                        self.logger.error("failed to set stop bits: {}".format(e))
      +                    self.serial.stopbits = backup
      +                else:
      +                    if self.logger:
      +                        self.logger.info("{} stop bits: {}".format('set' if stopbits else 'get', self.serial.stopbits))
      +                self.rfc2217_send_subnegotiation(
      +                    SERVER_SET_STOPSIZE,
      +                    struct.pack(b"!B", RFC2217_STOPBIT_MAP[self.serial.stopbits]))
      +            elif suboption[1:2] == SET_CONTROL:
      +                if suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING:
      +                    if self.serial.xonxoff:
      +                        self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
      +                    elif self.serial.rtscts:
      +                        self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
      +                    else:
      +                        self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
      +                elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL:
      +                    self.serial.xonxoff = False
      +                    self.serial.rtscts = False
      +                    if self.logger:
      +                        self.logger.info("changed flow control to None")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_NO_FLOW_CONTROL)
      +                elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTROL:
      +                    self.serial.xonxoff = True
      +                    if self.logger:
      +                        self.logger.info("changed flow control to XON/XOFF")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_SW_FLOW_CONTROL)
      +                elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTROL:
      +                    self.serial.rtscts = True
      +                    if self.logger:
      +                        self.logger.info("changed flow control to RTS/CTS")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_USE_HW_FLOW_CONTROL)
      +                elif suboption[2:3] == SET_CONTROL_REQ_BREAK_STATE:
      +                    if self.logger:
      +                        self.logger.warning("requested break state - not implemented")
      +                    pass  # XXX needs cached value
      +                elif suboption[2:3] == SET_CONTROL_BREAK_ON:
      +                    self.serial.break_condition = True
      +                    if self.logger:
      +                        self.logger.info("changed BREAK to active")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_ON)
      +                elif suboption[2:3] == SET_CONTROL_BREAK_OFF:
      +                    self.serial.break_condition = False
      +                    if self.logger:
      +                        self.logger.info("changed BREAK to inactive")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_BREAK_OFF)
      +                elif suboption[2:3] == SET_CONTROL_REQ_DTR:
      +                    if self.logger:
      +                        self.logger.warning("requested DTR state - not implemented")
      +                    pass  # XXX needs cached value
      +                elif suboption[2:3] == SET_CONTROL_DTR_ON:
      +                    self.serial.dtr = True
      +                    if self.logger:
      +                        self.logger.info("changed DTR to active")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_ON)
      +                elif suboption[2:3] == SET_CONTROL_DTR_OFF:
      +                    self.serial.dtr = False
      +                    if self.logger:
      +                        self.logger.info("changed DTR to inactive")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_DTR_OFF)
      +                elif suboption[2:3] == SET_CONTROL_REQ_RTS:
      +                    if self.logger:
      +                        self.logger.warning("requested RTS state - not implemented")
      +                    pass  # XXX needs cached value
      +                    #~ self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
      +                elif suboption[2:3] == SET_CONTROL_RTS_ON:
      +                    self.serial.rts = True
      +                    if self.logger:
      +                        self.logger.info("changed RTS to active")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_ON)
      +                elif suboption[2:3] == SET_CONTROL_RTS_OFF:
      +                    self.serial.rts = False
      +                    if self.logger:
      +                        self.logger.info("changed RTS to inactive")
      +                    self.rfc2217_send_subnegotiation(SERVER_SET_CONTROL, SET_CONTROL_RTS_OFF)
      +                #~ elif suboption[2:3] == SET_CONTROL_REQ_FLOW_SETTING_IN:
      +                #~ elif suboption[2:3] == SET_CONTROL_USE_NO_FLOW_CONTROL_IN:
      +                #~ elif suboption[2:3] == SET_CONTROL_USE_SW_FLOW_CONTOL_IN:
      +                #~ elif suboption[2:3] == SET_CONTROL_USE_HW_FLOW_CONTOL_IN:
      +                #~ elif suboption[2:3] == SET_CONTROL_USE_DCD_FLOW_CONTROL:
      +                #~ elif suboption[2:3] == SET_CONTROL_USE_DTR_FLOW_CONTROL:
      +                #~ elif suboption[2:3] == SET_CONTROL_USE_DSR_FLOW_CONTROL:
      +            elif suboption[1:2] == NOTIFY_LINESTATE:
      +                # client polls for current state
      +                self.rfc2217_send_subnegotiation(
      +                    SERVER_NOTIFY_LINESTATE,
      +                    to_bytes([0]))   # sorry, nothing like that implemented
      +            elif suboption[1:2] == NOTIFY_MODEMSTATE:
      +                if self.logger:
      +                    self.logger.info("request for modem state")
      +                # client polls for current state
      +                self.check_modem_lines(force_notification=True)
      +            elif suboption[1:2] == FLOWCONTROL_SUSPEND:
      +                if self.logger:
      +                    self.logger.info("suspend")
      +                self._remote_suspend_flow = True
      +            elif suboption[1:2] == FLOWCONTROL_RESUME:
      +                if self.logger:
      +                    self.logger.info("resume")
      +                self._remote_suspend_flow = False
      +            elif suboption[1:2] == SET_LINESTATE_MASK:
      +                self.linstate_mask = ord(suboption[2:3])  # ensure it is a number
      +                if self.logger:
      +                    self.logger.info("line state mask: 0x{:02x}".format(self.linstate_mask))
      +            elif suboption[1:2] == SET_MODEMSTATE_MASK:
      +                self.modemstate_mask = ord(suboption[2:3])  # ensure it is a number
      +                if self.logger:
      +                    self.logger.info("modem state mask: 0x{:02x}".format(self.modemstate_mask))
      +            elif suboption[1:2] == PURGE_DATA:
      +                if suboption[2:3] == PURGE_RECEIVE_BUFFER:
      +                    self.serial.reset_input_buffer()
      +                    if self.logger:
      +                        self.logger.info("purge in")
      +                    self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_RECEIVE_BUFFER)
      +                elif suboption[2:3] == PURGE_TRANSMIT_BUFFER:
      +                    self.serial.reset_output_buffer()
      +                    if self.logger:
      +                        self.logger.info("purge out")
      +                    self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_TRANSMIT_BUFFER)
      +                elif suboption[2:3] == PURGE_BOTH_BUFFERS:
      +                    self.serial.reset_input_buffer()
      +                    self.serial.reset_output_buffer()
      +                    if self.logger:
      +                        self.logger.info("purge both")
      +                    self.rfc2217_send_subnegotiation(SERVER_PURGE_DATA, PURGE_BOTH_BUFFERS)
      +                else:
      +                    if self.logger:
      +                        self.logger.error("undefined PURGE_DATA: {!r}".format(list(suboption[2:])))
      +            else:
      +                if self.logger:
      +                    self.logger.error("undefined COM_PORT_OPTION: {!r}".format(list(suboption[1:])))
      +        else:
      +            if self.logger:
      +                self.logger.warning("unknown subnegotiation: {!r}".format(suboption))
      +
      +
      +# simple client test
      +if __name__ == '__main__':
      +    import sys
      +    s = Serial('rfc2217://localhost:7000', 115200)
      +    sys.stdout.write('{}\n'.format(s))
      +
      +    sys.stdout.write("write...\n")
      +    s.write(b"hello\n")
      +    s.flush()
      +    sys.stdout.write("read: {}\n".format(s.read(5)))
      +    s.close()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rs485.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rs485.py
      new file mode 100644
      index 000000000..d7aff6f6a
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/rs485.py
      @@ -0,0 +1,94 @@
      +#!/usr/bin/env python
      +
      +# RS485 support
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +"""\
      +The settings for RS485 are stored in a dedicated object that can be applied to
      +serial ports (where supported).
      +NOTE: Some implementations may only support a subset of the settings.
      +"""
      +
      +from __future__ import absolute_import
      +
      +import time
      +import serial
      +
      +
      +class RS485Settings(object):
      +    def __init__(
      +            self,
      +            rts_level_for_tx=True,
      +            rts_level_for_rx=False,
      +            loopback=False,
      +            delay_before_tx=None,
      +            delay_before_rx=None):
      +        self.rts_level_for_tx = rts_level_for_tx
      +        self.rts_level_for_rx = rts_level_for_rx
      +        self.loopback = loopback
      +        self.delay_before_tx = delay_before_tx
      +        self.delay_before_rx = delay_before_rx
      +
      +
      +class RS485(serial.Serial):
      +    """\
      +    A subclass that replaces the write method with one that toggles RTS
      +    according to the RS485 settings.
      +
      +    NOTE: This may work unreliably on some serial ports (control signals not
      +          synchronized or delayed compared to data). Using delays may be
      +          unreliable (varying times, larger than expected) as the OS may not
      +          support very fine grained delays (no smaller than in the order of
      +          tens of milliseconds).
      +
      +    NOTE: Some implementations support this natively. Better performance
      +          can be expected when the native version is used.
      +
      +    NOTE: The loopback property is ignored by this implementation. The actual
      +          behavior depends on the used hardware.
      +
      +    Usage:
      +
      +        ser = RS485(...)
      +        ser.rs485_mode = RS485Settings(...)
      +        ser.write(b'hello')
      +    """
      +
      +    def __init__(self, *args, **kwargs):
      +        super(RS485, self).__init__(*args, **kwargs)
      +        self._alternate_rs485_settings = None
      +
      +    def write(self, b):
      +        """Write to port, controlling RTS before and after transmitting."""
      +        if self._alternate_rs485_settings is not None:
      +            # apply level for TX and optional delay
      +            self.setRTS(self._alternate_rs485_settings.rts_level_for_tx)
      +            if self._alternate_rs485_settings.delay_before_tx is not None:
      +                time.sleep(self._alternate_rs485_settings.delay_before_tx)
      +            # write and wait for data to be written
      +            super(RS485, self).write(b)
      +            super(RS485, self).flush()
      +            # optional delay and apply level for RX
      +            if self._alternate_rs485_settings.delay_before_rx is not None:
      +                time.sleep(self._alternate_rs485_settings.delay_before_rx)
      +            self.setRTS(self._alternate_rs485_settings.rts_level_for_rx)
      +        else:
      +            super(RS485, self).write(b)
      +
      +    # redirect where the property stores the settings so that underlying Serial
      +    # instance does not see them
      +    @property
      +    def rs485_mode(self):
      +        """\
      +        Enable RS485 mode and apply new settings, set to None to disable.
      +        See serial.rs485.RS485Settings for more info about the value.
      +        """
      +        return self._alternate_rs485_settings
      +
      +    @rs485_mode.setter
      +    def rs485_mode(self, rs485_settings):
      +        self._alternate_rs485_settings = rs485_settings
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialcli.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialcli.py
      new file mode 100644
      index 000000000..4614736ef
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialcli.py
      @@ -0,0 +1,253 @@
      +#! python
      +#
      +# Backend for .NET/Mono (IronPython), .NET >= 2
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2008-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +import System
      +import System.IO.Ports
      +from serial.serialutil import *
      +
      +# must invoke function with byte array, make a helper to convert strings
      +# to byte arrays
      +sab = System.Array[System.Byte]
      +
      +
      +def as_byte_array(string):
      +    return sab([ord(x) for x in string])  # XXX will require adaption when run with a 3.x compatible IronPython
      +
      +
      +class Serial(SerialBase):
      +    """Serial port implementation for .NET/Mono."""
      +
      +    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
      +                 9600, 19200, 38400, 57600, 115200)
      +
      +    def open(self):
      +        """\
      +        Open port with current settings. This may throw a SerialException
      +        if the port cannot be opened.
      +        """
      +        if self._port is None:
      +            raise SerialException("Port must be configured before it can be used.")
      +        if self.is_open:
      +            raise SerialException("Port is already open.")
      +        try:
      +            self._port_handle = System.IO.Ports.SerialPort(self.portstr)
      +        except Exception as msg:
      +            self._port_handle = None
      +            raise SerialException("could not open port %s: %s" % (self.portstr, msg))
      +
      +        # if RTS and/or DTR are not set before open, they default to True
      +        if self._rts_state is None:
      +            self._rts_state = True
      +        if self._dtr_state is None:
      +            self._dtr_state = True
      +
      +        self._reconfigure_port()
      +        self._port_handle.Open()
      +        self.is_open = True
      +        if not self._dsrdtr:
      +            self._update_dtr_state()
      +        if not self._rtscts:
      +            self._update_rts_state()
      +        self.reset_input_buffer()
      +
      +    def _reconfigure_port(self):
      +        """Set communication parameters on opened port."""
      +        if not self._port_handle:
      +            raise SerialException("Can only operate on a valid port handle")
      +
      +        #~ self._port_handle.ReceivedBytesThreshold = 1
      +
      +        if self._timeout is None:
      +            self._port_handle.ReadTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
      +        else:
      +            self._port_handle.ReadTimeout = int(self._timeout * 1000)
      +
      +        # if self._timeout != 0 and self._interCharTimeout is not None:
      +            # timeouts = (int(self._interCharTimeout * 1000),) + timeouts[1:]
      +
      +        if self._write_timeout is None:
      +            self._port_handle.WriteTimeout = System.IO.Ports.SerialPort.InfiniteTimeout
      +        else:
      +            self._port_handle.WriteTimeout = int(self._write_timeout * 1000)
      +
      +        # Setup the connection info.
      +        try:
      +            self._port_handle.BaudRate = self._baudrate
      +        except IOError as e:
      +            # catch errors from illegal baudrate settings
      +            raise ValueError(str(e))
      +
      +        if self._bytesize == FIVEBITS:
      +            self._port_handle.DataBits = 5
      +        elif self._bytesize == SIXBITS:
      +            self._port_handle.DataBits = 6
      +        elif self._bytesize == SEVENBITS:
      +            self._port_handle.DataBits = 7
      +        elif self._bytesize == EIGHTBITS:
      +            self._port_handle.DataBits = 8
      +        else:
      +            raise ValueError("Unsupported number of data bits: %r" % self._bytesize)
      +
      +        if self._parity == PARITY_NONE:
      +            self._port_handle.Parity = getattr(System.IO.Ports.Parity, 'None')  # reserved keyword in Py3k
      +        elif self._parity == PARITY_EVEN:
      +            self._port_handle.Parity = System.IO.Ports.Parity.Even
      +        elif self._parity == PARITY_ODD:
      +            self._port_handle.Parity = System.IO.Ports.Parity.Odd
      +        elif self._parity == PARITY_MARK:
      +            self._port_handle.Parity = System.IO.Ports.Parity.Mark
      +        elif self._parity == PARITY_SPACE:
      +            self._port_handle.Parity = System.IO.Ports.Parity.Space
      +        else:
      +            raise ValueError("Unsupported parity mode: %r" % self._parity)
      +
      +        if self._stopbits == STOPBITS_ONE:
      +            self._port_handle.StopBits = System.IO.Ports.StopBits.One
      +        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
      +            self._port_handle.StopBits = System.IO.Ports.StopBits.OnePointFive
      +        elif self._stopbits == STOPBITS_TWO:
      +            self._port_handle.StopBits = System.IO.Ports.StopBits.Two
      +        else:
      +            raise ValueError("Unsupported number of stop bits: %r" % self._stopbits)
      +
      +        if self._rtscts and self._xonxoff:
      +            self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSendXOnXOff
      +        elif self._rtscts:
      +            self._port_handle.Handshake = System.IO.Ports.Handshake.RequestToSend
      +        elif self._xonxoff:
      +            self._port_handle.Handshake = System.IO.Ports.Handshake.XOnXOff
      +        else:
      +            self._port_handle.Handshake = getattr(System.IO.Ports.Handshake, 'None')   # reserved keyword in Py3k
      +
      +    #~ def __del__(self):
      +        #~ self.close()
      +
      +    def close(self):
      +        """Close port"""
      +        if self.is_open:
      +            if self._port_handle:
      +                try:
      +                    self._port_handle.Close()
      +                except System.IO.Ports.InvalidOperationException:
      +                    # ignore errors. can happen for unplugged USB serial devices
      +                    pass
      +                self._port_handle = None
      +            self.is_open = False
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    @property
      +    def in_waiting(self):
      +        """Return the number of characters currently in the input buffer."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return self._port_handle.BytesToRead
      +
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        # must use single byte reads as this is the only way to read
      +        # without applying encodings
      +        data = bytearray()
      +        while size:
      +            try:
      +                data.append(self._port_handle.ReadByte())
      +            except System.TimeoutException:
      +                break
      +            else:
      +                size -= 1
      +        return bytes(data)
      +
      +    def write(self, data):
      +        """Output the given string over the serial port."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        #~ if not isinstance(data, (bytes, bytearray)):
      +            #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
      +        try:
      +            # must call overloaded method with byte array argument
      +            # as this is the only one not applying encodings
      +            self._port_handle.Write(as_byte_array(data), 0, len(data))
      +        except System.TimeoutException:
      +            raise SerialTimeoutException('Write timeout')
      +        return len(data)
      +
      +    def reset_input_buffer(self):
      +        """Clear input buffer, discarding all that is in the buffer."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._port_handle.DiscardInBuffer()
      +
      +    def reset_output_buffer(self):
      +        """\
      +        Clear output buffer, aborting the current output and
      +        discarding all that is in the buffer.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._port_handle.DiscardOutBuffer()
      +
      +    def _update_break_state(self):
      +        """
      +        Set break: Controls TXD. When active, to transmitting is possible.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._port_handle.BreakState = bool(self._break_state)
      +
      +    def _update_rts_state(self):
      +        """Set terminal status line: Request To Send"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._port_handle.RtsEnable = bool(self._rts_state)
      +
      +    def _update_dtr_state(self):
      +        """Set terminal status line: Data Terminal Ready"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._port_handle.DtrEnable = bool(self._dtr_state)
      +
      +    @property
      +    def cts(self):
      +        """Read terminal status line: Clear To Send"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return self._port_handle.CtsHolding
      +
      +    @property
      +    def dsr(self):
      +        """Read terminal status line: Data Set Ready"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return self._port_handle.DsrHolding
      +
      +    @property
      +    def ri(self):
      +        """Read terminal status line: Ring Indicator"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        #~ return self._port_handle.XXX
      +        return False  # XXX an error would be better
      +
      +    @property
      +    def cd(self):
      +        """Read terminal status line: Carrier Detect"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return self._port_handle.CDHolding
      +
      +    # - - platform specific - - - -
      +    # none
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialjava.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialjava.py
      new file mode 100644
      index 000000000..0789a7800
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialjava.py
      @@ -0,0 +1,251 @@
      +#!jython
      +#
      +# Backend Jython with JavaComm
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2002-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +from serial.serialutil import *
      +
      +
      +def my_import(name):
      +    mod = __import__(name)
      +    components = name.split('.')
      +    for comp in components[1:]:
      +        mod = getattr(mod, comp)
      +    return mod
      +
      +
      +def detect_java_comm(names):
      +    """try given list of modules and return that imports"""
      +    for name in names:
      +        try:
      +            mod = my_import(name)
      +            mod.SerialPort
      +            return mod
      +        except (ImportError, AttributeError):
      +            pass
      +    raise ImportError("No Java Communications API implementation found")
      +
      +
      +# Java Communications API implementations
      +# http://mho.republika.pl/java/comm/
      +
      +comm = detect_java_comm([
      +    'javax.comm',  # Sun/IBM
      +    'gnu.io',      # RXTX
      +])
      +
      +
      +def device(portnumber):
      +    """Turn a port number into a device name"""
      +    enum = comm.CommPortIdentifier.getPortIdentifiers()
      +    ports = []
      +    while enum.hasMoreElements():
      +        el = enum.nextElement()
      +        if el.getPortType() == comm.CommPortIdentifier.PORT_SERIAL:
      +            ports.append(el)
      +    return ports[portnumber].getName()
      +
      +
      +class Serial(SerialBase):
      +    """\
      +    Serial port class, implemented with Java Communications API and
      +    thus usable with jython and the appropriate java extension.
      +    """
      +
      +    def open(self):
      +        """\
      +        Open port with current settings. This may throw a SerialException
      +        if the port cannot be opened.
      +        """
      +        if self._port is None:
      +            raise SerialException("Port must be configured before it can be used.")
      +        if self.is_open:
      +            raise SerialException("Port is already open.")
      +        if type(self._port) == type(''):      # strings are taken directly
      +            portId = comm.CommPortIdentifier.getPortIdentifier(self._port)
      +        else:
      +            portId = comm.CommPortIdentifier.getPortIdentifier(device(self._port))     # numbers are transformed to a comport id obj
      +        try:
      +            self.sPort = portId.open("python serial module", 10)
      +        except Exception as msg:
      +            self.sPort = None
      +            raise SerialException("Could not open port: %s" % msg)
      +        self._reconfigurePort()
      +        self._instream = self.sPort.getInputStream()
      +        self._outstream = self.sPort.getOutputStream()
      +        self.is_open = True
      +
      +    def _reconfigurePort(self):
      +        """Set communication parameters on opened port."""
      +        if not self.sPort:
      +            raise SerialException("Can only operate on a valid port handle")
      +
      +        self.sPort.enableReceiveTimeout(30)
      +        if self._bytesize == FIVEBITS:
      +            jdatabits = comm.SerialPort.DATABITS_5
      +        elif self._bytesize == SIXBITS:
      +            jdatabits = comm.SerialPort.DATABITS_6
      +        elif self._bytesize == SEVENBITS:
      +            jdatabits = comm.SerialPort.DATABITS_7
      +        elif self._bytesize == EIGHTBITS:
      +            jdatabits = comm.SerialPort.DATABITS_8
      +        else:
      +            raise ValueError("unsupported bytesize: %r" % self._bytesize)
      +
      +        if self._stopbits == STOPBITS_ONE:
      +            jstopbits = comm.SerialPort.STOPBITS_1
      +        elif self._stopbits == STOPBITS_ONE_POINT_FIVE:
      +            jstopbits = comm.SerialPort.STOPBITS_1_5
      +        elif self._stopbits == STOPBITS_TWO:
      +            jstopbits = comm.SerialPort.STOPBITS_2
      +        else:
      +            raise ValueError("unsupported number of stopbits: %r" % self._stopbits)
      +
      +        if self._parity == PARITY_NONE:
      +            jparity = comm.SerialPort.PARITY_NONE
      +        elif self._parity == PARITY_EVEN:
      +            jparity = comm.SerialPort.PARITY_EVEN
      +        elif self._parity == PARITY_ODD:
      +            jparity = comm.SerialPort.PARITY_ODD
      +        elif self._parity == PARITY_MARK:
      +            jparity = comm.SerialPort.PARITY_MARK
      +        elif self._parity == PARITY_SPACE:
      +            jparity = comm.SerialPort.PARITY_SPACE
      +        else:
      +            raise ValueError("unsupported parity type: %r" % self._parity)
      +
      +        jflowin = jflowout = 0
      +        if self._rtscts:
      +            jflowin |= comm.SerialPort.FLOWCONTROL_RTSCTS_IN
      +            jflowout |= comm.SerialPort.FLOWCONTROL_RTSCTS_OUT
      +        if self._xonxoff:
      +            jflowin |= comm.SerialPort.FLOWCONTROL_XONXOFF_IN
      +            jflowout |= comm.SerialPort.FLOWCONTROL_XONXOFF_OUT
      +
      +        self.sPort.setSerialPortParams(self._baudrate, jdatabits, jstopbits, jparity)
      +        self.sPort.setFlowControlMode(jflowin | jflowout)
      +
      +        if self._timeout >= 0:
      +            self.sPort.enableReceiveTimeout(int(self._timeout*1000))
      +        else:
      +            self.sPort.disableReceiveTimeout()
      +
      +    def close(self):
      +        """Close port"""
      +        if self.is_open:
      +            if self.sPort:
      +                self._instream.close()
      +                self._outstream.close()
      +                self.sPort.close()
      +                self.sPort = None
      +            self.is_open = False
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    @property
      +    def in_waiting(self):
      +        """Return the number of characters currently in the input buffer."""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        return self._instream.available()
      +
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        read = bytearray()
      +        if size > 0:
      +            while len(read) < size:
      +                x = self._instream.read()
      +                if x == -1:
      +                    if self.timeout >= 0:
      +                        break
      +                else:
      +                    read.append(x)
      +        return bytes(read)
      +
      +    def write(self, data):
      +        """Output the given string over the serial port."""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        if not isinstance(data, (bytes, bytearray)):
      +            raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
      +        self._outstream.write(data)
      +        return len(data)
      +
      +    def reset_input_buffer(self):
      +        """Clear input buffer, discarding all that is in the buffer."""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self._instream.skip(self._instream.available())
      +
      +    def reset_output_buffer(self):
      +        """\
      +        Clear output buffer, aborting the current output and
      +        discarding all that is in the buffer.
      +        """
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self._outstream.flush()
      +
      +    def send_break(self, duration=0.25):
      +        """Send break condition. Timed, returns to idle state after given duration."""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.sendBreak(duration*1000.0)
      +
      +    def _update_break_state(self):
      +        """Set break: Controls TXD. When active, to transmitting is possible."""
      +        if self.fd is None:
      +            raise PortNotOpenError()
      +        raise SerialException("The _update_break_state function is not implemented in java.")
      +
      +    def _update_rts_state(self):
      +        """Set terminal status line: Request To Send"""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.setRTS(self._rts_state)
      +
      +    def _update_dtr_state(self):
      +        """Set terminal status line: Data Terminal Ready"""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.setDTR(self._dtr_state)
      +
      +    @property
      +    def cts(self):
      +        """Read terminal status line: Clear To Send"""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.isCTS()
      +
      +    @property
      +    def dsr(self):
      +        """Read terminal status line: Data Set Ready"""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.isDSR()
      +
      +    @property
      +    def ri(self):
      +        """Read terminal status line: Ring Indicator"""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.isRI()
      +
      +    @property
      +    def cd(self):
      +        """Read terminal status line: Carrier Detect"""
      +        if not self.sPort:
      +            raise PortNotOpenError()
      +        self.sPort.isCD()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialposix.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialposix.py
      new file mode 100644
      index 000000000..7aceb76db
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialposix.py
      @@ -0,0 +1,900 @@
      +#!/usr/bin/env python
      +#
      +# backend for serial IO for POSIX compatible systems, like Linux, OSX
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2001-2020 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +#
      +# parts based on code from Grant B. Edwards  :
      +#  ftp://ftp.visi.com/users/grante/python/PosixSerial.py
      +#
      +# references: http://www.easysw.com/~mike/serial/serial.html
      +
      +# Collection of port names (was previously used by number_to_device which was
      +# removed.
      +# - Linux                   /dev/ttyS%d (confirmed)
      +# - cygwin/win32            /dev/com%d (confirmed)
      +# - openbsd (OpenBSD)       /dev/cua%02d
      +# - bsd*, freebsd*          /dev/cuad%d
      +# - darwin (OS X)           /dev/cuad%d
      +# - netbsd                  /dev/dty%02d (NetBSD 1.6 testing by Erk)
      +# - irix (IRIX)             /dev/ttyf%d (partially tested) names depending on flow control
      +# - hp (HP-UX)              /dev/tty%dp0 (not tested)
      +# - sunos (Solaris/SunOS)   /dev/tty%c (letters, 'a'..'z') (confirmed)
      +# - aix (AIX)               /dev/tty%d
      +
      +
      +from __future__ import absolute_import
      +
      +# pylint: disable=abstract-method
      +import errno
      +import fcntl
      +import os
      +import select
      +import struct
      +import sys
      +import termios
      +
      +import serial
      +from serial.serialutil import SerialBase, SerialException, to_bytes, \
      +    PortNotOpenError, SerialTimeoutException, Timeout
      +
      +
      +class PlatformSpecificBase(object):
      +    BAUDRATE_CONSTANTS = {}
      +
      +    def _set_special_baudrate(self, baudrate):
      +        raise NotImplementedError('non-standard baudrates are not supported on this platform')
      +
      +    def _set_rs485_mode(self, rs485_settings):
      +        raise NotImplementedError('RS485 not supported on this platform')
      +
      +    def set_low_latency_mode(self, low_latency_settings):
      +        raise NotImplementedError('Low latency not supported on this platform')
      +
      +    def _update_break_state(self):
      +        """\
      +        Set break: Controls TXD. When active, no transmitting is possible.
      +        """
      +        if self._break_state:
      +            fcntl.ioctl(self.fd, TIOCSBRK)
      +        else:
      +            fcntl.ioctl(self.fd, TIOCCBRK)
      +    
      +
      +# some systems support an extra flag to enable the two in POSIX unsupported
      +# paritiy settings for MARK and SPACE
      +CMSPAR = 0  # default, for unsupported platforms, override below
      +
      +# try to detect the OS so that a device can be selected...
      +# this code block should supply a device() and set_special_baudrate() function
      +# for the platform
      +plat = sys.platform.lower()
      +
      +if plat[:5] == 'linux':    # Linux (confirmed)  # noqa
      +    import array
      +
      +    # extra termios flags
      +    CMSPAR = 0o10000000000  # Use "stick" (mark/space) parity
      +
      +    # baudrate ioctls
      +    TCGETS2 = 0x802C542A
      +    TCSETS2 = 0x402C542B
      +    BOTHER = 0o010000
      +
      +    # RS485 ioctls
      +    TIOCGRS485 = 0x542E
      +    TIOCSRS485 = 0x542F
      +    SER_RS485_ENABLED = 0b00000001
      +    SER_RS485_RTS_ON_SEND = 0b00000010
      +    SER_RS485_RTS_AFTER_SEND = 0b00000100
      +    SER_RS485_RX_DURING_TX = 0b00010000
      +
      +    class PlatformSpecific(PlatformSpecificBase):
      +        BAUDRATE_CONSTANTS = {
      +            0:       0o000000,  # hang up
      +            50:      0o000001,
      +            75:      0o000002,
      +            110:     0o000003,
      +            134:     0o000004,
      +            150:     0o000005,
      +            200:     0o000006,
      +            300:     0o000007,
      +            600:     0o000010,
      +            1200:    0o000011,
      +            1800:    0o000012,
      +            2400:    0o000013,
      +            4800:    0o000014,
      +            9600:    0o000015,
      +            19200:   0o000016,
      +            38400:   0o000017,
      +            57600:   0o010001,
      +            115200:  0o010002,
      +            230400:  0o010003,
      +            460800:  0o010004,
      +            500000:  0o010005,
      +            576000:  0o010006,
      +            921600:  0o010007,
      +            1000000: 0o010010,
      +            1152000: 0o010011,
      +            1500000: 0o010012,
      +            2000000: 0o010013,
      +            2500000: 0o010014,
      +            3000000: 0o010015,
      +            3500000: 0o010016,
      +            4000000: 0o010017
      +        }
      +
      +        def set_low_latency_mode(self, low_latency_settings):
      +            buf = array.array('i', [0] * 32)
      +
      +            try:
      +                # get serial_struct
      +                fcntl.ioctl(self.fd, termios.TIOCGSERIAL, buf)
      +
      +                # set or unset ASYNC_LOW_LATENCY flag
      +                if low_latency_settings:
      +                    buf[4] |= 0x2000
      +                else:
      +                    buf[4] &= ~0x2000
      +
      +                # set serial_struct
      +                fcntl.ioctl(self.fd, termios.TIOCSSERIAL, buf)
      +            except IOError as e:
      +                raise ValueError('Failed to update ASYNC_LOW_LATENCY flag to {}: {}'.format(low_latency_settings, e))
      +
      +        def _set_special_baudrate(self, baudrate):
      +            # right size is 44 on x86_64, allow for some growth
      +            buf = array.array('i', [0] * 64)
      +            try:
      +                # get serial_struct
      +                fcntl.ioctl(self.fd, TCGETS2, buf)
      +                # set custom speed
      +                buf[2] &= ~termios.CBAUD
      +                buf[2] |= BOTHER
      +                buf[9] = buf[10] = baudrate
      +
      +                # set serial_struct
      +                fcntl.ioctl(self.fd, TCSETS2, buf)
      +            except IOError as e:
      +                raise ValueError('Failed to set custom baud rate ({}): {}'.format(baudrate, e))
      +
      +        def _set_rs485_mode(self, rs485_settings):
      +            buf = array.array('i', [0] * 8)  # flags, delaytx, delayrx, padding
      +            try:
      +                fcntl.ioctl(self.fd, TIOCGRS485, buf)
      +                buf[0] |= SER_RS485_ENABLED
      +                if rs485_settings is not None:
      +                    if rs485_settings.loopback:
      +                        buf[0] |= SER_RS485_RX_DURING_TX
      +                    else:
      +                        buf[0] &= ~SER_RS485_RX_DURING_TX
      +                    if rs485_settings.rts_level_for_tx:
      +                        buf[0] |= SER_RS485_RTS_ON_SEND
      +                    else:
      +                        buf[0] &= ~SER_RS485_RTS_ON_SEND
      +                    if rs485_settings.rts_level_for_rx:
      +                        buf[0] |= SER_RS485_RTS_AFTER_SEND
      +                    else:
      +                        buf[0] &= ~SER_RS485_RTS_AFTER_SEND
      +                    if rs485_settings.delay_before_tx is not None:
      +                        buf[1] = int(rs485_settings.delay_before_tx * 1000)
      +                    if rs485_settings.delay_before_rx is not None:
      +                        buf[2] = int(rs485_settings.delay_before_rx * 1000)
      +                else:
      +                    buf[0] = 0  # clear SER_RS485_ENABLED
      +                fcntl.ioctl(self.fd, TIOCSRS485, buf)
      +            except IOError as e:
      +                raise ValueError('Failed to set RS485 mode: {}'.format(e))
      +
      +
      +elif plat == 'cygwin':       # cygwin/win32 (confirmed)
      +
      +    class PlatformSpecific(PlatformSpecificBase):
      +        BAUDRATE_CONSTANTS = {
      +            128000: 0x01003,
      +            256000: 0x01005,
      +            500000: 0x01007,
      +            576000: 0x01008,
      +            921600: 0x01009,
      +            1000000: 0x0100a,
      +            1152000: 0x0100b,
      +            1500000: 0x0100c,
      +            2000000: 0x0100d,
      +            2500000: 0x0100e,
      +            3000000: 0x0100f
      +        }
      +
      +
      +elif plat[:6] == 'darwin':   # OS X
      +    import array
      +    IOSSIOSPEED = 0x80045402  # _IOW('T', 2, speed_t)
      +
      +    class PlatformSpecific(PlatformSpecificBase):
      +        osx_version = os.uname()[2].split('.')
      +        TIOCSBRK = 0x2000747B # _IO('t', 123)
      +        TIOCCBRK = 0x2000747A # _IO('t', 122)
      +
      +        # Tiger or above can support arbitrary serial speeds
      +        if int(osx_version[0]) >= 8:
      +            def _set_special_baudrate(self, baudrate):
      +                # use IOKit-specific call to set up high speeds
      +                buf = array.array('i', [baudrate])
      +                fcntl.ioctl(self.fd, IOSSIOSPEED, buf, 1)
      +
      +        def _update_break_state(self):
      +            """\
      +            Set break: Controls TXD. When active, no transmitting is possible.
      +            """
      +            if self._break_state:
      +                fcntl.ioctl(self.fd, PlatformSpecific.TIOCSBRK)
      +            else:
      +                fcntl.ioctl(self.fd, PlatformSpecific.TIOCCBRK)
      +
      +elif plat[:3] == 'bsd' or \
      +     plat[:7] == 'freebsd' or \
      +     plat[:6] == 'netbsd' or \
      +     plat[:7] == 'openbsd':
      +
      +    class ReturnBaudrate(object):
      +        def __getitem__(self, key):
      +            return key
      +
      +    class PlatformSpecific(PlatformSpecificBase):
      +        # Only tested on FreeBSD:
      +        # The baud rate may be passed in as
      +        # a literal value.
      +        BAUDRATE_CONSTANTS = ReturnBaudrate()
      +
      +        TIOCSBRK = 0x2000747B # _IO('t', 123)
      +        TIOCCBRK = 0x2000747A # _IO('t', 122)
      +
      +        
      +        def _update_break_state(self):
      +            """\
      +            Set break: Controls TXD. When active, no transmitting is possible.
      +            """
      +            if self._break_state:
      +                fcntl.ioctl(self.fd, PlatformSpecific.TIOCSBRK)
      +            else:
      +                fcntl.ioctl(self.fd, PlatformSpecific.TIOCCBRK)
      +
      +else:
      +    class PlatformSpecific(PlatformSpecificBase):
      +        pass
      +
      +
      +# load some constants for later use.
      +# try to use values from termios, use defaults from linux otherwise
      +TIOCMGET = getattr(termios, 'TIOCMGET', 0x5415)
      +TIOCMBIS = getattr(termios, 'TIOCMBIS', 0x5416)
      +TIOCMBIC = getattr(termios, 'TIOCMBIC', 0x5417)
      +TIOCMSET = getattr(termios, 'TIOCMSET', 0x5418)
      +
      +# TIOCM_LE = getattr(termios, 'TIOCM_LE', 0x001)
      +TIOCM_DTR = getattr(termios, 'TIOCM_DTR', 0x002)
      +TIOCM_RTS = getattr(termios, 'TIOCM_RTS', 0x004)
      +# TIOCM_ST = getattr(termios, 'TIOCM_ST', 0x008)
      +# TIOCM_SR = getattr(termios, 'TIOCM_SR', 0x010)
      +
      +TIOCM_CTS = getattr(termios, 'TIOCM_CTS', 0x020)
      +TIOCM_CAR = getattr(termios, 'TIOCM_CAR', 0x040)
      +TIOCM_RNG = getattr(termios, 'TIOCM_RNG', 0x080)
      +TIOCM_DSR = getattr(termios, 'TIOCM_DSR', 0x100)
      +TIOCM_CD = getattr(termios, 'TIOCM_CD', TIOCM_CAR)
      +TIOCM_RI = getattr(termios, 'TIOCM_RI', TIOCM_RNG)
      +# TIOCM_OUT1 = getattr(termios, 'TIOCM_OUT1', 0x2000)
      +# TIOCM_OUT2 = getattr(termios, 'TIOCM_OUT2', 0x4000)
      +if hasattr(termios, 'TIOCINQ'):
      +    TIOCINQ = termios.TIOCINQ
      +else:
      +    TIOCINQ = getattr(termios, 'FIONREAD', 0x541B)
      +TIOCOUTQ = getattr(termios, 'TIOCOUTQ', 0x5411)
      +
      +TIOCM_zero_str = struct.pack('I', 0)
      +TIOCM_RTS_str = struct.pack('I', TIOCM_RTS)
      +TIOCM_DTR_str = struct.pack('I', TIOCM_DTR)
      +
      +TIOCSBRK = getattr(termios, 'TIOCSBRK', 0x5427)
      +TIOCCBRK = getattr(termios, 'TIOCCBRK', 0x5428)
      +
      +
      +class Serial(SerialBase, PlatformSpecific):
      +    """\
      +    Serial port class POSIX implementation. Serial port configuration is
      +    done with termios and fcntl. Runs on Linux and many other Un*x like
      +    systems.
      +    """
      +
      +    def open(self):
      +        """\
      +        Open port with current settings. This may throw a SerialException
      +        if the port cannot be opened."""
      +        if self._port is None:
      +            raise SerialException("Port must be configured before it can be used.")
      +        if self.is_open:
      +            raise SerialException("Port is already open.")
      +        self.fd = None
      +        # open
      +        try:
      +            self.fd = os.open(self.portstr, os.O_RDWR | os.O_NOCTTY | os.O_NONBLOCK)
      +        except OSError as msg:
      +            self.fd = None
      +            raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
      +        #~ fcntl.fcntl(self.fd, fcntl.F_SETFL, 0)  # set blocking
      +
      +        self.pipe_abort_read_r, self.pipe_abort_read_w = None, None
      +        self.pipe_abort_write_r, self.pipe_abort_write_w = None, None
      +
      +        try:
      +            self._reconfigure_port(force_update=True)
      +
      +            try:
      +                if not self._dsrdtr:
      +                    self._update_dtr_state()
      +                if not self._rtscts:
      +                    self._update_rts_state()
      +            except IOError as e:
      +                # ignore Invalid argument and Inappropriate ioctl
      +                if e.errno not in (errno.EINVAL, errno.ENOTTY):
      +                    raise
      +
      +            self._reset_input_buffer()
      +
      +            self.pipe_abort_read_r, self.pipe_abort_read_w = os.pipe()
      +            self.pipe_abort_write_r, self.pipe_abort_write_w = os.pipe()
      +            fcntl.fcntl(self.pipe_abort_read_r, fcntl.F_SETFL, os.O_NONBLOCK)
      +            fcntl.fcntl(self.pipe_abort_write_r, fcntl.F_SETFL, os.O_NONBLOCK)
      +        except BaseException:
      +            try:
      +                os.close(self.fd)
      +            except Exception:
      +                # ignore any exception when closing the port
      +                # also to keep original exception that happened when setting up
      +                pass
      +            self.fd = None
      +
      +            if self.pipe_abort_read_w is not None:
      +                os.close(self.pipe_abort_read_w)
      +                self.pipe_abort_read_w = None
      +            if self.pipe_abort_read_r is not None:
      +                os.close(self.pipe_abort_read_r)
      +                self.pipe_abort_read_r = None
      +            if self.pipe_abort_write_w is not None:
      +                os.close(self.pipe_abort_write_w)
      +                self.pipe_abort_write_w = None
      +            if self.pipe_abort_write_r is not None:
      +                os.close(self.pipe_abort_write_r)
      +                self.pipe_abort_write_r = None
      +
      +            raise
      +
      +        self.is_open = True
      +
      +    def _reconfigure_port(self, force_update=False):
      +        """Set communication parameters on opened port."""
      +        if self.fd is None:
      +            raise SerialException("Can only operate on a valid file descriptor")
      +
      +        # if exclusive lock is requested, create it before we modify anything else
      +        if self._exclusive is not None:
      +            if self._exclusive:
      +                try:
      +                    fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
      +                except IOError as msg:
      +                    raise SerialException(msg.errno, "Could not exclusively lock port {}: {}".format(self._port, msg))
      +            else:
      +                fcntl.flock(self.fd, fcntl.LOCK_UN)
      +
      +        custom_baud = None
      +
      +        vmin = vtime = 0                # timeout is done via select
      +        if self._inter_byte_timeout is not None:
      +            vmin = 1
      +            vtime = int(self._inter_byte_timeout * 10)
      +        try:
      +            orig_attr = termios.tcgetattr(self.fd)
      +            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
      +        except termios.error as msg:      # if a port is nonexistent but has a /dev file, it'll fail here
      +            raise SerialException("Could not configure port: {}".format(msg))
      +        # set up raw mode / no echo / binary
      +        cflag |= (termios.CLOCAL | termios.CREAD)
      +        lflag &= ~(termios.ICANON | termios.ECHO | termios.ECHOE |
      +                   termios.ECHOK | termios.ECHONL |
      +                   termios.ISIG | termios.IEXTEN)  # |termios.ECHOPRT
      +        for flag in ('ECHOCTL', 'ECHOKE'):  # netbsd workaround for Erk
      +            if hasattr(termios, flag):
      +                lflag &= ~getattr(termios, flag)
      +
      +        oflag &= ~(termios.OPOST | termios.ONLCR | termios.OCRNL)
      +        iflag &= ~(termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IGNBRK)
      +        if hasattr(termios, 'IUCLC'):
      +            iflag &= ~termios.IUCLC
      +        if hasattr(termios, 'PARMRK'):
      +            iflag &= ~termios.PARMRK
      +
      +        # setup baud rate
      +        try:
      +            ispeed = ospeed = getattr(termios, 'B{}'.format(self._baudrate))
      +        except AttributeError:
      +            try:
      +                ispeed = ospeed = self.BAUDRATE_CONSTANTS[self._baudrate]
      +            except KeyError:
      +                #~ raise ValueError('Invalid baud rate: %r' % self._baudrate)
      +
      +                # See if BOTHER is defined for this platform; if it is, use
      +                # this for a speed not defined in the baudrate constants list.
      +                try:
      +                    ispeed = ospeed = BOTHER
      +                except NameError:
      +                    # may need custom baud rate, it isn't in our list.
      +                    ispeed = ospeed = getattr(termios, 'B38400')
      +
      +                try:
      +                    custom_baud = int(self._baudrate)  # store for later
      +                except ValueError:
      +                    raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate))
      +                else:
      +                    if custom_baud < 0:
      +                        raise ValueError('Invalid baud rate: {!r}'.format(self._baudrate))
      +
      +        # setup char len
      +        cflag &= ~termios.CSIZE
      +        if self._bytesize == 8:
      +            cflag |= termios.CS8
      +        elif self._bytesize == 7:
      +            cflag |= termios.CS7
      +        elif self._bytesize == 6:
      +            cflag |= termios.CS6
      +        elif self._bytesize == 5:
      +            cflag |= termios.CS5
      +        else:
      +            raise ValueError('Invalid char len: {!r}'.format(self._bytesize))
      +        # setup stop bits
      +        if self._stopbits == serial.STOPBITS_ONE:
      +            cflag &= ~(termios.CSTOPB)
      +        elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
      +            cflag |= (termios.CSTOPB)  # XXX same as TWO.. there is no POSIX support for 1.5
      +        elif self._stopbits == serial.STOPBITS_TWO:
      +            cflag |= (termios.CSTOPB)
      +        else:
      +            raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits))
      +        # setup parity
      +        iflag &= ~(termios.INPCK | termios.ISTRIP)
      +        if self._parity == serial.PARITY_NONE:
      +            cflag &= ~(termios.PARENB | termios.PARODD | CMSPAR)
      +        elif self._parity == serial.PARITY_EVEN:
      +            cflag &= ~(termios.PARODD | CMSPAR)
      +            cflag |= (termios.PARENB)
      +        elif self._parity == serial.PARITY_ODD:
      +            cflag &= ~CMSPAR
      +            cflag |= (termios.PARENB | termios.PARODD)
      +        elif self._parity == serial.PARITY_MARK and CMSPAR:
      +            cflag |= (termios.PARENB | CMSPAR | termios.PARODD)
      +        elif self._parity == serial.PARITY_SPACE and CMSPAR:
      +            cflag |= (termios.PARENB | CMSPAR)
      +            cflag &= ~(termios.PARODD)
      +        else:
      +            raise ValueError('Invalid parity: {!r}'.format(self._parity))
      +        # setup flow control
      +        # xonxoff
      +        if hasattr(termios, 'IXANY'):
      +            if self._xonxoff:
      +                iflag |= (termios.IXON | termios.IXOFF)  # |termios.IXANY)
      +            else:
      +                iflag &= ~(termios.IXON | termios.IXOFF | termios.IXANY)
      +        else:
      +            if self._xonxoff:
      +                iflag |= (termios.IXON | termios.IXOFF)
      +            else:
      +                iflag &= ~(termios.IXON | termios.IXOFF)
      +        # rtscts
      +        if hasattr(termios, 'CRTSCTS'):
      +            if self._rtscts:
      +                cflag |= (termios.CRTSCTS)
      +            else:
      +                cflag &= ~(termios.CRTSCTS)
      +        elif hasattr(termios, 'CNEW_RTSCTS'):   # try it with alternate constant name
      +            if self._rtscts:
      +                cflag |= (termios.CNEW_RTSCTS)
      +            else:
      +                cflag &= ~(termios.CNEW_RTSCTS)
      +        # XXX should there be a warning if setting up rtscts (and xonxoff etc) fails??
      +
      +        # buffer
      +        # vmin "minimal number of characters to be read. 0 for non blocking"
      +        if vmin < 0 or vmin > 255:
      +            raise ValueError('Invalid vmin: {!r}'.format(vmin))
      +        cc[termios.VMIN] = vmin
      +        # vtime
      +        if vtime < 0 or vtime > 255:
      +            raise ValueError('Invalid vtime: {!r}'.format(vtime))
      +        cc[termios.VTIME] = vtime
      +        # activate settings
      +        if force_update or [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] != orig_attr:
      +            termios.tcsetattr(
      +                self.fd,
      +                termios.TCSANOW,
      +                [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
      +
      +        # apply custom baud rate, if any
      +        if custom_baud is not None:
      +            self._set_special_baudrate(custom_baud)
      +
      +        if self._rs485_mode is not None:
      +            self._set_rs485_mode(self._rs485_mode)
      +
      +    def close(self):
      +        """Close port"""
      +        if self.is_open:
      +            if self.fd is not None:
      +                os.close(self.fd)
      +                self.fd = None
      +                os.close(self.pipe_abort_read_w)
      +                os.close(self.pipe_abort_read_r)
      +                os.close(self.pipe_abort_write_w)
      +                os.close(self.pipe_abort_write_r)
      +                self.pipe_abort_read_r, self.pipe_abort_read_w = None, None
      +                self.pipe_abort_write_r, self.pipe_abort_write_w = None, None
      +            self.is_open = False
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    @property
      +    def in_waiting(self):
      +        """Return the number of bytes currently in the input buffer."""
      +        #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str)
      +        s = fcntl.ioctl(self.fd, TIOCINQ, TIOCM_zero_str)
      +        return struct.unpack('I', s)[0]
      +
      +    # select based implementation, proved to work on many systems
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        read = bytearray()
      +        timeout = Timeout(self._timeout)
      +        while len(read) < size:
      +            try:
      +                ready, _, _ = select.select([self.fd, self.pipe_abort_read_r], [], [], timeout.time_left())
      +                if self.pipe_abort_read_r in ready:
      +                    os.read(self.pipe_abort_read_r, 1000)
      +                    break
      +                # If select was used with a timeout, and the timeout occurs, it
      +                # returns with empty lists -> thus abort read operation.
      +                # For timeout == 0 (non-blocking operation) also abort when
      +                # there is nothing to read.
      +                if not ready:
      +                    break   # timeout
      +                buf = os.read(self.fd, size - len(read))
      +            except OSError as e:
      +                # this is for Python 3.x where select.error is a subclass of
      +                # OSError ignore BlockingIOErrors and EINTR. other errors are shown
      +                # https://www.python.org/dev/peps/pep-0475.
      +                if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
      +                    raise SerialException('read failed: {}'.format(e))
      +            except select.error as e:
      +                # this is for Python 2.x
      +                # ignore BlockingIOErrors and EINTR. all errors are shown
      +                # see also http://www.python.org/dev/peps/pep-3151/#select
      +                if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
      +                    raise SerialException('read failed: {}'.format(e))
      +            else:
      +                # read should always return some data as select reported it was
      +                # ready to read when we get to this point.
      +                if not buf:
      +                    # Disconnected devices, at least on Linux, show the
      +                    # behavior that they are always ready to read immediately
      +                    # but reading returns nothing.
      +                    raise SerialException(
      +                        'device reports readiness to read but returned no data '
      +                        '(device disconnected or multiple access on port?)')
      +                read.extend(buf)
      +
      +            if timeout.expired():
      +                break
      +        return bytes(read)
      +
      +    def cancel_read(self):
      +        if self.is_open:
      +            os.write(self.pipe_abort_read_w, b"x")
      +
      +    def cancel_write(self):
      +        if self.is_open:
      +            os.write(self.pipe_abort_write_w, b"x")
      +
      +    def write(self, data):
      +        """Output the given byte string over the serial port."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        d = to_bytes(data)
      +        tx_len = length = len(d)
      +        timeout = Timeout(self._write_timeout)
      +        while tx_len > 0:
      +            try:
      +                n = os.write(self.fd, d)
      +                if timeout.is_non_blocking:
      +                    # Zero timeout indicates non-blocking - simply return the
      +                    # number of bytes of data actually written
      +                    return n
      +                elif not timeout.is_infinite:
      +                    # when timeout is set, use select to wait for being ready
      +                    # with the time left as timeout
      +                    if timeout.expired():
      +                        raise SerialTimeoutException('Write timeout')
      +                    abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], timeout.time_left())
      +                    if abort:
      +                        os.read(self.pipe_abort_write_r, 1000)
      +                        break
      +                    if not ready:
      +                        raise SerialTimeoutException('Write timeout')
      +                else:
      +                    assert timeout.time_left() is None
      +                    # wait for write operation
      +                    abort, ready, _ = select.select([self.pipe_abort_write_r], [self.fd], [], None)
      +                    if abort:
      +                        os.read(self.pipe_abort_write_r, 1)
      +                        break
      +                    if not ready:
      +                        raise SerialException('write failed (select)')
      +                d = d[n:]
      +                tx_len -= n
      +            except SerialException:
      +                raise
      +            except OSError as e:
      +                # this is for Python 3.x where select.error is a subclass of
      +                # OSError ignore BlockingIOErrors and EINTR. other errors are shown
      +                # https://www.python.org/dev/peps/pep-0475.
      +                if e.errno not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
      +                    raise SerialException('write failed: {}'.format(e))
      +            except select.error as e:
      +                # this is for Python 2.x
      +                # ignore BlockingIOErrors and EINTR. all errors are shown
      +                # see also http://www.python.org/dev/peps/pep-3151/#select
      +                if e[0] not in (errno.EAGAIN, errno.EALREADY, errno.EWOULDBLOCK, errno.EINPROGRESS, errno.EINTR):
      +                    raise SerialException('write failed: {}'.format(e))
      +            if not timeout.is_non_blocking and timeout.expired():
      +                raise SerialTimeoutException('Write timeout')
      +        return length - len(d)
      +
      +    def flush(self):
      +        """\
      +        Flush of file like objects. In this case, wait until all data
      +        is written.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        termios.tcdrain(self.fd)
      +
      +    def _reset_input_buffer(self):
      +        """Clear input buffer, discarding all that is in the buffer."""
      +        termios.tcflush(self.fd, termios.TCIFLUSH)
      +
      +    def reset_input_buffer(self):
      +        """Clear input buffer, discarding all that is in the buffer."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._reset_input_buffer()
      +
      +    def reset_output_buffer(self):
      +        """\
      +        Clear output buffer, aborting the current output and discarding all
      +        that is in the buffer.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        termios.tcflush(self.fd, termios.TCOFLUSH)
      +
      +    def send_break(self, duration=0.25):
      +        """\
      +        Send break condition. Timed, returns to idle state after given
      +        duration.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        termios.tcsendbreak(self.fd, int(duration / 0.25))
      +
      +    def _update_rts_state(self):
      +        """Set terminal status line: Request To Send"""
      +        if self._rts_state:
      +            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_RTS_str)
      +        else:
      +            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_RTS_str)
      +
      +    def _update_dtr_state(self):
      +        """Set terminal status line: Data Terminal Ready"""
      +        if self._dtr_state:
      +            fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
      +        else:
      +            fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
      +
      +    @property
      +    def cts(self):
      +        """Read terminal status line: Clear To Send"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
      +        return struct.unpack('I', s)[0] & TIOCM_CTS != 0
      +
      +    @property
      +    def dsr(self):
      +        """Read terminal status line: Data Set Ready"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
      +        return struct.unpack('I', s)[0] & TIOCM_DSR != 0
      +
      +    @property
      +    def ri(self):
      +        """Read terminal status line: Ring Indicator"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
      +        return struct.unpack('I', s)[0] & TIOCM_RI != 0
      +
      +    @property
      +    def cd(self):
      +        """Read terminal status line: Carrier Detect"""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        s = fcntl.ioctl(self.fd, TIOCMGET, TIOCM_zero_str)
      +        return struct.unpack('I', s)[0] & TIOCM_CD != 0
      +
      +    # - - platform specific - - - -
      +
      +    @property
      +    def out_waiting(self):
      +        """Return the number of bytes currently in the output buffer."""
      +        #~ s = fcntl.ioctl(self.fd, termios.FIONREAD, TIOCM_zero_str)
      +        s = fcntl.ioctl(self.fd, TIOCOUTQ, TIOCM_zero_str)
      +        return struct.unpack('I', s)[0]
      +
      +    def fileno(self):
      +        """\
      +        For easier use of the serial port instance with select.
      +        WARNING: this function is not portable to different platforms!
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        return self.fd
      +
      +    def set_input_flow_control(self, enable=True):
      +        """\
      +        Manually control flow - when software flow control is enabled.
      +        This will send XON (true) or XOFF (false) to the other device.
      +        WARNING: this function is not portable to different platforms!
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if enable:
      +            termios.tcflow(self.fd, termios.TCION)
      +        else:
      +            termios.tcflow(self.fd, termios.TCIOFF)
      +
      +    def set_output_flow_control(self, enable=True):
      +        """\
      +        Manually control flow of outgoing data - when hardware or software flow
      +        control is enabled.
      +        WARNING: this function is not portable to different platforms!
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if enable:
      +            termios.tcflow(self.fd, termios.TCOON)
      +        else:
      +            termios.tcflow(self.fd, termios.TCOOFF)
      +
      +    def nonblocking(self):
      +        """DEPRECATED - has no use"""
      +        import warnings
      +        warnings.warn("nonblocking() has no effect, already nonblocking", DeprecationWarning)
      +
      +
      +class PosixPollSerial(Serial):
      +    """\
      +    Poll based read implementation. Not all systems support poll properly.
      +    However this one has better handling of errors, such as a device
      +    disconnecting while it's in use (e.g. USB-serial unplugged).
      +    """
      +
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        read = bytearray()
      +        timeout = Timeout(self._timeout)
      +        poll = select.poll()
      +        poll.register(self.fd, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL)
      +        poll.register(self.pipe_abort_read_r, select.POLLIN | select.POLLERR | select.POLLHUP | select.POLLNVAL)
      +        if size > 0:
      +            while len(read) < size:
      +                # print "\tread(): size",size, "have", len(read)    #debug
      +                # wait until device becomes ready to read (or something fails)
      +                for fd, event in poll.poll(None if timeout.is_infinite else (timeout.time_left() * 1000)):
      +                    if fd == self.pipe_abort_read_r:
      +                        break
      +                    if event & (select.POLLERR | select.POLLHUP | select.POLLNVAL):
      +                        raise SerialException('device reports error (poll)')
      +                    #  we don't care if it is select.POLLIN or timeout, that's
      +                    #  handled below
      +                if fd == self.pipe_abort_read_r:
      +                    os.read(self.pipe_abort_read_r, 1000)
      +                    break
      +                buf = os.read(self.fd, size - len(read))
      +                read.extend(buf)
      +                if timeout.expired() \
      +                        or (self._inter_byte_timeout is not None and self._inter_byte_timeout > 0) and not buf:
      +                    break   # early abort on timeout
      +        return bytes(read)
      +
      +
      +class VTIMESerial(Serial):
      +    """\
      +    Implement timeout using vtime of tty device instead of using select.
      +    This means that no inter character timeout can be specified and that
      +    the error handling is degraded.
      +
      +    Overall timeout is disabled when inter-character timeout is used.
      +
      +    Note that this implementation does NOT support cancel_read(), it will
      +    just ignore that.
      +    """
      +
      +    def _reconfigure_port(self, force_update=True):
      +        """Set communication parameters on opened port."""
      +        super(VTIMESerial, self)._reconfigure_port()
      +        fcntl.fcntl(self.fd, fcntl.F_SETFL, 0)  # clear O_NONBLOCK
      +
      +        if self._inter_byte_timeout is not None:
      +            vmin = 1
      +            vtime = int(self._inter_byte_timeout * 10)
      +        elif self._timeout is None:
      +            vmin = 1
      +            vtime = 0
      +        else:
      +            vmin = 0
      +            vtime = int(self._timeout * 10)
      +        try:
      +            orig_attr = termios.tcgetattr(self.fd)
      +            iflag, oflag, cflag, lflag, ispeed, ospeed, cc = orig_attr
      +        except termios.error as msg:      # if a port is nonexistent but has a /dev file, it'll fail here
      +            raise serial.SerialException("Could not configure port: {}".format(msg))
      +
      +        if vtime < 0 or vtime > 255:
      +            raise ValueError('Invalid vtime: {!r}'.format(vtime))
      +        cc[termios.VTIME] = vtime
      +        cc[termios.VMIN] = vmin
      +
      +        termios.tcsetattr(
      +                self.fd,
      +                termios.TCSANOW,
      +                [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
      +
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        read = bytearray()
      +        while len(read) < size:
      +            buf = os.read(self.fd, size - len(read))
      +            if not buf:
      +                break
      +            read.extend(buf)
      +        return bytes(read)
      +
      +    # hack to make hasattr return false
      +    cancel_read = property()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialutil.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialutil.py
      new file mode 100644
      index 000000000..789219e93
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialutil.py
      @@ -0,0 +1,697 @@
      +#! python
      +#
      +# Base class and support functions used by various backends.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2001-2020 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +import io
      +import time
      +
      +# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)``
      +# isn't returning the contents (very unfortunate). Therefore we need special
      +# cases and test for it. Ensure that there is a ``memoryview`` object for older
      +# Python versions. This is easier than making every test dependent on its
      +# existence.
      +try:
      +    memoryview
      +except (NameError, AttributeError):
      +    # implementation does not matter as we do not really use it.
      +    # it just must not inherit from something else we might care for.
      +    class memoryview(object):   # pylint: disable=redefined-builtin,invalid-name
      +        pass
      +
      +try:
      +    unicode
      +except (NameError, AttributeError):
      +    unicode = str       # for Python 3, pylint: disable=redefined-builtin,invalid-name
      +
      +try:
      +    basestring
      +except (NameError, AttributeError):
      +    basestring = (str,)    # for Python 3, pylint: disable=redefined-builtin,invalid-name
      +
      +
      +# "for byte in data" fails for python3 as it returns ints instead of bytes
      +def iterbytes(b):
      +    """Iterate over bytes, returning bytes instead of ints (python3)"""
      +    if isinstance(b, memoryview):
      +        b = b.tobytes()
      +    i = 0
      +    while True:
      +        a = b[i:i + 1]
      +        i += 1
      +        if a:
      +            yield a
      +        else:
      +            break
      +
      +
      +# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11'
      +# so a simple ``bytes(sequence)`` doesn't work for all versions
      +def to_bytes(seq):
      +    """convert a sequence to a bytes type"""
      +    if isinstance(seq, bytes):
      +        return seq
      +    elif isinstance(seq, bytearray):
      +        return bytes(seq)
      +    elif isinstance(seq, memoryview):
      +        return seq.tobytes()
      +    elif isinstance(seq, unicode):
      +        raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq))
      +    else:
      +        # handle list of integers and bytes (one or more items) for Python 2 and 3
      +        return bytes(bytearray(seq))
      +
      +
      +# create control bytes
      +XON = to_bytes([17])
      +XOFF = to_bytes([19])
      +
      +CR = to_bytes([13])
      +LF = to_bytes([10])
      +
      +
      +PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S'
      +STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2)
      +FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8)
      +
      +PARITY_NAMES = {
      +    PARITY_NONE: 'None',
      +    PARITY_EVEN: 'Even',
      +    PARITY_ODD: 'Odd',
      +    PARITY_MARK: 'Mark',
      +    PARITY_SPACE: 'Space',
      +}
      +
      +
      +class SerialException(IOError):
      +    """Base class for serial port related exceptions."""
      +
      +
      +class SerialTimeoutException(SerialException):
      +    """Write timeouts give an exception"""
      +
      +
      +class PortNotOpenError(SerialException):
      +    """Port is not open"""
      +    def __init__(self):
      +        super(PortNotOpenError, self).__init__('Attempting to use a port that is not open')
      +
      +
      +class Timeout(object):
      +    """\
      +    Abstraction for timeout operations. Using time.monotonic() if available
      +    or time.time() in all other cases.
      +
      +    The class can also be initialized with 0 or None, in order to support
      +    non-blocking and fully blocking I/O operations. The attributes
      +    is_non_blocking and is_infinite are set accordingly.
      +    """
      +    if hasattr(time, 'monotonic'):
      +        # Timeout implementation with time.monotonic(). This function is only
      +        # supported by Python 3.3 and above. It returns a time in seconds
      +        # (float) just as time.time(), but is not affected by system clock
      +        # adjustments.
      +        TIME = time.monotonic
      +    else:
      +        # Timeout implementation with time.time(). This is compatible with all
      +        # Python versions but has issues if the clock is adjusted while the
      +        # timeout is running.
      +        TIME = time.time
      +
      +    def __init__(self, duration):
      +        """Initialize a timeout with given duration"""
      +        self.is_infinite = (duration is None)
      +        self.is_non_blocking = (duration == 0)
      +        self.duration = duration
      +        if duration is not None:
      +            self.target_time = self.TIME() + duration
      +        else:
      +            self.target_time = None
      +
      +    def expired(self):
      +        """Return a boolean, telling if the timeout has expired"""
      +        return self.target_time is not None and self.time_left() <= 0
      +
      +    def time_left(self):
      +        """Return how many seconds are left until the timeout expires"""
      +        if self.is_non_blocking:
      +            return 0
      +        elif self.is_infinite:
      +            return None
      +        else:
      +            delta = self.target_time - self.TIME()
      +            if delta > self.duration:
      +                # clock jumped, recalculate
      +                self.target_time = self.TIME() + self.duration
      +                return self.duration
      +            else:
      +                return max(0, delta)
      +
      +    def restart(self, duration):
      +        """\
      +        Restart a timeout, only supported if a timeout was already set up
      +        before.
      +        """
      +        self.duration = duration
      +        self.target_time = self.TIME() + duration
      +
      +
      +class SerialBase(io.RawIOBase):
      +    """\
      +    Serial port base class. Provides __init__ function and properties to
      +    get/set port settings.
      +    """
      +
      +    # default values, may be overridden in subclasses that do not support all values
      +    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
      +                 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000,
      +                 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000,
      +                 3000000, 3500000, 4000000)
      +    BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS)
      +    PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE)
      +    STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO)
      +
      +    def __init__(self,
      +                 port=None,
      +                 baudrate=9600,
      +                 bytesize=EIGHTBITS,
      +                 parity=PARITY_NONE,
      +                 stopbits=STOPBITS_ONE,
      +                 timeout=None,
      +                 xonxoff=False,
      +                 rtscts=False,
      +                 write_timeout=None,
      +                 dsrdtr=False,
      +                 inter_byte_timeout=None,
      +                 exclusive=None,
      +                 **kwargs):
      +        """\
      +        Initialize comm port object. If a "port" is given, then the port will be
      +        opened immediately. Otherwise a Serial port object in closed state
      +        is returned.
      +        """
      +
      +        self.is_open = False
      +        self.portstr = None
      +        self.name = None
      +        # correct values are assigned below through properties
      +        self._port = None
      +        self._baudrate = None
      +        self._bytesize = None
      +        self._parity = None
      +        self._stopbits = None
      +        self._timeout = None
      +        self._write_timeout = None
      +        self._xonxoff = None
      +        self._rtscts = None
      +        self._dsrdtr = None
      +        self._inter_byte_timeout = None
      +        self._rs485_mode = None  # disabled by default
      +        self._rts_state = True
      +        self._dtr_state = True
      +        self._break_state = False
      +        self._exclusive = None
      +
      +        # assign values using get/set methods using the properties feature
      +        self.port = port
      +        self.baudrate = baudrate
      +        self.bytesize = bytesize
      +        self.parity = parity
      +        self.stopbits = stopbits
      +        self.timeout = timeout
      +        self.write_timeout = write_timeout
      +        self.xonxoff = xonxoff
      +        self.rtscts = rtscts
      +        self.dsrdtr = dsrdtr
      +        self.inter_byte_timeout = inter_byte_timeout
      +        self.exclusive = exclusive
      +
      +        # watch for backward compatible kwargs
      +        if 'writeTimeout' in kwargs:
      +            self.write_timeout = kwargs.pop('writeTimeout')
      +        if 'interCharTimeout' in kwargs:
      +            self.inter_byte_timeout = kwargs.pop('interCharTimeout')
      +        if kwargs:
      +            raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs))
      +
      +        if port is not None:
      +            self.open()
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    # to be implemented by subclasses:
      +    # def open(self):
      +    # def close(self):
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    @property
      +    def port(self):
      +        """\
      +        Get the current port setting. The value that was passed on init or using
      +        setPort() is passed back.
      +        """
      +        return self._port
      +
      +    @port.setter
      +    def port(self, port):
      +        """\
      +        Change the port.
      +        """
      +        if port is not None and not isinstance(port, basestring):
      +            raise ValueError('"port" must be None or a string, not {}'.format(type(port)))
      +        was_open = self.is_open
      +        if was_open:
      +            self.close()
      +        self.portstr = port
      +        self._port = port
      +        self.name = self.portstr
      +        if was_open:
      +            self.open()
      +
      +    @property
      +    def baudrate(self):
      +        """Get the current baud rate setting."""
      +        return self._baudrate
      +
      +    @baudrate.setter
      +    def baudrate(self, baudrate):
      +        """\
      +        Change baud rate. It raises a ValueError if the port is open and the
      +        baud rate is not possible. If the port is closed, then the value is
      +        accepted and the exception is raised when the port is opened.
      +        """
      +        try:
      +            b = int(baudrate)
      +        except TypeError:
      +            raise ValueError("Not a valid baudrate: {!r}".format(baudrate))
      +        else:
      +            if b < 0:
      +                raise ValueError("Not a valid baudrate: {!r}".format(baudrate))
      +            self._baudrate = b
      +            if self.is_open:
      +                self._reconfigure_port()
      +
      +    @property
      +    def bytesize(self):
      +        """Get the current byte size setting."""
      +        return self._bytesize
      +
      +    @bytesize.setter
      +    def bytesize(self, bytesize):
      +        """Change byte size."""
      +        if bytesize not in self.BYTESIZES:
      +            raise ValueError("Not a valid byte size: {!r}".format(bytesize))
      +        self._bytesize = bytesize
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def exclusive(self):
      +        """Get the current exclusive access setting."""
      +        return self._exclusive
      +
      +    @exclusive.setter
      +    def exclusive(self, exclusive):
      +        """Change the exclusive access setting."""
      +        self._exclusive = exclusive
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def parity(self):
      +        """Get the current parity setting."""
      +        return self._parity
      +
      +    @parity.setter
      +    def parity(self, parity):
      +        """Change parity setting."""
      +        if parity not in self.PARITIES:
      +            raise ValueError("Not a valid parity: {!r}".format(parity))
      +        self._parity = parity
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def stopbits(self):
      +        """Get the current stop bits setting."""
      +        return self._stopbits
      +
      +    @stopbits.setter
      +    def stopbits(self, stopbits):
      +        """Change stop bits size."""
      +        if stopbits not in self.STOPBITS:
      +            raise ValueError("Not a valid stop bit size: {!r}".format(stopbits))
      +        self._stopbits = stopbits
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def timeout(self):
      +        """Get the current timeout setting."""
      +        return self._timeout
      +
      +    @timeout.setter
      +    def timeout(self, timeout):
      +        """Change timeout setting."""
      +        if timeout is not None:
      +            try:
      +                timeout + 1     # test if it's a number, will throw a TypeError if not...
      +            except TypeError:
      +                raise ValueError("Not a valid timeout: {!r}".format(timeout))
      +            if timeout < 0:
      +                raise ValueError("Not a valid timeout: {!r}".format(timeout))
      +        self._timeout = timeout
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def write_timeout(self):
      +        """Get the current timeout setting."""
      +        return self._write_timeout
      +
      +    @write_timeout.setter
      +    def write_timeout(self, timeout):
      +        """Change timeout setting."""
      +        if timeout is not None:
      +            if timeout < 0:
      +                raise ValueError("Not a valid timeout: {!r}".format(timeout))
      +            try:
      +                timeout + 1     # test if it's a number, will throw a TypeError if not...
      +            except TypeError:
      +                raise ValueError("Not a valid timeout: {!r}".format(timeout))
      +
      +        self._write_timeout = timeout
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def inter_byte_timeout(self):
      +        """Get the current inter-character timeout setting."""
      +        return self._inter_byte_timeout
      +
      +    @inter_byte_timeout.setter
      +    def inter_byte_timeout(self, ic_timeout):
      +        """Change inter-byte timeout setting."""
      +        if ic_timeout is not None:
      +            if ic_timeout < 0:
      +                raise ValueError("Not a valid timeout: {!r}".format(ic_timeout))
      +            try:
      +                ic_timeout + 1     # test if it's a number, will throw a TypeError if not...
      +            except TypeError:
      +                raise ValueError("Not a valid timeout: {!r}".format(ic_timeout))
      +
      +        self._inter_byte_timeout = ic_timeout
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def xonxoff(self):
      +        """Get the current XON/XOFF setting."""
      +        return self._xonxoff
      +
      +    @xonxoff.setter
      +    def xonxoff(self, xonxoff):
      +        """Change XON/XOFF setting."""
      +        self._xonxoff = xonxoff
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def rtscts(self):
      +        """Get the current RTS/CTS flow control setting."""
      +        return self._rtscts
      +
      +    @rtscts.setter
      +    def rtscts(self, rtscts):
      +        """Change RTS/CTS flow control setting."""
      +        self._rtscts = rtscts
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def dsrdtr(self):
      +        """Get the current DSR/DTR flow control setting."""
      +        return self._dsrdtr
      +
      +    @dsrdtr.setter
      +    def dsrdtr(self, dsrdtr=None):
      +        """Change DsrDtr flow control setting."""
      +        if dsrdtr is None:
      +            # if not set, keep backwards compatibility and follow rtscts setting
      +            self._dsrdtr = self._rtscts
      +        else:
      +            # if defined independently, follow its value
      +            self._dsrdtr = dsrdtr
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    @property
      +    def rts(self):
      +        return self._rts_state
      +
      +    @rts.setter
      +    def rts(self, value):
      +        self._rts_state = value
      +        if self.is_open:
      +            self._update_rts_state()
      +
      +    @property
      +    def dtr(self):
      +        return self._dtr_state
      +
      +    @dtr.setter
      +    def dtr(self, value):
      +        self._dtr_state = value
      +        if self.is_open:
      +            self._update_dtr_state()
      +
      +    @property
      +    def break_condition(self):
      +        return self._break_state
      +
      +    @break_condition.setter
      +    def break_condition(self, value):
      +        self._break_state = value
      +        if self.is_open:
      +            self._update_break_state()
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +    # functions useful for RS-485 adapters
      +
      +    @property
      +    def rs485_mode(self):
      +        """\
      +        Enable RS485 mode and apply new settings, set to None to disable.
      +        See serial.rs485.RS485Settings for more info about the value.
      +        """
      +        return self._rs485_mode
      +
      +    @rs485_mode.setter
      +    def rs485_mode(self, rs485_settings):
      +        self._rs485_mode = rs485_settings
      +        if self.is_open:
      +            self._reconfigure_port()
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff',
      +                       'dsrdtr', 'rtscts', 'timeout', 'write_timeout',
      +                       'inter_byte_timeout')
      +
      +    def get_settings(self):
      +        """\
      +        Get current port settings as a dictionary. For use with
      +        apply_settings().
      +        """
      +        return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS])
      +
      +    def apply_settings(self, d):
      +        """\
      +        Apply stored settings from a dictionary returned from
      +        get_settings(). It's allowed to delete keys from the dictionary. These
      +        values will simply left unchanged.
      +        """
      +        for key in self._SAVED_SETTINGS:
      +            if key in d and d[key] != getattr(self, '_' + key):   # check against internal "_" value
      +                setattr(self, key, d[key])          # set non "_" value to use properties write function
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    def __repr__(self):
      +        """String representation of the current port settings and its state."""
      +        return '{name}(port={p.portstr!r}, ' \
      +               'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \
      +               'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \
      +               'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format(
      +                   name=self.__class__.__name__, id=id(self), p=self)
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +    # compatibility with io library
      +    # pylint: disable=invalid-name,missing-docstring
      +
      +    def readable(self):
      +        return True
      +
      +    def writable(self):
      +        return True
      +
      +    def seekable(self):
      +        return False
      +
      +    def readinto(self, b):
      +        data = self.read(len(b))
      +        n = len(data)
      +        try:
      +            b[:n] = data
      +        except TypeError as err:
      +            import array
      +            if not isinstance(b, array.array):
      +                raise err
      +            b[:n] = array.array('b', data)
      +        return n
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +    # context manager
      +
      +    def __enter__(self):
      +        if self._port is not None and not self.is_open:
      +            self.open()
      +        return self
      +
      +    def __exit__(self, *args, **kwargs):
      +        self.close()
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    def send_break(self, duration=0.25):
      +        """\
      +        Send break condition. Timed, returns to idle state after given
      +        duration.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self.break_condition = True
      +        time.sleep(duration)
      +        self.break_condition = False
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +    # backwards compatibility / deprecated functions
      +
      +    def flushInput(self):
      +        self.reset_input_buffer()
      +
      +    def flushOutput(self):
      +        self.reset_output_buffer()
      +
      +    def inWaiting(self):
      +        return self.in_waiting
      +
      +    def sendBreak(self, duration=0.25):
      +        self.send_break(duration)
      +
      +    def setRTS(self, value=1):
      +        self.rts = value
      +
      +    def setDTR(self, value=1):
      +        self.dtr = value
      +
      +    def getCTS(self):
      +        return self.cts
      +
      +    def getDSR(self):
      +        return self.dsr
      +
      +    def getRI(self):
      +        return self.ri
      +
      +    def getCD(self):
      +        return self.cd
      +
      +    def setPort(self, port):
      +        self.port = port
      +
      +    @property
      +    def writeTimeout(self):
      +        return self.write_timeout
      +
      +    @writeTimeout.setter
      +    def writeTimeout(self, timeout):
      +        self.write_timeout = timeout
      +
      +    @property
      +    def interCharTimeout(self):
      +        return self.inter_byte_timeout
      +
      +    @interCharTimeout.setter
      +    def interCharTimeout(self, interCharTimeout):
      +        self.inter_byte_timeout = interCharTimeout
      +
      +    def getSettingsDict(self):
      +        return self.get_settings()
      +
      +    def applySettingsDict(self, d):
      +        self.apply_settings(d)
      +
      +    def isOpen(self):
      +        return self.is_open
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +    # additional functionality
      +
      +    def read_all(self):
      +        """\
      +        Read all bytes currently available in the buffer of the OS.
      +        """
      +        return self.read(self.in_waiting)
      +
      +    def read_until(self, expected=LF, size=None):
      +        """\
      +        Read until an expected sequence is found ('\n' by default), the size
      +        is exceeded or until timeout occurs.
      +        """
      +        lenterm = len(expected)
      +        line = bytearray()
      +        timeout = Timeout(self._timeout)
      +        while True:
      +            c = self.read(1)
      +            if c:
      +                line += c
      +                if line[-lenterm:] == expected:
      +                    break
      +                if size is not None and len(line) >= size:
      +                    break
      +            else:
      +                break
      +            if timeout.expired():
      +                break
      +        return bytes(line)
      +
      +    def iread_until(self, *args, **kwargs):
      +        """\
      +        Read lines, implemented as generator. It will raise StopIteration on
      +        timeout (empty read).
      +        """
      +        while True:
      +            line = self.read_until(*args, **kwargs)
      +            if not line:
      +                break
      +            yield line
      +
      +
      +#  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +if __name__ == '__main__':
      +    import sys
      +    s = SerialBase()
      +    sys.stdout.write('port name:  {}\n'.format(s.name))
      +    sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES))
      +    sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES))
      +    sys.stdout.write('parities:   {}\n'.format(s.PARITIES))
      +    sys.stdout.write('stop bits:  {}\n'.format(s.STOPBITS))
      +    sys.stdout.write('{}\n'.format(s))
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialwin32.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialwin32.py
      new file mode 100644
      index 000000000..e7da929ad
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/serialwin32.py
      @@ -0,0 +1,477 @@
      +#! python
      +#
      +# backend for Windows ("win32" incl. 32/64 bit support)
      +#
      +# (C) 2001-2020 Chris Liechti 
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# SPDX-License-Identifier:    BSD-3-Clause
      +#
      +# Initial patch to use ctypes by Giovanni Bajo 
      +
      +from __future__ import absolute_import
      +
      +# pylint: disable=invalid-name,too-few-public-methods
      +import ctypes
      +import time
      +from serial import win32
      +
      +import serial
      +from serial.serialutil import SerialBase, SerialException, to_bytes, PortNotOpenError, SerialTimeoutException
      +
      +
      +class Serial(SerialBase):
      +    """Serial port implementation for Win32 based on ctypes."""
      +
      +    BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
      +                 9600, 19200, 38400, 57600, 115200)
      +
      +    def __init__(self, *args, **kwargs):
      +        self._port_handle = None
      +        self._overlapped_read = None
      +        self._overlapped_write = None
      +        super(Serial, self).__init__(*args, **kwargs)
      +
      +    def open(self):
      +        """\
      +        Open port with current settings. This may throw a SerialException
      +        if the port cannot be opened.
      +        """
      +        if self._port is None:
      +            raise SerialException("Port must be configured before it can be used.")
      +        if self.is_open:
      +            raise SerialException("Port is already open.")
      +        # the "\\.\COMx" format is required for devices other than COM1-COM8
      +        # not all versions of windows seem to support this properly
      +        # so that the first few ports are used with the DOS device name
      +        port = self.name
      +        try:
      +            if port.upper().startswith('COM') and int(port[3:]) > 8:
      +                port = '\\\\.\\' + port
      +        except ValueError:
      +            # for like COMnotanumber
      +            pass
      +        self._port_handle = win32.CreateFile(
      +            port,
      +            win32.GENERIC_READ | win32.GENERIC_WRITE,
      +            0,  # exclusive access
      +            None,  # no security
      +            win32.OPEN_EXISTING,
      +            win32.FILE_ATTRIBUTE_NORMAL | win32.FILE_FLAG_OVERLAPPED,
      +            0)
      +        if self._port_handle == win32.INVALID_HANDLE_VALUE:
      +            self._port_handle = None    # 'cause __del__ is called anyway
      +            raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
      +
      +        try:
      +            self._overlapped_read = win32.OVERLAPPED()
      +            self._overlapped_read.hEvent = win32.CreateEvent(None, 1, 0, None)
      +            self._overlapped_write = win32.OVERLAPPED()
      +            #~ self._overlapped_write.hEvent = win32.CreateEvent(None, 1, 0, None)
      +            self._overlapped_write.hEvent = win32.CreateEvent(None, 0, 0, None)
      +
      +            # Setup a 4k buffer
      +            win32.SetupComm(self._port_handle, 4096, 4096)
      +
      +            # Save original timeout values:
      +            self._orgTimeouts = win32.COMMTIMEOUTS()
      +            win32.GetCommTimeouts(self._port_handle, ctypes.byref(self._orgTimeouts))
      +
      +            self._reconfigure_port()
      +
      +            # Clear buffers:
      +            # Remove anything that was there
      +            win32.PurgeComm(
      +                self._port_handle,
      +                win32.PURGE_TXCLEAR | win32.PURGE_TXABORT |
      +                win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
      +        except:
      +            try:
      +                self._close()
      +            except:
      +                # ignore any exception when closing the port
      +                # also to keep original exception that happened when setting up
      +                pass
      +            self._port_handle = None
      +            raise
      +        else:
      +            self.is_open = True
      +
      +    def _reconfigure_port(self):
      +        """Set communication parameters on opened port."""
      +        if not self._port_handle:
      +            raise SerialException("Can only operate on a valid port handle")
      +
      +        # Set Windows timeout values
      +        # timeouts is a tuple with the following items:
      +        # (ReadIntervalTimeout,ReadTotalTimeoutMultiplier,
      +        #  ReadTotalTimeoutConstant,WriteTotalTimeoutMultiplier,
      +        #  WriteTotalTimeoutConstant)
      +        timeouts = win32.COMMTIMEOUTS()
      +        if self._timeout is None:
      +            pass  # default of all zeros is OK
      +        elif self._timeout == 0:
      +            timeouts.ReadIntervalTimeout = win32.MAXDWORD
      +        else:
      +            timeouts.ReadTotalTimeoutConstant = max(int(self._timeout * 1000), 1)
      +        if self._timeout != 0 and self._inter_byte_timeout is not None:
      +            timeouts.ReadIntervalTimeout = max(int(self._inter_byte_timeout * 1000), 1)
      +
      +        if self._write_timeout is None:
      +            pass
      +        elif self._write_timeout == 0:
      +            timeouts.WriteTotalTimeoutConstant = win32.MAXDWORD
      +        else:
      +            timeouts.WriteTotalTimeoutConstant = max(int(self._write_timeout * 1000), 1)
      +        win32.SetCommTimeouts(self._port_handle, ctypes.byref(timeouts))
      +
      +        win32.SetCommMask(self._port_handle, win32.EV_ERR)
      +
      +        # Setup the connection info.
      +        # Get state and modify it:
      +        comDCB = win32.DCB()
      +        win32.GetCommState(self._port_handle, ctypes.byref(comDCB))
      +        comDCB.BaudRate = self._baudrate
      +
      +        if self._bytesize == serial.FIVEBITS:
      +            comDCB.ByteSize = 5
      +        elif self._bytesize == serial.SIXBITS:
      +            comDCB.ByteSize = 6
      +        elif self._bytesize == serial.SEVENBITS:
      +            comDCB.ByteSize = 7
      +        elif self._bytesize == serial.EIGHTBITS:
      +            comDCB.ByteSize = 8
      +        else:
      +            raise ValueError("Unsupported number of data bits: {!r}".format(self._bytesize))
      +
      +        if self._parity == serial.PARITY_NONE:
      +            comDCB.Parity = win32.NOPARITY
      +            comDCB.fParity = 0  # Disable Parity Check
      +        elif self._parity == serial.PARITY_EVEN:
      +            comDCB.Parity = win32.EVENPARITY
      +            comDCB.fParity = 1  # Enable Parity Check
      +        elif self._parity == serial.PARITY_ODD:
      +            comDCB.Parity = win32.ODDPARITY
      +            comDCB.fParity = 1  # Enable Parity Check
      +        elif self._parity == serial.PARITY_MARK:
      +            comDCB.Parity = win32.MARKPARITY
      +            comDCB.fParity = 1  # Enable Parity Check
      +        elif self._parity == serial.PARITY_SPACE:
      +            comDCB.Parity = win32.SPACEPARITY
      +            comDCB.fParity = 1  # Enable Parity Check
      +        else:
      +            raise ValueError("Unsupported parity mode: {!r}".format(self._parity))
      +
      +        if self._stopbits == serial.STOPBITS_ONE:
      +            comDCB.StopBits = win32.ONESTOPBIT
      +        elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
      +            comDCB.StopBits = win32.ONE5STOPBITS
      +        elif self._stopbits == serial.STOPBITS_TWO:
      +            comDCB.StopBits = win32.TWOSTOPBITS
      +        else:
      +            raise ValueError("Unsupported number of stop bits: {!r}".format(self._stopbits))
      +
      +        comDCB.fBinary = 1  # Enable Binary Transmission
      +        # Char. w/ Parity-Err are replaced with 0xff (if fErrorChar is set to TRUE)
      +        if self._rs485_mode is None:
      +            if self._rtscts:
      +                comDCB.fRtsControl = win32.RTS_CONTROL_HANDSHAKE
      +            else:
      +                comDCB.fRtsControl = win32.RTS_CONTROL_ENABLE if self._rts_state else win32.RTS_CONTROL_DISABLE
      +            comDCB.fOutxCtsFlow = self._rtscts
      +        else:
      +            # checks for unsupported settings
      +            # XXX verify if platform really does not have a setting for those
      +            if not self._rs485_mode.rts_level_for_tx:
      +                raise ValueError(
      +                    'Unsupported value for RS485Settings.rts_level_for_tx: {!r} (only True is allowed)'.format(
      +                        self._rs485_mode.rts_level_for_tx,))
      +            if self._rs485_mode.rts_level_for_rx:
      +                raise ValueError(
      +                    'Unsupported value for RS485Settings.rts_level_for_rx: {!r} (only False is allowed)'.format(
      +                        self._rs485_mode.rts_level_for_rx,))
      +            if self._rs485_mode.delay_before_tx is not None:
      +                raise ValueError(
      +                    'Unsupported value for RS485Settings.delay_before_tx: {!r} (only None is allowed)'.format(
      +                        self._rs485_mode.delay_before_tx,))
      +            if self._rs485_mode.delay_before_rx is not None:
      +                raise ValueError(
      +                    'Unsupported value for RS485Settings.delay_before_rx: {!r} (only None is allowed)'.format(
      +                        self._rs485_mode.delay_before_rx,))
      +            if self._rs485_mode.loopback:
      +                raise ValueError(
      +                    'Unsupported value for RS485Settings.loopback: {!r} (only False is allowed)'.format(
      +                        self._rs485_mode.loopback,))
      +            comDCB.fRtsControl = win32.RTS_CONTROL_TOGGLE
      +            comDCB.fOutxCtsFlow = 0
      +
      +        if self._dsrdtr:
      +            comDCB.fDtrControl = win32.DTR_CONTROL_HANDSHAKE
      +        else:
      +            comDCB.fDtrControl = win32.DTR_CONTROL_ENABLE if self._dtr_state else win32.DTR_CONTROL_DISABLE
      +        comDCB.fOutxDsrFlow = self._dsrdtr
      +        comDCB.fOutX = self._xonxoff
      +        comDCB.fInX = self._xonxoff
      +        comDCB.fNull = 0
      +        comDCB.fErrorChar = 0
      +        comDCB.fAbortOnError = 0
      +        comDCB.XonChar = serial.XON
      +        comDCB.XoffChar = serial.XOFF
      +
      +        if not win32.SetCommState(self._port_handle, ctypes.byref(comDCB)):
      +            raise SerialException(
      +                'Cannot configure port, something went wrong. '
      +                'Original message: {!r}'.format(ctypes.WinError()))
      +
      +    #~ def __del__(self):
      +        #~ self.close()
      +
      +    def _close(self):
      +        """internal close port helper"""
      +        if self._port_handle is not None:
      +            # Restore original timeout values:
      +            win32.SetCommTimeouts(self._port_handle, self._orgTimeouts)
      +            if self._overlapped_read is not None:
      +                self.cancel_read()
      +                win32.CloseHandle(self._overlapped_read.hEvent)
      +                self._overlapped_read = None
      +            if self._overlapped_write is not None:
      +                self.cancel_write()
      +                win32.CloseHandle(self._overlapped_write.hEvent)
      +                self._overlapped_write = None
      +            win32.CloseHandle(self._port_handle)
      +            self._port_handle = None
      +
      +    def close(self):
      +        """Close port"""
      +        if self.is_open:
      +            self._close()
      +            self.is_open = False
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +
      +    @property
      +    def in_waiting(self):
      +        """Return the number of bytes currently in the input buffer."""
      +        flags = win32.DWORD()
      +        comstat = win32.COMSTAT()
      +        if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
      +            raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
      +        return comstat.cbInQue
      +
      +    def read(self, size=1):
      +        """\
      +        Read size bytes from the serial port. If a timeout is set it may
      +        return less characters as requested. With no timeout it will block
      +        until the requested number of bytes is read.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if size > 0:
      +            win32.ResetEvent(self._overlapped_read.hEvent)
      +            flags = win32.DWORD()
      +            comstat = win32.COMSTAT()
      +            if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
      +                raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
      +            n = min(comstat.cbInQue, size) if self.timeout == 0 else size
      +            if n > 0:
      +                buf = ctypes.create_string_buffer(n)
      +                rc = win32.DWORD()
      +                read_ok = win32.ReadFile(
      +                    self._port_handle,
      +                    buf,
      +                    n,
      +                    ctypes.byref(rc),
      +                    ctypes.byref(self._overlapped_read))
      +                if not read_ok and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
      +                    raise SerialException("ReadFile failed ({!r})".format(ctypes.WinError()))
      +                result_ok = win32.GetOverlappedResult(
      +                    self._port_handle,
      +                    ctypes.byref(self._overlapped_read),
      +                    ctypes.byref(rc),
      +                    True)
      +                if not result_ok:
      +                    if win32.GetLastError() != win32.ERROR_OPERATION_ABORTED:
      +                        raise SerialException("GetOverlappedResult failed ({!r})".format(ctypes.WinError()))
      +                read = buf.raw[:rc.value]
      +            else:
      +                read = bytes()
      +        else:
      +            read = bytes()
      +        return bytes(read)
      +
      +    def write(self, data):
      +        """Output the given byte string over the serial port."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        #~ if not isinstance(data, (bytes, bytearray)):
      +            #~ raise TypeError('expected %s or bytearray, got %s' % (bytes, type(data)))
      +        # convert data (needed in case of memoryview instance: Py 3.1 io lib), ctypes doesn't like memoryview
      +        data = to_bytes(data)
      +        if data:
      +            #~ win32event.ResetEvent(self._overlapped_write.hEvent)
      +            n = win32.DWORD()
      +            success = win32.WriteFile(self._port_handle, data, len(data), ctypes.byref(n), self._overlapped_write)
      +            if self._write_timeout != 0:  # if blocking (None) or w/ write timeout (>0)
      +                if not success and win32.GetLastError() not in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
      +                    raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError()))
      +
      +                # Wait for the write to complete.
      +                #~ win32.WaitForSingleObject(self._overlapped_write.hEvent, win32.INFINITE)
      +                win32.GetOverlappedResult(self._port_handle, self._overlapped_write, ctypes.byref(n), True)
      +                if win32.GetLastError() == win32.ERROR_OPERATION_ABORTED:
      +                    return n.value  # canceled IO is no error
      +                if n.value != len(data):
      +                    raise SerialTimeoutException('Write timeout')
      +                return n.value
      +            else:
      +                errorcode = win32.ERROR_SUCCESS if success else win32.GetLastError()
      +                if errorcode in (win32.ERROR_INVALID_USER_BUFFER, win32.ERROR_NOT_ENOUGH_MEMORY,
      +                                 win32.ERROR_OPERATION_ABORTED):
      +                    return 0
      +                elif errorcode in (win32.ERROR_SUCCESS, win32.ERROR_IO_PENDING):
      +                    # no info on true length provided by OS function in async mode
      +                    return len(data)
      +                else:
      +                    raise SerialException("WriteFile failed ({!r})".format(ctypes.WinError()))
      +        else:
      +            return 0
      +
      +    def flush(self):
      +        """\
      +        Flush of file like objects. In this case, wait until all data
      +        is written.
      +        """
      +        while self.out_waiting:
      +            time.sleep(0.05)
      +        # XXX could also use WaitCommEvent with mask EV_TXEMPTY, but it would
      +        # require overlapped IO and it's also only possible to set a single mask
      +        # on the port---
      +
      +    def reset_input_buffer(self):
      +        """Clear input buffer, discarding all that is in the buffer."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        win32.PurgeComm(self._port_handle, win32.PURGE_RXCLEAR | win32.PURGE_RXABORT)
      +
      +    def reset_output_buffer(self):
      +        """\
      +        Clear output buffer, aborting the current output and discarding all
      +        that is in the buffer.
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        win32.PurgeComm(self._port_handle, win32.PURGE_TXCLEAR | win32.PURGE_TXABORT)
      +
      +    def _update_break_state(self):
      +        """Set break: Controls TXD. When active, to transmitting is possible."""
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if self._break_state:
      +            win32.SetCommBreak(self._port_handle)
      +        else:
      +            win32.ClearCommBreak(self._port_handle)
      +
      +    def _update_rts_state(self):
      +        """Set terminal status line: Request To Send"""
      +        if self._rts_state:
      +            win32.EscapeCommFunction(self._port_handle, win32.SETRTS)
      +        else:
      +            win32.EscapeCommFunction(self._port_handle, win32.CLRRTS)
      +
      +    def _update_dtr_state(self):
      +        """Set terminal status line: Data Terminal Ready"""
      +        if self._dtr_state:
      +            win32.EscapeCommFunction(self._port_handle, win32.SETDTR)
      +        else:
      +            win32.EscapeCommFunction(self._port_handle, win32.CLRDTR)
      +
      +    def _GetCommModemStatus(self):
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        stat = win32.DWORD()
      +        win32.GetCommModemStatus(self._port_handle, ctypes.byref(stat))
      +        return stat.value
      +
      +    @property
      +    def cts(self):
      +        """Read terminal status line: Clear To Send"""
      +        return win32.MS_CTS_ON & self._GetCommModemStatus() != 0
      +
      +    @property
      +    def dsr(self):
      +        """Read terminal status line: Data Set Ready"""
      +        return win32.MS_DSR_ON & self._GetCommModemStatus() != 0
      +
      +    @property
      +    def ri(self):
      +        """Read terminal status line: Ring Indicator"""
      +        return win32.MS_RING_ON & self._GetCommModemStatus() != 0
      +
      +    @property
      +    def cd(self):
      +        """Read terminal status line: Carrier Detect"""
      +        return win32.MS_RLSD_ON & self._GetCommModemStatus() != 0
      +
      +    # - - platform specific - - - -
      +
      +    def set_buffer_size(self, rx_size=4096, tx_size=None):
      +        """\
      +        Recommend a buffer size to the driver (device driver can ignore this
      +        value). Must be called after the port is opened.
      +        """
      +        if tx_size is None:
      +            tx_size = rx_size
      +        win32.SetupComm(self._port_handle, rx_size, tx_size)
      +
      +    def set_output_flow_control(self, enable=True):
      +        """\
      +        Manually control flow - when software flow control is enabled.
      +        This will do the same as if XON (true) or XOFF (false) are received
      +        from the other device and control the transmission accordingly.
      +        WARNING: this function is not portable to different platforms!
      +        """
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        if enable:
      +            win32.EscapeCommFunction(self._port_handle, win32.SETXON)
      +        else:
      +            win32.EscapeCommFunction(self._port_handle, win32.SETXOFF)
      +
      +    @property
      +    def out_waiting(self):
      +        """Return how many bytes the in the outgoing buffer"""
      +        flags = win32.DWORD()
      +        comstat = win32.COMSTAT()
      +        if not win32.ClearCommError(self._port_handle, ctypes.byref(flags), ctypes.byref(comstat)):
      +            raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
      +        return comstat.cbOutQue
      +
      +    def _cancel_overlapped_io(self, overlapped):
      +        """Cancel a blocking read operation, may be called from other thread"""
      +        # check if read operation is pending
      +        rc = win32.DWORD()
      +        err = win32.GetOverlappedResult(
      +            self._port_handle,
      +            ctypes.byref(overlapped),
      +            ctypes.byref(rc),
      +            False)
      +        if not err and win32.GetLastError() in (win32.ERROR_IO_PENDING, win32.ERROR_IO_INCOMPLETE):
      +            # cancel, ignoring any errors (e.g. it may just have finished on its own)
      +            win32.CancelIoEx(self._port_handle, overlapped)
      +
      +    def cancel_read(self):
      +        """Cancel a blocking read operation, may be called from other thread"""
      +        self._cancel_overlapped_io(self._overlapped_read)
      +
      +    def cancel_write(self):
      +        """Cancel a blocking write operation, may be called from other thread"""
      +        self._cancel_overlapped_io(self._overlapped_write)
      +
      +    @SerialBase.exclusive.setter
      +    def exclusive(self, exclusive):
      +        """Change the exclusive access setting."""
      +        if exclusive is not None and not exclusive:
      +            raise ValueError('win32 only supports exclusive access (not: {})'.format(exclusive))
      +        else:
      +            serial.SerialBase.exclusive.__set__(self, exclusive)
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/threaded/__init__.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/threaded/__init__.py
      new file mode 100644
      index 000000000..b8940b6d3
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/threaded/__init__.py
      @@ -0,0 +1,297 @@
      +#!/usr/bin/env python3
      +#
      +# Working with threading and pySerial
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2015-2016 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +"""\
      +Support threading with serial ports.
      +"""
      +from __future__ import absolute_import
      +
      +import serial
      +import threading
      +
      +
      +class Protocol(object):
      +    """\
      +    Protocol as used by the ReaderThread. This base class provides empty
      +    implementations of all methods.
      +    """
      +
      +    def connection_made(self, transport):
      +        """Called when reader thread is started"""
      +
      +    def data_received(self, data):
      +        """Called with snippets received from the serial port"""
      +
      +    def connection_lost(self, exc):
      +        """\
      +        Called when the serial port is closed or the reader loop terminated
      +        otherwise.
      +        """
      +        if isinstance(exc, Exception):
      +            raise exc
      +
      +
      +class Packetizer(Protocol):
      +    """
      +    Read binary packets from serial port. Packets are expected to be terminated
      +    with a TERMINATOR byte (null byte by default).
      +
      +    The class also keeps track of the transport.
      +    """
      +
      +    TERMINATOR = b'\0'
      +
      +    def __init__(self):
      +        self.buffer = bytearray()
      +        self.transport = None
      +
      +    def connection_made(self, transport):
      +        """Store transport"""
      +        self.transport = transport
      +
      +    def connection_lost(self, exc):
      +        """Forget transport"""
      +        self.transport = None
      +        super(Packetizer, self).connection_lost(exc)
      +
      +    def data_received(self, data):
      +        """Buffer received data, find TERMINATOR, call handle_packet"""
      +        self.buffer.extend(data)
      +        while self.TERMINATOR in self.buffer:
      +            packet, self.buffer = self.buffer.split(self.TERMINATOR, 1)
      +            self.handle_packet(packet)
      +
      +    def handle_packet(self, packet):
      +        """Process packets - to be overridden by subclassing"""
      +        raise NotImplementedError('please implement functionality in handle_packet')
      +
      +
      +class FramedPacket(Protocol):
      +    """
      +    Read binary packets. Packets are expected to have a start and stop marker.
      +
      +    The class also keeps track of the transport.
      +    """
      +
      +    START = b'('
      +    STOP = b')'
      +
      +    def __init__(self):
      +        self.packet = bytearray()
      +        self.in_packet = False
      +        self.transport = None
      +
      +    def connection_made(self, transport):
      +        """Store transport"""
      +        self.transport = transport
      +
      +    def connection_lost(self, exc):
      +        """Forget transport"""
      +        self.transport = None
      +        self.in_packet = False
      +        del self.packet[:]
      +        super(FramedPacket, self).connection_lost(exc)
      +
      +    def data_received(self, data):
      +        """Find data enclosed in START/STOP, call handle_packet"""
      +        for byte in serial.iterbytes(data):
      +            if byte == self.START:
      +                self.in_packet = True
      +            elif byte == self.STOP:
      +                self.in_packet = False
      +                self.handle_packet(bytes(self.packet)) # make read-only copy
      +                del self.packet[:]
      +            elif self.in_packet:
      +                self.packet.extend(byte)
      +            else:
      +                self.handle_out_of_packet_data(byte)
      +
      +    def handle_packet(self, packet):
      +        """Process packets - to be overridden by subclassing"""
      +        raise NotImplementedError('please implement functionality in handle_packet')
      +
      +    def handle_out_of_packet_data(self, data):
      +        """Process data that is received outside of packets"""
      +        pass
      +
      +
      +class LineReader(Packetizer):
      +    """
      +    Read and write (Unicode) lines from/to serial port.
      +    The encoding is applied.
      +    """
      +
      +    TERMINATOR = b'\r\n'
      +    ENCODING = 'utf-8'
      +    UNICODE_HANDLING = 'replace'
      +
      +    def handle_packet(self, packet):
      +        self.handle_line(packet.decode(self.ENCODING, self.UNICODE_HANDLING))
      +
      +    def handle_line(self, line):
      +        """Process one line - to be overridden by subclassing"""
      +        raise NotImplementedError('please implement functionality in handle_line')
      +
      +    def write_line(self, text):
      +        """
      +        Write text to the transport. ``text`` is a Unicode string and the encoding
      +        is applied before sending ans also the newline is append.
      +        """
      +        # + is not the best choice but bytes does not support % or .format in py3 and we want a single write call
      +        self.transport.write(text.encode(self.ENCODING, self.UNICODE_HANDLING) + self.TERMINATOR)
      +
      +
      +class ReaderThread(threading.Thread):
      +    """\
      +    Implement a serial port read loop and dispatch to a Protocol instance (like
      +    the asyncio.Protocol) but do it with threads.
      +
      +    Calls to close() will close the serial port but it is also possible to just
      +    stop() this thread and continue the serial port instance otherwise.
      +    """
      +
      +    def __init__(self, serial_instance, protocol_factory):
      +        """\
      +        Initialize thread.
      +
      +        Note that the serial_instance' timeout is set to one second!
      +        Other settings are not changed.
      +        """
      +        super(ReaderThread, self).__init__()
      +        self.daemon = True
      +        self.serial = serial_instance
      +        self.protocol_factory = protocol_factory
      +        self.alive = True
      +        self._lock = threading.Lock()
      +        self._connection_made = threading.Event()
      +        self.protocol = None
      +
      +    def stop(self):
      +        """Stop the reader thread"""
      +        self.alive = False
      +        if hasattr(self.serial, 'cancel_read'):
      +            self.serial.cancel_read()
      +        self.join(2)
      +
      +    def run(self):
      +        """Reader loop"""
      +        if not hasattr(self.serial, 'cancel_read'):
      +            self.serial.timeout = 1
      +        self.protocol = self.protocol_factory()
      +        try:
      +            self.protocol.connection_made(self)
      +        except Exception as e:
      +            self.alive = False
      +            self.protocol.connection_lost(e)
      +            self._connection_made.set()
      +            return
      +        error = None
      +        self._connection_made.set()
      +        while self.alive and self.serial.is_open:
      +            try:
      +                # read all that is there or wait for one byte (blocking)
      +                data = self.serial.read(self.serial.in_waiting or 1)
      +            except serial.SerialException as e:
      +                # probably some I/O problem such as disconnected USB serial
      +                # adapters -> exit
      +                error = e
      +                break
      +            else:
      +                if data:
      +                    # make a separated try-except for called user code
      +                    try:
      +                        self.protocol.data_received(data)
      +                    except Exception as e:
      +                        error = e
      +                        break
      +        self.alive = False
      +        self.protocol.connection_lost(error)
      +        self.protocol = None
      +
      +    def write(self, data):
      +        """Thread safe writing (uses lock)"""
      +        with self._lock:
      +            return self.serial.write(data)
      +
      +    def close(self):
      +        """Close the serial port and exit reader thread (uses lock)"""
      +        # use the lock to let other threads finish writing
      +        with self._lock:
      +            # first stop reading, so that closing can be done on idle port
      +            self.stop()
      +            self.serial.close()
      +
      +    def connect(self):
      +        """
      +        Wait until connection is set up and return the transport and protocol
      +        instances.
      +        """
      +        if self.alive:
      +            self._connection_made.wait()
      +            if not self.alive:
      +                raise RuntimeError('connection_lost already called')
      +            return (self, self.protocol)
      +        else:
      +            raise RuntimeError('already stopped')
      +
      +    # - -  context manager, returns protocol
      +
      +    def __enter__(self):
      +        """\
      +        Enter context handler. May raise RuntimeError in case the connection
      +        could not be created.
      +        """
      +        self.start()
      +        self._connection_made.wait()
      +        if not self.alive:
      +            raise RuntimeError('connection_lost already called')
      +        return self.protocol
      +
      +    def __exit__(self, exc_type, exc_val, exc_tb):
      +        """Leave context: close port"""
      +        self.close()
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +# test
      +if __name__ == '__main__':
      +    # pylint: disable=wrong-import-position
      +    import sys
      +    import time
      +    import traceback
      +
      +    #~ PORT = 'spy:///dev/ttyUSB0'
      +    PORT = 'loop://'
      +
      +    class PrintLines(LineReader):
      +        def connection_made(self, transport):
      +            super(PrintLines, self).connection_made(transport)
      +            sys.stdout.write('port opened\n')
      +            self.write_line('hello world')
      +
      +        def handle_line(self, data):
      +            sys.stdout.write('line received: {!r}\n'.format(data))
      +
      +        def connection_lost(self, exc):
      +            if exc:
      +                traceback.print_exc(exc)
      +            sys.stdout.write('port closed\n')
      +
      +    ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1)
      +    with ReaderThread(ser, PrintLines) as protocol:
      +        protocol.write_line('hello')
      +        time.sleep(2)
      +
      +    # alternative usage
      +    ser = serial.serial_for_url(PORT, baudrate=115200, timeout=1)
      +    t = ReaderThread(ser, PrintLines)
      +    t.start()
      +    transport, protocol = t.connect()
      +    protocol.write_line('hello')
      +    time.sleep(2)
      +    t.close()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/__init__.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/__init__.py
      new file mode 100644
      index 000000000..e69de29bb
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/hexlify_codec.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/hexlify_codec.py
      new file mode 100644
      index 000000000..bd8f6b0df
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/hexlify_codec.py
      @@ -0,0 +1,126 @@
      +#! python
      +#
      +# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2015-2016 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +"""\
      +Python 'hex' Codec - 2-digit hex with spaces content transfer encoding.
      +
      +Encode and decode may be a bit missleading at first sight...
      +
      +The textual representation is a hex dump: e.g. "40 41"
      +The "encoded" data of this is the binary form, e.g. b"@A"
      +
      +Therefore decoding is binary to text and thus converting binary data to hex dump.
      +
      +"""
      +
      +from __future__ import absolute_import
      +
      +import codecs
      +import serial
      +
      +
      +try:
      +    unicode
      +except (NameError, AttributeError):
      +    unicode = str       # for Python 3, pylint: disable=redefined-builtin,invalid-name
      +
      +
      +HEXDIGITS = '0123456789ABCDEF'
      +
      +
      +# Codec APIs
      +
      +def hex_encode(data, errors='strict'):
      +    """'40 41 42' -> b'@ab'"""
      +    return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data))
      +
      +
      +def hex_decode(data, errors='strict'):
      +    """b'@ab' -> '40 41 42'"""
      +    return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data))
      +
      +
      +class Codec(codecs.Codec):
      +    def encode(self, data, errors='strict'):
      +        """'40 41 42' -> b'@ab'"""
      +        return serial.to_bytes([int(h, 16) for h in data.split()])
      +
      +    def decode(self, data, errors='strict'):
      +        """b'@ab' -> '40 41 42'"""
      +        return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
      +
      +
      +class IncrementalEncoder(codecs.IncrementalEncoder):
      +    """Incremental hex encoder"""
      +
      +    def __init__(self, errors='strict'):
      +        self.errors = errors
      +        self.state = 0
      +
      +    def reset(self):
      +        self.state = 0
      +
      +    def getstate(self):
      +        return self.state
      +
      +    def setstate(self, state):
      +        self.state = state
      +
      +    def encode(self, data, final=False):
      +        """\
      +        Incremental encode, keep track of digits and emit a byte when a pair
      +        of hex digits is found. The space is optional unless the error
      +        handling is defined to be 'strict'.
      +        """
      +        state = self.state
      +        encoded = []
      +        for c in data.upper():
      +            if c in HEXDIGITS:
      +                z = HEXDIGITS.index(c)
      +                if state:
      +                    encoded.append(z + (state & 0xf0))
      +                    state = 0
      +                else:
      +                    state = 0x100 + (z << 4)
      +            elif c == ' ':      # allow spaces to separate values
      +                if state and self.errors == 'strict':
      +                    raise UnicodeError('odd number of hex digits')
      +                state = 0
      +            else:
      +                if self.errors == 'strict':
      +                    raise UnicodeError('non-hex digit found: {!r}'.format(c))
      +        self.state = state
      +        return serial.to_bytes(encoded)
      +
      +
      +class IncrementalDecoder(codecs.IncrementalDecoder):
      +    """Incremental decoder"""
      +    def decode(self, data, final=False):
      +        return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
      +
      +
      +class StreamWriter(Codec, codecs.StreamWriter):
      +    """Combination of hexlify codec and StreamWriter"""
      +
      +
      +class StreamReader(Codec, codecs.StreamReader):
      +    """Combination of hexlify codec and StreamReader"""
      +
      +
      +def getregentry():
      +    """encodings module API"""
      +    return codecs.CodecInfo(
      +        name='hexlify',
      +        encode=hex_encode,
      +        decode=hex_decode,
      +        incrementalencoder=IncrementalEncoder,
      +        incrementaldecoder=IncrementalDecoder,
      +        streamwriter=StreamWriter,
      +        streamreader=StreamReader,
      +        #~ _is_text_encoding=True,
      +    )
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports.py
      new file mode 100644
      index 000000000..0d7e3d41a
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports.py
      @@ -0,0 +1,110 @@
      +#!/usr/bin/env python
      +#
      +# Serial port enumeration. Console tool and backend selection.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2011-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +"""\
      +This module will provide a function called comports that returns an
      +iterable (generator or list) that will enumerate available com ports. Note that
      +on some systems non-existent ports may be listed.
      +
      +Additionally a grep function is supplied that can be used to search for ports
      +based on their descriptions or hardware ID.
      +"""
      +
      +from __future__ import absolute_import
      +
      +import sys
      +import os
      +import re
      +
      +# chose an implementation, depending on os
      +#~ if sys.platform == 'cli':
      +#~ else:
      +if os.name == 'nt':  # sys.platform == 'win32':
      +    from serial.tools.list_ports_windows import comports
      +elif os.name == 'posix':
      +    from serial.tools.list_ports_posix import comports
      +#~ elif os.name == 'java':
      +else:
      +    raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +
      +
      +def grep(regexp, include_links=False):
      +    """\
      +    Search for ports using a regular expression. Port name, description and
      +    hardware ID are searched. The function returns an iterable that returns the
      +    same tuples as comport() would do.
      +    """
      +    r = re.compile(regexp, re.I)
      +    for info in comports(include_links):
      +        port, desc, hwid = info
      +        if r.search(port) or r.search(desc) or r.search(hwid):
      +            yield info
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +def main():
      +    import argparse
      +
      +    parser = argparse.ArgumentParser(description='Serial port enumeration')
      +
      +    parser.add_argument(
      +        'regexp',
      +        nargs='?',
      +        help='only show ports that match this regex')
      +
      +    parser.add_argument(
      +        '-v', '--verbose',
      +        action='store_true',
      +        help='show more messages')
      +
      +    parser.add_argument(
      +        '-q', '--quiet',
      +        action='store_true',
      +        help='suppress all messages')
      +
      +    parser.add_argument(
      +        '-n',
      +        type=int,
      +        help='only output the N-th entry')
      +
      +    parser.add_argument(
      +        '-s', '--include-links',
      +        action='store_true',
      +        help='include entries that are symlinks to real devices')
      +
      +    args = parser.parse_args()
      +
      +    hits = 0
      +    # get iteraror w/ or w/o filter
      +    if args.regexp:
      +        if not args.quiet:
      +            sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp))
      +        iterator = sorted(grep(args.regexp, include_links=args.include_links))
      +    else:
      +        iterator = sorted(comports(include_links=args.include_links))
      +    # list them
      +    for n, (port, desc, hwid) in enumerate(iterator, 1):
      +        if args.n is None or args.n == n:
      +            sys.stdout.write("{:20}\n".format(port))
      +            if args.verbose:
      +                sys.stdout.write("    desc: {}\n".format(desc))
      +                sys.stdout.write("    hwid: {}\n".format(hwid))
      +        hits += 1
      +    if not args.quiet:
      +        if hits:
      +            sys.stderr.write("{} ports found\n".format(hits))
      +        else:
      +            sys.stderr.write("no ports found\n")
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +# test
      +if __name__ == '__main__':
      +    main()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_common.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_common.py
      new file mode 100644
      index 000000000..617f3dc10
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_common.py
      @@ -0,0 +1,121 @@
      +#!/usr/bin/env python
      +#
      +# This is a helper module for the various platform dependent list_port
      +# implementations.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +import re
      +import glob
      +import os
      +import os.path
      +
      +
      +def numsplit(text):
      +    """\
      +    Convert string into a list of texts and numbers in order to support a
      +    natural sorting.
      +    """
      +    result = []
      +    for group in re.split(r'(\d+)', text):
      +        if group:
      +            try:
      +                group = int(group)
      +            except ValueError:
      +                pass
      +            result.append(group)
      +    return result
      +
      +
      +class ListPortInfo(object):
      +    """Info collection base class for serial ports"""
      +
      +    def __init__(self, device, skip_link_detection=False):
      +        self.device = device
      +        self.name = os.path.basename(device)
      +        self.description = 'n/a'
      +        self.hwid = 'n/a'
      +        # USB specific data
      +        self.vid = None
      +        self.pid = None
      +        self.serial_number = None
      +        self.location = None
      +        self.manufacturer = None
      +        self.product = None
      +        self.interface = None
      +        # special handling for links
      +        if not skip_link_detection and device is not None and os.path.islink(device):
      +            self.hwid = 'LINK={}'.format(os.path.realpath(device))
      +
      +    def usb_description(self):
      +        """return a short string to name the port based on USB info"""
      +        if self.interface is not None:
      +            return '{} - {}'.format(self.product, self.interface)
      +        elif self.product is not None:
      +            return self.product
      +        else:
      +            return self.name
      +
      +    def usb_info(self):
      +        """return a string with USB related information about device"""
      +        return 'USB VID:PID={:04X}:{:04X}{}{}'.format(
      +            self.vid or 0,
      +            self.pid or 0,
      +            ' SER={}'.format(self.serial_number) if self.serial_number is not None else '',
      +            ' LOCATION={}'.format(self.location) if self.location is not None else '')
      +
      +    def apply_usb_info(self):
      +        """update description and hwid from USB data"""
      +        self.description = self.usb_description()
      +        self.hwid = self.usb_info()
      +
      +    def __eq__(self, other):
      +        return isinstance(other, ListPortInfo) and self.device == other.device
      +
      +    def __hash__(self):
      +        return hash(self.device)
      +
      +    def __lt__(self, other):
      +        if not isinstance(other, ListPortInfo):
      +            raise TypeError('unorderable types: {}() and {}()'.format(
      +                type(self).__name__,
      +                type(other).__name__))
      +        return numsplit(self.device) < numsplit(other.device)
      +
      +    def __str__(self):
      +        return '{} - {}'.format(self.device, self.description)
      +
      +    def __getitem__(self, index):
      +        """Item access: backwards compatible -> (port, desc, hwid)"""
      +        if index == 0:
      +            return self.device
      +        elif index == 1:
      +            return self.description
      +        elif index == 2:
      +            return self.hwid
      +        else:
      +            raise IndexError('{} > 2'.format(index))
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +def list_links(devices):
      +    """\
      +    search all /dev devices and look for symlinks to known ports already
      +    listed in devices.
      +    """
      +    links = []
      +    for device in glob.glob('/dev/*'):
      +        if os.path.islink(device) and os.path.realpath(device) in devices:
      +            links.append(device)
      +    return links
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +# test
      +if __name__ == '__main__':
      +    print(ListPortInfo('dummy'))
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_linux.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_linux.py
      new file mode 100644
      index 000000000..c8c1cfc0d
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_linux.py
      @@ -0,0 +1,109 @@
      +#!/usr/bin/env python
      +#
      +# This is a module that gathers a list of serial ports including details on
      +# GNU/Linux systems.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2011-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +import glob
      +import os
      +from serial.tools import list_ports_common
      +
      +
      +class SysFS(list_ports_common.ListPortInfo):
      +    """Wrapper for easy sysfs access and device info"""
      +
      +    def __init__(self, device):
      +        super(SysFS, self).__init__(device)
      +        # special handling for links
      +        if device is not None and os.path.islink(device):
      +            device = os.path.realpath(device)
      +            is_link = True
      +        else:
      +            is_link = False
      +        self.usb_device_path = None
      +        if os.path.exists('/sys/class/tty/{}/device'.format(self.name)):
      +            self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name))
      +            self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem')))
      +        else:
      +            self.device_path = None
      +            self.subsystem = None
      +        # check device type
      +        if self.subsystem == 'usb-serial':
      +            self.usb_interface_path = os.path.dirname(self.device_path)
      +        elif self.subsystem == 'usb':
      +            self.usb_interface_path = self.device_path
      +        else:
      +            self.usb_interface_path = None
      +        # fill-in info for USB devices
      +        if self.usb_interface_path is not None:
      +            self.usb_device_path = os.path.dirname(self.usb_interface_path)
      +
      +            try:
      +                num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces'))
      +            except ValueError:
      +                num_if = 1
      +
      +            self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16)
      +            self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16)
      +            self.serial_number = self.read_line(self.usb_device_path, 'serial')
      +            if num_if > 1:  # multi interface devices like FT4232
      +                self.location = os.path.basename(self.usb_interface_path)
      +            else:
      +                self.location = os.path.basename(self.usb_device_path)
      +
      +            self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer')
      +            self.product = self.read_line(self.usb_device_path, 'product')
      +            self.interface = self.read_line(self.usb_interface_path, 'interface')
      +
      +        if self.subsystem in ('usb', 'usb-serial'):
      +            self.apply_usb_info()
      +        #~ elif self.subsystem in ('pnp', 'amba'):  # PCI based devices, raspi
      +        elif self.subsystem == 'pnp':  # PCI based devices
      +            self.description = self.name
      +            self.hwid = self.read_line(self.device_path, 'id')
      +        elif self.subsystem == 'amba':  # raspi
      +            self.description = self.name
      +            self.hwid = os.path.basename(self.device_path)
      +
      +        if is_link:
      +            self.hwid += ' LINK={}'.format(device)
      +
      +    def read_line(self, *args):
      +        """\
      +        Helper function to read a single line from a file.
      +        One or more parameters are allowed, they are joined with os.path.join.
      +        Returns None on errors..
      +        """
      +        try:
      +            with open(os.path.join(*args)) as f:
      +                line = f.readline().strip()
      +            return line
      +        except IOError:
      +            return None
      +
      +
      +def comports(include_links=False):
      +    devices = glob.glob('/dev/ttyS*')           # built-in serial ports
      +    devices.extend(glob.glob('/dev/ttyUSB*'))   # usb-serial with own driver
      +    devices.extend(glob.glob('/dev/ttyXRUSB*')) # xr-usb-serial port exar (DELL Edge 3001)
      +    devices.extend(glob.glob('/dev/ttyACM*'))   # usb-serial with CDC-ACM profile
      +    devices.extend(glob.glob('/dev/ttyAMA*'))   # ARM internal port (raspi)
      +    devices.extend(glob.glob('/dev/rfcomm*'))   # BT serial devices
      +    devices.extend(glob.glob('/dev/ttyAP*'))    # Advantech multi-port serial controllers
      +    if include_links:
      +        devices.extend(list_ports_common.list_links(devices))
      +    return [info
      +            for info in [SysFS(d) for d in devices]
      +            if info.subsystem != "platform"]    # hide non-present internal serial ports
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +# test
      +if __name__ == '__main__':
      +    for info in sorted(comports()):
      +        print("{0}: {0.subsystem}".format(info))
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_osx.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_osx.py
      new file mode 100644
      index 000000000..51b4e8c04
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_osx.py
      @@ -0,0 +1,299 @@
      +#!/usr/bin/env python
      +#
      +# This is a module that gathers a list of serial ports including details on OSX
      +#
      +# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools
      +# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston
      +# and modifications by cliechti, hoihu, hardkrash
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2013-2020
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +
      +# List all of the callout devices in OS/X by querying IOKit.
      +
      +# See the following for a reference of how to do this:
      +# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD
      +
      +# More help from darwin_hid.py
      +
      +# Also see the 'IORegistryExplorer' for an idea of what we are actually searching
      +
      +from __future__ import absolute_import
      +
      +import ctypes
      +
      +from serial.tools import list_ports_common
      +
      +iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
      +cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
      +
      +# kIOMasterPortDefault is no longer exported in BigSur but no biggie, using NULL works just the same
      +kIOMasterPortDefault = 0 # WAS: ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
      +kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault")
      +
      +kCFStringEncodingMacRoman = 0
      +kCFStringEncodingUTF8 = 0x08000100
      +
      +# defined in `IOKit/usb/USBSpec.h`
      +kUSBVendorString = 'USB Vendor Name'
      +kUSBSerialNumberString = 'USB Serial Number'
      +
      +# `io_name_t` defined as `typedef char io_name_t[128];`
      +# in `device/device_types.h`
      +io_name_size = 128
      +
      +# defined in `mach/kern_return.h`
      +KERN_SUCCESS = 0
      +# kern_return_t defined as `typedef int kern_return_t;` in `mach/i386/kern_return.h`
      +kern_return_t = ctypes.c_int
      +
      +iokit.IOServiceMatching.restype = ctypes.c_void_p
      +
      +iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
      +iokit.IOServiceGetMatchingServices.restype = kern_return_t
      +
      +iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
      +iokit.IOServiceGetMatchingServices.restype = kern_return_t
      +
      +iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32]
      +iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p
      +
      +iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
      +iokit.IORegistryEntryGetPath.restype = kern_return_t
      +
      +iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
      +iokit.IORegistryEntryGetName.restype = kern_return_t
      +
      +iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
      +iokit.IOObjectGetClass.restype = kern_return_t
      +
      +iokit.IOObjectRelease.argtypes = [ctypes.c_void_p]
      +
      +
      +cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32]
      +cf.CFStringCreateWithCString.restype = ctypes.c_void_p
      +
      +cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
      +cf.CFStringGetCStringPtr.restype = ctypes.c_char_p
      +
      +cf.CFStringGetCString.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long, ctypes.c_uint32]
      +cf.CFStringGetCString.restype = ctypes.c_bool
      +
      +cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p]
      +cf.CFNumberGetValue.restype = ctypes.c_void_p
      +
      +# void CFRelease ( CFTypeRef cf );
      +cf.CFRelease.argtypes = [ctypes.c_void_p]
      +cf.CFRelease.restype = None
      +
      +# CFNumber type defines
      +kCFNumberSInt8Type = 1
      +kCFNumberSInt16Type = 2
      +kCFNumberSInt32Type = 3
      +kCFNumberSInt64Type = 4
      +
      +
      +def get_string_property(device_type, property):
      +    """
      +    Search the given device for the specified string property
      +
      +    @param device_type Type of Device
      +    @param property String to search for
      +    @return Python string containing the value, or None if not found.
      +    """
      +    key = cf.CFStringCreateWithCString(
      +            kCFAllocatorDefault,
      +            property.encode("utf-8"),
      +            kCFStringEncodingUTF8)
      +
      +    CFContainer = iokit.IORegistryEntryCreateCFProperty(
      +            device_type,
      +            key,
      +            kCFAllocatorDefault,
      +            0)
      +    output = None
      +
      +    if CFContainer:
      +        output = cf.CFStringGetCStringPtr(CFContainer, 0)
      +        if output is not None:
      +            output = output.decode('utf-8')
      +        else:
      +            buffer = ctypes.create_string_buffer(io_name_size);
      +            success = cf.CFStringGetCString(CFContainer, ctypes.byref(buffer), io_name_size, kCFStringEncodingUTF8)
      +            if success:
      +                output = buffer.value.decode('utf-8')
      +        cf.CFRelease(CFContainer)
      +    return output
      +
      +
      +def get_int_property(device_type, property, cf_number_type):
      +    """
      +    Search the given device for the specified string property
      +
      +    @param device_type Device to search
      +    @param property String to search for
      +    @param cf_number_type CFType number
      +
      +    @return Python string containing the value, or None if not found.
      +    """
      +    key = cf.CFStringCreateWithCString(
      +            kCFAllocatorDefault,
      +            property.encode("utf-8"),
      +            kCFStringEncodingUTF8)
      +
      +    CFContainer = iokit.IORegistryEntryCreateCFProperty(
      +            device_type,
      +            key,
      +            kCFAllocatorDefault,
      +            0)
      +
      +    if CFContainer:
      +        if (cf_number_type == kCFNumberSInt32Type):
      +            number = ctypes.c_uint32()
      +        elif (cf_number_type == kCFNumberSInt16Type):
      +            number = ctypes.c_uint16()
      +        cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number))
      +        cf.CFRelease(CFContainer)
      +        return number.value
      +    return None
      +
      +def IORegistryEntryGetName(device):
      +    devicename = ctypes.create_string_buffer(io_name_size);
      +    res = iokit.IORegistryEntryGetName(device, ctypes.byref(devicename))
      +    if res != KERN_SUCCESS:
      +        return None
      +    # this works in python2 but may not be valid. Also I don't know if
      +    # this encoding is guaranteed. It may be dependent on system locale.
      +    return devicename.value.decode('utf-8')
      +
      +def IOObjectGetClass(device):
      +    classname = ctypes.create_string_buffer(io_name_size)
      +    iokit.IOObjectGetClass(device, ctypes.byref(classname))
      +    return classname.value
      +
      +def GetParentDeviceByType(device, parent_type):
      +    """ Find the first parent of a device that implements the parent_type
      +        @param IOService Service to inspect
      +        @return Pointer to the parent type, or None if it was not found.
      +    """
      +    # First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice.
      +    parent_type = parent_type.encode('utf-8')
      +    while IOObjectGetClass(device) != parent_type:
      +        parent = ctypes.c_void_p()
      +        response = iokit.IORegistryEntryGetParentEntry(
      +                device,
      +                "IOService".encode("utf-8"),
      +                ctypes.byref(parent))
      +        # If we weren't able to find a parent for the device, we're done.
      +        if response != KERN_SUCCESS:
      +            return None
      +        device = parent
      +    return device
      +
      +
      +def GetIOServicesByType(service_type):
      +    """
      +    returns iterator over specified service_type
      +    """
      +    serial_port_iterator = ctypes.c_void_p()
      +
      +    iokit.IOServiceGetMatchingServices(
      +            kIOMasterPortDefault,
      +            iokit.IOServiceMatching(service_type.encode('utf-8')),
      +            ctypes.byref(serial_port_iterator))
      +
      +    services = []
      +    while iokit.IOIteratorIsValid(serial_port_iterator):
      +        service = iokit.IOIteratorNext(serial_port_iterator)
      +        if not service:
      +            break
      +        services.append(service)
      +    iokit.IOObjectRelease(serial_port_iterator)
      +    return services
      +
      +
      +def location_to_string(locationID):
      +    """
      +    helper to calculate port and bus number from locationID
      +    """
      +    loc = ['{}-'.format(locationID >> 24)]
      +    while locationID & 0xf00000:
      +        if len(loc) > 1:
      +            loc.append('.')
      +        loc.append('{}'.format((locationID >> 20) & 0xf))
      +        locationID <<= 4
      +    return ''.join(loc)
      +
      +
      +class SuitableSerialInterface(object):
      +    pass
      +
      +
      +def scan_interfaces():
      +    """
      +    helper function to scan USB interfaces
      +    returns a list of SuitableSerialInterface objects with name and id attributes
      +    """
      +    interfaces = []
      +    for service in GetIOServicesByType('IOSerialBSDClient'):
      +        device = get_string_property(service, "IOCalloutDevice")
      +        if device:
      +            usb_device = GetParentDeviceByType(service, "IOUSBInterface")
      +            if usb_device:
      +                name = get_string_property(usb_device, "USB Interface Name") or None
      +                locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or ''
      +                i = SuitableSerialInterface()
      +                i.id = locationID
      +                i.name = name
      +                interfaces.append(i)
      +    return interfaces
      +
      +
      +def search_for_locationID_in_interfaces(serial_interfaces, locationID):
      +    for interface in serial_interfaces:
      +        if (interface.id == locationID):
      +            return interface.name
      +    return None
      +
      +
      +def comports(include_links=False):
      +    # XXX include_links is currently ignored. are links in /dev even supported here?
      +    # Scan for all iokit serial ports
      +    services = GetIOServicesByType('IOSerialBSDClient')
      +    ports = []
      +    serial_interfaces = scan_interfaces()
      +    for service in services:
      +        # First, add the callout device file.
      +        device = get_string_property(service, "IOCalloutDevice")
      +        if device:
      +            info = list_ports_common.ListPortInfo(device)
      +            # If the serial port is implemented by IOUSBDevice
      +            # NOTE IOUSBDevice was deprecated as of 10.11 and finally on Apple Silicon
      +            # devices has been completely removed.  Thanks to @oskay for this patch.
      +            usb_device = GetParentDeviceByType(service, "IOUSBHostDevice")
      +            if not usb_device:
      +                usb_device = GetParentDeviceByType(service, "IOUSBDevice")
      +            if usb_device:
      +                # fetch some useful informations from properties
      +                info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type)
      +                info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type)
      +                info.serial_number = get_string_property(usb_device, kUSBSerialNumberString)
      +                # We know this is a usb device, so the
      +                # IORegistryEntryName should always be aliased to the
      +                # usb product name string descriptor.
      +                info.product = IORegistryEntryGetName(usb_device) or 'n/a'
      +                info.manufacturer = get_string_property(usb_device, kUSBVendorString)
      +                locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type)
      +                info.location = location_to_string(locationID)
      +                info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID)
      +                info.apply_usb_info()
      +            ports.append(info)
      +    return ports
      +
      +# test
      +if __name__ == '__main__':
      +    for port, desc, hwid in sorted(comports()):
      +        print("{}: {} [{}]".format(port, desc, hwid))
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_posix.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_posix.py
      new file mode 100644
      index 000000000..79bc8ed1a
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_posix.py
      @@ -0,0 +1,119 @@
      +#!/usr/bin/env python
      +#
      +# This is a module that gathers a list of serial ports on POSIXy systems.
      +# For some specific implementations, see also list_ports_linux, list_ports_osx
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2011-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +"""\
      +The ``comports`` function is expected to return an iterable that yields tuples
      +of 3 strings: port name, human readable description and a hardware ID.
      +
      +As currently no method is known to get the second two strings easily, they are
      +currently just identical to the port name.
      +"""
      +
      +from __future__ import absolute_import
      +
      +import glob
      +import sys
      +import os
      +from serial.tools import list_ports_common
      +
      +# try to detect the OS so that a device can be selected...
      +plat = sys.platform.lower()
      +
      +if plat[:5] == 'linux':    # Linux (confirmed)  # noqa
      +    from serial.tools.list_ports_linux import comports
      +
      +elif plat[:6] == 'darwin':   # OS X (confirmed)
      +    from serial.tools.list_ports_osx import comports
      +
      +elif plat == 'cygwin':       # cygwin/win32
      +    # cygwin accepts /dev/com* in many contexts
      +    # (such as 'open' call, explicit 'ls'), but 'glob.glob'
      +    # and bare 'ls' do not; so use /dev/ttyS* instead
      +    def comports(include_links=False):
      +        devices = glob.glob('/dev/ttyS*')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:7] == 'openbsd':    # OpenBSD
      +    def comports(include_links=False):
      +        devices = glob.glob('/dev/cua*')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:3] == 'bsd' or plat[:7] == 'freebsd':
      +    def comports(include_links=False):
      +        devices = glob.glob('/dev/cua*[!.init][!.lock]')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:6] == 'netbsd':   # NetBSD
      +    def comports(include_links=False):
      +        """scan for available ports. return a list of device names."""
      +        devices = glob.glob('/dev/dty*')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:4] == 'irix':     # IRIX
      +    def comports(include_links=False):
      +        """scan for available ports. return a list of device names."""
      +        devices = glob.glob('/dev/ttyf*')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:2] == 'hp':       # HP-UX (not tested)
      +    def comports(include_links=False):
      +        """scan for available ports. return a list of device names."""
      +        devices = glob.glob('/dev/tty*p0')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:5] == 'sunos':    # Solaris/SunOS
      +    def comports(include_links=False):
      +        """scan for available ports. return a list of device names."""
      +        devices = glob.glob('/dev/tty*c')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +elif plat[:3] == 'aix':      # AIX
      +    def comports(include_links=False):
      +        """scan for available ports. return a list of device names."""
      +        devices = glob.glob('/dev/tty*')
      +        if include_links:
      +            devices.extend(list_ports_common.list_links(devices))
      +        return [list_ports_common.ListPortInfo(d) for d in devices]
      +
      +else:
      +    # platform detection has failed...
      +    import serial
      +    sys.stderr.write("""\
      +don't know how to enumerate ttys on this system.
      +! I you know how the serial ports are named send this information to
      +! the author of this module:
      +
      +sys.platform = {!r}
      +os.name = {!r}
      +pySerial version = {}
      +
      +also add the naming scheme of the serial ports and with a bit luck you can get
      +this module running...
      +""".format(sys.platform, os.name, serial.VERSION))
      +    raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
      +
      +# test
      +if __name__ == '__main__':
      +    for port, desc, hwid in sorted(comports()):
      +        print("{}: {} [{}]".format(port, desc, hwid))
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_windows.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_windows.py
      new file mode 100644
      index 000000000..0b4a5b1e6
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/list_ports_windows.py
      @@ -0,0 +1,427 @@
      +#! python
      +#
      +# Enumerate serial ports on Windows including a human readable description
      +# and hardware information.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2001-2016 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +# pylint: disable=invalid-name,too-few-public-methods
      +import re
      +import ctypes
      +from ctypes.wintypes import BOOL
      +from ctypes.wintypes import HWND
      +from ctypes.wintypes import DWORD
      +from ctypes.wintypes import WORD
      +from ctypes.wintypes import LONG
      +from ctypes.wintypes import ULONG
      +from ctypes.wintypes import HKEY
      +from ctypes.wintypes import BYTE
      +import serial
      +from serial.win32 import ULONG_PTR
      +from serial.tools import list_ports_common
      +
      +
      +def ValidHandle(value, func, arguments):
      +    if value == 0:
      +        raise ctypes.WinError()
      +    return value
      +
      +
      +NULL = 0
      +HDEVINFO = ctypes.c_void_p
      +LPCTSTR = ctypes.c_wchar_p
      +PCTSTR = ctypes.c_wchar_p
      +PTSTR = ctypes.c_wchar_p
      +LPDWORD = PDWORD = ctypes.POINTER(DWORD)
      +#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
      +LPBYTE = PBYTE = ctypes.c_void_p        # XXX avoids error about types
      +
      +ACCESS_MASK = DWORD
      +REGSAM = ACCESS_MASK
      +
      +
      +class GUID(ctypes.Structure):
      +    _fields_ = [
      +        ('Data1', DWORD),
      +        ('Data2', WORD),
      +        ('Data3', WORD),
      +        ('Data4', BYTE * 8),
      +    ]
      +
      +    def __str__(self):
      +        return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format(
      +            self.Data1,
      +            self.Data2,
      +            self.Data3,
      +            ''.join(["{:02x}".format(d) for d in self.Data4[:2]]),
      +            ''.join(["{:02x}".format(d) for d in self.Data4[2:]]),
      +        )
      +
      +
      +class SP_DEVINFO_DATA(ctypes.Structure):
      +    _fields_ = [
      +        ('cbSize', DWORD),
      +        ('ClassGuid', GUID),
      +        ('DevInst', DWORD),
      +        ('Reserved', ULONG_PTR),
      +    ]
      +
      +    def __str__(self):
      +        return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst)
      +
      +
      +PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
      +
      +PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
      +
      +setupapi = ctypes.windll.LoadLibrary("setupapi")
      +SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
      +SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
      +SetupDiDestroyDeviceInfoList.restype = BOOL
      +
      +SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW
      +SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
      +SetupDiClassGuidsFromName.restype = BOOL
      +
      +SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
      +SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
      +SetupDiEnumDeviceInfo.restype = BOOL
      +
      +SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW
      +SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
      +SetupDiGetClassDevs.restype = HDEVINFO
      +SetupDiGetClassDevs.errcheck = ValidHandle
      +
      +SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW
      +SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
      +SetupDiGetDeviceRegistryProperty.restype = BOOL
      +
      +SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW
      +SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
      +SetupDiGetDeviceInstanceId.restype = BOOL
      +
      +SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
      +SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
      +SetupDiOpenDevRegKey.restype = HKEY
      +
      +advapi32 = ctypes.windll.LoadLibrary("Advapi32")
      +RegCloseKey = advapi32.RegCloseKey
      +RegCloseKey.argtypes = [HKEY]
      +RegCloseKey.restype = LONG
      +
      +RegQueryValueEx = advapi32.RegQueryValueExW
      +RegQueryValueEx.argtypes = [HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
      +RegQueryValueEx.restype = LONG
      +
      +cfgmgr32 = ctypes.windll.LoadLibrary("Cfgmgr32")
      +CM_Get_Parent = cfgmgr32.CM_Get_Parent
      +CM_Get_Parent.argtypes = [PDWORD, DWORD, ULONG]
      +CM_Get_Parent.restype = LONG
      +
      +CM_Get_Device_IDW = cfgmgr32.CM_Get_Device_IDW
      +CM_Get_Device_IDW.argtypes = [DWORD, PTSTR, ULONG, ULONG]
      +CM_Get_Device_IDW.restype = LONG
      +
      +CM_MapCrToWin32Err = cfgmgr32.CM_MapCrToWin32Err
      +CM_MapCrToWin32Err.argtypes = [DWORD, DWORD]
      +CM_MapCrToWin32Err.restype = DWORD
      +
      +
      +DIGCF_PRESENT = 2
      +DIGCF_DEVICEINTERFACE = 16
      +INVALID_HANDLE_VALUE = 0
      +ERROR_INSUFFICIENT_BUFFER = 122
      +ERROR_NOT_FOUND = 1168
      +SPDRP_HARDWAREID = 1
      +SPDRP_FRIENDLYNAME = 12
      +SPDRP_LOCATION_PATHS = 35
      +SPDRP_MFG = 11
      +DICS_FLAG_GLOBAL = 1
      +DIREG_DEV = 0x00000001
      +KEY_READ = 0x20019
      +
      +
      +MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH = 5
      +
      +
      +def get_parent_serial_number(child_devinst, child_vid, child_pid, depth=0, last_serial_number=None):
      +    """ Get the serial number of the parent of a device.
      +
      +    Args:
      +        child_devinst: The device instance handle to get the parent serial number of.
      +        child_vid: The vendor ID of the child device.
      +        child_pid: The product ID of the child device.
      +        depth: The current iteration depth of the USB device tree.
      +    """
      +
      +    # If the traversal depth is beyond the max, abandon attempting to find the serial number.
      +    if depth > MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH:
      +        return '' if not last_serial_number else last_serial_number
      +
      +    # Get the parent device instance.
      +    devinst = DWORD()
      +    ret = CM_Get_Parent(ctypes.byref(devinst), child_devinst, 0)
      +
      +    if ret:
      +        win_error = CM_MapCrToWin32Err(DWORD(ret), DWORD(0))
      +
      +        # If there is no parent available, the child was the root device. We cannot traverse
      +        # further.
      +        if win_error == ERROR_NOT_FOUND:
      +            return '' if not last_serial_number else last_serial_number
      +
      +        raise ctypes.WinError(win_error)
      +
      +    # Get the ID of the parent device and parse it for vendor ID, product ID, and serial number.
      +    parentHardwareID = ctypes.create_unicode_buffer(250)
      +
      +    ret = CM_Get_Device_IDW(
      +        devinst,
      +        parentHardwareID,
      +        ctypes.sizeof(parentHardwareID) - 1,
      +        0)
      +
      +    if ret:
      +        raise ctypes.WinError(CM_MapCrToWin32Err(DWORD(ret), DWORD(0)))
      +
      +    parentHardwareID_str = parentHardwareID.value
      +    m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?',
      +                  parentHardwareID_str,
      +                  re.I)
      +
      +    # return early if we have no matches (likely malformed serial, traversed too far)
      +    if not m:
      +        return '' if not last_serial_number else last_serial_number
      +
      +    vid = None
      +    pid = None
      +    serial_number = None
      +    if m.group(1):
      +        vid = int(m.group(1), 16)
      +    if m.group(3):
      +        pid = int(m.group(3), 16)
      +    if m.group(7):
      +        serial_number = m.group(7)
      +
      +    # store what we found as a fallback for malformed serial values up the chain
      +    found_serial_number = serial_number
      +
      +    # Check that the USB serial number only contains alpha-numeric characters. It may be a windows
      +    # device ID (ephemeral ID).
      +    if serial_number and not re.match(r'^\w+$', serial_number):
      +        serial_number = None
      +
      +    if not vid or not pid:
      +        # If pid and vid are not available at this device level, continue to the parent.
      +        return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number)
      +
      +    if pid != child_pid or vid != child_vid:
      +        # If the VID or PID has changed, we are no longer looking at the same physical device. The
      +        # serial number is unknown.
      +        return '' if not last_serial_number else last_serial_number
      +
      +    # In this case, the vid and pid of the parent device are identical to the child. However, if
      +    # there still isn't a serial number available, continue to the next parent.
      +    if not serial_number:
      +        return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number)
      +
      +    # Finally, the VID and PID are identical to the child and a serial number is present, so return
      +    # it.
      +    return serial_number
      +
      +
      +def iterate_comports():
      +    """Return a generator that yields descriptions for serial ports"""
      +    PortsGUIDs = (GUID * 8)()  # so far only seen one used, so hope 8 are enough...
      +    ports_guids_size = DWORD()
      +    if not SetupDiClassGuidsFromName(
      +            "Ports",
      +            PortsGUIDs,
      +            ctypes.sizeof(PortsGUIDs),
      +            ctypes.byref(ports_guids_size)):
      +        raise ctypes.WinError()
      +
      +    ModemsGUIDs = (GUID * 8)()  # so far only seen one used, so hope 8 are enough...
      +    modems_guids_size = DWORD()
      +    if not SetupDiClassGuidsFromName(
      +            "Modem",
      +            ModemsGUIDs,
      +            ctypes.sizeof(ModemsGUIDs),
      +            ctypes.byref(modems_guids_size)):
      +        raise ctypes.WinError()
      +
      +    GUIDs = PortsGUIDs[:ports_guids_size.value] + ModemsGUIDs[:modems_guids_size.value]
      +
      +    # repeat for all possible GUIDs
      +    for index in range(len(GUIDs)):
      +        bInterfaceNumber = None
      +        g_hdi = SetupDiGetClassDevs(
      +            ctypes.byref(GUIDs[index]),
      +            None,
      +            NULL,
      +            DIGCF_PRESENT)  # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
      +
      +        devinfo = SP_DEVINFO_DATA()
      +        devinfo.cbSize = ctypes.sizeof(devinfo)
      +        index = 0
      +        while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
      +            index += 1
      +
      +            # get the real com port name
      +            hkey = SetupDiOpenDevRegKey(
      +                g_hdi,
      +                ctypes.byref(devinfo),
      +                DICS_FLAG_GLOBAL,
      +                0,
      +                DIREG_DEV,  # DIREG_DRV for SW info
      +                KEY_READ)
      +            port_name_buffer = ctypes.create_unicode_buffer(250)
      +            port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
      +            RegQueryValueEx(
      +                hkey,
      +                "PortName",
      +                None,
      +                None,
      +                ctypes.byref(port_name_buffer),
      +                ctypes.byref(port_name_length))
      +            RegCloseKey(hkey)
      +
      +            # unfortunately does this method also include parallel ports.
      +            # we could check for names starting with COM or just exclude LPT
      +            # and hope that other "unknown" names are serial ports...
      +            if port_name_buffer.value.startswith('LPT'):
      +                continue
      +
      +            # hardware ID
      +            szHardwareID = ctypes.create_unicode_buffer(250)
      +            # try to get ID that includes serial number
      +            if not SetupDiGetDeviceInstanceId(
      +                    g_hdi,
      +                    ctypes.byref(devinfo),
      +                    #~ ctypes.byref(szHardwareID),
      +                    szHardwareID,
      +                    ctypes.sizeof(szHardwareID) - 1,
      +                    None):
      +                # fall back to more generic hardware ID if that would fail
      +                if not SetupDiGetDeviceRegistryProperty(
      +                        g_hdi,
      +                        ctypes.byref(devinfo),
      +                        SPDRP_HARDWAREID,
      +                        None,
      +                        ctypes.byref(szHardwareID),
      +                        ctypes.sizeof(szHardwareID) - 1,
      +                        None):
      +                    # Ignore ERROR_INSUFFICIENT_BUFFER
      +                    if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
      +                        raise ctypes.WinError()
      +            # stringify
      +            szHardwareID_str = szHardwareID.value
      +
      +            info = list_ports_common.ListPortInfo(port_name_buffer.value, skip_link_detection=True)
      +
      +            # in case of USB, make a more readable string, similar to that form
      +            # that we also generate on other platforms
      +            if szHardwareID_str.startswith('USB'):
      +                m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?', szHardwareID_str, re.I)
      +                if m:
      +                    info.vid = int(m.group(1), 16)
      +                    if m.group(3):
      +                        info.pid = int(m.group(3), 16)
      +                    if m.group(5):
      +                        bInterfaceNumber = int(m.group(5))
      +
      +                    # Check that the USB serial number only contains alpha-numeric characters. It
      +                    # may be a windows device ID (ephemeral ID) for composite devices.
      +                    if m.group(7) and re.match(r'^\w+$', m.group(7)):
      +                        info.serial_number = m.group(7)
      +                    else:
      +                        info.serial_number = get_parent_serial_number(devinfo.DevInst, info.vid, info.pid)
      +
      +                # calculate a location string
      +                loc_path_str = ctypes.create_unicode_buffer(250)
      +                if SetupDiGetDeviceRegistryProperty(
      +                        g_hdi,
      +                        ctypes.byref(devinfo),
      +                        SPDRP_LOCATION_PATHS,
      +                        None,
      +                        ctypes.byref(loc_path_str),
      +                        ctypes.sizeof(loc_path_str) - 1,
      +                        None):
      +                    m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value)
      +                    location = []
      +                    for g in m:
      +                        if g.group(1):
      +                            location.append('{:d}'.format(int(g.group(1)) + 1))
      +                        else:
      +                            if len(location) > 1:
      +                                location.append('.')
      +                            else:
      +                                location.append('-')
      +                            location.append(g.group(2))
      +                    if bInterfaceNumber is not None:
      +                        location.append(':{}.{}'.format(
      +                            'x',  # XXX how to determine correct bConfigurationValue?
      +                            bInterfaceNumber))
      +                    if location:
      +                        info.location = ''.join(location)
      +                info.hwid = info.usb_info()
      +            elif szHardwareID_str.startswith('FTDIBUS'):
      +                m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I)
      +                if m:
      +                    info.vid = int(m.group(1), 16)
      +                    info.pid = int(m.group(2), 16)
      +                    if m.group(4):
      +                        info.serial_number = m.group(4)
      +                # USB location is hidden by FDTI driver :(
      +                info.hwid = info.usb_info()
      +            else:
      +                info.hwid = szHardwareID_str
      +
      +            # friendly name
      +            szFriendlyName = ctypes.create_unicode_buffer(250)
      +            if SetupDiGetDeviceRegistryProperty(
      +                    g_hdi,
      +                    ctypes.byref(devinfo),
      +                    SPDRP_FRIENDLYNAME,
      +                    #~ SPDRP_DEVICEDESC,
      +                    None,
      +                    ctypes.byref(szFriendlyName),
      +                    ctypes.sizeof(szFriendlyName) - 1,
      +                    None):
      +                info.description = szFriendlyName.value
      +            #~ else:
      +                # Ignore ERROR_INSUFFICIENT_BUFFER
      +                #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
      +                    #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
      +                # ignore errors and still include the port in the list, friendly name will be same as port name
      +
      +            # manufacturer
      +            szManufacturer = ctypes.create_unicode_buffer(250)
      +            if SetupDiGetDeviceRegistryProperty(
      +                    g_hdi,
      +                    ctypes.byref(devinfo),
      +                    SPDRP_MFG,
      +                    #~ SPDRP_DEVICEDESC,
      +                    None,
      +                    ctypes.byref(szManufacturer),
      +                    ctypes.sizeof(szManufacturer) - 1,
      +                    None):
      +                info.manufacturer = szManufacturer.value
      +            yield info
      +        SetupDiDestroyDeviceInfoList(g_hdi)
      +
      +
      +def comports(include_links=False):
      +    """Return a list of info objects about serial ports"""
      +    return list(iterate_comports())
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +# test
      +if __name__ == '__main__':
      +    for port, desc, hwid in sorted(comports()):
      +        print("{}: {} [{}]".format(port, desc, hwid))
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/miniterm.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/miniterm.py
      new file mode 100644
      index 000000000..2cceff635
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/tools/miniterm.py
      @@ -0,0 +1,1042 @@
      +#!/usr/bin/env python
      +#
      +# Very simple serial terminal
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C)2002-2020 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +from __future__ import absolute_import
      +
      +import codecs
      +import os
      +import sys
      +import threading
      +
      +import serial
      +from serial.tools.list_ports import comports
      +from serial.tools import hexlify_codec
      +
      +# pylint: disable=wrong-import-order,wrong-import-position
      +
      +codecs.register(lambda c: hexlify_codec.getregentry() if c == 'hexlify' else None)
      +
      +try:
      +    raw_input
      +except NameError:
      +    # pylint: disable=redefined-builtin,invalid-name
      +    raw_input = input   # in python3 it's "raw"
      +    unichr = chr
      +
      +
      +def key_description(character):
      +    """generate a readable description for a key"""
      +    ascii_code = ord(character)
      +    if ascii_code < 32:
      +        return 'Ctrl+{:c}'.format(ord('@') + ascii_code)
      +    else:
      +        return repr(character)
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +class ConsoleBase(object):
      +    """OS abstraction for console (input/output codec, no echo)"""
      +
      +    def __init__(self):
      +        if sys.version_info >= (3, 0):
      +            self.byte_output = sys.stdout.buffer
      +        else:
      +            self.byte_output = sys.stdout
      +        self.output = sys.stdout
      +
      +    def setup(self):
      +        """Set console to read single characters, no echo"""
      +
      +    def cleanup(self):
      +        """Restore default console settings"""
      +
      +    def getkey(self):
      +        """Read a single key from the console"""
      +        return None
      +
      +    def write_bytes(self, byte_string):
      +        """Write bytes (already encoded)"""
      +        self.byte_output.write(byte_string)
      +        self.byte_output.flush()
      +
      +    def write(self, text):
      +        """Write string"""
      +        self.output.write(text)
      +        self.output.flush()
      +
      +    def cancel(self):
      +        """Cancel getkey operation"""
      +
      +    #  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
      +    # context manager:
      +    # switch terminal temporary to normal mode (e.g. to get user input)
      +
      +    def __enter__(self):
      +        self.cleanup()
      +        return self
      +
      +    def __exit__(self, *args, **kwargs):
      +        self.setup()
      +
      +
      +if os.name == 'nt':  # noqa
      +    import msvcrt
      +    import ctypes
      +    import platform
      +
      +    class Out(object):
      +        """file-like wrapper that uses os.write"""
      +
      +        def __init__(self, fd):
      +            self.fd = fd
      +
      +        def flush(self):
      +            pass
      +
      +        def write(self, s):
      +            os.write(self.fd, s)
      +
      +    class Console(ConsoleBase):
      +        fncodes = {
      +            ';': '\1bOP',  # F1
      +            '<': '\1bOQ',  # F2
      +            '=': '\1bOR',  # F3
      +            '>': '\1bOS',  # F4
      +            '?': '\1b[15~',  # F5
      +            '@': '\1b[17~',  # F6
      +            'A': '\1b[18~',  # F7
      +            'B': '\1b[19~',  # F8
      +            'C': '\1b[20~',  # F9
      +            'D': '\1b[21~',  # F10
      +        }
      +        navcodes = {
      +            'H': '\x1b[A',  # UP
      +            'P': '\x1b[B',  # DOWN
      +            'K': '\x1b[D',  # LEFT
      +            'M': '\x1b[C',  # RIGHT
      +            'G': '\x1b[H',  # HOME
      +            'O': '\x1b[F',  # END
      +            'R': '\x1b[2~',  # INSERT
      +            'S': '\x1b[3~',  # DELETE
      +            'I': '\x1b[5~',  # PGUP
      +            'Q': '\x1b[6~',  # PGDN        
      +        }
      +        
      +        def __init__(self):
      +            super(Console, self).__init__()
      +            self._saved_ocp = ctypes.windll.kernel32.GetConsoleOutputCP()
      +            self._saved_icp = ctypes.windll.kernel32.GetConsoleCP()
      +            ctypes.windll.kernel32.SetConsoleOutputCP(65001)
      +            ctypes.windll.kernel32.SetConsoleCP(65001)
      +            # ANSI handling available through SetConsoleMode since Windows 10 v1511 
      +            # https://en.wikipedia.org/wiki/ANSI_escape_code#cite_note-win10th2-1
      +            if platform.release() == '10' and int(platform.version().split('.')[2]) > 10586:
      +                ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
      +                import ctypes.wintypes as wintypes
      +                if not hasattr(wintypes, 'LPDWORD'): # PY2
      +                    wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)
      +                SetConsoleMode = ctypes.windll.kernel32.SetConsoleMode
      +                GetConsoleMode = ctypes.windll.kernel32.GetConsoleMode
      +                GetStdHandle = ctypes.windll.kernel32.GetStdHandle
      +                mode = wintypes.DWORD()
      +                GetConsoleMode(GetStdHandle(-11), ctypes.byref(mode))
      +                if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
      +                    SetConsoleMode(GetStdHandle(-11), mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
      +                    self._saved_cm = mode
      +            self.output = codecs.getwriter('UTF-8')(Out(sys.stdout.fileno()), 'replace')
      +            # the change of the code page is not propagated to Python, manually fix it
      +            sys.stderr = codecs.getwriter('UTF-8')(Out(sys.stderr.fileno()), 'replace')
      +            sys.stdout = self.output
      +            self.output.encoding = 'UTF-8'  # needed for input
      +
      +        def __del__(self):
      +            ctypes.windll.kernel32.SetConsoleOutputCP(self._saved_ocp)
      +            ctypes.windll.kernel32.SetConsoleCP(self._saved_icp)
      +            try:
      +                ctypes.windll.kernel32.SetConsoleMode(ctypes.windll.kernel32.GetStdHandle(-11), self._saved_cm)
      +            except AttributeError: # in case no _saved_cm
      +                pass
      +
      +        def getkey(self):
      +            while True:
      +                z = msvcrt.getwch()
      +                if z == unichr(13):
      +                    return unichr(10)
      +                elif z is unichr(0) or z is unichr(0xe0):
      +                    try:
      +                        code = msvcrt.getwch()
      +                        if z is unichr(0):
      +                            return self.fncodes[code]
      +                        else:
      +                            return self.navcodes[code]
      +                    except KeyError:
      +                        pass
      +                else:
      +                    return z
      +
      +        def cancel(self):
      +            # CancelIo, CancelSynchronousIo do not seem to work when using
      +            # getwch, so instead, send a key to the window with the console
      +            hwnd = ctypes.windll.kernel32.GetConsoleWindow()
      +            ctypes.windll.user32.PostMessageA(hwnd, 0x100, 0x0d, 0)
      +
      +elif os.name == 'posix':
      +    import atexit
      +    import termios
      +    import fcntl
      +
      +    class Console(ConsoleBase):
      +        def __init__(self):
      +            super(Console, self).__init__()
      +            self.fd = sys.stdin.fileno()
      +            self.old = termios.tcgetattr(self.fd)
      +            atexit.register(self.cleanup)
      +            if sys.version_info < (3, 0):
      +                self.enc_stdin = codecs.getreader(sys.stdin.encoding)(sys.stdin)
      +            else:
      +                self.enc_stdin = sys.stdin
      +
      +        def setup(self):
      +            new = termios.tcgetattr(self.fd)
      +            new[3] = new[3] & ~termios.ICANON & ~termios.ECHO & ~termios.ISIG
      +            new[6][termios.VMIN] = 1
      +            new[6][termios.VTIME] = 0
      +            termios.tcsetattr(self.fd, termios.TCSANOW, new)
      +
      +        def getkey(self):
      +            c = self.enc_stdin.read(1)
      +            if c == unichr(0x7f):
      +                c = unichr(8)    # map the BS key (which yields DEL) to backspace
      +            return c
      +
      +        def cancel(self):
      +            fcntl.ioctl(self.fd, termios.TIOCSTI, b'\0')
      +
      +        def cleanup(self):
      +            termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old)
      +
      +else:
      +    raise NotImplementedError(
      +        'Sorry no implementation for your platform ({}) available.'.format(sys.platform))
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +
      +class Transform(object):
      +    """do-nothing: forward all data unchanged"""
      +    def rx(self, text):
      +        """text received from serial port"""
      +        return text
      +
      +    def tx(self, text):
      +        """text to be sent to serial port"""
      +        return text
      +
      +    def echo(self, text):
      +        """text to be sent but displayed on console"""
      +        return text
      +
      +
      +class CRLF(Transform):
      +    """ENTER sends CR+LF"""
      +
      +    def tx(self, text):
      +        return text.replace('\n', '\r\n')
      +
      +
      +class CR(Transform):
      +    """ENTER sends CR"""
      +
      +    def rx(self, text):
      +        return text.replace('\r', '\n')
      +
      +    def tx(self, text):
      +        return text.replace('\n', '\r')
      +
      +
      +class LF(Transform):
      +    """ENTER sends LF"""
      +
      +
      +class NoTerminal(Transform):
      +    """remove typical terminal control codes from input"""
      +
      +    REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32) if unichr(x) not in '\r\n\b\t')
      +    REPLACEMENT_MAP.update(
      +        {
      +            0x7F: 0x2421,  # DEL
      +            0x9B: 0x2425,  # CSI
      +        })
      +
      +    def rx(self, text):
      +        return text.translate(self.REPLACEMENT_MAP)
      +
      +    echo = rx
      +
      +
      +class NoControls(NoTerminal):
      +    """Remove all control codes, incl. CR+LF"""
      +
      +    REPLACEMENT_MAP = dict((x, 0x2400 + x) for x in range(32))
      +    REPLACEMENT_MAP.update(
      +        {
      +            0x20: 0x2423,  # visual space
      +            0x7F: 0x2421,  # DEL
      +            0x9B: 0x2425,  # CSI
      +        })
      +
      +
      +class Printable(Transform):
      +    """Show decimal code for all non-ASCII characters and replace most control codes"""
      +
      +    def rx(self, text):
      +        r = []
      +        for c in text:
      +            if ' ' <= c < '\x7f' or c in '\r\n\b\t':
      +                r.append(c)
      +            elif c < ' ':
      +                r.append(unichr(0x2400 + ord(c)))
      +            else:
      +                r.extend(unichr(0x2080 + ord(d) - 48) for d in '{:d}'.format(ord(c)))
      +                r.append(' ')
      +        return ''.join(r)
      +
      +    echo = rx
      +
      +
      +class Colorize(Transform):
      +    """Apply different colors for received and echo"""
      +
      +    def __init__(self):
      +        # XXX make it configurable, use colorama?
      +        self.input_color = '\x1b[37m'
      +        self.echo_color = '\x1b[31m'
      +
      +    def rx(self, text):
      +        return self.input_color + text
      +
      +    def echo(self, text):
      +        return self.echo_color + text
      +
      +
      +class DebugIO(Transform):
      +    """Print what is sent and received"""
      +
      +    def rx(self, text):
      +        sys.stderr.write(' [RX:{!r}] '.format(text))
      +        sys.stderr.flush()
      +        return text
      +
      +    def tx(self, text):
      +        sys.stderr.write(' [TX:{!r}] '.format(text))
      +        sys.stderr.flush()
      +        return text
      +
      +
      +# other ideas:
      +# - add date/time for each newline
      +# - insert newline after: a) timeout b) packet end character
      +
      +EOL_TRANSFORMATIONS = {
      +    'crlf': CRLF,
      +    'cr': CR,
      +    'lf': LF,
      +}
      +
      +TRANSFORMATIONS = {
      +    'direct': Transform,    # no transformation
      +    'default': NoTerminal,
      +    'nocontrol': NoControls,
      +    'printable': Printable,
      +    'colorize': Colorize,
      +    'debug': DebugIO,
      +}
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +def ask_for_port():
      +    """\
      +    Show a list of ports and ask the user for a choice. To make selection
      +    easier on systems with long device names, also allow the input of an
      +    index.
      +    """
      +    sys.stderr.write('\n--- Available ports:\n')
      +    ports = []
      +    for n, (port, desc, hwid) in enumerate(sorted(comports()), 1):
      +        sys.stderr.write('--- {:2}: {:20} {!r}\n'.format(n, port, desc))
      +        ports.append(port)
      +    while True:
      +        port = raw_input('--- Enter port index or full name: ')
      +        try:
      +            index = int(port) - 1
      +            if not 0 <= index < len(ports):
      +                sys.stderr.write('--- Invalid index!\n')
      +                continue
      +        except ValueError:
      +            pass
      +        else:
      +            port = ports[index]
      +        return port
      +
      +
      +class Miniterm(object):
      +    """\
      +    Terminal application. Copy data from serial port to console and vice versa.
      +    Handle special keys from the console to show menu etc.
      +    """
      +
      +    def __init__(self, serial_instance, echo=False, eol='crlf', filters=()):
      +        self.console = Console()
      +        self.serial = serial_instance
      +        self.echo = echo
      +        self.raw = False
      +        self.input_encoding = 'UTF-8'
      +        self.output_encoding = 'UTF-8'
      +        self.eol = eol
      +        self.filters = filters
      +        self.update_transformations()
      +        self.exit_character = unichr(0x1d)  # GS/CTRL+]
      +        self.menu_character = unichr(0x14)  # Menu: CTRL+T
      +        self.alive = None
      +        self._reader_alive = None
      +        self.receiver_thread = None
      +        self.rx_decoder = None
      +        self.tx_decoder = None
      +
      +    def _start_reader(self):
      +        """Start reader thread"""
      +        self._reader_alive = True
      +        # start serial->console thread
      +        self.receiver_thread = threading.Thread(target=self.reader, name='rx')
      +        self.receiver_thread.daemon = True
      +        self.receiver_thread.start()
      +
      +    def _stop_reader(self):
      +        """Stop reader thread only, wait for clean exit of thread"""
      +        self._reader_alive = False
      +        if hasattr(self.serial, 'cancel_read'):
      +            self.serial.cancel_read()
      +        self.receiver_thread.join()
      +
      +    def start(self):
      +        """start worker threads"""
      +        self.alive = True
      +        self._start_reader()
      +        # enter console->serial loop
      +        self.transmitter_thread = threading.Thread(target=self.writer, name='tx')
      +        self.transmitter_thread.daemon = True
      +        self.transmitter_thread.start()
      +        self.console.setup()
      +
      +    def stop(self):
      +        """set flag to stop worker threads"""
      +        self.alive = False
      +
      +    def join(self, transmit_only=False):
      +        """wait for worker threads to terminate"""
      +        self.transmitter_thread.join()
      +        if not transmit_only:
      +            if hasattr(self.serial, 'cancel_read'):
      +                self.serial.cancel_read()
      +            self.receiver_thread.join()
      +
      +    def close(self):
      +        self.serial.close()
      +
      +    def update_transformations(self):
      +        """take list of transformation classes and instantiate them for rx and tx"""
      +        transformations = [EOL_TRANSFORMATIONS[self.eol]] + [TRANSFORMATIONS[f]
      +                                                             for f in self.filters]
      +        self.tx_transformations = [t() for t in transformations]
      +        self.rx_transformations = list(reversed(self.tx_transformations))
      +
      +    def set_rx_encoding(self, encoding, errors='replace'):
      +        """set encoding for received data"""
      +        self.input_encoding = encoding
      +        self.rx_decoder = codecs.getincrementaldecoder(encoding)(errors)
      +
      +    def set_tx_encoding(self, encoding, errors='replace'):
      +        """set encoding for transmitted data"""
      +        self.output_encoding = encoding
      +        self.tx_encoder = codecs.getincrementalencoder(encoding)(errors)
      +
      +    def dump_port_settings(self):
      +        """Write current settings to sys.stderr"""
      +        sys.stderr.write("\n--- Settings: {p.name}  {p.baudrate},{p.bytesize},{p.parity},{p.stopbits}\n".format(
      +            p=self.serial))
      +        sys.stderr.write('--- RTS: {:8}  DTR: {:8}  BREAK: {:8}\n'.format(
      +            ('active' if self.serial.rts else 'inactive'),
      +            ('active' if self.serial.dtr else 'inactive'),
      +            ('active' if self.serial.break_condition else 'inactive')))
      +        try:
      +            sys.stderr.write('--- CTS: {:8}  DSR: {:8}  RI: {:8}  CD: {:8}\n'.format(
      +                ('active' if self.serial.cts else 'inactive'),
      +                ('active' if self.serial.dsr else 'inactive'),
      +                ('active' if self.serial.ri else 'inactive'),
      +                ('active' if self.serial.cd else 'inactive')))
      +        except serial.SerialException:
      +            # on RFC 2217 ports, it can happen if no modem state notification was
      +            # yet received. ignore this error.
      +            pass
      +        sys.stderr.write('--- software flow control: {}\n'.format('active' if self.serial.xonxoff else 'inactive'))
      +        sys.stderr.write('--- hardware flow control: {}\n'.format('active' if self.serial.rtscts else 'inactive'))
      +        sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding))
      +        sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding))
      +        sys.stderr.write('--- EOL: {}\n'.format(self.eol.upper()))
      +        sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters)))
      +
      +    def reader(self):
      +        """loop and copy serial->console"""
      +        try:
      +            while self.alive and self._reader_alive:
      +                # read all that is there or wait for one byte
      +                data = self.serial.read(self.serial.in_waiting or 1)
      +                if data:
      +                    if self.raw:
      +                        self.console.write_bytes(data)
      +                    else:
      +                        text = self.rx_decoder.decode(data)
      +                        for transformation in self.rx_transformations:
      +                            text = transformation.rx(text)
      +                        self.console.write(text)
      +        except serial.SerialException:
      +            self.alive = False
      +            self.console.cancel()
      +            raise       # XXX handle instead of re-raise?
      +
      +    def writer(self):
      +        """\
      +        Loop and copy console->serial until self.exit_character character is
      +        found. When self.menu_character is found, interpret the next key
      +        locally.
      +        """
      +        menu_active = False
      +        try:
      +            while self.alive:
      +                try:
      +                    c = self.console.getkey()
      +                except KeyboardInterrupt:
      +                    c = '\x03'
      +                if not self.alive:
      +                    break
      +                if menu_active:
      +                    self.handle_menu_key(c)
      +                    menu_active = False
      +                elif c == self.menu_character:
      +                    menu_active = True      # next char will be for menu
      +                elif c == self.exit_character:
      +                    self.stop()             # exit app
      +                    break
      +                else:
      +                    #~ if self.raw:
      +                    text = c
      +                    for transformation in self.tx_transformations:
      +                        text = transformation.tx(text)
      +                    self.serial.write(self.tx_encoder.encode(text))
      +                    if self.echo:
      +                        echo_text = c
      +                        for transformation in self.tx_transformations:
      +                            echo_text = transformation.echo(echo_text)
      +                        self.console.write(echo_text)
      +        except:
      +            self.alive = False
      +            raise
      +
      +    def handle_menu_key(self, c):
      +        """Implement a simple menu / settings"""
      +        if c == self.menu_character or c == self.exit_character:
      +            # Menu/exit character again -> send itself
      +            self.serial.write(self.tx_encoder.encode(c))
      +            if self.echo:
      +                self.console.write(c)
      +        elif c == '\x15':                       # CTRL+U -> upload file
      +            self.upload_file()
      +        elif c in '\x08hH?':                    # CTRL+H, h, H, ? -> Show help
      +            sys.stderr.write(self.get_help_text())
      +        elif c == '\x12':                       # CTRL+R -> Toggle RTS
      +            self.serial.rts = not self.serial.rts
      +            sys.stderr.write('--- RTS {} ---\n'.format('active' if self.serial.rts else 'inactive'))
      +        elif c == '\x04':                       # CTRL+D -> Toggle DTR
      +            self.serial.dtr = not self.serial.dtr
      +            sys.stderr.write('--- DTR {} ---\n'.format('active' if self.serial.dtr else 'inactive'))
      +        elif c == '\x02':                       # CTRL+B -> toggle BREAK condition
      +            self.serial.break_condition = not self.serial.break_condition
      +            sys.stderr.write('--- BREAK {} ---\n'.format('active' if self.serial.break_condition else 'inactive'))
      +        elif c == '\x05':                       # CTRL+E -> toggle local echo
      +            self.echo = not self.echo
      +            sys.stderr.write('--- local echo {} ---\n'.format('active' if self.echo else 'inactive'))
      +        elif c == '\x06':                       # CTRL+F -> edit filters
      +            self.change_filter()
      +        elif c == '\x0c':                       # CTRL+L -> EOL mode
      +            modes = list(EOL_TRANSFORMATIONS)   # keys
      +            eol = modes.index(self.eol) + 1
      +            if eol >= len(modes):
      +                eol = 0
      +            self.eol = modes[eol]
      +            sys.stderr.write('--- EOL: {} ---\n'.format(self.eol.upper()))
      +            self.update_transformations()
      +        elif c == '\x01':                       # CTRL+A -> set encoding
      +            self.change_encoding()
      +        elif c == '\x09':                       # CTRL+I -> info
      +            self.dump_port_settings()
      +        #~ elif c == '\x01':                       # CTRL+A -> cycle escape mode
      +        #~ elif c == '\x0c':                       # CTRL+L -> cycle linefeed mode
      +        elif c in 'pP':                         # P -> change port
      +            self.change_port()
      +        elif c in 'zZ':                         # S -> suspend / open port temporarily
      +            self.suspend_port()
      +        elif c in 'bB':                         # B -> change baudrate
      +            self.change_baudrate()
      +        elif c == '8':                          # 8 -> change to 8 bits
      +            self.serial.bytesize = serial.EIGHTBITS
      +            self.dump_port_settings()
      +        elif c == '7':                          # 7 -> change to 8 bits
      +            self.serial.bytesize = serial.SEVENBITS
      +            self.dump_port_settings()
      +        elif c in 'eE':                         # E -> change to even parity
      +            self.serial.parity = serial.PARITY_EVEN
      +            self.dump_port_settings()
      +        elif c in 'oO':                         # O -> change to odd parity
      +            self.serial.parity = serial.PARITY_ODD
      +            self.dump_port_settings()
      +        elif c in 'mM':                         # M -> change to mark parity
      +            self.serial.parity = serial.PARITY_MARK
      +            self.dump_port_settings()
      +        elif c in 'sS':                         # S -> change to space parity
      +            self.serial.parity = serial.PARITY_SPACE
      +            self.dump_port_settings()
      +        elif c in 'nN':                         # N -> change to no parity
      +            self.serial.parity = serial.PARITY_NONE
      +            self.dump_port_settings()
      +        elif c == '1':                          # 1 -> change to 1 stop bits
      +            self.serial.stopbits = serial.STOPBITS_ONE
      +            self.dump_port_settings()
      +        elif c == '2':                          # 2 -> change to 2 stop bits
      +            self.serial.stopbits = serial.STOPBITS_TWO
      +            self.dump_port_settings()
      +        elif c == '3':                          # 3 -> change to 1.5 stop bits
      +            self.serial.stopbits = serial.STOPBITS_ONE_POINT_FIVE
      +            self.dump_port_settings()
      +        elif c in 'xX':                         # X -> change software flow control
      +            self.serial.xonxoff = (c == 'X')
      +            self.dump_port_settings()
      +        elif c in 'rR':                         # R -> change hardware flow control
      +            self.serial.rtscts = (c == 'R')
      +            self.dump_port_settings()
      +        elif c in 'qQ':
      +            self.stop()                         # Q -> exit app
      +        else:
      +            sys.stderr.write('--- unknown menu character {} --\n'.format(key_description(c)))
      +
      +    def upload_file(self):
      +        """Ask user for filenname and send its contents"""
      +        sys.stderr.write('\n--- File to upload: ')
      +        sys.stderr.flush()
      +        with self.console:
      +            filename = sys.stdin.readline().rstrip('\r\n')
      +            if filename:
      +                try:
      +                    with open(filename, 'rb') as f:
      +                        sys.stderr.write('--- Sending file {} ---\n'.format(filename))
      +                        while True:
      +                            block = f.read(1024)
      +                            if not block:
      +                                break
      +                            self.serial.write(block)
      +                            # Wait for output buffer to drain.
      +                            self.serial.flush()
      +                            sys.stderr.write('.')   # Progress indicator.
      +                    sys.stderr.write('\n--- File {} sent ---\n'.format(filename))
      +                except IOError as e:
      +                    sys.stderr.write('--- ERROR opening file {}: {} ---\n'.format(filename, e))
      +
      +    def change_filter(self):
      +        """change the i/o transformations"""
      +        sys.stderr.write('\n--- Available Filters:\n')
      +        sys.stderr.write('\n'.join(
      +            '---   {:<10} = {.__doc__}'.format(k, v)
      +            for k, v in sorted(TRANSFORMATIONS.items())))
      +        sys.stderr.write('\n--- Enter new filter name(s) [{}]: '.format(' '.join(self.filters)))
      +        with self.console:
      +            new_filters = sys.stdin.readline().lower().split()
      +        if new_filters:
      +            for f in new_filters:
      +                if f not in TRANSFORMATIONS:
      +                    sys.stderr.write('--- unknown filter: {!r}\n'.format(f))
      +                    break
      +            else:
      +                self.filters = new_filters
      +                self.update_transformations()
      +        sys.stderr.write('--- filters: {}\n'.format(' '.join(self.filters)))
      +
      +    def change_encoding(self):
      +        """change encoding on the serial port"""
      +        sys.stderr.write('\n--- Enter new encoding name [{}]: '.format(self.input_encoding))
      +        with self.console:
      +            new_encoding = sys.stdin.readline().strip()
      +        if new_encoding:
      +            try:
      +                codecs.lookup(new_encoding)
      +            except LookupError:
      +                sys.stderr.write('--- invalid encoding name: {}\n'.format(new_encoding))
      +            else:
      +                self.set_rx_encoding(new_encoding)
      +                self.set_tx_encoding(new_encoding)
      +        sys.stderr.write('--- serial input encoding: {}\n'.format(self.input_encoding))
      +        sys.stderr.write('--- serial output encoding: {}\n'.format(self.output_encoding))
      +
      +    def change_baudrate(self):
      +        """change the baudrate"""
      +        sys.stderr.write('\n--- Baudrate: ')
      +        sys.stderr.flush()
      +        with self.console:
      +            backup = self.serial.baudrate
      +            try:
      +                self.serial.baudrate = int(sys.stdin.readline().strip())
      +            except ValueError as e:
      +                sys.stderr.write('--- ERROR setting baudrate: {} ---\n'.format(e))
      +                self.serial.baudrate = backup
      +            else:
      +                self.dump_port_settings()
      +
      +    def change_port(self):
      +        """Have a conversation with the user to change the serial port"""
      +        with self.console:
      +            try:
      +                port = ask_for_port()
      +            except KeyboardInterrupt:
      +                port = None
      +        if port and port != self.serial.port:
      +            # reader thread needs to be shut down
      +            self._stop_reader()
      +            # save settings
      +            settings = self.serial.getSettingsDict()
      +            try:
      +                new_serial = serial.serial_for_url(port, do_not_open=True)
      +                # restore settings and open
      +                new_serial.applySettingsDict(settings)
      +                new_serial.rts = self.serial.rts
      +                new_serial.dtr = self.serial.dtr
      +                new_serial.open()
      +                new_serial.break_condition = self.serial.break_condition
      +            except Exception as e:
      +                sys.stderr.write('--- ERROR opening new port: {} ---\n'.format(e))
      +                new_serial.close()
      +            else:
      +                self.serial.close()
      +                self.serial = new_serial
      +                sys.stderr.write('--- Port changed to: {} ---\n'.format(self.serial.port))
      +            # and restart the reader thread
      +            self._start_reader()
      +
      +    def suspend_port(self):
      +        """\
      +        open port temporarily, allow reconnect, exit and port change to get
      +        out of the loop
      +        """
      +        # reader thread needs to be shut down
      +        self._stop_reader()
      +        self.serial.close()
      +        sys.stderr.write('\n--- Port closed: {} ---\n'.format(self.serial.port))
      +        do_change_port = False
      +        while not self.serial.is_open:
      +            sys.stderr.write('--- Quit: {exit} | p: port change | any other key to reconnect ---\n'.format(
      +                exit=key_description(self.exit_character)))
      +            k = self.console.getkey()
      +            if k == self.exit_character:
      +                self.stop()             # exit app
      +                break
      +            elif k in 'pP':
      +                do_change_port = True
      +                break
      +            try:
      +                self.serial.open()
      +            except Exception as e:
      +                sys.stderr.write('--- ERROR opening port: {} ---\n'.format(e))
      +        if do_change_port:
      +            self.change_port()
      +        else:
      +            # and restart the reader thread
      +            self._start_reader()
      +            sys.stderr.write('--- Port opened: {} ---\n'.format(self.serial.port))
      +
      +    def get_help_text(self):
      +        """return the help text"""
      +        # help text, starts with blank line!
      +        return """
      +--- pySerial ({version}) - miniterm - help
      +---
      +--- {exit:8} Exit program (alias {menu} Q)
      +--- {menu:8} Menu escape key, followed by:
      +--- Menu keys:
      +---    {menu:7} Send the menu character itself to remote
      +---    {exit:7} Send the exit character itself to remote
      +---    {info:7} Show info
      +---    {upload:7} Upload file (prompt will be shown)
      +---    {repr:7} encoding
      +---    {filter:7} edit filters
      +--- Toggles:
      +---    {rts:7} RTS   {dtr:7} DTR   {brk:7} BREAK
      +---    {echo:7} echo  {eol:7} EOL
      +---
      +--- Port settings ({menu} followed by the following):
      +---    p          change port
      +---    7 8        set data bits
      +---    N E O S M  change parity (None, Even, Odd, Space, Mark)
      +---    1 2 3      set stop bits (1, 2, 1.5)
      +---    b          change baud rate
      +---    x X        disable/enable software flow control
      +---    r R        disable/enable hardware flow control
      +""".format(version=getattr(serial, 'VERSION', 'unknown version'),
      +           exit=key_description(self.exit_character),
      +           menu=key_description(self.menu_character),
      +           rts=key_description('\x12'),
      +           dtr=key_description('\x04'),
      +           brk=key_description('\x02'),
      +           echo=key_description('\x05'),
      +           info=key_description('\x09'),
      +           upload=key_description('\x15'),
      +           repr=key_description('\x01'),
      +           filter=key_description('\x06'),
      +           eol=key_description('\x0c'))
      +
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +# default args can be used to override when calling main() from an other script
      +# e.g to create a miniterm-my-device.py
      +def main(default_port=None, default_baudrate=9600, default_rts=None, default_dtr=None):
      +    """Command line tool, entry point"""
      +
      +    import argparse
      +
      +    parser = argparse.ArgumentParser(
      +        description='Miniterm - A simple terminal program for the serial port.')
      +
      +    parser.add_argument(
      +        'port',
      +        nargs='?',
      +        help='serial port name ("-" to show port list)',
      +        default=default_port)
      +
      +    parser.add_argument(
      +        'baudrate',
      +        nargs='?',
      +        type=int,
      +        help='set baud rate, default: %(default)s',
      +        default=default_baudrate)
      +
      +    group = parser.add_argument_group('port settings')
      +
      +    group.add_argument(
      +        '--parity',
      +        choices=['N', 'E', 'O', 'S', 'M'],
      +        type=lambda c: c.upper(),
      +        help='set parity, one of {N E O S M}, default: N',
      +        default='N')
      +
      +    group.add_argument(
      +        '--rtscts',
      +        action='store_true',
      +        help='enable RTS/CTS flow control (default off)',
      +        default=False)
      +
      +    group.add_argument(
      +        '--xonxoff',
      +        action='store_true',
      +        help='enable software flow control (default off)',
      +        default=False)
      +
      +    group.add_argument(
      +        '--rts',
      +        type=int,
      +        help='set initial RTS line state (possible values: 0, 1)',
      +        default=default_rts)
      +
      +    group.add_argument(
      +        '--dtr',
      +        type=int,
      +        help='set initial DTR line state (possible values: 0, 1)',
      +        default=default_dtr)
      +
      +    group.add_argument(
      +        '--non-exclusive',
      +        dest='exclusive',
      +        action='store_false',
      +        help='disable locking for native ports',
      +        default=True)
      +
      +    group.add_argument(
      +        '--ask',
      +        action='store_true',
      +        help='ask again for port when open fails',
      +        default=False)
      +
      +    group = parser.add_argument_group('data handling')
      +
      +    group.add_argument(
      +        '-e', '--echo',
      +        action='store_true',
      +        help='enable local echo (default off)',
      +        default=False)
      +
      +    group.add_argument(
      +        '--encoding',
      +        dest='serial_port_encoding',
      +        metavar='CODEC',
      +        help='set the encoding for the serial port (e.g. hexlify, Latin1, UTF-8), default: %(default)s',
      +        default='UTF-8')
      +
      +    group.add_argument(
      +        '-f', '--filter',
      +        action='append',
      +        metavar='NAME',
      +        help='add text transformation',
      +        default=[])
      +
      +    group.add_argument(
      +        '--eol',
      +        choices=['CR', 'LF', 'CRLF'],
      +        type=lambda c: c.upper(),
      +        help='end of line mode',
      +        default='CRLF')
      +
      +    group.add_argument(
      +        '--raw',
      +        action='store_true',
      +        help='Do no apply any encodings/transformations',
      +        default=False)
      +
      +    group = parser.add_argument_group('hotkeys')
      +
      +    group.add_argument(
      +        '--exit-char',
      +        type=int,
      +        metavar='NUM',
      +        help='Unicode of special character that is used to exit the application, default: %(default)s',
      +        default=0x1d)  # GS/CTRL+]
      +
      +    group.add_argument(
      +        '--menu-char',
      +        type=int,
      +        metavar='NUM',
      +        help='Unicode code of special character that is used to control miniterm (menu), default: %(default)s',
      +        default=0x14)  # Menu: CTRL+T
      +
      +    group = parser.add_argument_group('diagnostics')
      +
      +    group.add_argument(
      +        '-q', '--quiet',
      +        action='store_true',
      +        help='suppress non-error messages',
      +        default=False)
      +
      +    group.add_argument(
      +        '--develop',
      +        action='store_true',
      +        help='show Python traceback on error',
      +        default=False)
      +
      +    args = parser.parse_args()
      +
      +    if args.menu_char == args.exit_char:
      +        parser.error('--exit-char can not be the same as --menu-char')
      +
      +    if args.filter:
      +        if 'help' in args.filter:
      +            sys.stderr.write('Available filters:\n')
      +            sys.stderr.write('\n'.join(
      +                '{:<10} = {.__doc__}'.format(k, v)
      +                for k, v in sorted(TRANSFORMATIONS.items())))
      +            sys.stderr.write('\n')
      +            sys.exit(1)
      +        filters = args.filter
      +    else:
      +        filters = ['default']
      +
      +    while True:
      +        # no port given on command line -> ask user now
      +        if args.port is None or args.port == '-':
      +            try:
      +                args.port = ask_for_port()
      +            except KeyboardInterrupt:
      +                sys.stderr.write('\n')
      +                parser.error('user aborted and port is not given')
      +            else:
      +                if not args.port:
      +                    parser.error('port is not given')
      +        try:
      +            serial_instance = serial.serial_for_url(
      +                args.port,
      +                args.baudrate,
      +                parity=args.parity,
      +                rtscts=args.rtscts,
      +                xonxoff=args.xonxoff,
      +                do_not_open=True)
      +
      +            if not hasattr(serial_instance, 'cancel_read'):
      +                # enable timeout for alive flag polling if cancel_read is not available
      +                serial_instance.timeout = 1
      +
      +            if args.dtr is not None:
      +                if not args.quiet:
      +                    sys.stderr.write('--- forcing DTR {}\n'.format('active' if args.dtr else 'inactive'))
      +                serial_instance.dtr = args.dtr
      +            if args.rts is not None:
      +                if not args.quiet:
      +                    sys.stderr.write('--- forcing RTS {}\n'.format('active' if args.rts else 'inactive'))
      +                serial_instance.rts = args.rts
      +
      +            if isinstance(serial_instance, serial.Serial):
      +                serial_instance.exclusive = args.exclusive
      +
      +            serial_instance.open()
      +        except serial.SerialException as e:
      +            sys.stderr.write('could not open port {!r}: {}\n'.format(args.port, e))
      +            if args.develop:
      +                raise
      +            if not args.ask:
      +                sys.exit(1)
      +            else:
      +                args.port = '-'
      +        else:
      +            break
      +
      +    miniterm = Miniterm(
      +        serial_instance,
      +        echo=args.echo,
      +        eol=args.eol.lower(),
      +        filters=filters)
      +    miniterm.exit_character = unichr(args.exit_char)
      +    miniterm.menu_character = unichr(args.menu_char)
      +    miniterm.raw = args.raw
      +    miniterm.set_rx_encoding(args.serial_port_encoding)
      +    miniterm.set_tx_encoding(args.serial_port_encoding)
      +
      +    if not args.quiet:
      +        sys.stderr.write('--- Miniterm on {p.name}  {p.baudrate},{p.bytesize},{p.parity},{p.stopbits} ---\n'.format(
      +            p=miniterm.serial))
      +        sys.stderr.write('--- Quit: {} | Menu: {} | Help: {} followed by {} ---\n'.format(
      +            key_description(miniterm.exit_character),
      +            key_description(miniterm.menu_character),
      +            key_description(miniterm.menu_character),
      +            key_description('\x08')))
      +
      +    miniterm.start()
      +    try:
      +        miniterm.join(True)
      +    except KeyboardInterrupt:
      +        pass
      +    if not args.quiet:
      +        sys.stderr.write('\n--- exit ---\n')
      +    miniterm.join()
      +    miniterm.close()
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +if __name__ == '__main__':
      +    main()
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/__init__.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/__init__.py
      new file mode 100644
      index 000000000..e69de29bb
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py
      new file mode 100644
      index 000000000..2e666ca72
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_alt.py
      @@ -0,0 +1,57 @@
      +#! python
      +#
      +# This module implements a special URL handler that allows selecting an
      +# alternate implementation provided by some backends.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +#
      +# URL format:    alt://port[?option[=value][&option[=value]]]
      +# options:
      +# - class=X used class named X instead of Serial
      +#
      +# example:
      +#   use poll based implementation on Posix (Linux):
      +#   python -m serial.tools.miniterm alt:///dev/ttyUSB0?class=PosixPollSerial
      +
      +from __future__ import absolute_import
      +
      +try:
      +    import urlparse
      +except ImportError:
      +    import urllib.parse as urlparse
      +
      +import serial
      +
      +
      +def serial_class_for_url(url):
      +    """extract host and port from an URL string"""
      +    parts = urlparse.urlsplit(url)
      +    if parts.scheme != 'alt':
      +        raise serial.SerialException(
      +            'expected a string in the form "alt://port[?option[=value][&option[=value]]]": '
      +            'not starting with alt:// ({!r})'.format(parts.scheme))
      +    class_name = 'Serial'
      +    try:
      +        for option, values in urlparse.parse_qs(parts.query, True).items():
      +            if option == 'class':
      +                class_name = values[0]
      +            else:
      +                raise ValueError('unknown option: {!r}'.format(option))
      +    except ValueError as e:
      +        raise serial.SerialException(
      +            'expected a string in the form '
      +            '"alt://port[?option[=value][&option[=value]]]": {!r}'.format(e))
      +    if not hasattr(serial, class_name):
      +        raise ValueError('unknown class: {!r}'.format(class_name))
      +    cls = getattr(serial, class_name)
      +    if not issubclass(cls, serial.Serial):
      +        raise ValueError('class {!r} is not an instance of Serial'.format(class_name))
      +    return (''.join([parts.netloc, parts.path]), cls)
      +
      +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      +if __name__ == '__main__':
      +    s = serial.serial_for_url('alt:///dev/ttyS0?class=PosixPollSerial')
      +    print(s)
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py
      new file mode 100644
      index 000000000..44ad4eb44
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_cp2110.py
      @@ -0,0 +1,258 @@
      +#! python
      +#
      +# Backend for Silicon Labs CP2110/4 HID-to-UART devices.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2001-2015 Chris Liechti 
      +# (C) 2019 Google LLC
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +
      +# This backend implements support for HID-to-UART devices manufactured
      +# by Silicon Labs and marketed as CP2110 and CP2114. The
      +# implementation is (mostly) OS-independent and in userland. It relies
      +# on cython-hidapi (https://github.com/trezor/cython-hidapi).
      +
      +# The HID-to-UART protocol implemented by CP2110/4 is described in the
      +# AN434 document from Silicon Labs:
      +# https://www.silabs.com/documents/public/application-notes/AN434-CP2110-4-Interface-Specification.pdf
      +
      +# TODO items:
      +
      +# - rtscts support is configured for hardware flow control, but the
      +#   signaling is missing (AN434 suggests this is done through GPIO).
      +# - Cancelling reads and writes is not supported.
      +# - Baudrate validation is not implemented, as it depends on model and configuration.
      +
      +import struct
      +import threading
      +
      +try:
      +    import urlparse
      +except ImportError:
      +    import urllib.parse as urlparse
      +
      +try:
      +    import Queue
      +except ImportError:
      +    import queue as Queue
      +
      +import hid  # hidapi
      +
      +import serial
      +from serial.serialutil import SerialBase, SerialException, PortNotOpenError, to_bytes, Timeout
      +
      +
      +# Report IDs and related constant
      +_REPORT_GETSET_UART_ENABLE = 0x41
      +_DISABLE_UART = 0x00
      +_ENABLE_UART = 0x01
      +
      +_REPORT_SET_PURGE_FIFOS = 0x43
      +_PURGE_TX_FIFO = 0x01
      +_PURGE_RX_FIFO = 0x02
      +
      +_REPORT_GETSET_UART_CONFIG = 0x50
      +
      +_REPORT_SET_TRANSMIT_LINE_BREAK = 0x51
      +_REPORT_SET_STOP_LINE_BREAK = 0x52
      +
      +
      +class Serial(SerialBase):
      +    # This is not quite correct. AN343 specifies that the minimum
      +    # baudrate is different between CP2110 and CP2114, and it's halved
      +    # when using non-8-bit symbols.
      +    BAUDRATES = (300, 375, 600, 1200, 1800, 2400, 4800, 9600, 19200,
      +                 38400, 57600, 115200, 230400, 460800, 500000, 576000,
      +                 921600, 1000000)
      +
      +    def __init__(self, *args, **kwargs):
      +        self._hid_handle = None
      +        self._read_buffer = None
      +        self._thread = None
      +        super(Serial, self).__init__(*args, **kwargs)
      +
      +    def open(self):
      +        if self._port is None:
      +            raise SerialException("Port must be configured before it can be used.")
      +        if self.is_open:
      +            raise SerialException("Port is already open.")
      +
      +        self._read_buffer = Queue.Queue()
      +
      +        self._hid_handle = hid.device()
      +        try:
      +            portpath = self.from_url(self.portstr)
      +            self._hid_handle.open_path(portpath)
      +        except OSError as msg:
      +            raise SerialException(msg.errno, "could not open port {}: {}".format(self._port, msg))
      +
      +        try:
      +            self._reconfigure_port()
      +        except:
      +            try:
      +                self._hid_handle.close()
      +            except:
      +                pass
      +            self._hid_handle = None
      +            raise
      +        else:
      +            self.is_open = True
      +            self._thread = threading.Thread(target=self._hid_read_loop)
      +            self._thread.setDaemon(True)
      +            self._thread.setName('pySerial CP2110 reader thread for {}'.format(self._port))
      +            self._thread.start()
      +
      +    def from_url(self, url):
      +        parts = urlparse.urlsplit(url)
      +        if parts.scheme != "cp2110":
      +            raise SerialException(
      +                'expected a string in the forms '
      +                '"cp2110:///dev/hidraw9" or "cp2110://0001:0023:00": '
      +                'not starting with cp2110:// {{!r}}'.format(parts.scheme))
      +        if parts.netloc:  # cp2100://BUS:DEVICE:ENDPOINT, for libusb
      +            return parts.netloc.encode('utf-8')
      +        return parts.path.encode('utf-8')
      +
      +    def close(self):
      +        self.is_open = False
      +        if self._thread:
      +            self._thread.join(1)  # read timeout is 0.1
      +            self._thread = None
      +        self._hid_handle.close()
      +        self._hid_handle = None
      +
      +    def _reconfigure_port(self):
      +        parity_value = None
      +        if self._parity == serial.PARITY_NONE:
      +            parity_value = 0x00
      +        elif self._parity == serial.PARITY_ODD:
      +            parity_value = 0x01
      +        elif self._parity == serial.PARITY_EVEN:
      +            parity_value = 0x02
      +        elif self._parity == serial.PARITY_MARK:
      +            parity_value = 0x03
      +        elif self._parity == serial.PARITY_SPACE:
      +            parity_value = 0x04
      +        else:
      +            raise ValueError('Invalid parity: {!r}'.format(self._parity))
      +
      +        if self.rtscts:
      +            flow_control_value = 0x01
      +        else:
      +            flow_control_value = 0x00
      +
      +        data_bits_value = None
      +        if self._bytesize == 5:
      +            data_bits_value = 0x00
      +        elif self._bytesize == 6:
      +            data_bits_value = 0x01
      +        elif self._bytesize == 7:
      +            data_bits_value = 0x02
      +        elif self._bytesize == 8:
      +            data_bits_value = 0x03
      +        else:
      +            raise ValueError('Invalid char len: {!r}'.format(self._bytesize))
      +
      +        stop_bits_value = None
      +        if self._stopbits == serial.STOPBITS_ONE:
      +            stop_bits_value = 0x00
      +        elif self._stopbits == serial.STOPBITS_ONE_POINT_FIVE:
      +            stop_bits_value = 0x01
      +        elif self._stopbits == serial.STOPBITS_TWO:
      +            stop_bits_value = 0x01
      +        else:
      +            raise ValueError('Invalid stop bit specification: {!r}'.format(self._stopbits))
      +
      +        configuration_report = struct.pack(
      +            '>BLBBBB',
      +            _REPORT_GETSET_UART_CONFIG,
      +            self._baudrate,
      +            parity_value,
      +            flow_control_value,
      +            data_bits_value,
      +            stop_bits_value)
      +
      +        self._hid_handle.send_feature_report(configuration_report)
      +
      +        self._hid_handle.send_feature_report(
      +            bytes((_REPORT_GETSET_UART_ENABLE, _ENABLE_UART)))
      +        self._update_break_state()
      +
      +    @property
      +    def in_waiting(self):
      +        return self._read_buffer.qsize()
      +
      +    def reset_input_buffer(self):
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._hid_handle.send_feature_report(
      +            bytes((_REPORT_SET_PURGE_FIFOS, _PURGE_RX_FIFO)))
      +        # empty read buffer
      +        while self._read_buffer.qsize():
      +            self._read_buffer.get(False)
      +
      +    def reset_output_buffer(self):
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        self._hid_handle.send_feature_report(
      +            bytes((_REPORT_SET_PURGE_FIFOS, _PURGE_TX_FIFO)))
      +
      +    def _update_break_state(self):
      +        if not self._hid_handle:
      +            raise PortNotOpenError()
      +
      +        if self._break_state:
      +            self._hid_handle.send_feature_report(
      +                bytes((_REPORT_SET_TRANSMIT_LINE_BREAK, 0)))
      +        else:
      +            # Note that while AN434 states "There are no data bytes in
      +            # the payload other than the Report ID", either hidapi or
      +            # Linux does not seem to send the report otherwise.
      +            self._hid_handle.send_feature_report(
      +                bytes((_REPORT_SET_STOP_LINE_BREAK, 0)))
      +
      +    def read(self, size=1):
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +
      +        data = bytearray()
      +        try:
      +            timeout = Timeout(self._timeout)
      +            while len(data) < size:
      +                if self._thread is None:
      +                    raise SerialException('connection failed (reader thread died)')
      +                buf = self._read_buffer.get(True, timeout.time_left())
      +                if buf is None:
      +                    return bytes(data)
      +                data += buf
      +                if timeout.expired():
      +                    break
      +        except Queue.Empty:  # -> timeout
      +            pass
      +        return bytes(data)
      +
      +    def write(self, data):
      +        if not self.is_open:
      +            raise PortNotOpenError()
      +        data = to_bytes(data)
      +        tx_len = len(data)
      +        while tx_len > 0:
      +            to_be_sent = min(tx_len, 0x3F)
      +            report = to_bytes([to_be_sent]) + data[:to_be_sent]
      +            self._hid_handle.write(report)
      +
      +            data = data[to_be_sent:]
      +            tx_len = len(data)
      +
      +    def _hid_read_loop(self):
      +        try:
      +            while self.is_open:
      +                data = self._hid_handle.read(64, timeout_ms=100)
      +                if not data:
      +                    continue
      +                data_len = data.pop(0)
      +                assert data_len == len(data)
      +                self._read_buffer.put(bytearray(data))
      +        finally:
      +            self._thread = None
      diff --git a/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py
      new file mode 100644
      index 000000000..1a288c945
      --- /dev/null
      +++ b/dependencies/darwin_amd64/python-portable-darwin-3.8.4/lib/python3.8/site-packages/serial/urlhandler/protocol_hwgrep.py
      @@ -0,0 +1,91 @@
      +#! python
      +#
      +# This module implements a special URL handler that uses the port listing to
      +# find ports by searching the string descriptions.
      +#
      +# This file is part of pySerial. https://github.com/pyserial/pyserial
      +# (C) 2011-2015 Chris Liechti 
      +#
      +# SPDX-License-Identifier:    BSD-3-Clause
      +#
      +# URL format:    hwgrep://&
      From 331631234e635b6347ec89924ea854a6f1cd6c82 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Thu, 25 May 2023 10:17:00 +0300 Subject: [PATCH 42/54] Add stock bootloader support --- .../services/BinaryFlashingStrategy/BinaryConfigurator/index.ts | 1 + .../BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index c3d8457c3..5d3db84dc 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -40,6 +40,7 @@ export default class BinaryConfigurator { wifi: 'wifi', etx: 'etx', stlink: 'stlink', + stock: 'stock', }; if (typeof mapped[input] === 'string') { return mapped[input]; diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts index d527991f6..0167d7d48 100644 --- a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts @@ -64,6 +64,8 @@ export default class DeviceDescriptionsLoader { return FlashingMethod.UART; case 'wifi': return FlashingMethod.WIFI; + case 'stock': + return FlashingMethod.Stock_BL; default: throw new Error(`Upload Method ${uploadMethod} Not Recognized!`); } From 2a1949fb2dd1cb2336ecc68355c7a818d9b971b3 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Fri, 26 May 2023 19:55:36 +0300 Subject: [PATCH 43/54] Implement stock bootloader (radio) flashing method. Closes #510 --- .../BinaryConfigurator/index.ts | 28 +++++++---- .../services/BinaryFlashingStrategy/index.ts | 47 +++++++++++++++++-- src/ui/components/DeviceTargetForm/index.tsx | 9 +--- .../FlashingMethodDescription/index.tsx | 16 +------ .../FlashingMethodOptions/index.tsx | 12 ++--- src/ui/views/ConfiguratorView/index.tsx | 1 - 6 files changed, 67 insertions(+), 46 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index 5d3db84dc..065ce6865 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -1,3 +1,4 @@ +import path from 'path'; import { BuildFlashFirmwareParams } from '../../FlashingStrategyLocator/BuildFlashFirmwareParams'; import BuildJobType from '../../../models/enum/BuildJobType'; import UserDefine from '../../../models/UserDefine'; @@ -48,8 +49,15 @@ export default class BinaryConfigurator { return input; } - buildBinaryConfigFlags(params: BuildFlashFirmwareParams): string[][] { + buildBinaryConfigFlags( + firmwareBinaryPath: string, + hardwareDefinitionsPath: string, + params: BuildFlashFirmwareParams + ): string[][] { const flags: string[][] = []; + + flags.push(['--dir', hardwareDefinitionsPath]); + const [manufacturer, subType, device, uploadMethod] = params.target.split('.'); flags.push(['--target', `${manufacturer}.${subType}.${device}`]); @@ -69,6 +77,12 @@ export default class BinaryConfigurator { flags.push(['--flash', this.mapUploadMethod(uploadMethod)]); } + // https://github.com/ExpressLRS/ExpressLRS/pull/2224/files + if (uploadMethod === 'stock') { + flags.push(['--flash', 'stock']); + flags.push(['--out', path.dirname(firmwareBinaryPath)]); + } + if ( params.serialDevice?.length !== undefined && params.serialDevice?.length > 0 @@ -76,6 +90,9 @@ export default class BinaryConfigurator { flags.push(['--port', params.serialDevice]); } + // must be the last one, since it a positional arg + flags.push([firmwareBinaryPath]); + return flags; } @@ -165,23 +182,16 @@ export default class BinaryConfigurator { async run( firmwareSourcePath: string, - hardwareDefinitionsPath: string, flasherPath: string, - firmwareBinaryPath: string, flags: string[][], onUpdate: OnOutputFunc = NoOpFunc ) { this.logger.log('flags', { firmwareSourcePath, flags: maskSensitiveFlags(flags), - hardwareDefinitionsPath, - firmwareBinaryPath, flasherPath, }); - const binaryConfiguratorArgs = [ - ...this.stringifyFlags([...flags, ['--dir', hardwareDefinitionsPath]]), - firmwareBinaryPath, - ]; + const binaryConfiguratorArgs = this.stringifyFlags(flags); return this.python.runPythonScript( flasherPath, binaryConfiguratorArgs, diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 80fe312ad..40ad06aba 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -361,6 +361,38 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return target; } + getFirmwareBinFiles(firmwareSearchPath: string): string[] { + const binaryExtensions = ['.elrs', '.bin']; + + const firmwareBinFiles = fs + .readdirSync(firmwareSearchPath) + .filter((filename) => binaryExtensions.includes(path.extname(filename))); + + return firmwareBinFiles.map((filename) => + path.join(firmwareSearchPath, filename) + ); + } + + searchFirmwareBinPath(target: string, firmwareSearchPath: string): string { + const firmwareBinFiles = this.getFirmwareBinFiles(firmwareSearchPath); + let searchValues = ['backpack.bin', 'firmware.bin']; + if (target.endsWith('.stock')) { + searchValues = ['firmware.elrs', ...searchValues]; + } + const matchedFilenameFile = searchValues.find((searchFile) => { + return ( + firmwareBinFiles.find( + (firmwareBinPath) => searchFile === path.basename(firmwareBinPath) + ) !== undefined + ); + }); + if (matchedFilenameFile !== undefined) { + return path.join(firmwareSearchPath, matchedFilenameFile); + } + + return path.join(firmwareSearchPath, 'firmware.bin'); + } + async buildFlashFirmware( params: BuildFlashFirmwareParams, gitRepository: GitRepository @@ -484,13 +516,14 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildFirmwareStep.BUILDING_FIRMWARE ); - const flags: string[][] = - this.binaryConfigurator.buildBinaryConfigFlags(params); + const flags: string[][] = this.binaryConfigurator.buildBinaryConfigFlags( + firmwareBinaryPath, + hardwareDefinitionsPath, + params + ); const binaryConfiguratorResult = await this.binaryConfigurator.run( firmwareSourcePath, - hardwareDefinitionsPath, flasherPath, - firmwareBinaryPath, flags, (output) => { this.updateLogs(output); @@ -510,9 +543,13 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } if (params.type === BuildJobType.Build) { + const bin = this.searchFirmwareBinPath( + params.target, + path.dirname(firmwareBinaryPath) + ); const canonicalBinPath = await createBinaryCopyWithCanonicalName( params, - firmwareBinaryPath + bin ); return new BuildFlashFirmwareResult( true, diff --git a/src/ui/components/DeviceTargetForm/index.tsx b/src/ui/components/DeviceTargetForm/index.tsx index 8cf273eff..31dabb4b0 100644 --- a/src/ui/components/DeviceTargetForm/index.tsx +++ b/src/ui/components/DeviceTargetForm/index.tsx @@ -8,11 +8,7 @@ import React, { import { Alert, AlertTitle, Box } from '@mui/material'; import { SxProps, Theme } from '@mui/system'; import Omnibox from '../Omnibox'; -import { - Device, - FirmwareVersionDataInput, - Target, -} from '../../gql/generated/types'; +import { Device, Target } from '../../gql/generated/types'; import FlashingMethodOptions, { sortDeviceTargets, } from '../FlashingMethodOptions'; @@ -30,14 +26,12 @@ interface FirmwareVersionCardProps { currentTarget: Target | null; onChange: (data: Target | null) => void; deviceOptions: Device[] | null; - firmwareVersionData: FirmwareVersionDataInput | null; } const DeviceTargetForm: FunctionComponent = ({ onChange, currentTarget, deviceOptions, - firmwareVersionData, }) => { const [currentDevice, setCurrentDevice] = useState(null); @@ -204,7 +198,6 @@ const DeviceTargetForm: FunctionComponent = ({ onChange={onFlashingMethodChange} currentTarget={currentTarget} currentDevice={currentDevice} - firmwareVersionData={firmwareVersionData} /> )} diff --git a/src/ui/components/FlashingMethodDescription/index.tsx b/src/ui/components/FlashingMethodDescription/index.tsx index 9e5f77c43..e54094c0f 100644 --- a/src/ui/components/FlashingMethodDescription/index.tsx +++ b/src/ui/components/FlashingMethodDescription/index.tsx @@ -2,10 +2,7 @@ import React, { FunctionComponent } from 'react'; import { Box, Tooltip } from '@mui/material'; import QuestionIcon from '@mui/icons-material/Help'; import { SxProps, Theme } from '@mui/system'; -import { - FirmwareVersionDataInput, - FlashingMethod, -} from '../../gql/generated/types'; +import { FlashingMethod } from '../../gql/generated/types'; import DocumentationLink from '../DocumentationLink'; const styles: Record> = { @@ -22,12 +19,11 @@ const styles: Record> = { interface FlashingMethodDescriptionProps { flashingMethod: FlashingMethod; deviceWikiUrl: string | null; - firmwareVersionData: FirmwareVersionDataInput | null; } const FlashingMethodDescription: FunctionComponent< FlashingMethodDescriptionProps -> = ({ flashingMethod, deviceWikiUrl, firmwareVersionData }) => { +> = ({ flashingMethod, deviceWikiUrl }) => { const wikiUrl = (deviceWikiUrl ?? '').length > 0 ? deviceWikiUrl : null; const toText = (key: FlashingMethod) => { switch (key) { @@ -52,7 +48,6 @@ const FlashingMethodDescription: FunctionComponent<

    > = { @@ -42,7 +38,6 @@ interface FlashingMethodsListProps { currentTarget: Target | null; currentDevice: Device | null; onChange: (data: Target | null) => void; - firmwareVersionData: FirmwareVersionDataInput | null; } export const sortDeviceTargets = (targets: readonly Target[]): Target[] => { @@ -61,7 +56,7 @@ export const sortDeviceTargets = (targets: readonly Target[]): Target[] => { const FlashingMethodOptions: FunctionComponent = ( props ) => { - const { onChange, currentTarget, currentDevice, firmwareVersionData } = props; + const { onChange, currentTarget, currentDevice } = props; const targetMappingsSorted = useMemo( () => sortDeviceTargets(currentDevice?.targets ?? []), [currentDevice?.targets] @@ -88,7 +83,6 @@ const FlashingMethodOptions: FunctionComponent = ( )} @@ -104,7 +98,7 @@ const FlashingMethodOptions: FunctionComponent = ( /> ); }, - [currentDevice?.wikiUrl, firmwareVersionData] + [currentDevice?.wikiUrl] ); return ( diff --git a/src/ui/views/ConfiguratorView/index.tsx b/src/ui/views/ConfiguratorView/index.tsx index ad1e98b47..6b575f0fe 100644 --- a/src/ui/views/ConfiguratorView/index.tsx +++ b/src/ui/views/ConfiguratorView/index.tsx @@ -799,7 +799,6 @@ const ConfiguratorView: FunctionComponent = (props) => { )} From 28febb51159193ad24ffb62a76b5fdf2eef4d37c Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 4 Jun 2023 22:06:52 +0300 Subject: [PATCH 44/54] fix issues after merge --- devices/diy-900.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/devices/diy-900.json b/devices/diy-900.json index 77a2d22e0..91e3c0cee 100644 --- a/devices/diy-900.json +++ b/devices/diy-900.json @@ -206,9 +206,6 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", - "ARM_CHANNEL", "LOCK_ON_FIRST_CONNECTION", "RCVR_UART_BAUD" ], @@ -231,14 +228,8 @@ "REGULATORY_DOMAIN_FCC_915", "REGULATORY_DOMAIN_IN_866", "BINDING_PHRASE", - "HYBRID_SWITCHES_8", - "ENABLE_TELEMETRY", "TLM_REPORT_INTERVAL_MS", - "NO_SYNC_ON_ARM", - "ARM_CHANNEL", - "FEATURE_OPENTX_SYNC", - "UART_INVERTED", - "USE_DYNAMIC_POWER" + "UART_INVERTED" ], "wikiUrl": "https://www.expresslrs.org/quick-start/receivers/diy900/", "deviceType": "ExpressLRS", From ee5e082621e563ee756e97bcf1c8d0bbbb616c2f Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 4 Jun 2023 22:16:04 +0300 Subject: [PATCH 45/54] Search for the presence of gziped bin --- src/api/src/library/FirmwareBuilder/index.ts | 15 +++++++++++---- .../src/services/BinaryFlashingStrategy/index.ts | 5 +++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/api/src/library/FirmwareBuilder/index.ts b/src/api/src/library/FirmwareBuilder/index.ts index 116382496..93eec92a6 100644 --- a/src/api/src/library/FirmwareBuilder/index.ts +++ b/src/api/src/library/FirmwareBuilder/index.ts @@ -49,13 +49,15 @@ export default class FirmwareBuilder { getFirmwareBinFiles(target: string, firmwarePath: string): string[] { const firmwareBuildPath = this.getFirmwareBuildPath(target, firmwarePath); - const binaryExtensions = ['.elrs', '.bin']; + const binaryExtensions = ['.bin.gz', '.elrs', '.bin']; const firmwareBinFiles = fs .readdirSync(firmwareBuildPath) - .filter((filename) => binaryExtensions.includes(path.extname(filename))); + .filter((filename: string) => + binaryExtensions.includes(path.extname(filename)) + ); - return firmwareBinFiles.map((filename) => + return firmwareBinFiles.map((filename: string) => path.join(firmwareBuildPath, filename) ); } @@ -63,7 +65,12 @@ export default class FirmwareBuilder { getFirmwareBinPath(target: string, firmwarePath: string): string { const firmwareBuildPath = this.getFirmwareBuildPath(target, firmwarePath); const firmwareBinFiles = this.getFirmwareBinFiles(target, firmwarePath); - const searchValues = ['firmware.elrs', 'backpack.bin', 'firmware.bin']; + const searchValues = [ + 'firmware.elrs', + 'firmware.bin.gz', + 'backpack.bin', + 'firmware.bin', + ]; const matchedBinFile = firmwareBinFiles.find((firmwareBinPath) => searchValues.find( diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 40ad06aba..f6d0d3c07 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -127,6 +127,8 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } if ( + gitRepository.url.toLowerCase() === + 'https://github.com/expresslrs/expresslrs'.toLowerCase() && params.source === FirmwareSource.GitTag && semver.lt(params.gitTag, '3.0.0') ) { @@ -379,6 +381,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { if (target.endsWith('.stock')) { searchValues = ['firmware.elrs', ...searchValues]; } + if (target.endsWith('.wifi')) { + searchValues = ['firmware.bin.gz', ...searchValues]; + } const matchedFilenameFile = searchValues.find((searchFile) => { return ( firmwareBinFiles.find( From 7518d1f2ce4790867845538f420e71f1bd5a2555 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 4 Jun 2023 22:35:49 +0300 Subject: [PATCH 46/54] Backpack repository support --- .../DeviceDescriptionsLoader/index.ts | 2 ++ src/api/src/services/BinaryFlashingStrategy/index.ts | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts index 0167d7d48..9f4782120 100644 --- a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts @@ -58,6 +58,8 @@ export default class DeviceDescriptionsLoader { return FlashingMethod.DFU; case 'etx': return FlashingMethod.EdgeTxPassthrough; + case 'passthru': + return FlashingMethod.Passthrough; case 'stlink': return FlashingMethod.STLink; case 'uart': diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index f6d0d3c07..4b6d806c0 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -120,8 +120,10 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { async isCompatible(params: IsCompatibleArgs, gitRepository: GitRepository) { if ( - gitRepository.url.toLowerCase() !== - 'https://github.com/expresslrs/expresslrs'.toLowerCase() + gitRepository.url.toLowerCase() === + 'https://github.com/expresslrs/backpack'.toLowerCase() && + params.source === FirmwareSource.GitTag && + semver.lte(params.gitTag, '1.3.0') ) { return false; } @@ -345,6 +347,10 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { platformioTarget: string, userDefines: UserDefine[] ): Promise { + if (platformioTarget.includes('Backpack')) { + return `${platformioTarget}/firmware.bin`; + } + let regulatoryDomain: 'LBT' | 'FCC' = 'FCC'; const regDomainCE2400 = userDefines.find( ({ key }) => key === UserDefineKey.REGULATORY_DOMAIN_EU_CE_2400 From 3990c96773df90c7386adccee9892b9a5097e232 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sun, 4 Jun 2023 22:54:43 +0300 Subject: [PATCH 47/54] map upload method --- .../services/BinaryFlashingStrategy/BinaryConfigurator/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index 065ce6865..7d88c2870 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -38,6 +38,7 @@ export default class BinaryConfigurator { const mapped: { [n: string]: string } = { uart: 'uart', betaflight: 'bf', + passthru: 'passthru', wifi: 'wifi', etx: 'etx', stlink: 'stlink', From ef97b34b87714f0ee22721e9cfdaa80e20505cd9 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Mon, 5 Jun 2023 10:53:58 +0300 Subject: [PATCH 48/54] Remove AUTO_WIFI_ON_INTERVAL from backpack cloud builds --- .../DeviceDescriptionsLoader/index.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts index 9f4782120..87cd673c6 100644 --- a/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/DeviceDescriptionsLoader/index.ts @@ -294,9 +294,11 @@ export default class DeviceDescriptionsLoader { userDefines.push( targetUserDefinesFactory.build(UserDefineKey.HOME_WIFI_PASSWORD) ); - userDefines.push( - targetUserDefinesFactory.build(UserDefineKey.AUTO_WIFI_ON_INTERVAL) - ); + if (!config.firmware.endsWith('_Backpack')) { + userDefines.push( + targetUserDefinesFactory.build(UserDefineKey.AUTO_WIFI_ON_INTERVAL) + ); + } } if (config.features && config.features.includes('buzzer')) { userDefines.push( From 64f6c6143a3b29d2ee7a584a1331bf4aa03a4a6a Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Tue, 6 Jun 2023 09:40:29 +0300 Subject: [PATCH 49/54] Add support for multiple artifact output. Do not modify cached binaries in place, move them to workdir and modify there. Add support for gziped binaries (.bin.gz). --- .../BinaryConfigurator/index.ts | 44 +++++----- .../services/BinaryFlashingStrategy/index.ts | 86 +++++++++++++------ .../FlashingStrategyLocator/artefacts.ts | 12 ++- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index 7d88c2870..89ab13446 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -1,4 +1,3 @@ -import path from 'path'; import { BuildFlashFirmwareParams } from '../../FlashingStrategyLocator/BuildFlashFirmwareParams'; import BuildJobType from '../../../models/enum/BuildJobType'; import UserDefine from '../../../models/UserDefine'; @@ -34,23 +33,8 @@ export default class BinaryConfigurator { // Map targets.json flashing methods to binary_flash.py // https://github.com/ExpressLRS/ExpressLRS/blame/master/src/python/binary_flash.py - mapUploadMethod(input: string): string { - const mapped: { [n: string]: string } = { - uart: 'uart', - betaflight: 'bf', - passthru: 'passthru', - wifi: 'wifi', - etx: 'etx', - stlink: 'stlink', - stock: 'stock', - }; - if (typeof mapped[input] === 'string') { - return mapped[input]; - } - return input; - } - buildBinaryConfigFlags( + outputDirectory: string, firmwareBinaryPath: string, hardwareDefinitionsPath: string, params: BuildFlashFirmwareParams @@ -78,10 +62,11 @@ export default class BinaryConfigurator { flags.push(['--flash', this.mapUploadMethod(uploadMethod)]); } + flags.push(['--out', outputDirectory]); + // https://github.com/ExpressLRS/ExpressLRS/pull/2224/files if (uploadMethod === 'stock') { flags.push(['--flash', 'stock']); - flags.push(['--out', path.dirname(firmwareBinaryPath)]); } if ( @@ -97,6 +82,22 @@ export default class BinaryConfigurator { return flags; } + mapUploadMethod(input: string): string { + const mapped: { [n: string]: string } = { + uart: 'uart', + betaflight: 'bf', + passthru: 'passthru', + wifi: 'wifi', + etx: 'etx', + stlink: 'stlink', + stock: 'stock', + }; + if (typeof mapped[input] === 'string') { + return mapped[input]; + } + return input; + } + userDefinesToFlags(userDefines: UserDefine[]): string[][] { const flags: string[][] = []; userDefines @@ -182,13 +183,11 @@ export default class BinaryConfigurator { } async run( - firmwareSourcePath: string, flasherPath: string, flags: string[][], onUpdate: OnOutputFunc = NoOpFunc ) { this.logger.log('flags', { - firmwareSourcePath, flags: maskSensitiveFlags(flags), flasherPath, }); @@ -196,10 +195,7 @@ export default class BinaryConfigurator { return this.python.runPythonScript( flasherPath, binaryConfiguratorArgs, - onUpdate, - { - cwd: firmwareSourcePath, - } + onUpdate ); } } diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 4b6d806c0..0aaf3ae0b 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -370,7 +370,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } getFirmwareBinFiles(firmwareSearchPath: string): string[] { - const binaryExtensions = ['.elrs', '.bin']; + const binaryExtensions = ['.elrs', '.bin', '.gz']; const firmwareBinFiles = fs .readdirSync(firmwareSearchPath) @@ -381,15 +381,15 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { ); } - searchFirmwareBinPath(target: string, firmwareSearchPath: string): string { + searchFirmwareBinPath(firmwareSearchPath: string): string { const firmwareBinFiles = this.getFirmwareBinFiles(firmwareSearchPath); - let searchValues = ['backpack.bin', 'firmware.bin']; - if (target.endsWith('.stock')) { - searchValues = ['firmware.elrs', ...searchValues]; - } - if (target.endsWith('.wifi')) { - searchValues = ['firmware.bin.gz', ...searchValues]; - } + const searchValues = [ + 'firmware.elrs', + 'firmware.bin.gz', + 'firmware.bin', + 'backpack.bin.gz', + 'backpack.bin', + ]; const matchedFilenameFile = searchValues.find((searchFile) => { return ( firmwareBinFiles.find( @@ -401,7 +401,22 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { return path.join(firmwareSearchPath, matchedFilenameFile); } - return path.join(firmwareSearchPath, 'firmware.bin'); + throw new Error('failed to find firmware binary path'); + } + + async createWorkingDirectory(target: string): Promise { + return fs.promises.mkdtemp(path.join(os.tmpdir(), `${target}_`)); + } + + async copyFirmwareArtifacts(sourceDir: string, target: string) { + const firmwareBinFiles = this.getFirmwareBinFiles(sourceDir); + const jobs = firmwareBinFiles.map((artifact) => { + return fs.promises.copyFile( + artifact, + path.join(target, path.basename(artifact)) + ); + }); + await Promise.all(jobs); } async buildFlashFirmware( @@ -461,8 +476,9 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } ); - let firmwareBinaryPath = ''; - let hardwareDefinitionsPath = firmwareSourcePath; + let sourceFirmwareBinPath = ''; + let firmwareArtifactsDirPath = ''; + let hardwareDescriptionsPath = firmwareSourcePath; let flasherPath = path.join( firmwareSourcePath, 'python', @@ -484,13 +500,20 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { config.firmware, params.userDefines ); - firmwareBinaryPath = path.join(cacheLocation, cachedBinaryPath); + sourceFirmwareBinPath = path.join(cacheLocation, cachedBinaryPath); const flasherPyzPath = path.join(cacheLocation, 'flasher.pyz'); if (fs.existsSync(flasherPyzPath)) { flasherPath = flasherPyzPath; } - hardwareDefinitionsPath = cacheLocation; + hardwareDescriptionsPath = cacheLocation; + this.logger.log('paths', { + cacheLocation, + cachedBinaryPath, + flasherPyzPath, + sourceFirmwareBinaryPath: sourceFirmwareBinPath, + hardwareDescriptionsPath, + }); } catch (e) { this.logger.log( 'failed to get cached build, reverting to building firmware locally', @@ -500,7 +523,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } ); const target = this.getCompileTarget(config); - firmwareBinaryPath = await this.compileBinary( + sourceFirmwareBinPath = await this.compileBinary( target, firmwareSourcePath, params.userDefinesMode, @@ -510,7 +533,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } } else { const target = this.getCompileTarget(config); - firmwareBinaryPath = await this.compileBinary( + sourceFirmwareBinPath = await this.compileBinary( target, firmwareSourcePath, params.userDefinesMode, @@ -519,7 +542,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { ); } this.logger.log('firmware binaries path', { - firmwareBinaryPath, + firmwareBinaryPath: sourceFirmwareBinPath, }); await this.updateProgress( @@ -527,15 +550,26 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildFirmwareStep.BUILDING_FIRMWARE ); - const flags: string[][] = this.binaryConfigurator.buildBinaryConfigFlags( - firmwareBinaryPath, - hardwareDefinitionsPath, + // In some cases we need to copy multiple artifacts, for example hdzero goggles contains + // boot_app0.bin, bootloader.bin, firmware.bin, partitions.bin files + firmwareArtifactsDirPath = path.dirname(sourceFirmwareBinPath); + + const outputDirectory = await this.createWorkingDirectory(params.target); + await this.copyFirmwareArtifacts( + firmwareArtifactsDirPath, + outputDirectory + ); + const firmwareBinFile = path.join(outputDirectory, 'firmware.bin'); + + const flasherArgs = this.binaryConfigurator.buildBinaryConfigFlags( + outputDirectory, + firmwareBinFile, + hardwareDescriptionsPath, params ); const binaryConfiguratorResult = await this.binaryConfigurator.run( - firmwareSourcePath, flasherPath, - flags, + flasherArgs, (output) => { this.updateLogs(output); } @@ -554,13 +588,13 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } if (params.type === BuildJobType.Build) { - const bin = this.searchFirmwareBinPath( - params.target, - path.dirname(firmwareBinaryPath) + const mainArtifactBinary = this.searchFirmwareBinPath( + path.dirname(firmwareBinFile) ); const canonicalBinPath = await createBinaryCopyWithCanonicalName( params, - bin + mainArtifactBinary, + outputDirectory ); return new BuildFlashFirmwareResult( true, diff --git a/src/api/src/services/FlashingStrategyLocator/artefacts.ts b/src/api/src/services/FlashingStrategyLocator/artefacts.ts index e4792a72e..1d290cfb9 100644 --- a/src/api/src/services/FlashingStrategyLocator/artefacts.ts +++ b/src/api/src/services/FlashingStrategyLocator/artefacts.ts @@ -44,15 +44,19 @@ export const generateFirmwareFileName = ( export const createBinaryCopyWithCanonicalName = async ( params: BuildFlashFirmwareParams, - firmwareBinPath: string + firmwareBinPath: string, + tmpPath = '' ): Promise => { if (fs.existsSync(firmwareBinPath)) { const newFirmwareBaseName = generateFirmwareFileName(params); const firmwareExtension = path.extname(firmwareBinPath); - const tmpPath = await fs.promises.mkdtemp( - path.join(os.tmpdir(), `${params.target}_`) - ); + if (tmpPath.length === 0) { + // eslint-disable-next-line no-param-reassign + tmpPath = await fs.promises.mkdtemp( + path.join(os.tmpdir(), `${params.target}_`) + ); + } // copy with original filename to tmpPath const tmpFirmwareBinPathOriginalName = path.join( From c4ad0241a512bc1b0e2434a5c619860965013bad Mon Sep 17 00:00:00 2001 From: Justin <38869875+justinlampley@users.noreply.github.com> Date: Tue, 6 Jun 2023 17:27:57 -0400 Subject: [PATCH 50/54] Add frequency to device category --- .../TargetsJSONLoader/index.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts b/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts index f73ec5cd4..f81eef461 100644 --- a/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/TargetsJSONLoader/index.ts @@ -13,6 +13,8 @@ export interface DeviceDescription { features?: string[]; prior_target_name: string; overlay?: { [key: string]: string }; + frequency?: number; + type?: string; } export type TargetsJSONRaw = { @@ -61,9 +63,23 @@ export class TargetsJSONLoader { Object.keys(configs).forEach((deviceDescriptionKey) => { const id = `${categoryKey}.${subTypeKey}.${deviceDescriptionKey}`; const config = configs[deviceDescriptionKey]; + + const [type, frequency] = subTypeKey.split('_'); + config.type = type.toUpperCase(); + const frequencyNumber = parseInt(frequency, 10); + + let category = categoryName; + + if (frequencyNumber) { + config.frequency = frequencyNumber; + category = `${categoryName} ${this.frequencyFormatter( + config.frequency + )}`; + } + if (this.validConfig(id, config)) { result[id] = { - category: categoryName, + category, config, }; } @@ -106,4 +122,11 @@ export class TargetsJSONLoader { } return valid; } + + private frequencyFormatter(frequency: number) { + if (frequency >= 1000) { + return `${(frequency / 1000).toFixed(1)} GHz`; + } + return `${frequency} MHz`; + } } From 6e0a4a1a32bcd2ee001fcf46463f9310a35c12f0 Mon Sep 17 00:00:00 2001 From: Paul Kendall Date: Tue, 6 Jun 2023 14:18:57 +1200 Subject: [PATCH 51/54] Correctly call the flasher for build-only "flashing" --- .../BinaryConfigurator/index.ts | 20 ++--- .../services/BinaryFlashingStrategy/index.ts | 75 ++++++++++--------- 2 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index 89ab13446..e6cdbe4c3 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -55,20 +55,20 @@ export default class BinaryConfigurator { flags.push(['--force']); } - if ( + // https://github.com/ExpressLRS/ExpressLRS/pull/2224/files + if (uploadMethod === 'stock') { + flags.push(['--flash', 'stock']); + flags.push(['--out', outputDirectory]); + } else if (params.type === BuildJobType.Build && outputDirectory !== '') { + flags.push(['--flash', 'dir']); + flags.push(['--out', outputDirectory]); + } else if ( params.type === BuildJobType.BuildAndFlash || params.type === BuildJobType.ForceFlash ) { flags.push(['--flash', this.mapUploadMethod(uploadMethod)]); } - flags.push(['--out', outputDirectory]); - - // https://github.com/ExpressLRS/ExpressLRS/pull/2224/files - if (uploadMethod === 'stock') { - flags.push(['--flash', 'stock']); - } - if ( params.serialDevice?.length !== undefined && params.serialDevice?.length > 0 @@ -77,7 +77,9 @@ export default class BinaryConfigurator { } // must be the last one, since it a positional arg - flags.push([firmwareBinaryPath]); + if (firmwareBinaryPath !== '') { + flags.push([firmwareBinaryPath]); + } return flags; } diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 0aaf3ae0b..47c858325 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -478,12 +478,22 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { let sourceFirmwareBinPath = ''; let firmwareArtifactsDirPath = ''; + let outputDirectory = ''; + let firmwareBinFile = ''; let hardwareDescriptionsPath = firmwareSourcePath; let flasherPath = path.join( firmwareSourcePath, 'python', 'binary_configurator.py' ); + + if ( + params.type === BuildJobType.Build || + params.target.endsWith('.stock') + ) { + outputDirectory = await this.createWorkingDirectory(params.target); + } + if (this.isRequestCompatibleWithCache(params)) { const currentCommitHash = await this.getCurrentSourceCommit( gitRepositoryUrl @@ -522,16 +532,10 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { currentCommitHash, } ); - const target = this.getCompileTarget(config); - sourceFirmwareBinPath = await this.compileBinary( - target, - firmwareSourcePath, - params.userDefinesMode, - params.userDefinesTxt, - params.userDefines - ); } - } else { + } + + if (hardwareDescriptionsPath === firmwareSourcePath) { const target = this.getCompileTarget(config); sourceFirmwareBinPath = await this.compileBinary( target, @@ -540,6 +544,15 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { params.userDefinesTxt, params.userDefines ); + // In some cases we need to copy multiple artifacts, for example hdzero goggles contains + // boot_app0.bin, bootloader.bin, firmware.bin, partitions.bin files + firmwareArtifactsDirPath = path.dirname(sourceFirmwareBinPath); + + await this.copyFirmwareArtifacts( + firmwareArtifactsDirPath, + outputDirectory + ); + firmwareBinFile = path.join(outputDirectory, 'firmware.bin'); } this.logger.log('firmware binaries path', { firmwareBinaryPath: sourceFirmwareBinPath, @@ -550,23 +563,13 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { BuildFirmwareStep.BUILDING_FIRMWARE ); - // In some cases we need to copy multiple artifacts, for example hdzero goggles contains - // boot_app0.bin, bootloader.bin, firmware.bin, partitions.bin files - firmwareArtifactsDirPath = path.dirname(sourceFirmwareBinPath); - - const outputDirectory = await this.createWorkingDirectory(params.target); - await this.copyFirmwareArtifacts( - firmwareArtifactsDirPath, - outputDirectory - ); - const firmwareBinFile = path.join(outputDirectory, 'firmware.bin'); - - const flasherArgs = this.binaryConfigurator.buildBinaryConfigFlags( - outputDirectory, - firmwareBinFile, - hardwareDescriptionsPath, - params - ); + const flasherArgs: string[][] = + this.binaryConfigurator.buildBinaryConfigFlags( + outputDirectory, + firmwareBinFile, + hardwareDescriptionsPath, + params + ); const binaryConfiguratorResult = await this.binaryConfigurator.run( flasherPath, flasherArgs, @@ -588,14 +591,18 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } if (params.type === BuildJobType.Build) { - const mainArtifactBinary = this.searchFirmwareBinPath( - path.dirname(firmwareBinFile) - ); - const canonicalBinPath = await createBinaryCopyWithCanonicalName( - params, - mainArtifactBinary, - outputDirectory - ); + let mainArtifactBinary = this.searchFirmwareBinPath(outputDirectory); + let canonicalBinPath = mainArtifactBinary; + if (firmwareBinFile !== '') { + mainArtifactBinary = this.searchFirmwareBinPath( + path.dirname(firmwareBinFile) + ); + canonicalBinPath = await createBinaryCopyWithCanonicalName( + params, + mainArtifactBinary, + outputDirectory + ); + } return new BuildFlashFirmwareResult( true, undefined, From b11ca69cd07754dafd6147c71a211673d6ee89c7 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 10 Jun 2023 11:41:31 +0300 Subject: [PATCH 52/54] Fix manual user defines --- .../BinaryConfigurator/index.ts | 2 +- .../src/services/BinaryFlashingStrategy/index.ts | 16 +++++----------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts index e6cdbe4c3..37886bb95 100644 --- a/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/BinaryConfigurator/index.ts @@ -59,7 +59,7 @@ export default class BinaryConfigurator { if (uploadMethod === 'stock') { flags.push(['--flash', 'stock']); flags.push(['--out', outputDirectory]); - } else if (params.type === BuildJobType.Build && outputDirectory !== '') { + } else if (params.type === BuildJobType.Build) { flags.push(['--flash', 'dir']); flags.push(['--out', outputDirectory]); } else if ( diff --git a/src/api/src/services/BinaryFlashingStrategy/index.ts b/src/api/src/services/BinaryFlashingStrategy/index.ts index 47c858325..8be493286 100644 --- a/src/api/src/services/BinaryFlashingStrategy/index.ts +++ b/src/api/src/services/BinaryFlashingStrategy/index.ts @@ -478,7 +478,8 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { let sourceFirmwareBinPath = ''; let firmwareArtifactsDirPath = ''; - let outputDirectory = ''; + const workingDirectory = await this.createWorkingDirectory(params.target); + const outputDirectory = await this.createWorkingDirectory(params.target); let firmwareBinFile = ''; let hardwareDescriptionsPath = firmwareSourcePath; let flasherPath = path.join( @@ -487,13 +488,6 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { 'binary_configurator.py' ); - if ( - params.type === BuildJobType.Build || - params.target.endsWith('.stock') - ) { - outputDirectory = await this.createWorkingDirectory(params.target); - } - if (this.isRequestCompatibleWithCache(params)) { const currentCommitHash = await this.getCurrentSourceCommit( gitRepositoryUrl @@ -535,6 +529,7 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { } } + // we were not able to find cloud binaries, so we will build them on the spot if (hardwareDescriptionsPath === firmwareSourcePath) { const target = this.getCompileTarget(config); sourceFirmwareBinPath = await this.compileBinary( @@ -547,12 +542,11 @@ export default class BinaryFlashingStrategyService implements FlashingStrategy { // In some cases we need to copy multiple artifacts, for example hdzero goggles contains // boot_app0.bin, bootloader.bin, firmware.bin, partitions.bin files firmwareArtifactsDirPath = path.dirname(sourceFirmwareBinPath); - await this.copyFirmwareArtifacts( firmwareArtifactsDirPath, - outputDirectory + workingDirectory ); - firmwareBinFile = path.join(outputDirectory, 'firmware.bin'); + firmwareBinFile = path.join(workingDirectory, 'firmware.bin'); } this.logger.log('firmware binaries path', { firmwareBinaryPath: sourceFirmwareBinPath, From 4f068d28a95979c31c34a5c00357469e96060a18 Mon Sep 17 00:00:00 2001 From: Julius Jurgelenas Date: Sat, 10 Jun 2023 11:41:53 +0300 Subject: [PATCH 53/54] Hide Build button for non Wifi/Sock_Bl build methods --- src/ui/views/ConfiguratorView/index.tsx | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ui/views/ConfiguratorView/index.tsx b/src/ui/views/ConfiguratorView/index.tsx index 6b575f0fe..12f52c4ae 100644 --- a/src/ui/views/ConfiguratorView/index.tsx +++ b/src/ui/views/ConfiguratorView/index.tsx @@ -901,14 +901,23 @@ const ConfiguratorView: FunctionComponent = (props) => { onChange={onWifiDevice} /> )} - + {deviceTarget?.flashingMethod !== FlashingMethod.UART && + deviceTarget?.flashingMethod !== FlashingMethod.Passthrough && + deviceTarget?.flashingMethod !== + FlashingMethod.EdgeTxPassthrough && + deviceTarget?.flashingMethod !== FlashingMethod.DFU && + deviceTarget?.flashingMethod !== FlashingMethod.STLink && + deviceTarget?.flashingMethod !== + FlashingMethod.BetaflightPassthrough && ( + + )} {deviceTarget?.flashingMethod !== FlashingMethod.Stock_BL && ( Date: Sat, 10 Jun 2023 11:42:07 +0300 Subject: [PATCH 54/54] Bump to 1.6.0 --- src/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/package.json b/src/package.json index cd31519e5..2b875140c 100644 --- a/src/package.json +++ b/src/package.json @@ -1,7 +1,7 @@ { "name": "expresslrs-configurator", "productName": "ExpressLRS Configurator", - "version": "1.5.10", + "version": "1.6.0", "description": "Cross platform configuration tool for the ExpressLRS firmware", "main": "./main.prod.js", "author": {